From f3c2ed8aabcba9fbf4bf469dc017cbd19ef190f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 6 Mar 2024 15:54:10 -0500 Subject: [PATCH 1/3] bootstrap devolutions agent --- Cargo.toml | 1 + devolutions-agent/.gitignore | 2 + devolutions-agent/Cargo.toml | 14 ++++ devolutions-agent/build.rs | 84 ++++++++++++++++++++++ devolutions-agent/src/main.rs | 127 ++++++++++++++++++++++++++++++++++ 5 files changed, 228 insertions(+) create mode 100644 devolutions-agent/.gitignore create mode 100644 devolutions-agent/Cargo.toml create mode 100644 devolutions-agent/build.rs create mode 100644 devolutions-agent/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 971343188..fde435bcc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] resolver = "2" members = [ + "devolutions-agent", "devolutions-gateway", "jetsocat", "crates/*", diff --git a/devolutions-agent/.gitignore b/devolutions-agent/.gitignore new file mode 100644 index 000000000..2c96eb1b6 --- /dev/null +++ b/devolutions-agent/.gitignore @@ -0,0 +1,2 @@ +target/ +Cargo.lock diff --git a/devolutions-agent/Cargo.toml b/devolutions-agent/Cargo.toml new file mode 100644 index 000000000..bb1f072c4 --- /dev/null +++ b/devolutions-agent/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "devolutions-agent" +version = "2024.1.2" +edition = "2021" +license = "MIT/Apache-2.0" +authors = ["Marc-André Moreau "] +build = "build.rs" + +[dependencies] +ctrlc = "3.1" +ceviche = "0.5" + +[target.'cfg(windows)'.build-dependencies] +embed-resource = "2.4" diff --git a/devolutions-agent/build.rs b/devolutions-agent/build.rs new file mode 100644 index 000000000..a15066ead --- /dev/null +++ b/devolutions-agent/build.rs @@ -0,0 +1,84 @@ +fn main() { + #[cfg(target_os = "windows")] + win::embed_version_rc(); +} + +#[cfg(target_os = "windows")] +mod win { + use std::{env, fs}; + + pub(super) fn embed_version_rc() { + let out_dir = env::var("OUT_DIR").unwrap(); + let version_rc_file = format!("{}/version.rc", out_dir); + let version_rc_data = generate_version_rc(); + fs::write(&version_rc_file, version_rc_data).unwrap(); + + embed_resource::compile(&version_rc_file, embed_resource::NONE); + } + + fn generate_version_rc() -> String { + let output_name = "DevolutionsAgent"; + let filename = format!("{}.exe", output_name); + let company_name = "Devolutions Inc."; + let legal_copyright = format!("Copyright 2020-2024 {}", company_name); + + let version_number = env::var("CARGO_PKG_VERSION").unwrap() + ".0"; + let version_commas = version_number.replace('.', ","); + let file_description = output_name; + let file_version = version_number.clone(); + let internal_name = filename.clone(); + let original_filename = filename; + let product_name = output_name; + let product_version = version_number; + let vs_file_version = version_commas.clone(); + let vs_product_version = version_commas; + + let version_rc = format!( + r#"#include +VS_VERSION_INFO VERSIONINFO + FILEVERSION {vs_file_version} + PRODUCTVERSION {vs_product_version} + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "{company_name}" + VALUE "FileDescription", "{file_description}" + VALUE "FileVersion", "{file_version}" + VALUE "InternalName", "{internal_name}" + VALUE "LegalCopyright", "{legal_copyright}" + VALUE "OriginalFilename", "{original_filename}" + VALUE "ProductName", "{product_name}" + VALUE "ProductVersion", "{product_version}" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END"#, + vs_file_version = vs_file_version, + vs_product_version = vs_product_version, + company_name = company_name, + file_description = file_description, + file_version = file_version, + internal_name = internal_name, + legal_copyright = legal_copyright, + original_filename = original_filename, + product_name = product_name, + product_version = product_version + ); + + version_rc + } +} diff --git a/devolutions-agent/src/main.rs b/devolutions-agent/src/main.rs new file mode 100644 index 000000000..7e5668db4 --- /dev/null +++ b/devolutions-agent/src/main.rs @@ -0,0 +1,127 @@ +use std::env; +use std::sync::mpsc; + +use ceviche::controller::*; +use ceviche::{Service, ServiceEvent}; + +pub const SERVICE_NAME: &str = "devolutions-agent"; +pub const DISPLAY_NAME: &str = "Devolutions Agent"; +pub const DESCRIPTION: &str = "Devolutions Agent service"; +pub const COMPANY_NAME: &str = "Devolutions"; + +pub struct AgentService { + pub service_name: String, + pub display_name: String, + pub description: String, + pub company_name: String, +} + +impl AgentService { + pub fn load() -> Option { + Some(AgentService { + service_name: SERVICE_NAME.to_string(), + display_name: DISPLAY_NAME.to_string(), + description: DESCRIPTION.to_string(), + company_name: COMPANY_NAME.to_string(), + }) + } + + pub fn get_service_name(&self) -> &str { + self.service_name.as_str() + } + + pub fn get_display_name(&self) -> &str { + self.display_name.as_str() + } + + pub fn get_description(&self) -> &str { + self.service_name.as_str() + } + + pub fn get_company_name(&self) -> &str { + self.company_name.as_str() + } + + pub fn start(&self) {} + + pub fn stop(&self) {} +} + +enum AgentServiceEvent {} + +fn agent_service_main( + rx: mpsc::Receiver>, + _tx: mpsc::Sender>, + _args: Vec, + _standalone_mode: bool, +) -> u32 { + let service = AgentService::load().expect("unable to load agent"); + + service.start(); + + loop { + if let Ok(control_code) = rx.recv() { + match control_code { + ServiceEvent::Stop => { + service.stop(); + break; + } + _ => (), + } + } + } + + 0 +} + +Service!("agent", agent_service_main); + +fn main() { + let service = AgentService::load().expect("unable to load agent service"); + let mut controller = Controller::new( + service.get_service_name(), + service.get_display_name(), + service.get_description(), + ); + + if let Some(cmd) = env::args().nth(1) { + match cmd.as_str() { + "create" => { + if let Err(e) = controller.create() { + println!("{}", e); + } + } + "delete" => { + if let Err(e) = controller.delete() { + println!("{}", e); + } + } + "start" => { + if let Err(e) = controller.start() { + println!("{}", e); + } + } + "stop" => { + if let Err(e) = controller.stop() { + println!("{}", e); + } + } + "run" => { + let (tx, rx) = mpsc::channel(); + let _tx = tx.clone(); + + ctrlc::set_handler(move || { + let _ = tx.send(ServiceEvent::Stop); + }) + .expect("Failed to register Ctrl-C handler"); + + agent_service_main(rx, _tx, vec![], true); + } + _ => { + println!("invalid command: {}", cmd); + } + } + } else { + let _result = controller.register(service_main_wrapper); + } +} From 35a87bd3c5dda3657eecc7c804d257ac7c66addc Mon Sep 17 00:00:00 2001 From: Vladyslav Nikonov Date: Sat, 1 Jun 2024 12:26:30 +0300 Subject: [PATCH 2/3] feat(agent): Devolutions Agent bootstrap --- .github/workflows/ci.yml | 123 +++- .github/workflows/package.yml | 51 +- Cargo.lock | 117 +++- ci/tlk.ps1 | 304 +++++++--- crates/devolutions-log/Cargo.toml | 28 + crates/devolutions-log/README.md | 2 + crates/devolutions-log/src/lib.rs | 215 +++++++ devolutions-agent/Cargo.toml | 31 +- devolutions-agent/src/config.rs | 265 +++++++++ devolutions-agent/src/log.rs | 9 + devolutions-agent/src/main.rs | 102 ++-- devolutions-agent/src/service.rs | 180 ++++++ devolutions-gateway/Cargo.toml | 1 + devolutions-gateway/src/api/diagnostics.rs | 3 +- devolutions-gateway/src/config.rs | 16 + devolutions-gateway/src/log.rs | 213 +------ devolutions-gateway/src/service.rs | 13 +- package/AgentLinux/Dockerfile | 16 + package/AgentLinux/agent/debian/copyright | 8 + package/AgentLinux/agent/debian/postinst | 16 + package/AgentLinux/agent/debian/postrm | 17 + package/AgentLinux/agent/debian/prerm | 19 + package/AgentLinux/agent/template/control | 13 + package/AgentLinux/agent/template/rules | 14 + package/AgentLinux/template/changelog | 6 + package/AgentLinux/template/copyright | 9 + package/AgentWindowsManaged/.editorconfig | 7 + package/AgentWindowsManaged/.gitignore | 5 + package/AgentWindowsManaged/Actions/Buffer.cs | 32 + .../Actions/CustomActions.cs | 558 ++++++++++++++++++ .../Actions/GatewayActions.cs | 234 ++++++++ .../Actions/LogDelegate.cs | 43 ++ .../AgentWindowsManaged/Actions/Service.cs | 251 ++++++++ .../Actions/ServiceManager.cs | 38 ++ package/AgentWindowsManaged/Actions/WinAPI.cs | 307 ++++++++++ package/AgentWindowsManaged/App.config | 6 + .../Controls/ElevatedButton.cs | 44 ++ .../DevolutionsAgent.csproj | 83 +++ .../DevolutionsAgent.csproj.user | 30 + .../AgentWindowsManaged/DevolutionsAgent.sln | 25 + .../Dialogs/AgentDialog.cs | 83 +++ .../Dialogs/ExitDialog.Designer.cs | 239 ++++++++ .../AgentWindowsManaged/Dialogs/ExitDialog.cs | 62 ++ .../Dialogs/ExitDialog.resx | 120 ++++ .../Dialogs/InstallDirDialog.Designer.cs | 315 ++++++++++ .../Dialogs/InstallDirDialog.cs | 90 +++ .../Dialogs/InstallDirDialog.resx | 123 ++++ .../Dialogs/MaintenanceTypeDialog.Designer.cs | 388 ++++++++++++ .../Dialogs/MaintenanceTypeDialog.cs | 70 +++ .../Dialogs/MaintenanceTypeDialog.resx | 120 ++++ .../Dialogs/ProgressDialog.Designer.cs | 292 +++++++++ .../Dialogs/ProgressDialog.cs | 200 +++++++ .../Dialogs/ProgressDialog.resx | 120 ++++ .../Dialogs/VerifyReadyDialog.Designer.cs | 274 +++++++++ .../Dialogs/VerifyReadyDialog.cs | 68 +++ .../Dialogs/VerifyReadyDialog.resx | 123 ++++ .../Dialogs/WelcomeDialog.Designer.cs | 230 ++++++++ .../Dialogs/WelcomeDialog.cs | 32 + .../Dialogs/WelcomeDialog.resx | 120 ++++ package/AgentWindowsManaged/Dialogs/Wizard.cs | 45 ++ .../AgentWindowsManaged/Helpers/AppSearch.cs | 15 + .../AgentWindowsManaged/Helpers/Debouncer.cs | 75 +++ .../Helpers/DisplayEnum.cs | 58 ++ .../Helpers/Localization.cs | 45 ++ .../AgentWindowsManaged/Helpers/StockIcon.cs | 47 ++ .../AgentWindowsManaged/Helpers/Validation.cs | 23 + package/AgentWindowsManaged/Program.cs | 364 ++++++++++++ .../Properties/AgentProperties.cs | 41 ++ .../Properties/AgentProperties.g.cs | 314 ++++++++++ .../Properties/AgentProperties.g.tt | 217 +++++++ .../Properties/WixProperty.cs | 114 ++++ package/AgentWindowsManaged/README.md | 23 + .../Resources/DevolutionsAgent.ico | Bin 0 -> 112498 bytes .../Resources/DevolutionsAgent_en-us.wxl | 52 ++ .../Resources/DevolutionsAgent_fr-fr.wxl | 60 ++ .../AgentWindowsManaged/Resources/Includes.cs | 55 ++ .../Resources/Newtonsoft.Json.dll | Bin 0 -> 711952 bytes .../Resources/Strings.g.cs | 148 +++++ .../Resources/Strings.g.tt | 213 +++++++ .../Resources/Strings_en-US.json | 171 ++++++ .../Resources/Strings_fr-FR.json | 201 +++++++ .../Resources/WixUIBanner.jpg | Bin 0 -> 5411 bytes .../Resources/WixUIDialog.jpg | Bin 0 -> 20983 bytes 83 files changed, 8442 insertions(+), 362 deletions(-) create mode 100644 crates/devolutions-log/Cargo.toml create mode 100644 crates/devolutions-log/README.md create mode 100644 crates/devolutions-log/src/lib.rs create mode 100644 devolutions-agent/src/config.rs create mode 100644 devolutions-agent/src/log.rs create mode 100644 devolutions-agent/src/service.rs create mode 100644 package/AgentLinux/Dockerfile create mode 100644 package/AgentLinux/agent/debian/copyright create mode 100644 package/AgentLinux/agent/debian/postinst create mode 100644 package/AgentLinux/agent/debian/postrm create mode 100644 package/AgentLinux/agent/debian/prerm create mode 100644 package/AgentLinux/agent/template/control create mode 100644 package/AgentLinux/agent/template/rules create mode 100644 package/AgentLinux/template/changelog create mode 100644 package/AgentLinux/template/copyright create mode 100644 package/AgentWindowsManaged/.editorconfig create mode 100644 package/AgentWindowsManaged/.gitignore create mode 100644 package/AgentWindowsManaged/Actions/Buffer.cs create mode 100644 package/AgentWindowsManaged/Actions/CustomActions.cs create mode 100644 package/AgentWindowsManaged/Actions/GatewayActions.cs create mode 100644 package/AgentWindowsManaged/Actions/LogDelegate.cs create mode 100644 package/AgentWindowsManaged/Actions/Service.cs create mode 100644 package/AgentWindowsManaged/Actions/ServiceManager.cs create mode 100644 package/AgentWindowsManaged/Actions/WinAPI.cs create mode 100644 package/AgentWindowsManaged/App.config create mode 100644 package/AgentWindowsManaged/Controls/ElevatedButton.cs create mode 100644 package/AgentWindowsManaged/DevolutionsAgent.csproj create mode 100644 package/AgentWindowsManaged/DevolutionsAgent.csproj.user create mode 100644 package/AgentWindowsManaged/DevolutionsAgent.sln create mode 100644 package/AgentWindowsManaged/Dialogs/AgentDialog.cs create mode 100644 package/AgentWindowsManaged/Dialogs/ExitDialog.Designer.cs create mode 100644 package/AgentWindowsManaged/Dialogs/ExitDialog.cs create mode 100644 package/AgentWindowsManaged/Dialogs/ExitDialog.resx create mode 100644 package/AgentWindowsManaged/Dialogs/InstallDirDialog.Designer.cs create mode 100644 package/AgentWindowsManaged/Dialogs/InstallDirDialog.cs create mode 100644 package/AgentWindowsManaged/Dialogs/InstallDirDialog.resx create mode 100644 package/AgentWindowsManaged/Dialogs/MaintenanceTypeDialog.Designer.cs create mode 100644 package/AgentWindowsManaged/Dialogs/MaintenanceTypeDialog.cs create mode 100644 package/AgentWindowsManaged/Dialogs/MaintenanceTypeDialog.resx create mode 100644 package/AgentWindowsManaged/Dialogs/ProgressDialog.Designer.cs create mode 100644 package/AgentWindowsManaged/Dialogs/ProgressDialog.cs create mode 100644 package/AgentWindowsManaged/Dialogs/ProgressDialog.resx create mode 100644 package/AgentWindowsManaged/Dialogs/VerifyReadyDialog.Designer.cs create mode 100644 package/AgentWindowsManaged/Dialogs/VerifyReadyDialog.cs create mode 100644 package/AgentWindowsManaged/Dialogs/VerifyReadyDialog.resx create mode 100644 package/AgentWindowsManaged/Dialogs/WelcomeDialog.Designer.cs create mode 100644 package/AgentWindowsManaged/Dialogs/WelcomeDialog.cs create mode 100644 package/AgentWindowsManaged/Dialogs/WelcomeDialog.resx create mode 100644 package/AgentWindowsManaged/Dialogs/Wizard.cs create mode 100644 package/AgentWindowsManaged/Helpers/AppSearch.cs create mode 100644 package/AgentWindowsManaged/Helpers/Debouncer.cs create mode 100644 package/AgentWindowsManaged/Helpers/DisplayEnum.cs create mode 100644 package/AgentWindowsManaged/Helpers/Localization.cs create mode 100644 package/AgentWindowsManaged/Helpers/StockIcon.cs create mode 100644 package/AgentWindowsManaged/Helpers/Validation.cs create mode 100644 package/AgentWindowsManaged/Program.cs create mode 100644 package/AgentWindowsManaged/Properties/AgentProperties.cs create mode 100644 package/AgentWindowsManaged/Properties/AgentProperties.g.cs create mode 100644 package/AgentWindowsManaged/Properties/AgentProperties.g.tt create mode 100644 package/AgentWindowsManaged/Properties/WixProperty.cs create mode 100644 package/AgentWindowsManaged/README.md create mode 100644 package/AgentWindowsManaged/Resources/DevolutionsAgent.ico create mode 100644 package/AgentWindowsManaged/Resources/DevolutionsAgent_en-us.wxl create mode 100644 package/AgentWindowsManaged/Resources/DevolutionsAgent_fr-fr.wxl create mode 100644 package/AgentWindowsManaged/Resources/Includes.cs create mode 100644 package/AgentWindowsManaged/Resources/Newtonsoft.Json.dll create mode 100644 package/AgentWindowsManaged/Resources/Strings.g.cs create mode 100644 package/AgentWindowsManaged/Resources/Strings.g.tt create mode 100644 package/AgentWindowsManaged/Resources/Strings_en-US.json create mode 100644 package/AgentWindowsManaged/Resources/Strings_fr-FR.json create mode 100644 package/AgentWindowsManaged/Resources/WixUIBanner.jpg create mode 100644 package/AgentWindowsManaged/Resources/WixUIDialog.jpg diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a06f40b97..6a8e6e298 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,7 @@ jobs: rust-profile: ${{ steps.rust-profile.outputs.rust-profile }} jetsocat-build-matrix: ${{ steps.setup-matrix.outputs.jetsocat-build-matrix }} gateway-build-matrix: ${{ steps.setup-matrix.outputs.gateway-build-matrix }} + agent-build-matrix: ${{ steps.setup-matrix.outputs.agent-build-matrix }} steps: - name: Setup matrix @@ -51,6 +52,9 @@ jobs: $GatewayMatrix = ConvertTo-JSON $Jobs -Compress echo "gateway-build-matrix=$GatewayMatrix" >> $Env:GITHUB_OUTPUT + $AgentMatrix = ConvertTo-JSON $Jobs -Compress + echo "agent-build-matrix=$AgentMatrix" >> $Env:GITHUB_OUTPUT + $Jobs = @() $Platforms += 'macos' @@ -211,7 +215,7 @@ jobs: $Env:JETSOCAT_EXECUTABLE = Join-Path $TargetOutputPath $ExecutableFileName $Env:CARGO_PACKAGE = "jetsocat" - ./ci/tlk.ps1 build -Platform ${{ matrix.os }} -Architecture ${{ matrix.arch }} -CargoProfile ${{ needs.preflight.outputs.rust-profile }} + ./ci/tlk.ps1 build -Product jetsocat -Platform ${{ matrix.os }} -Architecture ${{ matrix.arch }} -CargoProfile ${{ needs.preflight.outputs.rust-profile }} echo "staging-path=$StagingPath" >> $Env:GITHUB_OUTPUT @@ -402,7 +406,7 @@ jobs: TARGET_OUTPUT_PATH: ${{ steps.load-variables.outputs.target-output-path }} DGATEWAY_EXECUTABLE: ${{ steps.load-variables.outputs.dgateway-executable }} CARGO_PACKAGE: devolutions-gateway - run: ./ci/tlk.ps1 build -Platform ${{ matrix.os }} -Architecture ${{ matrix.arch }} -CargoProfile ${{ needs.preflight.outputs.rust-profile }} + run: ./ci/tlk.ps1 build -Product gateway -Platform ${{ matrix.os }} -Architecture ${{ matrix.arch }} -CargoProfile ${{ needs.preflight.outputs.rust-profile }} - name: Build PowerShell module if: matrix.os == 'windows' @@ -426,8 +430,8 @@ jobs: $Env:DGATEWAY_PSMODULE_PATH = "${{ steps.load-variables.outputs.dgateway-psmodule-output-path }}" } $Env:DGATEWAY_WEBCLIENT_PATH = Join-Path "webapp" "client" | Resolve-Path - - ./ci/tlk.ps1 package -Platform ${{ matrix.os }} -Architecture ${{ matrix.arch }} -CargoProfile ${{ needs.preflight.outputs.rust-profile }} + + ./ci/tlk.ps1 package -Product gateway -Platform ${{ matrix.os }} -Architecture ${{ matrix.arch }} -CargoProfile ${{ needs.preflight.outputs.rust-profile }} - name: Upload artifacts uses: actions/upload-artifact@v4 @@ -448,6 +452,110 @@ jobs: pattern: devolutions-gateway-* delete-merged: true + devolutions-agent: + name: devolutions-agent [${{ matrix.os }} ${{ matrix.arch }}] + runs-on: ${{ matrix.runner }} + needs: [preflight] + if: always() + strategy: + matrix: + include: ${{ fromJson(needs.preflight.outputs.agent-build-matrix) }} + + steps: + - name: Checkout ${{ github.repository }} + uses: actions/checkout@v4 + with: + ref: ${{ needs.preflight.outputs.ref }} + + - name: Load dynamic variables + id: load-variables + shell: pwsh + run: | + $PackageVersion = "${{ needs.preflight.outputs.version }}" + $StagingPath = Join-Path $Env:RUNNER_TEMP "staging" + $TargetOutputPath = Join-Path $StagingPath ${{ matrix.os }} ${{ matrix.arch }} + $ExecutableFileName = "DevolutionsAgent_${{ runner.os }}_${PackageVersion}_${{ matrix.arch }}" + + if ($Env:RUNNER_OS -eq "Windows") { + $ExecutableFileName = "$($ExecutableFileName).exe" + $PackageFileName = "DevolutionsAgent-${{ matrix.arch }}-${PackageVersion}.msi" + $DAgentPackage = Join-Path $TargetOutputPath $PackageFileName + + echo "dagent-package=$DAgentPackage" >> $Env:GITHUB_OUTPUT + } + + $DAgentExecutable = Join-Path $TargetOutputPath $ExecutableFileName + echo "staging-path=$StagingPath" >> $Env:GITHUB_OUTPUT + echo "target-output-path=$TargetOutputPath" >> $Env:GITHUB_OUTPUT + echo "dagent-executable=$DAgentExecutable" >> $Env:GITHUB_OUTPUT + + - name: Configure Linux runner + if: matrix.os == 'linux' + run: | + sudo apt-get update + sudo apt-get -o Acquire::Retries=3 install python3-wget python3-setuptools libsystemd-dev dh-make + + - name: Configure Linux (arm) runner + if: matrix.os == 'linux' && matrix.arch == 'arm64' + run: | + sudo dpkg --add-architecture arm64 + sudo apt-get -o Acquire::Retries=3 install -qy binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu g++-aarch64-linux-gnu qemu-user + rustup target add aarch64-unknown-linux-gnu + echo "STRIP_EXECUTABLE=aarch64-linux-gnu-strip" >> $GITHUB_ENV + + # WiX is installed on Windows runners but not in the PATH + - name: Configure Windows runner + if: matrix.os == 'windows' + run: | + # https://github.com/actions/runner-images/issues/9667 + choco uninstall wixtoolset + choco install wixtoolset --version 3.14.0 --allow-downgrade --force + echo "C:\Program Files (x86)\WiX Toolset v3.14\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + + - name: Build + shell: pwsh + env: + TARGET_OUTPUT_PATH: ${{ steps.load-variables.outputs.target-output-path }} + DAGENT_EXECUTABLE: ${{ steps.load-variables.outputs.dagent-executable }} + CARGO_PACKAGE: devolutions-agent + run: ./ci/tlk.ps1 build -Product agent -Platform ${{ matrix.os }} -Architecture ${{ matrix.arch }} -CargoProfile ${{ needs.preflight.outputs.rust-profile }} + + - name: Add msbuild to PATH + if: matrix.os == 'windows' + uses: microsoft/setup-msbuild@v2 + + - name: Package + shell: pwsh + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + env: + TARGET_OUTPUT_PATH: ${{ steps.load-variables.outputs.target-output-path }} + DAGENT_EXECUTABLE: ${{ steps.load-variables.outputs.dagent-executable }} + run: | + if ($Env:RUNNER_OS -eq "Windows") { + $Env:DAGENT_PACKAGE = "${{ steps.load-variables.outputs.dagent-package }}" + } + + ./ci/tlk.ps1 package -Product agent -Platform ${{ matrix.os }} -Architecture ${{ matrix.arch }} -CargoProfile ${{ needs.preflight.outputs.rust-profile }} + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: devolutions-agent-${{ matrix.os }}-${{ matrix.arch }} + path: ${{ steps.load-variables.outputs.staging-path }} + + devolutions-agent-merge: + name: devolutions agent merge artifacts + runs-on: ubuntu-latest + needs: [preflight, devolutions-agent] + + steps: + - name: Merge Artifacts + uses: actions/upload-artifact/merge@v4 + with: + name: devolutions-agent + pattern: devolutions-agent-* + delete-merged: true + upload-git-log: name: Upload git-log output runs-on: ubuntu-20.04 @@ -480,6 +588,8 @@ jobs: - preflight - devolutions-gateway - devolutions-gateway-merge + - devolutions-agent + - devolutions-agent-merge - jetsocat - jetsocat-lipo - upload-git-log @@ -518,6 +628,11 @@ jobs: name: devolutions-gateway path: ${{ runner.temp }}/artifacts_raw + - uses: actions/download-artifact@v4 + with: + name: devolutions-agent + path: ${{ runner.temp }}/artifacts_raw + - uses: actions/download-artifact@v4 with: name: git-log diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 971b0df10..fe22306a9 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -122,7 +122,7 @@ jobs: needs: preflight strategy: matrix: - project: [ jetsocat, devolutions-gateway ] + project: [ jetsocat, devolutions-gateway, devolutions-agent ] os: [ windows, macos, linux ] include: - os: windows @@ -134,6 +134,8 @@ jobs: exclude: - project: devolutions-gateway os: macos + - project: devolutions-agent + os: macos steps: - name: Checkout ${{ github.repository }} @@ -198,6 +200,7 @@ jobs: run: | $IncludePattern = switch ('${{ matrix.project }}') { 'devolutions-gateway' { 'DevolutionsGateway_*.exe' } + 'devolutions-agent' { 'DevolutionsAgent_*.exe' } 'jetsocat' { 'jetsocat_*' } } $ExcludePattern = "*.pdb" @@ -238,14 +241,14 @@ jobs: gh run download ${{ needs.preflight.outputs.run }} -n webapp-client -D "$Destination" - name: Add msbuild to PATH - if: matrix.os == 'windows' && matrix.project == 'devolutions-gateway' + if: matrix.os == 'windows' && (matrix.project == 'devolutions-gateway' || matrix.project == 'devolutions-agent') uses: microsoft/setup-msbuild@v2 - name: Regenerate MSI - if: matrix.project == 'devolutions-gateway' && matrix.os == 'windows' + if: (matrix.project == 'devolutions-gateway' || matrix.project == 'devolutions-agent') && matrix.os == 'windows' shell: pwsh run: | - $PackageRoot = Join-Path ${{ runner.temp }} devolutions-gateway + $PackageRoot = Join-Path ${{ runner.temp }} ${{ matrix.project}} $Env:DGATEWAY_EXECUTABLE = Get-ChildItem -Path $PackageRoot -Recurse -Include '*DevolutionsGateway*.exe' | Select -First 1 $Env:DGATEWAY_PSMODULE_PATH = Join-Path $PackageRoot PowerShell DevolutionsGateway $Env:DGATEWAY_WEBCLIENT_PATH = Join-Path "webapp" "client" | Resolve-Path @@ -257,7 +260,7 @@ jobs: ./ci/tlk.ps1 package -PackageOption generate - name: Sign msi runtime - if: matrix.project == 'devolutions-gateway' && matrix.os == 'windows' + if: (matrix.project == 'devolutions-gateway' || matrix.project == 'devolutions-agent') && matrix.os == 'windows' shell: pwsh working-directory: package/WindowsManaged/Release run: | @@ -273,17 +276,26 @@ jobs: AzureSignTool @Params $_.FullName } - - name: Repackage + - name: Repackage gateway if: matrix.project == 'devolutions-gateway' && matrix.os == 'windows' shell: pwsh run: | $PackageRoot = Join-Path ${{ runner.temp }} devolutions-gateway $Env:DGATEWAY_PACKAGE = Get-ChildItem -Path $PackageRoot -Recurse -Include '*DevolutionsGateway*.msi' | Where-Object { $_.Name -NotLike "*legacy*"} | Select -First 1 - ./ci/tlk.ps1 package -PackageOption assemble + ./ci/tlk.ps1 package -Product gateway -PackageOption assemble + + - name: Repackage agent + if: matrix.project == 'devolutions-agent' && matrix.os == 'windows' + shell: pwsh + run: | + $PackageRoot = Join-Path ${{ runner.temp }} devolutions-agent + $Env:DAGENT_PACKAGE = Get-ChildItem -Path $PackageRoot -Recurse -Include '*DevolutionsAgent*.msi' | Where-Object { $_.Name -NotLike "*legacy*"} | Select -First 1 + + ./ci/tlk.ps1 package -Product agent -PackageOption assemble - name: Sign packages - if: matrix.project == 'devolutions-gateway' && matrix.os == 'windows' + if: (matrix.project == 'devolutions-gateway' || matrix.project == 'devolutions-agent') && matrix.os == 'windows' shell: pwsh run: | Get-ChildItem -Path ${{ runner.temp }} -Recurse -Include '*.msi' | % { @@ -331,7 +343,7 @@ jobs: retention-days: 1 devolutions-gateway-merge: - name: Merge Artifacts + name: Merge gateway artifacts runs-on: ubuntu-latest needs: [preflight, codesign] @@ -349,8 +361,27 @@ jobs: name: devolutions-gateway overwrite: true + devolutions-agent-merge: + name: Merge agent artifacts + runs-on: ubuntu-latest + needs: [preflight, codesign] + + steps: + - name: Download Artifacts + uses: actions/download-artifact@v4 + with: + pattern: devolutions-agent-* + merge-multiple: true + + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + path: ${{ github.workspace }}/**/* + name: devolutions-agent + overwrite: true + jetsocat-merge: - name: Merge Artifacts + name: Merge jetsocat artifacts runs-on: ubuntu-latest needs: [preflight, codesign] diff --git a/Cargo.lock b/Cargo.lock index 3400f231f..d4ef32970 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -550,6 +550,26 @@ dependencies = [ "libc", ] +[[package]] +name = "ceviche" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd1a44085e79b3b02c2a5d18a54c8cb943bc7ae85207f76679b89f1e1c3c68c9" +dependencies = [ + "cfg-if", + "chrono", + "core-foundation", + "core-foundation-sys", + "ctrlc", + "libc", + "log", + "system-configuration-sys", + "systemd-rs", + "timer", + "widestring 0.4.3", + "winapi", +] + [[package]] name = "ceviche" version = "0.6.0" @@ -761,7 +781,7 @@ version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b" dependencies = [ - "nix", + "nix 0.27.1", "windows-sys 0.52.0", ] @@ -900,6 +920,28 @@ dependencies = [ "cipher", ] +[[package]] +name = "devolutions-agent" +version = "2024.1.2" +dependencies = [ + "anyhow", + "camino", + "ceviche 0.5.2", + "cfg-if", + "ctrlc", + "devolutions-gateway-task", + "devolutions-log", + "embed-resource", + "futures", + "parking_lot", + "serde", + "serde_derive", + "serde_json", + "tap", + "tokio", + "tracing", +] + [[package]] name = "devolutions-gateway" version = "2024.2.1" @@ -912,10 +954,11 @@ dependencies = [ "backoff", "bytes", "camino", - "ceviche", + "ceviche 0.6.0", "cfg-if", "devolutions-gateway-generators", "devolutions-gateway-task", + "devolutions-log", "dlopen", "dlopen_derive", "embed-resource", @@ -988,6 +1031,20 @@ dependencies = [ "tokio", ] +[[package]] +name = "devolutions-log" +version = "2024.2.1" +dependencies = [ + "anyhow", + "async-trait", + "camino", + "devolutions-gateway-task", + "tokio", + "tracing", + "tracing-appender", + "tracing-subscriber", +] + [[package]] name = "digest" version = "0.10.7" @@ -1147,6 +1204,16 @@ dependencies = [ "winreg 0.52.0", ] +[[package]] +name = "epoll" +version = "4.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79" +dependencies = [ + "bitflags 2.4.2", + "libc", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -2032,6 +2099,15 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.17" @@ -2285,6 +2361,18 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset", +] + [[package]] name = "nix" version = "0.27.1" @@ -3333,7 +3421,7 @@ dependencies = [ "netlink-packet-utils", "netlink-proto", "netlink-sys", - "nix", + "nix 0.27.1", "thiserror", "tokio", ] @@ -3961,6 +4049,29 @@ dependencies = [ "libc", ] +[[package]] +name = "systemd-rs" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba28cdec1491faf8b9820305aa6cf9df1dff3378c1adfe41d5bd1261d4a17b59" +dependencies = [ + "epoll", + "libc", + "log", + "nix 0.24.3", + "systemd-sys", +] + +[[package]] +name = "systemd-sys" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4816f770cdd05c0a0b8bd048178c3eb3048221016945469c6124a1dd1afc6400" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "tap" version = "1.0.1" diff --git a/ci/tlk.ps1 b/ci/tlk.ps1 index cb91a94ad..f109b36d7 100644 --- a/ci/tlk.ps1 +++ b/ci/tlk.ps1 @@ -46,7 +46,7 @@ function Merge-Tokens }) if ($OutputFile) { - $AsByteStream = if ($PSEdition -eq 'Core') { @{AsByteStream = $true} } else { @{'Encoding' = 'Byte'} } + $AsByteStream = if ($PSEdition -Eq 'Core') { @{AsByteStream = $true} } else { @{'Encoding' = 'Byte'} } $OutputBytes = $([System.Text.Encoding]::UTF8).GetBytes($OutputValue) Set-Content -Path $OutputFile -Value $OutputBytes @AsByteStream } @@ -144,6 +144,19 @@ function Get-TlkArchitecture { $Architecture } +function Get-TlkProduct { + param( + [Parameter(Position=0)] + [string] $Product + ) + + if (-Not $Product) { + $Product = 'gateway' + } + + $Product +} + class TlkTarget { [string] $Platform @@ -170,15 +183,15 @@ class TlkTarget } [bool] IsWindows() { - return $this.Platform -eq 'Windows' + return $this.Platform -Eq 'Windows' } [bool] IsMacOS() { - return $this.Platform -eq 'macOS' + return $this.Platform -Eq 'macOS' } [bool] IsLinux() { - return $this.Platform -eq 'Linux' + return $this.Platform -Eq 'Linux' } [string] CargoTarget() { @@ -212,7 +225,7 @@ class TlkTarget "x86_64" { "x64" } "aarch64" { "ARM64" } } - + return $WindowsArchitecture } @@ -225,14 +238,14 @@ class TlkTarget "x86_64" { "amd64" } "arm64" { "arm64" } } - + return $DebianArchitecture } } class TlkRecipe { - [string] $PackageName + [string] $Product [string] $Version [string] $SourcePath [bool] $Verbose @@ -255,10 +268,30 @@ class TlkRecipe [void] Init() { $this.SourcePath = $($PSScriptRoot | Get-Item).Parent.FullName - $this.PackageName = "DevolutionsGateway" $this.Version = $(Get-Content -Path "$($this.SourcePath)/VERSION").Trim() $this.Verbose = $true $this.Target = [TlkTarget]::new() + $this.Product = Get-TlkProduct + } + + [string] CargoPackage() { + $CargoPackage = ` + switch ($this.Product) { + "gateway" { "devolutions-gateway" } + "agent" { "devolutions-agent" } + "jetsocat" { "jetsocat" } + } + return $CargoPackage + } + + [string] PackageName() { + $PackageName = switch ($this.Product) { + "gateway" { "DevolutionsGateway" } + "agent" { "DevolutionsAgent" } + "jetsocat" { "jetsocat" } + } + + return $PackageName } [void] Cargo([string[]]$CargoArgs) { @@ -285,18 +318,18 @@ class TlkRecipe if (Test-Path Env:TARGET_OUTPUT_PATH) { $BuildStagingDirectory = $Env:TARGET_OUTPUT_PATH } - + if ($this.Target.IsWindows()) { $Env:RUSTFLAGS = "-C target-feature=+crt-static" } - + $OutputPath = "${BuildStagingDirectory}/$($this.Target.Platform)/$($this.Target.Architecture)" New-Item -Path $OutputPath -ItemType 'Directory' -Force | Out-Null - + Push-Location Set-Location $this.SourcePath - $CargoPackage = "devolutions-gateway" + $CargoPackage = $this.CargoPackage() if (Test-Path Env:CARGO_PACKAGE) { $CargoPackage = $Env:CARGO_PACKAGE } @@ -310,14 +343,31 @@ class TlkRecipe $SrcExecutableName = $CargoPackage, $this.Target.ExecutableExtension -ne '' -Join '.' $SrcExecutablePath = "$($this.SourcePath)/target/${CargoTarget}/${CargoProfile}/${SrcExecutableName}" - if (Test-Path Env:DGATEWAY_EXECUTABLE) { - $DGatewayExecutable = $Env:DGATEWAY_EXECUTABLE - $DestinationExecutable = $DGatewayExecutable - } elseif (Test-Path Env:JETSOCAT_EXECUTABLE) { - $JetsocatExecutable = $Env:JETSOCAT_EXECUTABLE - $DestinationExecutable = $JetsocatExecutable - } else { - $DestinationExecutable = $null + $DestinationExecutable = switch ($this.Product) { + "gateway" { + if (Test-Path Env:DGATEWAY_EXECUTABLE) { + $Env:DGATEWAY_EXECUTABLE + } else { + $null + } + } + "agent" { + if (Test-Path Env:DAGENT_EXECUTABLE) { + $Env:DAGENT_EXECUTABLE + } else { + $null + } + } + "jetsocat" { + if (Test-Path Env:JETSOCAT_EXECUTABLE) { + $Env:JETSOCAT_EXECUTABLE + } else { + $null + } + } + Default { + $null + } } if ($this.Target.IsWindows() -And $DestinationExecutable) { @@ -384,14 +434,14 @@ class TlkRecipe $TargetConfiguration = "Release" # Build the base (en-US) MSI - & .\$TargetConfiguration\Build_DevolutionsGateway.cmd + & ".\$TargetConfiguration\Build_$($this.PackageName()).cmd" - $BaseMsi = Join-Path $TargetConfiguration "$($this.PackageName).msi" + $BaseMsi = Join-Path $TargetConfiguration "$($this.PackageName()).msi" foreach ($PackageLanguage in $([TlkRecipe]::PackageLanguages | Select-Object -Skip 1)) { # Build the localized MSI - & .\$TargetConfiguration\$($PackageLanguage.Name)\Build_DevolutionsGateway.cmd - $LangMsi = Join-Path $TargetConfiguration $($PackageLanguage.Name) "$($this.PackageName).msi" + & ".\$TargetConfiguration\$($PackageLanguage.Name)\Build_$($this.PackageName()).cmd" + $LangMsi = Join-Path $TargetConfiguration $($PackageLanguage.Name) "$($this.PackageName()).msi" $Transform = Join-Path $TargetConfiguration "$($PackageLanguage.Name).mst" # Generate a language transform & 'torch.exe' "$BaseMsi" "$LangMsi" "-o" "$Transform" | Out-Host @@ -403,15 +453,25 @@ class TlkRecipe $LCIDs = ([TlkRecipe]::PackageLanguages | ForEach-Object { $_.LCID }) -join ',' & 'cscript.exe' "/nologo" "../Windows/WiLangId.vbs" "$BaseMsi" "Package" "$LCIDs" | Out-Host - if (Test-Path Env:DGATEWAY_PACKAGE) { - $DGatewayPackage = $Env:DGATEWAY_PACKAGE - Copy-Item -Path "$BaseMsi" -Destination $DGatewayPackage + switch ($this.Product) { + "gateway" { + if (Test-Path Env:DGATEWAY_PACKAGE) { + $DGatewayPackage = $Env:DGATEWAY_PACKAGE + Copy-Item -Path "$BaseMsi" -Destination $DGatewayPackage + } + } + "agent" { + if (Test-Path Env:DAGENT_PACKAGE) { + $DAgentPackage = $Env:DAGENT_PACKAGE + Copy-Item -Path "$BaseMsi" -Destination $DAgentPackage + } + } } Pop-Location } - [void] Package_Windows_Managed([bool] $SourceOnlyBuild) { + [void] Package_Windows_Managed_Gateway([bool] $SourceOnlyBuild) { $ShortVersion = $this.Version.Substring(2) # msi version $Env:DGATEWAY_VERSION="$ShortVersion" @@ -431,10 +491,6 @@ class TlkRecipe $TargetConfiguration = "Release" - if ((Get-Command "MSBuild.exe" -ErrorAction SilentlyContinue) -Eq $Null) { - throw 'MSBuild was not found in the PATH' - } - if ($SourceOnlyBuild) { $Env:DGATEWAY_MSI_SOURCE_ONLY_BUILD = "1" } @@ -459,20 +515,79 @@ class TlkRecipe if (!$SourceOnlyBuild -And (Test-Path Env:DGATEWAY_PACKAGE)) { $DGatewayPackage = $Env:DGATEWAY_PACKAGE - $MsiPath = Join-Path "Release" "$($this.PackageName).msi" + $MsiPath = Join-Path "Release" "$($this.PackageName()).msi" Copy-Item -Path "$MsiPath" -Destination $DGatewayPackage } Pop-Location } + [void] Package_Windows_Managed_Agent([bool] $SourceOnlyBuild) { + $ShortVersion = $this.Version.Substring(2) # msi version + + $Env:DAGENT_VERSION="$ShortVersion" + + Push-Location + Set-Location "$($this.SourcePath)/package/Agent$($this.Target.Platform)Managed" + + if (Test-Path Env:DAGENT_EXECUTABLE) { + $DGatewayExecutable = $Env:DAGENT_EXECUTABLE + } else { + throw ("Specify DAGENT_EXECUTABLE environment variable") + } + + $TargetConfiguration = "Release" + + if ($SourceOnlyBuild) { + $Env:DAGENT_MSI_SOURCE_ONLY_BUILD = "1" + } + + & 'MSBuild.exe' "DevolutionsAgent.sln" "/t:restore,build" "/p:Configuration=$TargetConfiguration" | Out-Host + + if ($SourceOnlyBuild) { + foreach ($PackageLanguage in $([TlkRecipe]::PackageLanguages | Select-Object -Skip 1)) { + $Env:DAGENT_MSI_LANG_ID = $PackageLanguage.Name + & 'MSBuild.exe' "DevolutionsAgent.sln" "/t:restore,build" "/p:Configuration=$TargetConfiguration" | Out-Host + } + } + + $Env:DAGENT_MSI_SOURCE_ONLY_BUILD = "" + $Env:DAGENT_MSI_LANG_ID = "" + + if (!$SourceOnlyBuild -And (Test-Path Env:DAGENT_PACKAGE)) { + $DAgentPackage = $Env:DAGENT_PACKAGE + $MsiPath = Join-Path "Release" "$($this.PackageName()).msi" + Copy-Item -Path "$MsiPath" -Destination $DAgentPackage + } + + Pop-Location + } + + [void] Package_Windows_Managed([bool] $SourceOnlyBuild) { + if ((Get-Command "MSBuild.exe" -ErrorAction SilentlyContinue) -Eq $Null) { + throw 'MSBuild was not found in the PATH' + } + + if ($this.Product -eq 'gateway') { + $this.Package_Windows_Managed_Gateway($SourceOnlyBuild) + } elseif ($this.Product -eq 'agent') { + $this.Package_Windows_Managed_Agent($SourceOnlyBuild) + } else { + throw "Managed packaging for $($this.Product) is not supported" + } + } + [void] Package_Windows() { + if ($this.Product -ne 'gateway') { + throw "Legacy packaging for $($this.Product) is not supported" + } + $ShortVersion = $this.Version.Substring(2) # msi version $TargetArch = $this.Target.WindowsArchitecture() Push-Location Set-Location "$($this.SourcePath)/package/$($this.Target.Platform)" - + if (Test-Path Env:DGATEWAY_EXECUTABLE) { $DGatewayExecutable = $Env:DGATEWAY_EXECUTABLE } else { @@ -484,7 +599,7 @@ class TlkRecipe $DGatewayPSModuleStagingPath = $PSModulePaths[1] $TargetConfiguration = "Release" - $ActionsProjectPath = Join-Path $(Get-Location) 'Actions' + $ActionsProjectPath = Join-Path $(Get-Location) 'Actions' if ((Get-Command "MSBuild.exe" -ErrorAction SilentlyContinue) -Eq $Null) { throw 'MSBuild was not found in the PATH' @@ -497,42 +612,42 @@ class TlkRecipe '-cg', 'CG.DGatewayPSComponentGroup', '-var', 'var.DGatewayPSSourceDir', '-nologo', '-srd', '-suid', '-scom', '-sreg', '-sfrag', '-gg') - - & 'heat.exe' $HeatArgs + @('-t', 'HeatTransform64.xslt', '-o', "$($this.PackageName)-$TargetArch.wxs") | Out-Host + + & 'heat.exe' $HeatArgs + @('-t', 'HeatTransform64.xslt', '-o', "$($this.PackageName())-$TargetArch.wxs") | Out-Host $WixExtensions = @('WixUtilExtension', 'WixUIExtension', 'WixFirewallExtension') $WixExtensions += $(Join-Path $(Get-Location) 'WixUserPrivilegesExtension.dll') - + $WixArgs = @($WixExtensions | ForEach-Object { @('-ext', $_) }) + @( "-dDGatewayPSSourceDir=$DGatewayPSModuleStagingPath", "-dDGatewayExecutable=$DGatewayExecutable", "-dVersion=$ShortVersion", "-dActionsLib=$(Join-Path $ActionsProjectPath $TargetArch $TargetConfiguration 'DevolutionsGateway.Installer.Actions.dll')", "-v") - - $WixFiles = Get-ChildItem -Include '*.wxs' -Recurse + + $WixFiles = Get-ChildItem -Include '*.wxs' -Recurse $InputFiles = $WixFiles | Foreach-Object { Resolve-Path $_.FullName -Relative } $ObjectFiles = $WixFiles | ForEach-Object { $_.BaseName + '.wixobj' } $Cultures = @('en-US', 'fr-FR') - + foreach ($Culture in $Cultures) { & 'candle.exe' '-nologo' $InputFiles $WixArgs "-dPlatform=$TargetArch" | Out-Host - $OutputFile = "$($this.PackageName)_${Culture}.msi" - - if ($Culture -eq 'en-US') { - $OutputFile = "$($this.PackageName).msi" + $OutputFile = "$($this.PackageName())_${Culture}.msi" + + if ($Culture -Eq 'en-US') { + $OutputFile = "$($this.PackageName()).msi" } - + & 'light.exe' "-nologo" $ObjectFiles "-cultures:${Culture}" "-loc" "$($this.PackageName)_${Culture}.wxl" ` "-out" $OutputFile $WixArgs "-dPlatform=$TargetArch" "-sice:ICE61" | Out-Host } - + foreach ($Culture in $($Cultures | Select-Object -Skip 1)) { - & 'torch.exe' "$($this.PackageName).msi" "$($this.PackageName)_${Culture}.msi" "-o" "${Culture}_$TargetArch.mst" | Out-Host - & 'cscript.exe' "/nologo" "WiSubStg.vbs" "$($this.PackageName).msi" "${Culture}_$TargetArch.mst" "1036" | Out-Host - & 'cscript.exe' "/nologo" "WiLangId.vbs" "$($this.PackageName).msi" "Package" "1033,1036" | Out-Host + & 'torch.exe' "$($this.PackageName()).msi" "$($this.PackageName())_${Culture}.msi" "-o" "${Culture}_$TargetArch.mst" | Out-Host + & 'cscript.exe' "/nologo" "WiSubStg.vbs" "$($this.PackageName()).msi" "${Culture}_$TargetArch.mst" "1036" | Out-Host + & 'cscript.exe' "/nologo" "WiLangId.vbs" "$($this.PackageName()).msi" "Package" "1033,1036" | Out-Host } if (Test-Path Env:DGATEWAY_PSMODULE_CLEAN) { @@ -543,7 +658,7 @@ class TlkRecipe if (Test-Path Env:DGATEWAY_PACKAGE) { $DGatewayPackage = $Env:DGATEWAY_PACKAGE - Copy-Item -Path "$($this.PackageName).msi" -Destination $DGatewayPackage + Copy-Item -Path "$($this.PackageName()).msi" -Destination $DGatewayPackage } Pop-Location @@ -567,24 +682,44 @@ class TlkRecipe $Env:DEBFULLNAME = $Packager $Env:DEBEMAIL = $Email - if (Test-Path Env:DGATEWAY_EXECUTABLE) { - $DGatewayExecutable = $Env:DGATEWAY_EXECUTABLE - } else { - throw ("Specify DGATEWAY_EXECUTABLE environment variable") + $DGatewayExecutable = $null + $DGatewayWebClient = $null + $DAgentExecutable = $null + + switch ($this.Product) { + "gateway" { + if (Test-Path Env:DGATEWAY_EXECUTABLE) { + $DGatewayExecutable = $Env:DGATEWAY_EXECUTABLE + } else { + throw ("Specify DGATEWAY_EXECUTABLE environment variable") + } + + if (Test-Path Env:DGATEWAY_WEBCLIENT_PATH) { + $DGatewayWebClient = $Env:DGATEWAY_WEBCLIENT_PATH + } else { + throw ("Specify DGATEWAY_WEBCLIENT_PATH environment variable") + } + } + "agent" { + if (Test-Path Env:DAGENT_EXECUTABLE) { + $DAgentExecutable = $Env:DAGENT_EXECUTABLE + } else { + throw ("Specify DAGENT_EXECUTABLE environment variable") + } + } } - if (Test-Path Env:DGATEWAY_WEBCLIENT_PATH) { - $DGatewayWebClient = $Env:DGATEWAY_WEBCLIENT_PATH - } else { - throw ("Specify DGATEWAY_WEBCLIENT_PATH environment variable") + $InputPackagePathPrefix = switch ($this.Product) { + "gateway" { "" } + "agent" { "Agent" } } - $InputPackagePath = Join-Path $this.SourcePath "package/Linux" + $InputPackagePath = Join-Path $this.SourcePath "package/$($InputPackagePathPrefix)Linux" $OutputPath = Join-Path $this.SourcePath "output" New-Item -Path $OutputPath -ItemType 'Directory' -Force | Out-Null - $OutputPackagePath = Join-Path $OutputPath "gateway" + $OutputPackagePath = Join-Path $OutputPath "$($this.Product)" $OutputDebianPath = Join-Path $OutputPackagePath "debian" @($OutputPath, $OutputPackagePath, $OutputDebianPath) | % { @@ -594,10 +729,10 @@ class TlkRecipe Push-Location Set-Location $OutputPackagePath - $DebPkgName = "devolutions-gateway" + $DebPkgName = "devolutions-$($this.Product)" $PkgNameVersion = "${DebPkgName}_$($this.Version).0" $PkgNameTarget = "${PkgNameVersion}_${DebianArchitecture}" - $CopyrightFile = Join-Path $InputPackagePath "gateway/copyright" + $CopyrightFile = Join-Path $InputPackagePath "$($this.Product)/copyright" # dh_make @@ -619,23 +754,34 @@ class TlkRecipe # debian/rules $RulesFile = Join-Path $OutputDebianPath "rules" - $RulesTemplate = Join-Path $InputPackagePath "gateway/template/rules" + $RulesTemplate = Join-Path $InputPackagePath "$($this.Product)/template/rules" $DhShLibDepsOverride = ""; if ($this.Target.DebianArchitecture() -Eq "amd64") { $DhShLibDepsOverride = "dh_shlibdeps" } - Merge-Tokens -TemplateFile $RulesTemplate -Tokens @{ - dgateway_executable = $DGatewayExecutable - dgateway_webclient = $DGatewayWebClient - platform_dir = $InputPackagePath - dh_shlibdeps = $DhShLibDepsOverride - } -OutputFile $RulesFile + switch ($this.Product) { + "gateway" { + Merge-Tokens -TemplateFile $RulesTemplate -Tokens @{ + dgateway_executable = $DGatewayExecutable + dgateway_webclient = $DGatewayWebClient + platform_dir = $InputPackagePath + dh_shlibdeps = $DhShLibDepsOverride + } -OutputFile $RulesFile + } + "agent" { + Merge-Tokens -TemplateFile $RulesTemplate -Tokens @{ + dagent_executable = $DAgentExecutable + platform_dir = $InputPackagePath + dh_shlibdeps = $DhShLibDepsOverride + } -OutputFile $RulesFile + } + } # debian/control $ControlFile = Join-Path $OutputDebianPath "control" - $ControlTemplate = Join-Path $InputPackagePath "gateway/template/control" + $ControlTemplate = Join-Path $InputPackagePath "$($this.Product)/template/control" Merge-Tokens -TemplateFile $ControlTemplate -Tokens @{ arch = $DebianArchitecture deps = $($Dependencies -Join ", ") @@ -669,7 +815,7 @@ class TlkRecipe } -OutputFile $ChangelogFile @('postinst', 'prerm', 'postrm') | % { - $InputFile = Join-Path $InputPackagePath "gateway/debian/$_" + $InputFile = Join-Path $InputPackagePath "$($this.Product)/debian/$_" $OutputFile = Join-Path $OutputDebianPath $_ Copy-Item $InputFile $OutputFile } @@ -693,6 +839,10 @@ class TlkRecipe } [void] Package([string]$PackageOption) { + if ($this.Product -Eq 'jetsocat') { + throw "Packaging for $($this.Product) is not supported" + } + if ($this.Target.IsWindows()) { if (-Not $PackageOption ) { $this.Package_Windows_Managed($false) @@ -755,7 +905,9 @@ function Invoke-TlkStep { [ValidateSet('x86','x86_64','arm64')] [string] $Architecture, [ValidateSet('release', 'production')] - [string] $CargoProfile + [string] $CargoProfile, + [ValidateSet('gateway', 'agent', 'jetsocat')] + [string] $Product ) if (-Not $Platform) { @@ -770,6 +922,11 @@ function Invoke-TlkStep { $CargoProfile = 'release' } + if (-Not $Product) { + Write-Warning "`[LEGACY] Product` parameter is not specified, defaulting to 'gateway'" + $Product = 'gateway' + } + $RootPath = Split-Path -Parent $PSScriptRoot $tlk = [TlkRecipe]::new() @@ -777,6 +934,7 @@ function Invoke-TlkStep { $tlk.Target.Platform = $Platform $tlk.Target.Architecture = $Architecture $tlk.Target.CargoProfile = $CargoProfile + $tlk.Product = $Product switch ($TlkVerb) { "build" { $tlk.Build() } diff --git a/crates/devolutions-log/Cargo.toml b/crates/devolutions-log/Cargo.toml new file mode 100644 index 000000000..e0f5a4649 --- /dev/null +++ b/crates/devolutions-log/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "devolutions-log" +version = "2024.2.1" +edition = "2021" +readme = "README.md" +license = "MIT/Apache-2.0" +authors = ["Devolutions Inc. "] +description = "Logging utils library for Devolutions apps" +publish = false + +[dependencies] +# In-house +devolutions-gateway-task = { path = "../devolutions-gateway-task" } + +# Error handling +anyhow = "1.0" + +# Utils, misc +camino = { version = "1.1", features = ["serde1"] } + +# Async +async-trait = "0.1" +tokio = { version = "1.37", features = ["signal", "net", "io-util", "time", "rt", "rt-multi-thread", "sync", "macros", "parking_lot", "fs"] } + +# Logging +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter", "parking_lot", "smallvec", "local-time", "tracing-log"] } +tracing-appender = "0.2" \ No newline at end of file diff --git a/crates/devolutions-log/README.md b/crates/devolutions-log/README.md new file mode 100644 index 000000000..0d97e21a5 --- /dev/null +++ b/crates/devolutions-log/README.md @@ -0,0 +1,2 @@ +# devolutions-log +Common code for logging in devolutions apps \ No newline at end of file diff --git a/crates/devolutions-log/src/lib.rs b/crates/devolutions-log/src/lib.rs new file mode 100644 index 000000000..488a5a17e --- /dev/null +++ b/crates/devolutions-log/src/lib.rs @@ -0,0 +1,215 @@ +#[macro_use] +extern crate tracing; + +use std::io; +use std::time::SystemTime; + +use anyhow::Context as _; +use async_trait::async_trait; +use camino::{Utf8Path, Utf8PathBuf}; +use devolutions_gateway_task::{ShutdownSignal, Task}; +use tokio::fs; +use tokio::time::{sleep, Duration}; +use tracing_appender::non_blocking::WorkerGuard; +use tracing_appender::rolling; +use tracing_subscriber::prelude::*; +use tracing_subscriber::{fmt, EnvFilter}; + +pub trait StaticLogConfig { + const MAX_BYTES_PER_LOG_FILE: u64; + const MAX_LOG_FILES: usize; + const LOG_FILE_PREFIX: &'static str; +} + +pub struct LoggerGuard { + _file_guard: WorkerGuard, + _stdio_guard: WorkerGuard, +} + +struct LogPathCfg<'a, C: StaticLogConfig> { + folder: &'a Utf8Path, + prefix: &'a str, + _phantom: std::marker::PhantomData, +} + +impl<'a, C: StaticLogConfig> LogPathCfg<'a, C> { + pub fn from_path(path: &'a Utf8Path) -> anyhow::Result { + if path.is_dir() { + Ok(Self { + folder: path, + prefix: C::LOG_FILE_PREFIX, + _phantom: std::marker::PhantomData, + }) + } else { + Ok(Self { + folder: path.parent().context("invalid log path (parent)")?, + prefix: path.file_name().context("invalid log path (file_name)")?, + _phantom: std::marker::PhantomData, + }) + } + } +} + +pub fn init( + path: &Utf8Path, + log_filter: &str, + debug_filtering_directives: Option<&str>, +) -> anyhow::Result { + let log_cfg = LogPathCfg::::from_path(path)?; + let file_appender = rolling::Builder::new() + .rotation(rolling::Rotation::max_bytes(C::MAX_BYTES_PER_LOG_FILE)) + .filename_prefix(log_cfg.prefix) + .filename_suffix("log") + .max_log_files(C::MAX_LOG_FILES) + .build(log_cfg.folder) + .context("couldn’t create file appender")?; + let (file_non_blocking, file_guard) = tracing_appender::non_blocking(file_appender); + let file_layer = fmt::layer().with_writer(file_non_blocking).with_ansi(false); + + let (non_blocking_stdio, stdio_guard) = tracing_appender::non_blocking(std::io::stdout()); + let stdio_layer = fmt::layer().with_writer(non_blocking_stdio); + + let env_filter = EnvFilter::try_new(log_filter).context("invalid built-in filtering directives (this is a bug)")?; + + // Optionally add additional debugging filtering directives + let env_filter = debug_filtering_directives + .into_iter() + .flat_map(|directives| directives.split(',')) + .fold(env_filter, |env_filter, directive| { + env_filter.add_directive(directive.parse().unwrap()) + }); + + tracing_subscriber::registry() + .with(file_layer) + .with(stdio_layer) + .with(env_filter) + .init(); + + Ok(LoggerGuard { + _file_guard: file_guard, + _stdio_guard: stdio_guard, + }) +} + +/// Find latest log file (by age) +/// +/// Given path is used to filter out by file name prefix. +#[instrument] +pub async fn find_latest_log_file(prefix: &Utf8Path) -> anyhow::Result { + let cfg = LogPathCfg::::from_path(prefix)?; + + let mut read_dir = fs::read_dir(cfg.folder).await.context("couldn't read directory")?; + + let mut most_recent_time = SystemTime::UNIX_EPOCH; + let mut most_recent = None; + + while let Ok(Some(entry)) = read_dir.next_entry().await { + match entry.file_name().to_str() { + Some(file_name) if file_name.starts_with(cfg.prefix) && file_name.contains("log") => { + debug!(file_name, "Found a log file"); + match entry.metadata().await.and_then(|metadata| metadata.modified()) { + Ok(modified) if modified > most_recent_time => { + most_recent_time = modified; + most_recent = Some(entry.path()); + } + Ok(_) => {} + Err(error) => { + warn!(%error, file_name, "Couldn't retrieve metadata for file"); + } + } + } + _ => continue, + } + } + + most_recent.context("no file found") +} + +/// File deletion task (by age) +/// +/// Given path is used to filter out by file name prefix. +pub struct LogDeleterTask { + path: Utf8PathBuf, + _phantom: std::marker::PhantomData, +} + +impl LogDeleterTask { + pub fn new(path: Utf8PathBuf) -> Self { + Self { + path, + _phantom: std::marker::PhantomData, + } + } +} + +#[async_trait] +impl Task for LogDeleterTask { + type Output = anyhow::Result<()>; + + const NAME: &'static str = "log deleter"; + + async fn run(self, shutdown_signal: ShutdownSignal) -> Self::Output { + log_deleter_task::(&self.path, shutdown_signal).await + } +} + +#[instrument(skip(shutdown_signal))] +async fn log_deleter_task( + path: &Utf8Path, + mut shutdown_signal: ShutdownSignal, +) -> anyhow::Result<()> { + const TASK_INTERVAL: Duration = Duration::from_secs(60 * 60 * 24); // once per day + const MAX_AGE: Duration = Duration::from_secs(60 * 60 * 24 * 90); // 90 days + + debug!("Task started"); + + let cfg = LogPathCfg::::from_path(path)?; + + loop { + match fs::read_dir(cfg.folder).await { + Ok(mut read_dir) => { + while let Ok(Some(entry)) = read_dir.next_entry().await { + match entry.file_name().to_str() { + Some(file_name) if file_name.starts_with(cfg.prefix) && file_name.contains("log") => { + debug!(file_name, "Found a log file"); + match entry + .metadata() + .await + .and_then(|metadata| metadata.modified()) + .and_then(|time| time.elapsed().map_err(|e| io::Error::new(io::ErrorKind::Other, e))) + { + Ok(modified) if modified > MAX_AGE => { + info!(file_name, "Delete log file"); + if let Err(error) = fs::remove_file(entry.path()).await { + warn!(%error, file_name, "Couldn't delete log file"); + } + } + Ok(_) => { + trace!(file_name, "Keep this log file"); + } + Err(error) => { + warn!(%error, file_name, "Couldn't retrieve metadata for file"); + } + } + } + _ => continue, + } + } + } + Err(error) => { + warn!(%error, "Couldn't read log folder"); + } + } + + tokio::select! { + _ = sleep(TASK_INTERVAL) => {} + _ = shutdown_signal.wait() => { + break; + } + } + } + + debug!("Task terminated"); + + Ok(()) +} diff --git a/devolutions-agent/Cargo.toml b/devolutions-agent/Cargo.toml index bb1f072c4..8685fface 100644 --- a/devolutions-agent/Cargo.toml +++ b/devolutions-agent/Cargo.toml @@ -3,12 +3,39 @@ name = "devolutions-agent" version = "2024.1.2" edition = "2021" license = "MIT/Apache-2.0" -authors = ["Marc-André Moreau "] +authors = ["Devolutions Inc. "] build = "build.rs" +publish = false [dependencies] -ctrlc = "3.1" +# In-house +devolutions-gateway-task = { path = "../crates/devolutions-gateway-task" } +devolutions-log = { path = "../crates/devolutions-log" } + +# Lifecycle ceviche = "0.5" +ctrlc = "3.1" + +# Serialization +serde = "1.0" +serde_derive = "1.0" +serde_json = "1.0" + +# Error handling +anyhow = "1.0" + +# Utils, misc +camino = { version = "1.1", features = ["serde1"] } +cfg-if = "1.0" +parking_lot = "0.12" +tap = "1.0" + +# Async +futures = "0.3" +tokio = { version = "1.37", features = ["signal", "net", "io-util", "time", "rt", "rt-multi-thread", "sync", "macros", "parking_lot", "fs"] } + +# Logging +tracing = "0.1" [target.'cfg(windows)'.build-dependencies] embed-resource = "2.4" diff --git a/devolutions-agent/src/config.rs b/devolutions-agent/src/config.rs new file mode 100644 index 000000000..64707e514 --- /dev/null +++ b/devolutions-agent/src/config.rs @@ -0,0 +1,265 @@ +use std::env; +use std::fs::File; +use std::io::BufReader; +use std::sync::Arc; + +use anyhow::{bail, Context}; +use camino::{Utf8Path, Utf8PathBuf}; +use cfg_if::cfg_if; +use serde::{Deserialize, Serialize}; +use tap::prelude::*; + +cfg_if! { + if #[cfg(target_os = "windows")] { + const COMPANY_DIR: &str = "Devolutions"; + const PROGRAM_DIR: &str = "Agent"; + const APPLICATION_DIR: &str = "Devolutions\\Agent"; + } else if #[cfg(target_os = "macos")] { + const COMPANY_DIR: &str = "Devolutions"; + const PROGRAM_DIR: &str = "Agent"; + const APPLICATION_DIR: &str = "Devolutions Agent"; + } else { + const COMPANY_DIR: &str = "devolutions"; + const PROGRAM_DIR: &str = "agent"; + const APPLICATION_DIR: &str = "devolutions-agent"; + } +} + +#[derive(Debug, Clone)] +pub struct Conf { + pub log_file: Utf8PathBuf, + pub verbosity_profile: dto::VerbosityProfile, + pub debug: dto::DebugConf, +} + +impl Conf { + pub fn from_conf_file(conf_file: &dto::ConfFile) -> anyhow::Result { + let data_dir = get_data_dir(); + + let log_file = conf_file + .log_file + .clone() + .unwrap_or_else(|| Utf8PathBuf::from("agent")) + .pipe_ref(|path| normalize_data_path(path, &data_dir)); + + Ok(Conf { + log_file, + verbosity_profile: conf_file.verbosity_profile.unwrap_or_default(), + debug: conf_file.debug.clone().unwrap_or_default(), + }) + } +} + +/// Configuration Handle, source of truth for current configuration state +#[derive(Clone)] +pub struct ConfHandle { + inner: Arc, +} + +struct ConfHandleInner { + conf: parking_lot::RwLock>, + conf_file: parking_lot::RwLock>, +} + +impl ConfHandle { + /// Initializes configuration for this instance. + /// + /// It's best to call this only once to avoid inconsistencies. + pub fn init() -> anyhow::Result { + let conf_file = load_conf_file_or_generate_new()?; + let conf = Conf::from_conf_file(&conf_file).context("invalid configuration file")?; + + Ok(Self { + inner: Arc::new(ConfHandleInner { + conf: parking_lot::RwLock::new(Arc::new(conf)), + conf_file: parking_lot::RwLock::new(Arc::new(conf_file)), + }), + }) + } + + /// Returns current configuration state (do not hold it forever as it may become outdated) + pub fn get_conf(&self) -> Arc { + self.inner.conf.read().clone() + } + + /// Returns current configuration file state (do not hold it forever as it may become outdated) + pub fn get_conf_file(&self) -> Arc { + self.inner.conf_file.read().clone() + } +} + +fn save_config(conf: &dto::ConfFile) -> anyhow::Result<()> { + let conf_file_path = get_conf_file_path(); + let json = serde_json::to_string_pretty(conf).context("failed JSON serialization of configuration")?; + std::fs::write(&conf_file_path, json).with_context(|| format!("failed to write file at {conf_file_path}"))?; + Ok(()) +} + +fn get_data_dir() -> Utf8PathBuf { + if let Ok(config_path_env) = env::var("DAGENT_CONFIG_PATH") { + Utf8PathBuf::from(config_path_env) + } else { + let mut config_path = Utf8PathBuf::new(); + + if cfg!(target_os = "windows") { + let program_data_env = env::var("ProgramData").expect("ProgramData env variable"); + config_path.push(program_data_env); + config_path.push(COMPANY_DIR); + config_path.push(PROGRAM_DIR); + } else if cfg!(target_os = "macos") { + config_path.push("/Library/Application Support"); + config_path.push(APPLICATION_DIR); + } else { + config_path.push("/etc"); + config_path.push(APPLICATION_DIR); + } + + config_path + } +} + +fn get_conf_file_path() -> Utf8PathBuf { + get_data_dir().join("agent.json") +} + +fn normalize_data_path(path: &Utf8Path, data_dir: &Utf8Path) -> Utf8PathBuf { + if path.is_absolute() { + path.to_owned() + } else { + data_dir.join(path) + } +} + +fn load_conf_file(conf_path: &Utf8Path) -> anyhow::Result> { + match File::open(conf_path) { + Ok(file) => BufReader::new(file) + .pipe(serde_json::from_reader) + .map(Some) + .with_context(|| format!("invalid config file at {conf_path}")), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None), + Err(e) => Err(anyhow::anyhow!(e).context(format!("couldn't open config file at {conf_path}"))), + } +} + +pub fn load_conf_file_or_generate_new() -> anyhow::Result { + let conf_file_path = get_conf_file_path(); + + let conf_file = match load_conf_file(&conf_file_path).context("failed to load configuration")? { + Some(conf_file) => conf_file, + None => { + let defaults = dto::ConfFile::generate_new(); + println!("Write default configuration to disk…"); + save_config(&defaults).context("failed to save configuration")?; + defaults + } + }; + + Ok(conf_file) +} + +pub mod dto { + use super::*; + + /// Source of truth for Agent configuration + /// + /// This struct represents the JSON file used for configuration as close as possible + /// and is not trying to be too smart. + /// + /// Unstable options are subject to change + #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] + #[serde(rename_all = "PascalCase")] + pub struct ConfFile { + /// Verbosity profile + #[serde(skip_serializing_if = "Option::is_none")] + pub verbosity_profile: Option, + + /// (Unstable) Folder and prefix for log files + #[serde(skip_serializing_if = "Option::is_none")] + pub log_file: Option, + + /// (Unstable) Unsafe debug options for developers + #[serde(default, rename = "__debug__", skip_serializing_if = "Option::is_none")] + pub debug: Option, + + // Other unofficial options. + // This field is useful so that we can deserialize + // and then losslessly serialize back all root keys of the config file. + #[serde(flatten)] + pub rest: serde_json::Map, + } + + impl ConfFile { + pub fn generate_new() -> Self { + Self { + verbosity_profile: None, + log_file: None, + debug: None, + rest: serde_json::Map::new(), + } + } + } + + /// Verbosity profile (pre-defined tracing directives) + #[derive(PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize, Default)] + pub enum VerbosityProfile { + /// The default profile, mostly info records + #[default] + Default, + /// Recommended profile for developers + Debug, + /// Show all traces + All, + /// Only show warnings and errors + Quiet, + } + + impl VerbosityProfile { + pub fn to_log_filter(self) -> &'static str { + match self { + VerbosityProfile::Default => "info", + VerbosityProfile::Debug => "info,devolutions_agent=debug", + VerbosityProfile::All => "trace", + VerbosityProfile::Quiet => "warn", + } + } + } + + /// Unsafe debug options that should only ever be used at development stage + /// + /// These options might change or get removed without further notice. + /// + /// Note to developers: all options should be safe by default, never add an option + /// that needs to be overridden manually in order to be safe. + #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] + pub struct DebugConf { + /// Directives string in the same form as the RUST_LOG environment variable + pub log_directives: Option, + } + + /// Manual Default trait implementation just to make sure default values are deliberates + #[allow(clippy::derivable_impls)] + impl Default for DebugConf { + fn default() -> Self { + Self { log_directives: None } + } + } + + impl DebugConf { + pub fn is_default(&self) -> bool { + Self::default().eq(self) + } + } +} + +pub fn handle_cli(command: &str) -> Result<(), anyhow::Error> { + match command { + "init" => { + let _config = load_conf_file_or_generate_new()?; + } + _ => { + bail!("unknown config command: {}", command); + } + } + + Ok(()) +} diff --git a/devolutions-agent/src/log.rs b/devolutions-agent/src/log.rs new file mode 100644 index 000000000..1876d00e6 --- /dev/null +++ b/devolutions-agent/src/log.rs @@ -0,0 +1,9 @@ +use devolutions_log::StaticLogConfig; + +pub struct AgentLog; + +impl StaticLogConfig for AgentLog { + const MAX_BYTES_PER_LOG_FILE: u64 = 3_000_000; // 3 MB; + const MAX_LOG_FILES: usize = 10; + const LOG_FILE_PREFIX: &'static str = "agent"; +} diff --git a/devolutions-agent/src/main.rs b/devolutions-agent/src/main.rs index 7e5668db4..06da0d61a 100644 --- a/devolutions-agent/src/main.rs +++ b/devolutions-agent/src/main.rs @@ -1,51 +1,21 @@ +#[macro_use] +extern crate tracing; + +mod config; +mod log; +mod service; + use std::env; use std::sync::mpsc; use ceviche::controller::*; use ceviche::{Service, ServiceEvent}; -pub const SERVICE_NAME: &str = "devolutions-agent"; -pub const DISPLAY_NAME: &str = "Devolutions Agent"; -pub const DESCRIPTION: &str = "Devolutions Agent service"; -pub const COMPANY_NAME: &str = "Devolutions"; - -pub struct AgentService { - pub service_name: String, - pub display_name: String, - pub description: String, - pub company_name: String, -} - -impl AgentService { - pub fn load() -> Option { - Some(AgentService { - service_name: SERVICE_NAME.to_string(), - display_name: DISPLAY_NAME.to_string(), - description: DESCRIPTION.to_string(), - company_name: COMPANY_NAME.to_string(), - }) - } - - pub fn get_service_name(&self) -> &str { - self.service_name.as_str() - } - - pub fn get_display_name(&self) -> &str { - self.display_name.as_str() - } - - pub fn get_description(&self) -> &str { - self.service_name.as_str() - } - - pub fn get_company_name(&self) -> &str { - self.company_name.as_str() - } - - pub fn start(&self) {} +use config::ConfHandle; +use service::AgentService; - pub fn stop(&self) {} -} +const BAD_CONFIG_ERR_CODE: u32 = 1; +const START_FAILED_ERR_CODE: u32 = 2; enum AgentServiceEvent {} @@ -55,34 +25,48 @@ fn agent_service_main( _args: Vec, _standalone_mode: bool, ) -> u32 { - let service = AgentService::load().expect("unable to load agent"); + let Ok(conf_handle) = ConfHandle::init() else { + // At this point, the logger is not yet initialized. + return BAD_CONFIG_ERR_CODE; + }; + + let mut service = match AgentService::load(conf_handle) { + Ok(service) => service, + Err(error) => { + // At this point, the logger may or may not be initialized. + error!(error = format!("{error:#}"), "Failed to load service"); + return START_FAILED_ERR_CODE; + } + }; - service.start(); + match service.start() { + Ok(()) => info!("{} service started", service::SERVICE_NAME), + Err(error) => { + error!(error = format!("{error:#}"), "Failed to start"); + return START_FAILED_ERR_CODE; + } + } loop { if let Ok(control_code) = rx.recv() { - match control_code { - ServiceEvent::Stop => { - service.stop(); - break; - } - _ => (), + info!("Received control code: {}", control_code); + + if let ServiceEvent::Stop = control_code { + service.stop(); + break; } } } + info!("{} service stopping", service::SERVICE_NAME); + 0 } Service!("agent", agent_service_main); fn main() { - let service = AgentService::load().expect("unable to load agent service"); - let mut controller = Controller::new( - service.get_service_name(), - service.get_display_name(), - service.get_description(), - ); + let mut controller = Controller::new(service::SERVICE_NAME, service::DISPLAY_NAME, service::DESCRIPTION); if let Some(cmd) = env::args().nth(1) { match cmd.as_str() { @@ -117,8 +101,14 @@ fn main() { agent_service_main(rx, _tx, vec![], true); } + "config" => { + let subcommand = env::args().nth(2).expect("missing config subcommand"); + if let Err(e) = config::handle_cli(subcommand.as_str()) { + eprintln!("[ERROR] Agent configuration failed: {}", e); + } + } _ => { - println!("invalid command: {}", cmd); + eprintln!("invalid command: {}", cmd); } } } else { diff --git a/devolutions-agent/src/service.rs b/devolutions-agent/src/service.rs new file mode 100644 index 000000000..1303ff94b --- /dev/null +++ b/devolutions-agent/src/service.rs @@ -0,0 +1,180 @@ +use tokio::runtime::{self, Runtime}; + +use crate::{config::ConfHandle, log::AgentLog}; +use anyhow::Context; +use devolutions_gateway_task::{ChildTask, ShutdownHandle, ShutdownSignal}; +use devolutions_log::{self, LoggerGuard}; +use std::time::Duration; + +pub const SERVICE_NAME: &str = "devolutions-agent"; +pub const DISPLAY_NAME: &str = "Devolutions Agent"; +pub const DESCRIPTION: &str = "Devolutions Agent service"; + +#[allow(clippy::large_enum_variant)] // `Running` variant is bigger than `Stopped` but we don't care +enum AgentState { + Stopped, + Running { + shutdown_handle: ShutdownHandle, + runtime: Runtime, + }, +} + +pub struct AgentService { + conf_handle: ConfHandle, + state: AgentState, + _logger_guard: LoggerGuard, +} + +impl AgentService { + pub fn load(conf_handle: ConfHandle) -> anyhow::Result { + let conf = conf_handle.get_conf(); + + let logger_guard = devolutions_log::init::( + &conf.log_file, + conf.verbosity_profile.to_log_filter(), + conf.debug.log_directives.as_deref(), + ) + .context("failed to setup logger")?; + + info!(version = env!("CARGO_PKG_VERSION")); + + let conf_file = conf_handle.get_conf_file(); + trace!(?conf_file); + + if !conf.debug.is_default() { + warn!( + ?conf.debug, + "**DEBUG OPTIONS ARE ENABLED, PLEASE DO NOT USE IN PRODUCTION**", + ); + } + + Ok(AgentService { + conf_handle, + state: AgentState::Stopped, + _logger_guard: logger_guard, + }) + } + + pub fn start(&mut self) -> anyhow::Result<()> { + let runtime = runtime::Builder::new_multi_thread() + .enable_all() + .build() + .expect("failed to create runtime"); + + let config = self.conf_handle.clone(); + + // create_futures needs to be run in the runtime in order to bind the sockets. + let tasks = runtime.block_on(spawn_tasks(config))?; + + trace!("Tasks created"); + + let mut join_all = futures::future::select_all(tasks.inner.into_iter().map(|child| Box::pin(child.join()))); + + runtime.spawn(async { + loop { + let (result, _, rest) = join_all.await; + + match result { + Ok(Ok(())) => trace!("A task terminated gracefully"), + Ok(Err(error)) => error!(error = format!("{error:#}"), "A task failed"), + Err(error) => error!(%error, "Something went very wrong with a task"), + } + + if rest.is_empty() { + break; + } else { + join_all = futures::future::select_all(rest); + } + } + }); + + self.state = AgentState::Running { + shutdown_handle: tasks.shutdown_handle, + runtime, + }; + + Ok(()) + } + + pub fn stop(&mut self) { + match std::mem::replace(&mut self.state, AgentState::Stopped) { + AgentState::Stopped => { + info!("Attempted to stop agent service, but it's already stopped"); + } + AgentState::Running { + shutdown_handle, + runtime, + } => { + info!("Stopping agent service"); + + // Send shutdown signals to all tasks + shutdown_handle.signal(); + + runtime.block_on(async move { + const MAX_COUNT: usize = 3; + let mut count = 0; + + loop { + tokio::select! { + _ = shutdown_handle.all_closed() => { + debug!("All tasks are terminated"); + break; + } + _ = tokio::time::sleep(Duration::from_secs(10)) => { + count += 1; + + if count >= MAX_COUNT { + warn!("Terminate forcefully the lingering tasks"); + break; + } else { + warn!("Termination of certain tasks is experiencing significant delays"); + } + } + } + } + }); + + // Wait for 1 more second before forcefully shutting down the runtime + runtime.shutdown_timeout(Duration::from_secs(1)); + + self.state = AgentState::Stopped; + } + } + } +} + +struct Tasks { + inner: Vec>>, + shutdown_handle: ShutdownHandle, + shutdown_signal: ShutdownSignal, +} + +impl Tasks { + fn new() -> Self { + let (shutdown_handle, shutdown_signal) = devolutions_gateway_task::ShutdownHandle::new(); + + Self { + inner: Vec::new(), + shutdown_handle, + shutdown_signal, + } + } + + fn register(&mut self, task: T) + where + T: devolutions_gateway_task::Task> + 'static, + { + let child = devolutions_gateway_task::spawn_task(task, self.shutdown_signal.clone()); + self.inner.push(child); + } +} + +async fn spawn_tasks(conf_handle: ConfHandle) -> anyhow::Result { + let conf = conf_handle.get_conf(); + + let mut tasks = Tasks::new(); + + tasks.register(devolutions_log::LogDeleterTask::::new(conf.log_file.clone())); + + Ok(tasks) +} diff --git a/devolutions-gateway/Cargo.toml b/devolutions-gateway/Cargo.toml index 252d8ef60..d0613fe93 100644 --- a/devolutions-gateway/Cargo.toml +++ b/devolutions-gateway/Cargo.toml @@ -19,6 +19,7 @@ openapi = ["dep:utoipa"] transport = { path = "../crates/transport" } jmux-proxy = { path = "../crates/jmux-proxy" } devolutions-gateway-task = { path = "../crates/devolutions-gateway-task" } +devolutions-log = { path = "../crates/devolutions-log" } ironrdp-pdu = { version = "0.1", git = "https://github.com/Devolutions/IronRDP", rev = "4844e77b7f65024d85ba74b1824013eda6eb32b2" } ironrdp-rdcleanpath = { version = "0.1", git = "https://github.com/Devolutions/IronRDP", rev = "4844e77b7f65024d85ba74b1824013eda6eb32b2" } ceviche = "0.6" diff --git a/devolutions-gateway/src/api/diagnostics.rs b/devolutions-gateway/src/api/diagnostics.rs index bdea12f02..38daa4a2b 100644 --- a/devolutions-gateway/src/api/diagnostics.rs +++ b/devolutions-gateway/src/api/diagnostics.rs @@ -9,6 +9,7 @@ use crate::config::Conf; use crate::extract::DiagnosticsReadScope; use crate::http::HttpError; use crate::listener::ListenerUrls; +use crate::log::GatewayLog; use crate::DgwState; pub fn make_router(state: DgwState) -> Router { @@ -122,7 +123,7 @@ async fn get_logs( ) -> Result { let conf = conf_handle.get_conf(); - let latest_log_file_path = crate::log::find_latest_log_file(conf.log_file.as_path()) + let latest_log_file_path = devolutions_log::find_latest_log_file::(conf.log_file.as_path()) .await .map_err(HttpError::internal().with_msg("latest log file not found").err())?; diff --git a/devolutions-gateway/src/config.rs b/devolutions-gateway/src/config.rs index ee84c2be5..ec14b7a5d 100644 --- a/devolutions-gateway/src/config.rs +++ b/devolutions-gateway/src/config.rs @@ -1035,6 +1035,22 @@ pub mod dto { Quiet, } + impl VerbosityProfile { + pub fn to_log_filter(self) -> &'static str { + match self { + VerbosityProfile::Default => "info", + VerbosityProfile::Debug => { + "info,devolutions_gateway=debug,devolutions_gateway::api=trace,jmux_proxy=debug,tower_http=trace" + } + VerbosityProfile::Tls => { + "info,devolutions_gateway=debug,devolutions_gateway::tls=trace,rustls=trace,tokio_rustls=debug" + } + VerbosityProfile::All => "trace", + VerbosityProfile::Quiet => "warn", + } + } + } + /// Unsafe debug options that should only ever be used at development stage /// /// These options might change or get removed without further notice. diff --git a/devolutions-gateway/src/log.rs b/devolutions-gateway/src/log.rs index 2d8597584..80a580e5d 100644 --- a/devolutions-gateway/src/log.rs +++ b/devolutions-gateway/src/log.rs @@ -1,210 +1,9 @@ -use std::io; -use std::time::SystemTime; +use devolutions_log::StaticLogConfig; -use anyhow::Context as _; -use async_trait::async_trait; -use camino::{Utf8Path, Utf8PathBuf}; -use devolutions_gateway_task::{ShutdownSignal, Task}; -use tokio::fs; -use tokio::time::{sleep, Duration}; -use tracing_appender::non_blocking::WorkerGuard; -use tracing_appender::rolling; -use tracing_subscriber::prelude::*; -use tracing_subscriber::{fmt, EnvFilter}; +pub struct GatewayLog; -use crate::config::dto::VerbosityProfile; - -const MAX_BYTES_PER_LOG_FILE: u64 = 3_000_000; // 3 MB -const MAX_LOG_FILES: usize = 10; - -pub struct LoggerGuard { - _file_guard: WorkerGuard, - _stdio_guard: WorkerGuard, -} - -struct LogPathCfg<'a> { - folder: &'a Utf8Path, - prefix: &'a str, -} - -impl<'a> LogPathCfg<'a> { - pub fn from_path(path: &'a Utf8Path) -> anyhow::Result { - if path.is_dir() { - Ok(Self { - folder: path, - prefix: "gateway", - }) - } else { - Ok(Self { - folder: path.parent().context("invalid log path (parent)")?, - prefix: path.file_name().context("invalid log path (file_name)")?, - }) - } - } -} - -fn profile_to_directives(profile: VerbosityProfile) -> &'static str { - match profile { - VerbosityProfile::Default => "info", - VerbosityProfile::Debug => { - "info,devolutions_gateway=debug,devolutions_gateway::api=trace,jmux_proxy=debug,tower_http=trace" - } - VerbosityProfile::Tls => { - "info,devolutions_gateway=debug,devolutions_gateway::tls=trace,rustls=trace,tokio_rustls=debug" - } - VerbosityProfile::All => "trace", - VerbosityProfile::Quiet => "warn", - } -} - -pub fn init( - path: &Utf8Path, - profile: VerbosityProfile, - debug_filtering_directives: Option<&str>, -) -> anyhow::Result { - let log_cfg = LogPathCfg::from_path(path)?; - let file_appender = rolling::Builder::new() - .rotation(rolling::Rotation::max_bytes(MAX_BYTES_PER_LOG_FILE)) - .filename_prefix(log_cfg.prefix) - .filename_suffix("log") - .max_log_files(MAX_LOG_FILES) - .build(log_cfg.folder) - .context("couldn’t create file appender")?; - let (file_non_blocking, file_guard) = tracing_appender::non_blocking(file_appender); - let file_layer = fmt::layer().with_writer(file_non_blocking).with_ansi(false); - - let (non_blocking_stdio, stdio_guard) = tracing_appender::non_blocking(std::io::stdout()); - let stdio_layer = fmt::layer().with_writer(non_blocking_stdio); - - let env_filter = EnvFilter::try_new(profile_to_directives(profile)) - .context("invalid built-in filtering directives (this is a bug)")?; - - // Optionally add additional debugging filtering directives - let env_filter = debug_filtering_directives - .into_iter() - .flat_map(|directives| directives.split(',')) - .fold(env_filter, |env_filter, directive| { - env_filter.add_directive(directive.parse().unwrap()) - }); - - tracing_subscriber::registry() - .with(file_layer) - .with(stdio_layer) - .with(env_filter) - .init(); - - Ok(LoggerGuard { - _file_guard: file_guard, - _stdio_guard: stdio_guard, - }) -} - -/// Find latest log file (by age) -/// -/// Given path is used to filter out by file name prefix. -#[instrument] -pub async fn find_latest_log_file(prefix: &Utf8Path) -> anyhow::Result { - let cfg = LogPathCfg::from_path(prefix)?; - - let mut read_dir = fs::read_dir(cfg.folder).await.context("couldn't read directory")?; - - let mut most_recent_time = SystemTime::UNIX_EPOCH; - let mut most_recent = None; - - while let Ok(Some(entry)) = read_dir.next_entry().await { - match entry.file_name().to_str() { - Some(file_name) if file_name.starts_with(cfg.prefix) && file_name.contains("log") => { - debug!(file_name, "Found a log file"); - match entry.metadata().await.and_then(|metadata| metadata.modified()) { - Ok(modified) if modified > most_recent_time => { - most_recent_time = modified; - most_recent = Some(entry.path()); - } - Ok(_) => {} - Err(error) => { - warn!(%error, file_name, "Couldn't retrieve metadata for file"); - } - } - } - _ => continue, - } - } - - most_recent.context("no file found") -} - -/// File deletion task (by age) -/// -/// Given path is used to filter out by file name prefix. -pub struct LogDeleterTask { - pub prefix: Utf8PathBuf, -} - -#[async_trait] -impl Task for LogDeleterTask { - type Output = anyhow::Result<()>; - - const NAME: &'static str = "log deleter"; - - async fn run(self, shutdown_signal: ShutdownSignal) -> Self::Output { - log_deleter_task(&self.prefix, shutdown_signal).await - } -} - -#[instrument(skip(shutdown_signal))] -async fn log_deleter_task(prefix: &Utf8Path, mut shutdown_signal: ShutdownSignal) -> anyhow::Result<()> { - const TASK_INTERVAL: Duration = Duration::from_secs(60 * 60 * 24); // once per day - const MAX_AGE: Duration = Duration::from_secs(60 * 60 * 24 * 90); // 90 days - - debug!("Task started"); - - let cfg = LogPathCfg::from_path(prefix)?; - - loop { - match fs::read_dir(cfg.folder).await { - Ok(mut read_dir) => { - while let Ok(Some(entry)) = read_dir.next_entry().await { - match entry.file_name().to_str() { - Some(file_name) if file_name.starts_with(cfg.prefix) && file_name.contains("log") => { - debug!(file_name, "Found a log file"); - match entry - .metadata() - .await - .and_then(|metadata| metadata.modified()) - .and_then(|time| time.elapsed().map_err(|e| io::Error::new(io::ErrorKind::Other, e))) - { - Ok(modified) if modified > MAX_AGE => { - info!(file_name, "Delete log file"); - if let Err(error) = fs::remove_file(entry.path()).await { - warn!(%error, file_name, "Couldn't delete log file"); - } - } - Ok(_) => { - trace!(file_name, "Keep this log file"); - } - Err(error) => { - warn!(%error, file_name, "Couldn't retrieve metadata for file"); - } - } - } - _ => continue, - } - } - } - Err(error) => { - warn!(%error, "Couldn't read log folder"); - } - } - - tokio::select! { - _ = sleep(TASK_INTERVAL) => {} - _ = shutdown_signal.wait() => { - break; - } - } - } - - debug!("Task terminated"); - - Ok(()) +impl StaticLogConfig for GatewayLog { + const MAX_BYTES_PER_LOG_FILE: u64 = 3_000_000; // 3 MB; + const MAX_LOG_FILES: usize = 10; + const LOG_FILE_PREFIX: &'static str = "gateway"; } diff --git a/devolutions-gateway/src/service.rs b/devolutions-gateway/src/service.rs index 289914879..dceaf4cdd 100644 --- a/devolutions-gateway/src/service.rs +++ b/devolutions-gateway/src/service.rs @@ -1,13 +1,14 @@ use anyhow::Context as _; use devolutions_gateway::config::{Conf, ConfHandle}; use devolutions_gateway::listener::GatewayListener; -use devolutions_gateway::log::{self, LoggerGuard}; +use devolutions_gateway::log::GatewayLog; use devolutions_gateway::recording::recording_message_channel; use devolutions_gateway::session::session_manager_channel; use devolutions_gateway::subscriber::subscriber_channel; use devolutions_gateway::token::{CurrentJrl, JrlTokenClaims}; use devolutions_gateway::DgwState; use devolutions_gateway_task::{ChildTask, ShutdownHandle, ShutdownSignal}; +use devolutions_log::{self, LoggerGuard}; use parking_lot::Mutex; use std::sync::Arc; use std::time::Duration; @@ -37,9 +38,9 @@ impl GatewayService { pub fn load(conf_handle: ConfHandle) -> anyhow::Result { let conf = conf_handle.get_conf(); - let logger_guard = log::init( + let logger_guard = devolutions_log::init::( &conf.log_file, - conf.verbosity_profile, + conf.verbosity_profile.to_log_filter(), conf.debug.log_directives.as_deref(), ) .context("failed to setup logger")?; @@ -237,9 +238,9 @@ async fn spawn_tasks(conf_handle: ConfHandle) -> anyhow::Result { tasks.register(devolutions_gateway::token::CleanupTask { token_cache }); - tasks.register(devolutions_gateway::log::LogDeleterTask { - prefix: conf.log_file.clone(), - }); + tasks.register(devolutions_log::LogDeleterTask::::new( + conf.log_file.clone(), + )); tasks.register(devolutions_gateway::subscriber::SubscriberPollingTask { sessions: session_manager_handle, diff --git a/package/AgentLinux/Dockerfile b/package/AgentLinux/Dockerfile new file mode 100644 index 000000000..089b21ab2 --- /dev/null +++ b/package/AgentLinux/Dockerfile @@ -0,0 +1,16 @@ +FROM mcr.microsoft.com/powershell:debian-bullseye-slim +LABEL maintainer "Devolutions Inc." + +WORKDIR /opt/devolutions/agent + +ENV DAGENT_EXECUTABLE_PATH="/opt/devolutions/agent/devolutions-agent" + +COPY devolutions-agent $DAGENT_EXECUTABLE_PATH + +RUN apt-get update +RUN apt-get install -y --no-install-recommends ca-certificates curl +RUN rm -rf /var/lib/apt/lists/* + +EXPOSE 8080 + +ENTRYPOINT [ "./devolutions-agent" ] diff --git a/package/AgentLinux/agent/debian/copyright b/package/AgentLinux/agent/debian/copyright new file mode 100644 index 000000000..344399144 --- /dev/null +++ b/package/AgentLinux/agent/debian/copyright @@ -0,0 +1,8 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: devolutions-agent +Source: https://devolutions.net/agent + +Files: * +Copyright: 2020-2024 Devolutions Inc. +License: non-free + . diff --git a/package/AgentLinux/agent/debian/postinst b/package/AgentLinux/agent/debian/postinst new file mode 100644 index 000000000..641dc5c54 --- /dev/null +++ b/package/AgentLinux/agent/debian/postinst @@ -0,0 +1,16 @@ +#!/bin/sh + +# System-wide package configuration. +DEFAULTS_FILE="/etc/default/devolutions-agent" + +if [ ! -d /etc/devolutions-agent ]; then + /bin/mkdir /etc/devolutions-agent + /bin/chmod 655 /etc/devolutions-agent +fi + +if [ -d /run/systemd/system ]; then + /usr/bin/devolutions-agent service register >/dev/null + /usr/bin/devolutions-agent service config init >/dev/null + systemctl restart devolutions-agent >/dev/null 2>&1 + systemctl daemon-reload +fi diff --git a/package/AgentLinux/agent/debian/postrm b/package/AgentLinux/agent/debian/postrm new file mode 100644 index 000000000..a9cb7e8be --- /dev/null +++ b/package/AgentLinux/agent/debian/postrm @@ -0,0 +1,17 @@ +#!/bin/sh + +set -e + +action="$1" + +# Only do complete clean-up on purge. +if [ "$action" != "purge" ] ; then + exit 0 +fi + +# System-wide package configuration. +DEFAULTS_FILE="/etc/default/devolutions-agent" + +if [ -s "$DEFAULTS_FILE" ]; then + rm "$DEFAULTS_FILE" || exit 1 +fi diff --git a/package/AgentLinux/agent/debian/prerm b/package/AgentLinux/agent/debian/prerm new file mode 100644 index 000000000..853042f30 --- /dev/null +++ b/package/AgentLinux/agent/debian/prerm @@ -0,0 +1,19 @@ +#!/bin/sh + +set -e + +action="$1" +if [ "$2" = "in-favour" ]; then + # Treat conflict remove as an upgrade. + action="upgrade" +fi + +# Don't clean-up just for an upgrade.` +if [ "$action" = "upgrade" ] ; then + exit 0 +fi + +if [ -d /run/systemd/system ]; then + systemctl stop devolutions-agent >/dev/null 2>&1 + /usr/bin/devolutions-agent service unregister >/dev/null +fi diff --git a/package/AgentLinux/agent/template/control b/package/AgentLinux/agent/template/control new file mode 100644 index 000000000..b372455dd --- /dev/null +++ b/package/AgentLinux/agent/template/control @@ -0,0 +1,13 @@ +Source: devolutions-agent +Section: non-free/net +Priority: optional +Maintainer: {{ packager }} <{{ email }}> +Build-Depends: debhelper (>= 8.0.0) +Standards-Version: 3.9.4 +Homepage: {{ website }} + +Package: devolutions-agent +Architecture: {{ arch }} +Depends: {{ deps }} +Description: Devolutions Agent + {{ website }} diff --git a/package/AgentLinux/agent/template/rules b/package/AgentLinux/agent/template/rules new file mode 100644 index 000000000..84cbef0ed --- /dev/null +++ b/package/AgentLinux/agent/template/rules @@ -0,0 +1,14 @@ +#!/usr/bin/make -f +%: + dh $@ +override_dh_auto_clean: +override_dh_auto_configure: +override_dh_auto_build: +override_dh_auto_test: +override_dh_auto_install: +override_dh_usrlocal: + install -D -m 0755 {{ dagent_executable }} $$(pwd)/debian/devolutions-agent/usr/bin/devolutions-agent +override_dh_install: + dh_install +override_dh_shlibdeps: + {{ dh_shlibdeps }} \ No newline at end of file diff --git a/package/AgentLinux/template/changelog b/package/AgentLinux/template/changelog new file mode 100644 index 000000000..a7630c312 --- /dev/null +++ b/package/AgentLinux/template/changelog @@ -0,0 +1,6 @@ +{{ package }} ({{ version }}) {{ distro }}; urgency=low + + * + + -- {{ packager }} <{{ email }}> {{ date }} + diff --git a/package/AgentLinux/template/copyright b/package/AgentLinux/template/copyright new file mode 100644 index 000000000..b3eb01440 --- /dev/null +++ b/package/AgentLinux/template/copyright @@ -0,0 +1,9 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: {{ package }} +Source: {{ website }} + +Files: * +Copyright: {{ year }} {{ packager }} +License: non-free + . + diff --git a/package/AgentWindowsManaged/.editorconfig b/package/AgentWindowsManaged/.editorconfig new file mode 100644 index 000000000..690bde15e --- /dev/null +++ b/package/AgentWindowsManaged/.editorconfig @@ -0,0 +1,7 @@ +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +[*.tt] +end_of_line = crlf \ No newline at end of file diff --git a/package/AgentWindowsManaged/.gitignore b/package/AgentWindowsManaged/.gitignore new file mode 100644 index 000000000..47fa67f51 --- /dev/null +++ b/package/AgentWindowsManaged/.gitignore @@ -0,0 +1,5 @@ +Debug +Release +wix +fr-FR.* +*_missing.json \ No newline at end of file diff --git a/package/AgentWindowsManaged/Actions/Buffer.cs b/package/AgentWindowsManaged/Actions/Buffer.cs new file mode 100644 index 000000000..1e14f28f1 --- /dev/null +++ b/package/AgentWindowsManaged/Actions/Buffer.cs @@ -0,0 +1,32 @@ +using System; +using System.Runtime.InteropServices; + +namespace DevolutionsAgent.Actions; + +internal class Buffer : IDisposable +{ + public IntPtr Handle { get; } + + public Buffer(int size) + { + this.Handle = Marshal.AllocHGlobal(size); + } + + public static implicit operator IntPtr(Buffer b) => b.Handle; + + private void ReleaseUnmanagedResources() + { + Marshal.FreeHGlobal(this.Handle); + } + + public void Dispose() + { + ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); + } + + ~Buffer() + { + ReleaseUnmanagedResources(); + } +} diff --git a/package/AgentWindowsManaged/Actions/CustomActions.cs b/package/AgentWindowsManaged/Actions/CustomActions.cs new file mode 100644 index 000000000..efc1aa472 --- /dev/null +++ b/package/AgentWindowsManaged/Actions/CustomActions.cs @@ -0,0 +1,558 @@ +using DevolutionsAgent.Properties; +using DevolutionsAgent.Resources; +using Microsoft.Deployment.WindowsInstaller; +using Microsoft.Win32; +using Microsoft.Win32.SafeHandles; +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Runtime.InteropServices; +using System.ServiceProcess; +using System.Text; +using WixSharp; +using File = System.IO.File; + +namespace DevolutionsAgent.Actions +{ + public class CustomActions + { + private static readonly string[] ConfigFiles = new[] { + "agent.json", + }; + + private const int MAX_PATH = 260; // Defined in windows.h + + private static string ProgramDataDirectory => Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), + "Devolutions", "Agent"); + + [CustomAction] + public static ActionResult CheckInstalledNetFx45Version(Session session) + { + uint version = session.Get(AgentProperties.netFx45Version); + + if (version < 394802) //4.6.2 + { + session.Log($"netfx45 version: {version} is too old"); + return ActionResult.Failure; + } + + return ActionResult.Success; + } + + [CustomAction] + public static ActionResult CleanAgentConfig(Session session) + { + if (!ConfigFiles.Any(x => File.Exists(Path.Combine(ProgramDataDirectory, x)))) + { + return ActionResult.Success; + } + + try + { + string zipFile = $"{Path.Combine(Path.GetTempPath(), session.Get(AgentProperties.installId).ToString())}.zip"; + using ZipArchive archive = ZipFile.Open(zipFile, ZipArchiveMode.Create); + + WinAPI.MoveFileEx(zipFile, IntPtr.Zero, WinAPI.MOVEFILE_DELAY_UNTIL_REBOOT); + + foreach (string configFile in ConfigFiles) + { + string configFilePath = Path.Combine(ProgramDataDirectory, configFile); + + if (File.Exists(configFilePath)) + { + archive.CreateEntryFromFile(configFilePath, configFile); + } + } + + foreach (string configFile in ConfigFiles) + { + try + { + File.Delete(Path.Combine(ProgramDataDirectory, configFile)); + } + catch + { + } + } + } + catch (Exception e) + { + session.Log($"failed to archive existing config: {e}"); + return ActionResult.Failure; + } + + + return ActionResult.Success; + } + + [CustomAction] + public static ActionResult CleanAgentConfigRollback(Session session) + { + string zipFile = $"{Path.Combine(Path.GetTempPath(), session.Get(AgentProperties.installId).ToString())}.zip"; + + if (!File.Exists(zipFile)) + { + return ActionResult.Success; + } + + try + { + foreach (string configFile in ConfigFiles) + { + try + { + File.Delete(Path.Combine(ProgramDataDirectory, configFile)); + } + catch + { + } + } + + using ZipArchive archive = ZipFile.Open(zipFile, ZipArchiveMode.Read); + archive.ExtractToDirectory(ProgramDataDirectory); + + try + { + File.Delete(zipFile); + } + catch + { + } + } + catch (Exception e) + { + session.Log($"failed to restore existing config: {e}"); + return ActionResult.Failure; + } + + return ActionResult.Failure; + } + + [CustomAction] + public static ActionResult CreateProgramDataDirectory(Session session) + { + string path = ProgramDataDirectory; + + try + { + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + } + catch (Exception e) + { + session.Log($"failed to evaluate or create path {path}: {e}"); + return ActionResult.Failure; + } + + return ActionResult.Success; + } + + [CustomAction] + public static ActionResult GetInstallDirFromRegistry(Session session) + { + try + { + using RegistryKey localKey = RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, RegistryView.Registry64); + using RegistryKey agentKey = localKey.OpenSubKey($@"Software\{Includes.VENDOR_NAME}\{Includes.SHORT_NAME}"); + string installDirValue = (string)agentKey.GetValue("InstallDir"); + + if (string.IsNullOrEmpty(installDirValue)) + { + throw new Exception("failed to read installdir path from registry: path is null or empty"); + } + + session.Log($"read installdir path from registry: {installDirValue}"); + session[AgentProperties.InstallDir] = installDirValue; + + return ActionResult.Success; + } + catch (Exception e) + { + session.Log($"failed to read installdir path from registry: {e}"); + } + + return ActionResult.Failure; + } + + [CustomAction] + public static ActionResult GetInstalledNetFx45Version(Session session) + { + if (!TryGetInstalledNetFx45Version(out uint version)) + { + return ActionResult.Failure; + } + + session.Log($"read netFxRelease path from registry: {version}"); + session.Set(AgentProperties.netFx45Version, version); + + return ActionResult.Success; + } + + [CustomAction] + public static ActionResult RestartAgent(Session session) + { + try + { + using ServiceManager sm = new(WinAPI.SC_MANAGER_CONNECT, LogDelegate.WithSession(session)); + + if (!Service.TryOpen( + sm, Includes.SERVICE_NAME, + WinAPI.SERVICE_START | WinAPI.SERVICE_QUERY_STATUS | WinAPI.SERVICE_STOP, + out Service service, LogDelegate.WithSession(session))) + { + return ActionResult.Failure; + } + + using (service) + { + service.Restart(); + } + + return ActionResult.Success; + } + catch (Exception e) + { + session.Log($"failed to restart service: {e}"); + return ActionResult.Failure; + } + } + + [CustomAction] + public static ActionResult RollbackConfig(Session session) + { + string path = ProgramDataDirectory; + + foreach (string configFile in ConfigFiles.Select(x => Path.Combine(path, x))) + { + try + { + if (!File.Exists(configFile)) + { + continue; + } + + File.Delete(configFile); + } + catch (Exception e) + { + session.Log($"failed to rollback file {configFile}: {e}"); + } + } + + // Best effort, always return success + return ActionResult.Success; + } + + [CustomAction] + public static ActionResult SetInstallId(Session session) + { + session.Set(AgentProperties.installId, Guid.NewGuid()); + return ActionResult.Success; + } + + [CustomAction] + public static ActionResult SetProgramDataDirectoryPermissions(Session session) + { + try + { + SetFileSecurity(session, ProgramDataDirectory, Includes.PROGRAM_DATA_SDDL); + return ActionResult.Success; + } + catch (Exception e) + { + session.Log($"failed to set permissions: {e}"); + return ActionResult.Failure; + } + } + + [CustomAction] + public static ActionResult StartAgentIfNeeded(Session session) + { + try + { + using ServiceManager sm = new(WinAPI.SC_MANAGER_CONNECT); + + if (!Service.TryOpen(sm, Includes.SERVICE_NAME, + WinAPI.SERVICE_START | WinAPI.SERVICE_QUERY_CONFIG, + out Service service, LogDelegate.WithSession(session))) + { + return ActionResult.Failure; + } + + using (service) + { + service.StartIfNeeded(); + } + + return ActionResult.Success; + } + catch (Exception e) + { + session.Log($"failed to start service: {e}"); + return ActionResult.Failure; + } + } + + private static SafeFileHandle CreateSharedTempFile(Session session) + { + string tempPath = Path.GetTempPath(); + StringBuilder sb = new(MAX_PATH); + + if (WinAPI.GetTempFileName(tempPath, "DGW", 0, sb) == 0) + { + session.Log($"GetTempFileName failed (error: {Marshal.GetLastWin32Error()})"); + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + string tempFilePath = sb.ToString(); + + WinAPI.SECURITY_ATTRIBUTES sa = new() + { + nLength = (uint)Marshal.SizeOf(), + lpSecurityDescriptor = IntPtr.Zero, + bInheritHandle = true + }; + + using Buffer pSa = new(Marshal.SizeOf()); + Marshal.StructureToPtr(sa, pSa, false); + + SafeFileHandle handle = WinAPI.CreateFile(tempFilePath, + WinAPI.GENERIC_WRITE, WinAPI.FILE_SHARE_READ | WinAPI.FILE_SHARE_WRITE, pSa, WinAPI.CREATE_ALWAYS, + WinAPI.FILE_ATTRIBUTE_NORMAL, IntPtr.Zero); + + if (handle.IsInvalid) + { + int errno = Marshal.GetLastWin32Error(); + session.Log($"CreateFile failed (error: {errno})"); + + handle.Dispose(); + + if (!WinAPI.DeleteFile(tempFilePath)) + { + session.Log($"DeleteFile failed (error: {Marshal.GetLastWin32Error()})"); + } + + throw new Win32Exception(errno); + } + + if (!WinAPI.MoveFileEx(tempFilePath, IntPtr.Zero, WinAPI.MOVEFILE_DELAY_UNTIL_REBOOT)) + { + session.Log($"MoveFileEx failed (error: {Marshal.GetLastWin32Error()})"); + } + + return handle; + } + + private static uint ExecuteCommand(Session session, SafeFileHandle hTempFile, string command) + { + WinAPI.STARTUPINFO si = new() + { + cb = (uint)Marshal.SizeOf() + }; + + if (hTempFile.IsInvalid) + { + session.Log($"got an invalid file handle; command output will not be redirected"); + } + else + { + si.dwFlags = WinAPI.STARTF_USESTDHANDLES; + si.hStdInput = WinAPI.GetStdHandle(WinAPI.STD_INPUT_HANDLE); + si.hStdOutput = hTempFile.DangerousGetHandle(); + si.hStdError = hTempFile.DangerousGetHandle(); + } + + using Buffer pSi = new(Marshal.SizeOf()); + Marshal.StructureToPtr(si, pSi, false); + + WinAPI.PROCESS_INFORMATION pi = new(); + + using Buffer pPi = new(Marshal.SizeOf()); + Marshal.StructureToPtr(pi, pPi, false); + + if (session.Get(AgentProperties.debugPowerShell)) + { + session.Log($"Executing command: {command}"); + } + + if (!WinAPI.CreateProcess(null, command, IntPtr.Zero, IntPtr.Zero, + true, WinAPI.CREATE_NO_WINDOW, IntPtr.Zero, null, pSi, pPi)) + { + session.Log($"CreateProcess failed (error: {Marshal.GetLastWin32Error()})"); + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + uint exitCode = 1; + + try + { + pi = Marshal.PtrToStructure(pPi); + + // Give the process reasonable time to finish, don't hang the installer + if (WinAPI.WaitForSingleObject(pi.hProcess, (uint)TimeSpan.FromMinutes(1).TotalMilliseconds) != 0) // WAIT_OBJECT_0 + { + session.Log("timeout or error waiting for sub process"); + + if (!WinAPI.TerminateProcess(pi.hProcess, exitCode)) + { + session.Log($"TerminateProcess failed (error: {Marshal.GetLastWin32Error()})"); + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + } + + if (!WinAPI.GetExitCodeProcess(pi.hProcess, out exitCode)) + { + session.Log($"GetExitCodeProcess failed (error: {Marshal.GetLastWin32Error()})"); + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + if (exitCode != 0) + { + session.Log($"sub process returned a non-zero exit code: {exitCode})"); + } + } + finally + { + WinAPI.CloseHandle(pi.hProcess); + WinAPI.CloseHandle(pi.hThread); + } + + return exitCode; + } + + private static ActionResult ExecuteCommand(Session session, string command) + { + using SafeFileHandle hTempFile = CreateSharedTempFile(session); + + uint exitCode; + + try + { + exitCode = ExecuteCommand(session, hTempFile, command); + } + catch (Exception e) + { + session.Log($"command execution failure: {e}"); + + using Record record = new(3) + { + FormatString = "Command execution failure: [1]", + }; + + record.SetString(1, e.ToString()); + session.Message(InstallMessage.Error | (uint)MessageButtons.OK, record); + return ActionResult.Failure; + } + + if (exitCode != 0) + { + StringBuilder tempFilePathBuilder = new(MAX_PATH); + uint pathLength = WinAPI.GetFinalPathNameByHandle(hTempFile.DangerousGetHandle(), tempFilePathBuilder, MAX_PATH, 0); + string result = "unknown"; + + try + { + if (pathLength is > 0 and < MAX_PATH) + { + string tempFilePath = tempFilePathBuilder.ToString().TrimStart('\\', '?'); + + using FileStream fileStream = new(tempFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using StreamReader streamReader = new(fileStream); + result = streamReader.ReadToEnd(); + } + } + catch (Exception e) + { + session.Log($"error reading error from temp file: {e}"); + } + + using Record record = new(3) + { + FormatString = "Command execution failure: [1]", + }; + + hTempFile.Close(); + + record.SetString(1, result); + session.Message(InstallMessage.Error | (uint)MessageButtons.OK, record); + + return ActionResult.Failure; + } + + return ActionResult.Success; + } + + private static string FormatHttpUrl(string scheme, uint port) + { + string url = $"{scheme}://*"; + + if ((scheme.Equals(Constants.HttpProtocol) && port != 80) || (scheme.Equals(Constants.HttpsProtocol) && port != 443)) + { + url += $":{port}"; + } + + return url; + } + + public static void SetFileSecurity(Session session, string path, string sddl) + { + const uint sdRevision = 1; + IntPtr pSd = new IntPtr(); + UIntPtr pSzSd = new UIntPtr(); + + try + { + if (!WinAPI.ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl, sdRevision, out pSd, out pSzSd)) + { + session.Log($"ConvertStringSecurityDescriptorToSecurityDescriptorW failed (error: {Marshal.GetLastWin32Error()})"); + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + if (!WinAPI.SetFileSecurityW(path, WinAPI.DACL_SECURITY_INFORMATION, pSd)) + { + session.Log($"SetFileSecurityW failed (error: {Marshal.GetLastWin32Error()})"); + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + } + finally + { + if (pSd != IntPtr.Zero) + { + WinAPI.LocalFree(pSd); + } + } + } + + public static bool TryGetInstalledNetFx45Version(out uint version) + { + version = 0; + + try + { + // https://learn.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed + using RegistryKey localKey = RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, RegistryView.Registry64); + using RegistryKey netFxKey = localKey.OpenSubKey(@"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full"); + + if (netFxKey is null) + { + // If the Full subkey is missing, then .NET Framework 4.5 or above isn't installed + return false; + } + + version = Convert.ToUInt32(netFxKey.GetValue("Release")); + + return true; + } + catch + { + return false; + } + } + } +} diff --git a/package/AgentWindowsManaged/Actions/GatewayActions.cs b/package/AgentWindowsManaged/Actions/GatewayActions.cs new file mode 100644 index 000000000..2cc596651 --- /dev/null +++ b/package/AgentWindowsManaged/Actions/GatewayActions.cs @@ -0,0 +1,234 @@ +using DevolutionsAgent.Properties; +using DevolutionsAgent.Resources; +using System; +using System.Collections.Generic; +using System.Linq; +using WixSharp; +using Action = WixSharp.Action; + +namespace DevolutionsAgent.Actions; + +internal static class GatewayActions +{ + // Immediate sequence + + // Set helper properties to determine what the installer is doing + private static readonly SetPropertyAction isFirstInstall = new( + new Id(nameof(isFirstInstall)), AgentProperties.firstInstall.Id, $"{true}", Return.check, When.After, Step.FindRelatedProducts, + new Condition("NOT Installed AND NOT WIX_UPGRADE_DETECTED AND NOT WIX_DOWNGRADE_DETECTED")); + + private static readonly SetPropertyAction isUpgrading = new( + new Id(nameof(isUpgrading)), AgentProperties.upgrading.Id, $"{true}", Return.check, When.After, new Step(isFirstInstall.Id), + new Condition("WIX_UPGRADE_DETECTED AND NOT(REMOVE= \"ALL\")")); + + private static readonly SetPropertyAction isRemovingForUpgrade = new( + new Id(nameof(isRemovingForUpgrade)), AgentProperties.removingForUpgrade.Id, Return.check, When.After, Step.RemoveExistingProducts, + new Condition("(REMOVE = \"ALL\") AND UPGRADINGPRODUCTCODE")); + + private static readonly SetPropertyAction isUninstalling = new( + new Id(nameof(isUninstalling)), AgentProperties.uninstalling.Id, $"{true}", Return.check, When.After, new Step(isUpgrading.Id), + new Condition("Installed AND REMOVE AND NOT(WIX_UPGRADE_DETECTED OR UPGRADINGPRODUCTCODE)")); + + private static readonly SetPropertyAction isMaintenance = new( + new Id(nameof(isMaintenance)), AgentProperties.maintenance.Id, $"{true}", Return.check, When.After, new Step(isUninstalling.Id), + new Condition($"Installed AND NOT {AgentProperties.upgrading.Id} AND NOT {AgentProperties.uninstalling.Id} AND NOT UPGRADINGPRODUCTCODE")); + + private static readonly ManagedAction getNetFxInstalledVersion = new( + new Id($"CA.{nameof(getNetFxInstalledVersion)}"), + CustomActions.GetInstalledNetFx45Version, + Return.check, When.After, Step.LaunchConditions, Condition.Always) + { + Execute = Execute.immediate, + }; + + private static readonly ManagedAction checkNetFxInstalledVersion = new( + CustomActions.CheckInstalledNetFx45Version, + Return.check, When.After, new Step(getNetFxInstalledVersion.Id), Condition.Always) + { + Execute = Execute.immediate, + }; + + private static readonly ManagedAction setInstallId = new( + CustomActions.SetInstallId, + Return.ignore, When.After, Step.InstallInitialize, Condition.Always) + { + Execute = Execute.immediate + }; + + /// + /// Set the ARP installation location to the chosen install directory + /// + private static readonly SetPropertyAction setArpInstallLocation = new("ARPINSTALLLOCATION", $"[{AgentProperties.InstallDir}]") + { + Execute = Execute.immediate, + Sequence = Sequence.InstallExecuteSequence, + When = When.After, + Step = Step.CostFinalize, + }; + + /// + /// Read the previous installation directory from the registry into the `INSTALLDIR` property + /// + private static readonly ManagedAction getInstallDirFromRegistry = new( + CustomActions.GetInstallDirFromRegistry, + Return.ignore, + When.Before, Step.LaunchConditions, + new Condition(AgentProperties.InstallDir, string.Empty), // If the property hasn't already been explicitly set + Sequence.InstallExecuteSequence); + + // Deferred sequence + + /// + /// Create the path %programdata%\Devolutions\Gateway if it does not exist + /// + /// + /// It's hard to tell the installer not to remove directories on uninstall. Since we want this folder to persist, + /// it's easy to create it with a custom action than workaround Windows Installer. + /// + private static readonly ElevatedManagedAction createProgramDataDirectory = new( + new Id($"CA.{nameof(createProgramDataDirectory)}"), + CustomActions.CreateProgramDataDirectory, + Return.check, + When.After, Step.CreateFolders, + Condition.Always, + Sequence.InstallExecuteSequence); + + /// + /// Set or reset the ACL on %programdata%\Devolutions\Gateway + /// + private static readonly ElevatedManagedAction setProgramDataDirectoryPermissions = new( + new Id($"CA.{nameof(setProgramDataDirectoryPermissions)}"), + CustomActions.SetProgramDataDirectoryPermissions, + Return.ignore, + When.After, new Step(createProgramDataDirectory.Id), + Condition.Always, + Sequence.InstallExecuteSequence) + { + Execute = Execute.deferred, + Impersonate = false, + }; + + private static readonly ElevatedManagedAction cleanAgentConfigIfNeeded = new( + new Id($"CA.{nameof(cleanAgentConfigIfNeeded)}"), + CustomActions.CleanAgentConfig, + Return.check, + When.Before, Step.StartServices, + AgentProperties.firstInstall.Equal(true) & AgentProperties.configureAgent.Equal(true), + Sequence.InstallExecuteSequence) + { + Execute = Execute.deferred, + Impersonate = false, + UsesProperties = UseProperties(new [] { AgentProperties.installId }) + }; + + private static readonly ElevatedManagedAction cleanAgentConfigIfNeededRollback = new( + new Id($"CA.{nameof(cleanAgentConfigIfNeededRollback)}"), + CustomActions.CleanAgentConfigRollback, + Return.ignore, + When.Before, new Step(cleanAgentConfigIfNeeded.Id), + AgentProperties.firstInstall.Equal(true) & AgentProperties.configureAgent.Equal(true), + Sequence.InstallExecuteSequence) + { + Execute = Execute.rollback, + Impersonate = false, + UsesProperties = UseProperties(new[] { AgentProperties.installId }) + }; + + /// + /// Execute the installed DevolutionsAgent with the --config-init-only argument + /// + /// + /// Ensures a default configuration file is created + /// + private static readonly WixQuietExecAction initAgentConfigIfNeeded = new( + new Id($"CA.{nameof(initAgentConfigIfNeeded)}"), + $"[{AgentProperties.InstallDir}]{Includes.EXECUTABLE_NAME}", + "config init", + Return.check, + When.After, new Step(cleanAgentConfigIfNeeded.Id), + AgentProperties.firstInstall.Equal(true) & AgentProperties.configureAgent.Equal(false), + Sequence.InstallExecuteSequence) + { + Execute = Execute.deferred, + Impersonate = false, + }; + + /// + /// Start the installed DevolutionsAgent service + /// + /// + /// The service will be started if it's StartMode is "Automatic". May be overridden with the + /// public property `NoStartService`. + /// + private static readonly ElevatedManagedAction startAgentIfNeeded = new( + CustomActions.StartAgentIfNeeded, + Return.ignore, + When.After, Step.StartServices, + Condition.NOT_BeingRemoved, + Sequence.InstallExecuteSequence); + + /// + /// Attempt to restart the DevolutionsAgent service (if it's running) on maintenance installs + /// + /// + /// This was necessary in the old Wayk installer to reread configurations that may have been updated + /// by the installer. It's usefulness os questionable with Devolutions Gateway. + /// + private static readonly ElevatedManagedAction restartAgent= new( + CustomActions.RestartAgent, + Return.ignore, + When.After, Step.StartServices, + AgentProperties.maintenance.Equal(true), + Sequence.InstallExecuteSequence); + + /// + /// Attempt to rollback any configuration files created + /// + private static readonly ElevatedManagedAction rollbackConfig = new( + CustomActions.RollbackConfig, + Return.ignore, + When.Before, new Step(cleanAgentConfigIfNeeded.Id), + AgentProperties.firstInstall.Equal(true), + Sequence.InstallExecuteSequence) + { + Execute = Execute.rollback, + }; + + private static string UseProperties(IEnumerable properties) + { + if (!properties?.Any() ?? false) + { + return null; + } + + if (properties.Any(p => p.Public && !p.Secure)) // Sanity check at project build time + { + throw new Exception($"property {properties.First(p => !p.Secure).Id} must be secure"); + } + + return string.Join(";", properties.Distinct().Select(x => $"{x.Id}=[{x.Id}]")); + } + + internal static readonly Action[] Actions = + { + isFirstInstall, + isUpgrading, + isRemovingForUpgrade, + isUninstalling, + isMaintenance, + setInstallId, + getNetFxInstalledVersion, + checkNetFxInstalledVersion, + getInstallDirFromRegistry, + setArpInstallLocation, + createProgramDataDirectory, + setProgramDataDirectoryPermissions, + + cleanAgentConfigIfNeeded, + cleanAgentConfigIfNeededRollback, + + startAgentIfNeeded, + restartAgent, + rollbackConfig, + }; +} diff --git a/package/AgentWindowsManaged/Actions/LogDelegate.cs b/package/AgentWindowsManaged/Actions/LogDelegate.cs new file mode 100644 index 000000000..9547e4725 --- /dev/null +++ b/package/AgentWindowsManaged/Actions/LogDelegate.cs @@ -0,0 +1,43 @@ +using Microsoft.Deployment.WindowsInstaller; + +namespace DevolutionsAgent.Actions; + +internal interface ILogger +{ + void Log(string msg); + + void Log(string format, params object[] args); +} + +internal class NullLogger : ILogger +{ + public void Log(string msg) + { + } + + public void Log(string format, params object[] args) + { + } +} + +internal class LogDelegate : ILogger +{ + private readonly Session session; + + public LogDelegate(Session session) + { + this.session = session; + } + + public static LogDelegate WithSession(Session session) => new(session); + + public void Log(string msg) + { + this.session?.Log(msg); + } + + public void Log(string format, params object[] args) + { + this.session?.Log(format, args); + } +} diff --git a/package/AgentWindowsManaged/Actions/Service.cs b/package/AgentWindowsManaged/Actions/Service.cs new file mode 100644 index 000000000..604d1a4f6 --- /dev/null +++ b/package/AgentWindowsManaged/Actions/Service.cs @@ -0,0 +1,251 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.ServiceProcess; +using System.Threading.Tasks; +using System.Threading; + +namespace DevolutionsAgent.Actions; + +internal class Service : IDisposable +{ + private IntPtr hService = IntPtr.Zero; + + private readonly ILogger logger; + + private readonly string name; + + internal IntPtr Handle => this.hService; + + public static bool TryOpen(ServiceManager serviceManager, string name, uint desiredAccess, out Service service, ILogger logger = null) + { + service = null; + + try + { + service = new Service(serviceManager, name, desiredAccess, logger); + return true; + } + catch + { + return false; + } + } + + public Service(ServiceManager serviceManager, string name, uint desiredAccess, ILogger logger = null) + { + this.logger = logger ??= new NullLogger(); + this.name = name; + + logger.Log($"opening service {name} with desired access {desiredAccess}"); + + this.hService = WinAPI.OpenService(serviceManager.Handle, name, desiredAccess); + + if (this.hService == IntPtr.Zero) + { + logger.Log($"failed to open service {name} with desired access {desiredAccess} (error: {Marshal.GetLastWin32Error()})"); + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + } + + public void Dispose() + { + if (this.hService == IntPtr.Zero) + { + return; + } + + WinAPI.CloseServiceHandle(this.hService); + this.hService = IntPtr.Zero; + } + + public ServiceStartMode GetStartupType() + { + uint bytesNeeded = 0; + const int errorInsufficientBuffer = 122; + + if (!WinAPI.QueryServiceConfig(this.Handle, IntPtr.Zero, bytesNeeded, ref bytesNeeded)) + { + if (Marshal.GetLastWin32Error() != errorInsufficientBuffer) + { + logger.Log($"QueryServiceConfig failed (error: {Marshal.GetLastWin32Error()})"); + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + } + + using Buffer buffer = new((int)bytesNeeded); + + if (!WinAPI.QueryServiceConfig(this.Handle, buffer, bytesNeeded, ref bytesNeeded)) + { + logger.Log($"QueryServiceConfig failed (error: {Marshal.GetLastWin32Error()})"); + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + WinAPI.QUERY_SERVICE_CONFIG serviceConfig = Marshal.PtrToStructure(buffer); + logger.Log($"service {name} has start type: {serviceConfig.dwStartType}"); + return (ServiceStartMode)serviceConfig.dwStartType; + } + + public void SetStartupType(ServiceStartMode startMode) + { + logger.Log($"setting service {name} start mode to {startMode}"); + + if (!WinAPI.ChangeServiceConfig(this.Handle, WinAPI.SERVICE_NO_CHANGE, + (uint)startMode, WinAPI.SERVICE_NO_CHANGE, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, + IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero)) + { + logger.Log($"ChangeServiceConfig failed (error: {Marshal.GetLastWin32Error()})"); + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + } + + /// + /// Request the service to start if it's startup mode is "Automatic" + /// + public void StartIfNeeded() + { + ServiceStartMode currentStartMode = GetStartupType(); + + if (currentStartMode != ServiceStartMode.Automatic) + { + logger.Log($"service {name} not configured for automatic start, not starting"); + return; + } + + logger.Log($"starting service {name}"); + + if (WinAPI.StartService(this.Handle, 0, IntPtr.Zero)) + { + return; + } + + const int errorServiceAlreadyRunning = 1056; + + if (Marshal.GetLastWin32Error() != errorServiceAlreadyRunning) + { + logger.Log($"StartService failed (error: {Marshal.GetLastWin32Error()})"); + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + else + { + logger.Log($"service {name} is already running"); + } + } + + public void Restart() + { + if (!Stop(TimeSpan.FromSeconds(60))) + { + return; + } + + logger.Log($"starting service {name}"); + + if (!WinAPI.StartService(this.Handle, 0, IntPtr.Zero)) + { + logger.Log($"StartService failed (error: {Marshal.GetLastWin32Error()})"); + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + } + + // Returns `false` if the service already stopped or stop pending, `true` if we stopped the service + private bool Stop(TimeSpan timeout) + { + logger.Log($"stopping service {name}"); + + using Buffer pSsp = new(Marshal.SizeOf()); + + if (!WinAPI.QueryServiceStatusEx(this.Handle, WinAPI.SC_STATUS_PROCESS_INFO, pSsp, (uint)Marshal.SizeOf(), out var dwBytesNeeded)) + { + logger.Log($"QueryServiceStatusEx failed (error: {Marshal.GetLastWin32Error()})"); + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + WinAPI.SERVICE_STATUS_PROCESS ssp = Marshal.PtrToStructure(pSsp); + + if (ssp.dwCurrentState == WinAPI.SERVICE_STOPPED) + { + logger.Log($"service {name} is already stopped"); + return false; + } + + using CancellationTokenSource cts = new(timeout); + + while (ssp.dwCurrentState == WinAPI.SERVICE_STOP_PENDING) + { + uint waitTime = ssp.dwWaitHint / 10; + + if (waitTime < TimeSpan.FromSeconds(1).TotalMilliseconds) + { + waitTime = (uint)TimeSpan.FromSeconds(1).TotalMilliseconds; + } + else if (waitTime > TimeSpan.FromSeconds(10).TotalMilliseconds) + { + waitTime = (uint)TimeSpan.FromSeconds(10).TotalMilliseconds; + } + + logger.Log($"service {name} is stop pending; waiting {waitTime}ms"); + + Task.Delay((int)waitTime, cts.Token); + + if (!WinAPI.QueryServiceStatusEx(this.Handle, WinAPI.SC_STATUS_PROCESS_INFO, pSsp, (uint)Marshal.SizeOf(), out dwBytesNeeded)) + { + logger.Log($"QueryServiceStatusEx failed (error: {Marshal.GetLastWin32Error()})"); + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + ssp = Marshal.PtrToStructure(pSsp); + + if (ssp.dwCurrentState == WinAPI.SERVICE_STOPPED) + { + logger.Log($"service {name} is stopped"); + return false; + } + + if (cts.IsCancellationRequested) + { + logger.Log($"timeout waiting for service {name} to stop"); + throw new OperationCanceledException(); + } + } + + // Currently Devolutions Agent has no dependent services; but if it did, we would need to stop them here + + using Buffer pSs = new(Marshal.SizeOf()); + + logger.Log($"requesting service {name} to stop"); + + if (!WinAPI.ControlService(this.Handle, WinAPI.SERVICE_CONTROL_STOP, pSs)) + { + logger.Log($"ControlService failed (error: {Marshal.GetLastWin32Error()})"); + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + while (ssp.dwCurrentState != WinAPI.SERVICE_STOPPED) + { + Task.Delay((int)ssp.dwWaitHint, cts.Token); + + if (!WinAPI.QueryServiceStatusEx(this.Handle, WinAPI.SC_STATUS_PROCESS_INFO, pSsp, (uint)Marshal.SizeOf(), out dwBytesNeeded)) + { + logger.Log($"QueryServiceStatusEx failed (error: {Marshal.GetLastWin32Error()})"); + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + ssp = Marshal.PtrToStructure(pSsp); + + if (ssp.dwCurrentState == WinAPI.SERVICE_STOPPED) + { + logger.Log($"service {name} is stopped"); + return true; + } + + if (cts.IsCancellationRequested) + { + logger.Log($"timeout waiting for service {name} to stop"); + throw new OperationCanceledException(); + } + } + + return true; + } +} diff --git a/package/AgentWindowsManaged/Actions/ServiceManager.cs b/package/AgentWindowsManaged/Actions/ServiceManager.cs new file mode 100644 index 000000000..69c94d03e --- /dev/null +++ b/package/AgentWindowsManaged/Actions/ServiceManager.cs @@ -0,0 +1,38 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace DevolutionsAgent.Actions; + +internal class ServiceManager : IDisposable +{ + private IntPtr hServiceManager = IntPtr.Zero; + + private readonly ILogger logger; + + internal IntPtr Handle => this.hServiceManager; + + public ServiceManager(uint desiredAccess, ILogger logger = null) + { + this.logger = logger ??= new NullLogger(); + + this.hServiceManager = WinAPI.OpenSCManager(IntPtr.Zero, IntPtr.Zero, desiredAccess); + + if (this.hServiceManager == IntPtr.Zero) + { + logger.Log($"failed to open service manager with desired access {desiredAccess} (error: {Marshal.GetLastWin32Error()})"); + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + } + + public void Dispose() + { + if (this.hServiceManager == IntPtr.Zero) + { + return; + } + + WinAPI.CloseServiceHandle(this.hServiceManager); + this.hServiceManager = IntPtr.Zero; + } +} diff --git a/package/AgentWindowsManaged/Actions/WinAPI.cs b/package/AgentWindowsManaged/Actions/WinAPI.cs new file mode 100644 index 000000000..1b6b292b4 --- /dev/null +++ b/package/AgentWindowsManaged/Actions/WinAPI.cs @@ -0,0 +1,307 @@ +using Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace DevolutionsAgent.Actions; + +internal static class WinAPI +{ + internal static uint CREATE_ALWAYS = 2; + + internal static uint CREATE_NO_WINDOW = 0x08000000; + + internal const uint DACL_SECURITY_INFORMATION = 0x00000004; + + internal const int EM_SETCUEBANNER = 0x1501; + + internal static uint FILE_ATTRIBUTE_NORMAL = 0x00000080; + + internal static uint FILE_SHARE_READ = 0x00000001; + + internal static uint FILE_SHARE_WRITE = 0x00000002; + + internal static uint GENERIC_WRITE = 0x40000000; + + internal static uint MOVEFILE_DELAY_UNTIL_REBOOT = 0x04; + + internal const uint SC_MANAGER_ALL_ACCESS = 0xF003F; + + internal const uint SC_MANAGER_CONNECT = 0x0001; + + internal const uint SC_STATUS_PROCESS_INFO = 0; + + internal const uint SERVICE_CONTROL_STOP = 0x00000001; + + internal const uint SERVICE_NO_CHANGE = 0xFFFFFFFF; + + internal const uint SERVICE_CHANGE_CONFIG = 0x0002; + + internal const uint SERVICE_QUERY_CONFIG = 0x0001; + + internal const uint SERVICE_QUERY_STATUS = 0x0004; + + internal const uint SERVICE_START = 0x0010; + + internal const uint SERVICE_STOP = 0x0020; + + internal const uint SERVICE_STOP_PENDING = 0x00000003; + + internal const uint SERVICE_STOPPED = 0x00000001; + + internal static uint STARTF_USESTDHANDLES = 0x00000100; + + internal static int STD_INPUT_HANDLE = -10; + + internal static uint WAIT_TIMEOUT = 0x00000102; + + [StructLayout(LayoutKind.Sequential)] + internal struct QUERY_SERVICE_CONFIG + { + internal uint dwServiceType; + + internal uint dwStartType; + + internal uint dwErrorControl; + + internal IntPtr lpBinaryPathName; + + internal IntPtr lpLoadOrderGroup; + + internal uint dwTagId; + + internal IntPtr lpDependencies; + + internal IntPtr lpServiceStartName; + + internal IntPtr lpDisplayName; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct PROCESS_INFORMATION + { + internal IntPtr hProcess; + + internal IntPtr hThread; + + internal uint dwProcessId; + + internal uint dwThreadId; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct SECURITY_ATTRIBUTES + { + internal uint nLength; + internal IntPtr lpSecurityDescriptor; + [MarshalAs(UnmanagedType.Bool)] internal bool bInheritHandle; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SERVICE_STATUS + { + public uint dwServiceType; + + public uint dwCurrentState; + + public uint dwControlsAccepted; + + public uint dwWin32ExitCode; + + public uint dwServiceSpecificExitCode; + + public uint dwCheckPoint; + + public uint dwWaitHint; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct SERVICE_STATUS_PROCESS + { + internal uint dwServiceType; + + internal uint dwCurrentState; + + internal uint dwControlsAccepted; + + internal uint dwWin32ExitCode; + + internal uint dwServiceSpecificExitCode; + + internal uint dwCheckPoint; + + internal uint dwWaitHint; + + internal uint dwProcessId; + + internal uint dwServiceFlags; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct STARTUPINFO + { + internal uint cb; + + internal IntPtr lpReserved; + + internal IntPtr lpDesktop; + + internal IntPtr lpTitle; + + internal uint dwX; + + internal uint dwY; + + internal uint dwXSize; + + internal uint dwYSize; + + internal uint dwXCountChars; + + internal uint dwYCountChars; + + internal uint dwFillAttributes; + + internal uint dwFlags; + + internal short wShowWindow; + + internal short cbReserved; + + internal IntPtr lpReserved2; + + internal IntPtr hStdInput; + + internal IntPtr hStdOutput; + + internal IntPtr hStdError; + } + + [DllImport("advapi32", EntryPoint = "ChangeServiceConfigW", SetLastError = true)] + internal static extern bool ChangeServiceConfig( + IntPtr hService, + uint nServiceType, + uint nStartType, + uint nErrorControl, + IntPtr lpBinaryPathName, + IntPtr lpLoadOrderGroup, + IntPtr lpdwTagId, + IntPtr lpDependencies, + IntPtr lpServiceStartName, + IntPtr lpPassword, + IntPtr lpDisplayName); + + [DllImport("kernel32")] + internal static extern bool CloseHandle(IntPtr handle); + + [DllImport("advapi32", EntryPoint = "CloseServiceHandle")] + internal static extern int CloseServiceHandle(IntPtr hSCObject); + + [DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)] + internal static extern bool ConvertStringSecurityDescriptorToSecurityDescriptorW(string StringSecurityDescriptor, uint StringSDRevision, out IntPtr SecurityDescriptor, out UIntPtr SecurityDescriptorSize); + + + [DllImport("advapi32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ControlService(IntPtr hService, uint dwControl, IntPtr lpServiceStatus); + + [DllImport("kernel32", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern SafeFileHandle CreateFile( + [MarshalAs(UnmanagedType.LPWStr)] string lpFileName, + uint dwDesiredAccess, + uint dwShareMode, + IntPtr lpSecurityAttributes, + uint dwCreationDisposition, + uint dwFlagsAndAttributes, + IntPtr hTemplateFile + ); + + [DllImport("kernel32", EntryPoint = "CreateProcessW", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern bool CreateProcess( + [MarshalAs(UnmanagedType.LPWStr)] string lpApplicationName, + [MarshalAs(UnmanagedType.LPWStr)] string lpCommandLine, + IntPtr lpProcessAttributes, + IntPtr lpThreadAttributes, + bool bInheritHandles, + uint dwCreationFlags, + IntPtr lpEnvironment, + string lpCurrentDirectory, + IntPtr lpStartupInfo, + IntPtr lpProcessInformation); + + [DllImport("kernel32", EntryPoint = "DeleteFileW", CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool DeleteFile( + [MarshalAs(UnmanagedType.LPWStr)] string lpFileName + ); + + [DllImport("kernel32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool GetExitCodeProcess(IntPtr hProcess, out uint lpExitCode); + + [DllImport("Kernel32", EntryPoint = "GetFinalPathNameByHandleW", CharSet = CharSet.Auto, SetLastError = true)] + internal static extern uint GetFinalPathNameByHandle( + IntPtr hFile, + [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszFilePath, + uint cchFilePath, + uint dwFlags); + + [DllImport("kernel32", SetLastError = true)] + internal static extern IntPtr GetStdHandle(int nStdHandle); + + [DllImport("kernel32", EntryPoint = "GetTempFileNameW", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern uint GetTempFileName( + [MarshalAs(UnmanagedType.LPWStr)] string lpPathName, + [MarshalAs(UnmanagedType.LPWStr)] string lpPrefixString, + uint uUnique, + [Out] StringBuilder lpTempFileName); + + [DllImport("kernel32", SetLastError = true)] + internal static extern IntPtr LocalFree(IntPtr hMem); + + [DllImport("kernel32", EntryPoint = "MoveFileExW", CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool MoveFileEx( + [MarshalAs(UnmanagedType.LPWStr)] string lpExistingFileName, + IntPtr lpNewFileName, + uint dwFlags + ); + + [DllImport("advapi32", EntryPoint = "OpenSCManagerW", SetLastError = true)] + internal static extern IntPtr OpenSCManager(IntPtr machineName, IntPtr databaseName, uint dwAccess); + + [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern IntPtr OpenService( + IntPtr hSCManager, + [MarshalAs(UnmanagedType.LPWStr)] string lpServiceName, + uint dwDesiredAccess); + + [DllImport("advapi32", EntryPoint = "QueryServiceConfigW", SetLastError = true)] + internal static extern bool QueryServiceConfig(IntPtr hService, IntPtr lpServiceConfig, uint cbBufSize, + ref uint pcbBytesNeeded); + + [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool QueryServiceStatusEx(IntPtr serviceHandle, uint infoLevel, IntPtr buffer, uint bufferSize, + out uint bytesNeeded); + + [DllImport("user32", EntryPoint = "SendMessageW", CharSet = CharSet.Unicode)] + internal static extern int SendMessage( + IntPtr hWnd, + int msg, + int wParam, + [MarshalAs(UnmanagedType.LPWStr)] string lParam); + + [DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)] + internal static extern bool SetFileSecurityW(string lpFileName, uint SecurityInformation, IntPtr pSecurityDescriptor); + + [DllImport("advapi32", EntryPoint = "StartServiceW", SetLastError = true)] + internal static extern bool StartService(IntPtr hService, uint dwNumServiceArgs, IntPtr lpServiceArgVectors); + + [DllImport("kernel32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool TerminateProcess(IntPtr hProcess, uint uExitCode); + + [DllImport("kernel32", SetLastError = true)] + internal static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds); +} diff --git a/package/AgentWindowsManaged/App.config b/package/AgentWindowsManaged/App.config new file mode 100644 index 000000000..6c301d331 --- /dev/null +++ b/package/AgentWindowsManaged/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/package/AgentWindowsManaged/Controls/ElevatedButton.cs b/package/AgentWindowsManaged/Controls/ElevatedButton.cs new file mode 100644 index 000000000..e709e6b27 --- /dev/null +++ b/package/AgentWindowsManaged/Controls/ElevatedButton.cs @@ -0,0 +1,44 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.Security.Principal; +using System.Windows.Forms; + +namespace DevolutionsAgent.Controls +{ + public class ElevatedButton : Button + { + /// + /// The constructor to create the button with a UAC shield if necessary. + /// + public ElevatedButton() + { + FlatStyle = FlatStyle.System; + + if (LicenseManager.UsageMode != LicenseUsageMode.Designtime) + { + if (!IsElevated()) ShowShield(); + } + } + + + [DllImport("user32.dll")] + private static extern IntPtr SendMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam); + + private uint BCM_SETSHIELD = 0x0000160C; + + private bool IsElevated() + { + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + WindowsPrincipal principal = new WindowsPrincipal(identity); + return principal.IsInRole(WindowsBuiltInRole.Administrator); + } + + private void ShowShield() + { + IntPtr wParam = new IntPtr(0); + IntPtr lParam = new IntPtr(1); + SendMessage(new HandleRef(this, Handle), BCM_SETSHIELD, wParam, lParam); + } + } +} diff --git a/package/AgentWindowsManaged/DevolutionsAgent.csproj b/package/AgentWindowsManaged/DevolutionsAgent.csproj new file mode 100644 index 000000000..3c48c0344 --- /dev/null +++ b/package/AgentWindowsManaged/DevolutionsAgent.csproj @@ -0,0 +1,83 @@ + + + + Exe + net452 + latest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.1\System.ServiceProcess.dll + + + + + + + True + True + AgentProperties.g.tt + + + True + True + AgentProperties.g.tt + + + True + True + Strings.g.tt + + + True + True + AgentProperties.g.tt + + + True + True + Strings.g.tt + + + + + + + + + + TextTemplatingFileGenerator + AgentProperties.g.cs + + + TextTemplatingFileGenerator + Strings.g.cs + + + diff --git a/package/AgentWindowsManaged/DevolutionsAgent.csproj.user b/package/AgentWindowsManaged/DevolutionsAgent.csproj.user new file mode 100644 index 000000000..7e9b5231d --- /dev/null +++ b/package/AgentWindowsManaged/DevolutionsAgent.csproj.user @@ -0,0 +1,30 @@ + + + + + + Component + + + Form + + + Form + + + Form + + + Form + + + Form + + + Form + + + Form + + + \ No newline at end of file diff --git a/package/AgentWindowsManaged/DevolutionsAgent.sln b/package/AgentWindowsManaged/DevolutionsAgent.sln new file mode 100644 index 000000000..e78e9a870 --- /dev/null +++ b/package/AgentWindowsManaged/DevolutionsAgent.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34221.43 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevolutionsAgent", "DevolutionsAgent.csproj", "{AEF5323D-2509-440D-B3F6-6F36B071A8AA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AEF5323D-2509-440D-B3F6-6F36B071A8AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AEF5323D-2509-440D-B3F6-6F36B071A8AA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AEF5323D-2509-440D-B3F6-6F36B071A8AA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AEF5323D-2509-440D-B3F6-6F36B071A8AA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6B26DD11-2DE8-46E4-AC4B-6EF6BB032E0F} + EndGlobalSection +EndGlobal diff --git a/package/AgentWindowsManaged/Dialogs/AgentDialog.cs b/package/AgentWindowsManaged/Dialogs/AgentDialog.cs new file mode 100644 index 000000000..a6b53f0ef --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/AgentDialog.cs @@ -0,0 +1,83 @@ +using System; +using System.Windows.Forms; +using DevolutionsAgent.Resources; +using WixSharp; +using WixSharp.UI.Forms; + +namespace DevolutionsAgent.Dialogs; + +public class AgentDialog : ManagedForm +{ + public AgentDialog() + { + this.AutoScaleMode = AutoScaleMode.Font; + } + + public virtual void FromProperties() + { + } + + public virtual bool ToProperties() => true; + + public virtual bool DoValidate() => true; + + public virtual void OnLoad(object sender, EventArgs e) + { + this.Text = "[AgentDlg_Title]".LocalizeWith(this.MsiRuntime.Localize); + + this.FromProperties(); + } + + protected virtual void Back_Click(object sender, EventArgs e) + { + if (this.ToProperties()) + { + Shell.GoTo(Wizard.GetPrevious(this)); + } + } + + protected virtual void Next_Click(object sender, EventArgs e) + { + if (!this.DoValidate()) + { + return; + } + + if (this.ToProperties()) + { + Shell.GoTo(Wizard.GetNext(this)); + } + } + + protected virtual void Cancel_Click(object sender, EventArgs e) + { + if (MessageBox.Show( + this.Localize("[CancelDlgText]"), + this.Localize("[CancelDlg_Title]"), + MessageBoxButtons.YesNo, + MessageBoxIcon.Warning) == DialogResult.Yes) + { + Shell.Cancel(); + } + } + + protected string I18n(string key) => MsiRuntime.I18n(key); + + protected void ShowValidationError(string message = null) + { + string errorMessage = string.IsNullOrEmpty(message) ? MsiRuntime.I18n(Strings.ThereIsAProblemWithTheEnteredData) : message; + + this.ShowValidationErrorString(errorMessage); + } + + protected void ShowValidationErrorString(string message) + { + MessageBox.Show( + message, + this.Localize("[AgentDlg_Title]"), + MessageBoxButtons.OK, + MessageBoxIcon.Warning); + } + + protected string Localize(string message) => message.LocalizeWith(MsiRuntime.Localize); +} diff --git a/package/AgentWindowsManaged/Dialogs/ExitDialog.Designer.cs b/package/AgentWindowsManaged/Dialogs/ExitDialog.Designer.cs new file mode 100644 index 000000000..ae11228a7 --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/ExitDialog.Designer.cs @@ -0,0 +1,239 @@ +using WixSharp; +using WixSharp.UI.Forms; + +namespace WixSharpSetup.Dialogs +{ + partial class ExitDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.imgPanel = new System.Windows.Forms.Panel(); + this.textPanel = new System.Windows.Forms.Panel(); + this.title = new System.Windows.Forms.Label(); + this.description = new System.Windows.Forms.Label(); + this.image = new System.Windows.Forms.PictureBox(); + this.bottomPanel = new System.Windows.Forms.Panel(); + this.viewLog = new System.Windows.Forms.LinkLabel(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.back = new System.Windows.Forms.Button(); + this.next = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.border1 = new System.Windows.Forms.Panel(); + this.imgPanel.SuspendLayout(); + this.textPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.image)).BeginInit(); + this.bottomPanel.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // imgPanel + // + this.imgPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.imgPanel.Controls.Add(this.textPanel); + this.imgPanel.Controls.Add(this.image); + this.imgPanel.Location = new System.Drawing.Point(0, 0); + this.imgPanel.Name = "imgPanel"; + this.imgPanel.Size = new System.Drawing.Size(494, 312); + this.imgPanel.TabIndex = 8; + // + // textPanel + // + this.textPanel.Controls.Add(this.title); + this.textPanel.Controls.Add(this.description); + this.textPanel.Location = new System.Drawing.Point(162, 12); + this.textPanel.Name = "textPanel"; + this.textPanel.Size = new System.Drawing.Size(320, 289); + this.textPanel.TabIndex = 8; + // + // title + // + this.title.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.title.BackColor = System.Drawing.Color.Transparent; + this.title.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.title.Location = new System.Drawing.Point(3, 0); + this.title.Name = "title"; + this.title.Size = new System.Drawing.Size(314, 61); + this.title.TabIndex = 6; + this.title.Text = "[ExitDialogTitle]"; + // + // description + // + this.description.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.description.BackColor = System.Drawing.Color.Transparent; + this.description.Location = new System.Drawing.Point(4, 88); + this.description.Name = "description"; + this.description.Size = new System.Drawing.Size(313, 201); + this.description.TabIndex = 7; + this.description.Text = "[ExitDialogDescription]"; + // + // image + // + this.image.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.image.Location = new System.Drawing.Point(0, 0); + this.image.Name = "image"; + this.image.Size = new System.Drawing.Size(494, 312); + this.image.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; + this.image.TabIndex = 4; + this.image.TabStop = false; + // + // bottomPanel + // + this.bottomPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.bottomPanel.BackColor = System.Drawing.SystemColors.Control; + this.bottomPanel.Controls.Add(this.viewLog); + this.bottomPanel.Controls.Add(this.tableLayoutPanel1); + this.bottomPanel.Controls.Add(this.border1); + this.bottomPanel.Location = new System.Drawing.Point(0, 312); + this.bottomPanel.Name = "bottomPanel"; + this.bottomPanel.Size = new System.Drawing.Size(494, 49); + this.bottomPanel.TabIndex = 5; + // + // viewLog + // + this.viewLog.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.viewLog.AutoSize = true; + this.viewLog.BackColor = System.Drawing.Color.Transparent; + this.viewLog.Location = new System.Drawing.Point(16, 17); + this.viewLog.Name = "viewLog"; + this.viewLog.Size = new System.Drawing.Size(85, 13); + this.viewLog.TabIndex = 1; + this.viewLog.TabStop = true; + this.viewLog.Text = "[ViewLogButton]"; + this.viewLog.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.viewLog_LinkClicked); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.tableLayoutPanel1.ColumnCount = 5; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 14F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.Controls.Add(this.back, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.next, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.cancel, 4, 0); + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 3); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 1; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(491, 43); + this.tableLayoutPanel1.TabIndex = 0; + // + // back + // + this.back.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.back.AutoSize = true; + this.back.Enabled = false; + this.back.Location = new System.Drawing.Point(218, 10); + this.back.MinimumSize = new System.Drawing.Size(75, 0); + this.back.Name = "back"; + this.back.Size = new System.Drawing.Size(77, 23); + this.back.TabIndex = 1; + this.back.Text = "[WixUIBack]"; + this.back.UseVisualStyleBackColor = true; + // + // next + // + this.next.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.next.AutoSize = true; + this.next.Location = new System.Drawing.Point(301, 10); + this.next.MinimumSize = new System.Drawing.Size(75, 0); + this.next.Name = "next"; + this.next.Size = new System.Drawing.Size(81, 23); + this.next.TabIndex = 0; + this.next.Text = "[WixUIFinish]"; + this.next.UseVisualStyleBackColor = true; + this.next.Click += new System.EventHandler(this.finish_Click); + // + // cancel + // + this.cancel.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.cancel.AutoSize = true; + this.cancel.Enabled = false; + this.cancel.Location = new System.Drawing.Point(402, 10); + this.cancel.MinimumSize = new System.Drawing.Size(75, 0); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(86, 23); + this.cancel.TabIndex = 2; + this.cancel.Text = "[WixUICancel]"; + this.cancel.UseVisualStyleBackColor = true; + // + // border1 + // + this.border1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.border1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.border1.Location = new System.Drawing.Point(0, 0); + this.border1.Name = "border1"; + this.border1.Size = new System.Drawing.Size(494, 1); + this.border1.TabIndex = 9; + // + // ExitDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.BackColor = System.Drawing.Color.White; + this.ClientSize = new System.Drawing.Size(494, 361); + this.Controls.Add(this.imgPanel); + this.Controls.Add(this.bottomPanel); + this.Name = "ExitDialog"; + this.Text = "[ExitDialog_Title]"; + this.Load += new System.EventHandler(this.OnLoad); + this.imgPanel.ResumeLayout(false); + this.textPanel.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.image)).EndInit(); + this.bottomPanel.ResumeLayout(false); + this.bottomPanel.PerformLayout(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Label description; + private System.Windows.Forms.Label title; + private System.Windows.Forms.Panel bottomPanel; + private System.Windows.Forms.PictureBox image; + private System.Windows.Forms.LinkLabel viewLog; + private System.Windows.Forms.Panel imgPanel; + private System.Windows.Forms.Panel border1; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Button back; + private System.Windows.Forms.Button next; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Panel textPanel; + } +} \ No newline at end of file diff --git a/package/AgentWindowsManaged/Dialogs/ExitDialog.cs b/package/AgentWindowsManaged/Dialogs/ExitDialog.cs new file mode 100644 index 000000000..6e660e475 --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/ExitDialog.cs @@ -0,0 +1,62 @@ +using System; +using DevolutionsAgent.Dialogs; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Windows.Forms; + +namespace WixSharpSetup.Dialogs; + +public partial class ExitDialog : AgentDialog +{ + public ExitDialog() + { + InitializeComponent(); + + this.textPanel.BackColor = Color.FromArgb(233, 233, 233); + } + + public override void OnLoad(object sender, System.EventArgs e) + { + image.Image = Runtime.Session.GetResourceBitmap("WixUI_Bmp_Dialog"); + + if (Shell.UserInterrupted || Shell.Log.Contains("User cancelled installation.")) + { + title.Text = "[UserExitTitle]"; + description.Text = "[UserExitDescription1]"; + this.Localize(); + } + else if (Shell.ErrorDetected) + { + title.Text = "[FatalErrorTitle]"; + description.Text = Shell.CustomErrorDescription ?? "[FatalErrorDescription1]"; + this.Localize(); + } + + base.OnLoad(sender, e); + } + + void finish_Click(object sender, System.EventArgs e) + { + Shell.Exit(); + } + + void viewLog_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + try + { + string wixSharpDir = Path.Combine(Path.GetTempPath(), @"WixSharp"); + if (!Directory.Exists(wixSharpDir)) + Directory.CreateDirectory(wixSharpDir); + + string logFile = Path.Combine(wixSharpDir, Runtime.ProductName + ".log"); + File.WriteAllText(logFile, Shell.Log); + Process.Start(logFile); + } + catch + { + //Catch all, we don't want the installer to crash in an + //attempt to view the log. + } + } +} diff --git a/package/AgentWindowsManaged/Dialogs/ExitDialog.resx b/package/AgentWindowsManaged/Dialogs/ExitDialog.resx new file mode 100644 index 000000000..1af7de150 --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/ExitDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/package/AgentWindowsManaged/Dialogs/InstallDirDialog.Designer.cs b/package/AgentWindowsManaged/Dialogs/InstallDirDialog.Designer.cs new file mode 100644 index 000000000..0f148c8fc --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/InstallDirDialog.Designer.cs @@ -0,0 +1,315 @@ +using WixSharp; +using WixSharp.UI.Forms; + +namespace WixSharpSetup.Dialogs +{ + partial class InstallDirDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); + this.copyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.middlePanel = new System.Windows.Forms.Panel(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.change = new System.Windows.Forms.Button(); + this.label3 = new System.Windows.Forms.Label(); + this.installDir = new System.Windows.Forms.TextBox(); + this.topBorder = new System.Windows.Forms.Panel(); + this.topPanel = new System.Windows.Forms.Panel(); + this.label2 = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.banner = new System.Windows.Forms.PictureBox(); + this.bottomPanel = new System.Windows.Forms.Panel(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.back = new System.Windows.Forms.Button(); + this.next = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.border1 = new System.Windows.Forms.Panel(); + this.contextMenuStrip1.SuspendLayout(); + this.middlePanel.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.topPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.banner)).BeginInit(); + this.bottomPanel.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // contextMenuStrip1 + // + this.contextMenuStrip1.ImageScalingSize = new System.Drawing.Size(32, 32); + this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.copyToolStripMenuItem}); + this.contextMenuStrip1.Name = "contextMenuStrip1"; + this.contextMenuStrip1.Size = new System.Drawing.Size(103, 26); + // + // copyToolStripMenuItem + // + this.copyToolStripMenuItem.Name = "copyToolStripMenuItem"; + this.copyToolStripMenuItem.Size = new System.Drawing.Size(102, 22); + this.copyToolStripMenuItem.Text = "Copy"; + // + // middlePanel + // + this.middlePanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.middlePanel.Controls.Add(this.tableLayoutPanel2); + this.middlePanel.Location = new System.Drawing.Point(22, 75); + this.middlePanel.Name = "middlePanel"; + this.middlePanel.Size = new System.Drawing.Size(449, 139); + this.middlePanel.TabIndex = 0; + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.ColumnCount = 1; + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.Controls.Add(this.change, 0, 2); + this.tableLayoutPanel2.Controls.Add(this.label3, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.installDir, 0, 1); + this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 3; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.Size = new System.Drawing.Size(449, 139); + this.tableLayoutPanel2.TabIndex = 0; + // + // change + // + this.change.AutoSize = true; + this.change.Location = new System.Drawing.Point(3, 48); + this.change.Name = "change"; + this.change.Size = new System.Drawing.Size(116, 23); + this.change.TabIndex = 1; + this.change.Text = "[InstallDirDlgChange]"; + this.change.UseVisualStyleBackColor = true; + this.change.Click += new System.EventHandler(this.Change_Click); + // + // label3 + // + this.label3.AutoSize = true; + this.label3.BackColor = System.Drawing.Color.Transparent; + this.label3.Location = new System.Drawing.Point(3, 3); + this.label3.Margin = new System.Windows.Forms.Padding(3); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(124, 13); + this.label3.TabIndex = 11; + this.label3.Text = "[InstallDirDlgFolderLabel]"; + // + // installDir + // + this.installDir.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.installDir.Location = new System.Drawing.Point(3, 22); + this.installDir.Name = "installDir"; + this.installDir.Size = new System.Drawing.Size(443, 20); + this.installDir.TabIndex = 0; + // + // topBorder + // + this.topBorder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.topBorder.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.topBorder.Location = new System.Drawing.Point(0, 58); + this.topBorder.Name = "topBorder"; + this.topBorder.Size = new System.Drawing.Size(494, 1); + this.topBorder.TabIndex = 15; + // + // topPanel + // + this.topPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.topPanel.BackColor = System.Drawing.SystemColors.Control; + this.topPanel.Controls.Add(this.label2); + this.topPanel.Controls.Add(this.label1); + this.topPanel.Controls.Add(this.banner); + this.topPanel.Location = new System.Drawing.Point(0, 0); + this.topPanel.Name = "topPanel"; + this.topPanel.Size = new System.Drawing.Size(494, 58); + this.topPanel.TabIndex = 10; + // + // label2 + // + this.label2.AutoEllipsis = true; + this.label2.BackColor = System.Drawing.Color.Transparent; + this.label2.ForeColor = System.Drawing.SystemColors.HighlightText; + this.label2.Location = new System.Drawing.Point(18, 31); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(409, 24); + this.label2.TabIndex = 1; + this.label2.Text = "[InstallDirDlgDescription]"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.BackColor = System.Drawing.Color.Transparent; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.ForeColor = System.Drawing.SystemColors.HighlightText; + this.label1.Location = new System.Drawing.Point(11, 8); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(109, 13); + this.label1.TabIndex = 1; + this.label1.Text = "[InstallDirDlgTitle]"; + // + // banner + // + this.banner.BackColor = System.Drawing.Color.White; + this.banner.Location = new System.Drawing.Point(0, 0); + this.banner.Name = "banner"; + this.banner.Size = new System.Drawing.Size(494, 58); + this.banner.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; + this.banner.TabIndex = 0; + this.banner.TabStop = false; + // + // bottomPanel + // + this.bottomPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.bottomPanel.BackColor = System.Drawing.SystemColors.Control; + this.bottomPanel.Controls.Add(this.tableLayoutPanel1); + this.bottomPanel.Controls.Add(this.border1); + this.bottomPanel.Location = new System.Drawing.Point(0, 312); + this.bottomPanel.Name = "bottomPanel"; + this.bottomPanel.Size = new System.Drawing.Size(494, 49); + this.bottomPanel.TabIndex = 9; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.tableLayoutPanel1.ColumnCount = 5; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 14F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.Controls.Add(this.back, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.next, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.cancel, 4, 0); + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 3); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 1; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(493, 43); + this.tableLayoutPanel1.TabIndex = 8; + // + // back + // + this.back.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.back.AutoSize = true; + this.back.Location = new System.Drawing.Point(224, 10); + this.back.MinimumSize = new System.Drawing.Size(75, 0); + this.back.Name = "back"; + this.back.Size = new System.Drawing.Size(77, 23); + this.back.TabIndex = 1; + this.back.Text = "[WixUIBack]"; + this.back.UseVisualStyleBackColor = true; + this.back.Click += new System.EventHandler(this.Back_Click); + // + // next + // + this.next.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.next.AutoSize = true; + this.next.Location = new System.Drawing.Point(307, 10); + this.next.MinimumSize = new System.Drawing.Size(75, 0); + this.next.Name = "next"; + this.next.Size = new System.Drawing.Size(77, 23); + this.next.TabIndex = 0; + this.next.Text = "[WixUINext]"; + this.next.UseVisualStyleBackColor = true; + this.next.Click += new System.EventHandler(this.Next_Click); + // + // cancel + // + this.cancel.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.cancel.AutoSize = true; + this.cancel.Location = new System.Drawing.Point(404, 10); + this.cancel.MinimumSize = new System.Drawing.Size(75, 0); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(86, 23); + this.cancel.TabIndex = 2; + this.cancel.Text = "[WixUICancel]"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.Cancel_Click); + // + // border1 + // + this.border1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.border1.Dock = System.Windows.Forms.DockStyle.Top; + this.border1.Location = new System.Drawing.Point(0, 0); + this.border1.Name = "border1"; + this.border1.Size = new System.Drawing.Size(494, 1); + this.border1.TabIndex = 14; + // + // InstallDirDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.ClientSize = new System.Drawing.Size(494, 361); + this.Controls.Add(this.middlePanel); + this.Controls.Add(this.topBorder); + this.Controls.Add(this.topPanel); + this.Controls.Add(this.bottomPanel); + this.Name = "InstallDirDialog"; + this.Load += new System.EventHandler(this.OnLoad); + this.contextMenuStrip1.ResumeLayout(false); + this.middlePanel.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + this.topPanel.ResumeLayout(false); + this.topPanel.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.banner)).EndInit(); + this.bottomPanel.ResumeLayout(false); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.PictureBox banner; + private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; + private System.Windows.Forms.ToolStripMenuItem copyToolStripMenuItem; + private System.Windows.Forms.Panel topPanel; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Panel bottomPanel; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Button change; + private System.Windows.Forms.TextBox installDir; + private System.Windows.Forms.Panel border1; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Button back; + private System.Windows.Forms.Button next; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Panel topBorder; + private System.Windows.Forms.Panel middlePanel; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + } +} \ No newline at end of file diff --git a/package/AgentWindowsManaged/Dialogs/InstallDirDialog.cs b/package/AgentWindowsManaged/Dialogs/InstallDirDialog.cs new file mode 100644 index 000000000..4a61c17b5 --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/InstallDirDialog.cs @@ -0,0 +1,90 @@ +using DevolutionsAgent.Dialogs; +using DevolutionsAgent.Properties; +using DevolutionsAgent.Resources; + +using Microsoft.Win32; + +using System; +using System.Windows.Forms; + +using WixSharp; + +namespace WixSharpSetup.Dialogs; + +public partial class InstallDirDialog : AgentDialog +{ + public InstallDirDialog() + { + InitializeComponent(); + label1.MakeTransparentOn(banner); + label2.MakeTransparentOn(banner); + } + + public override bool ToProperties() + { + Runtime.Session[AgentProperties.InstallDir] = installDir.Text; + + return true; + } + + public override void OnLoad(object sender, EventArgs e) + { + banner.Image = Runtime.Session.GetResourceBitmap("WixUI_Bmp_Banner"); + + string installDirProperty = Runtime.Session.Property("WixSharp_UI_INSTALLDIR"); + string installDirValue = Runtime.Session.Property(installDirProperty); + + if (string.IsNullOrEmpty(installDirValue)) + { + try + { + RegistryKey localKey = RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, RegistryView.Registry64); + RegistryKey gatewayKey = localKey.OpenSubKey($@"Software\{Includes.VENDOR_NAME}\{Includes.SHORT_NAME}"); + installDirValue = (string)gatewayKey.GetValue("InstallDir"); + } + catch + { + } + + if (string.IsNullOrEmpty(installDirValue)) + { + //We are executed before any of the MSI actions are invoked so the INSTALLDIR (if set to absolute path) + //is not resolved yet. So we need to do it manually + this.installDir.Text = Runtime.Session.GetDirectoryPath(installDirProperty); + + if (this.installDir.Text == "ABSOLUTEPATH") + this.installDir.Text = Runtime.Session.Property("INSTALLDIR_ABSOLUTEPATH"); + } + else + { + this.installDir.Text = installDirValue; + } + } + else + { + //INSTALLDIR set either from the command line or by one of the early setup events (e.g. UILoaded) + this.installDir.Text = installDirValue; + } + + base.OnLoad(sender, e); + } + + // ReSharper disable once RedundantOverriddenMember + protected override void Back_Click(object sender, EventArgs e) => base.Back_Click(sender, e); + + // ReSharper disable once RedundantOverriddenMember + protected override void Next_Click(object sender, EventArgs e) => base.Next_Click(sender, e); + + // ReSharper disable once RedundantOverriddenMember + protected override void Cancel_Click(object sender, EventArgs e) => base.Cancel_Click(sender, e); + + void Change_Click(object sender, EventArgs e) + { + using var dialog = new FolderBrowserDialog { SelectedPath = installDir.Text }; + + if (dialog.ShowDialog(this) == DialogResult.OK) + { + installDir.Text = dialog.SelectedPath; + } + } +} diff --git a/package/AgentWindowsManaged/Dialogs/InstallDirDialog.resx b/package/AgentWindowsManaged/Dialogs/InstallDirDialog.resx new file mode 100644 index 000000000..ad537526f --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/InstallDirDialog.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/package/AgentWindowsManaged/Dialogs/MaintenanceTypeDialog.Designer.cs b/package/AgentWindowsManaged/Dialogs/MaintenanceTypeDialog.Designer.cs new file mode 100644 index 000000000..250b613b4 --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/MaintenanceTypeDialog.Designer.cs @@ -0,0 +1,388 @@ +using DevolutionsAgent.Controls; +using WixSharp; +using WixSharp.UI.Forms; + +namespace WixSharpSetup.Dialogs +{ + partial class MaintenanceTypeDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.topBorder = new System.Windows.Forms.Panel(); + this.label5 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.remove = new DevolutionsAgent.Controls.ElevatedButton(); + this.repair = new DevolutionsAgent.Controls.ElevatedButton(); + this.change = new DevolutionsAgent.Controls.ElevatedButton(); + this.topPanel = new System.Windows.Forms.Panel(); + this.label2 = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.banner = new System.Windows.Forms.PictureBox(); + this.bottomPanel = new System.Windows.Forms.Panel(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.back = new System.Windows.Forms.Button(); + this.next = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.border1 = new System.Windows.Forms.Panel(); + this.middlePanel = new System.Windows.Forms.TableLayoutPanel(); + this.panel3 = new System.Windows.Forms.Panel(); + this.panel4 = new System.Windows.Forms.Panel(); + this.panel5 = new System.Windows.Forms.Panel(); + this.topPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.banner)).BeginInit(); + this.bottomPanel.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.middlePanel.SuspendLayout(); + this.panel3.SuspendLayout(); + this.panel4.SuspendLayout(); + this.panel5.SuspendLayout(); + this.SuspendLayout(); + // + // topBorder + // + this.topBorder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.topBorder.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.topBorder.Location = new System.Drawing.Point(0, 58); + this.topBorder.Name = "topBorder"; + this.topBorder.Size = new System.Drawing.Size(494, 1); + this.topBorder.TabIndex = 18; + // + // label5 + // + this.label5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label5.AutoEllipsis = true; + this.label5.BackColor = System.Drawing.Color.Transparent; + this.label5.Location = new System.Drawing.Point(28, 40); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(442, 38); + this.label5.TabIndex = 1; + this.label5.Text = "[MaintenanceTypeDlgRemoveText]"; + // + // label4 + // + this.label4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label4.AutoEllipsis = true; + this.label4.BackColor = System.Drawing.Color.Transparent; + this.label4.Location = new System.Drawing.Point(28, 40); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(440, 34); + this.label4.TabIndex = 1; + this.label4.Text = "[MaintenanceTypeDlgRepairText]"; + // + // label3 + // + this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label3.AutoEllipsis = true; + this.label3.BackColor = System.Drawing.Color.Transparent; + this.label3.Location = new System.Drawing.Point(28, 40); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(440, 34); + this.label3.TabIndex = 1; + this.label3.Text = "[MaintenanceTypeDlgChangeDisabledText]"; + // + // remove + // + this.remove.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.remove.AutoSize = true; + this.remove.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.remove.Location = new System.Drawing.Point(0, 5); + this.remove.MaximumSize = new System.Drawing.Size(113, 0); + this.remove.MinimumSize = new System.Drawing.Size(113, 0); + this.remove.Name = "remove"; + this.remove.Size = new System.Drawing.Size(113, 22); + this.remove.TabIndex = 0; + this.remove.Text = "[MaintenanceTypeDlgRemoveButton]"; + this.remove.UseVisualStyleBackColor = true; + this.remove.Click += new System.EventHandler(this.remove_Click); + // + // repair + // + this.repair.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.repair.AutoSize = true; + this.repair.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.repair.Location = new System.Drawing.Point(0, 5); + this.repair.MaximumSize = new System.Drawing.Size(113, 0); + this.repair.MinimumSize = new System.Drawing.Size(113, 0); + this.repair.Name = "repair"; + this.repair.Size = new System.Drawing.Size(113, 22); + this.repair.TabIndex = 0; + this.repair.Text = "[MaintenanceTypeDlgRepairButton]"; + this.repair.UseVisualStyleBackColor = true; + this.repair.Click += new System.EventHandler(this.repair_Click); + // + // change + // + this.change.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.change.AutoSize = true; + this.change.Enabled = false; + this.change.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.change.Location = new System.Drawing.Point(0, 5); + this.change.MaximumSize = new System.Drawing.Size(113, 0); + this.change.MinimumSize = new System.Drawing.Size(113, 0); + this.change.Name = "change"; + this.change.Size = new System.Drawing.Size(113, 22); + this.change.TabIndex = 0; + this.change.Text = "[MaintenanceTypeDlgChangeButton]"; + this.change.UseVisualStyleBackColor = true; + this.change.Click += new System.EventHandler(this.change_Click); + // + // topPanel + // + this.topPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.topPanel.BackColor = System.Drawing.SystemColors.Control; + this.topPanel.Controls.Add(this.label2); + this.topPanel.Controls.Add(this.label1); + this.topPanel.Controls.Add(this.banner); + this.topPanel.Location = new System.Drawing.Point(0, 0); + this.topPanel.Name = "topPanel"; + this.topPanel.Size = new System.Drawing.Size(494, 58); + this.topPanel.TabIndex = 13; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.BackColor = System.Drawing.Color.Transparent; + this.label2.ForeColor = System.Drawing.SystemColors.HighlightText; + this.label2.Location = new System.Drawing.Point(19, 31); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(168, 13); + this.label2.TabIndex = 1; + this.label2.Text = "[MaintenanceTypeDlgDescription]"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.BackColor = System.Drawing.Color.Transparent; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.ForeColor = System.Drawing.SystemColors.HighlightText; + this.label1.Location = new System.Drawing.Point(11, 8); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(160, 13); + this.label1.TabIndex = 1; + this.label1.Text = "[MaintenanceTypeDlgTitle]"; + // + // banner + // + this.banner.BackColor = System.Drawing.Color.White; + this.banner.Location = new System.Drawing.Point(0, 0); + this.banner.Name = "banner"; + this.banner.Size = new System.Drawing.Size(494, 58); + this.banner.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; + this.banner.TabIndex = 0; + this.banner.TabStop = false; + // + // bottomPanel + // + this.bottomPanel.BackColor = System.Drawing.SystemColors.Control; + this.bottomPanel.Controls.Add(this.tableLayoutPanel1); + this.bottomPanel.Controls.Add(this.border1); + this.bottomPanel.Dock = System.Windows.Forms.DockStyle.Bottom; + this.bottomPanel.Location = new System.Drawing.Point(0, 312); + this.bottomPanel.Name = "bottomPanel"; + this.bottomPanel.Size = new System.Drawing.Size(494, 49); + this.bottomPanel.TabIndex = 12; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.tableLayoutPanel1.ColumnCount = 5; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 14F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.Controls.Add(this.back, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.next, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.cancel, 4, 0); + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 3); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 1; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(491, 43); + this.tableLayoutPanel1.TabIndex = 7; + // + // back + // + this.back.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.back.AutoSize = true; + this.back.Enabled = false; + this.back.Location = new System.Drawing.Point(222, 10); + this.back.MinimumSize = new System.Drawing.Size(75, 0); + this.back.Name = "back"; + this.back.Size = new System.Drawing.Size(77, 23); + this.back.TabIndex = 0; + this.back.Text = "[WixUIBack]"; + this.back.UseVisualStyleBackColor = true; + this.back.Click += new System.EventHandler(this.Back_Click); + // + // next + // + this.next.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.next.AutoSize = true; + this.next.Enabled = false; + this.next.Location = new System.Drawing.Point(305, 10); + this.next.MinimumSize = new System.Drawing.Size(75, 0); + this.next.Name = "next"; + this.next.Size = new System.Drawing.Size(77, 23); + this.next.TabIndex = 1; + this.next.Text = "[WixUINext]"; + this.next.UseVisualStyleBackColor = true; + this.next.Click += new System.EventHandler(this.Next_Click); + // + // cancel + // + this.cancel.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.cancel.AutoSize = true; + this.cancel.Location = new System.Drawing.Point(402, 10); + this.cancel.MinimumSize = new System.Drawing.Size(75, 0); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(86, 23); + this.cancel.TabIndex = 2; + this.cancel.Text = "[WixUICancel]"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.Cancel_Click); + // + // border1 + // + this.border1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.border1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.border1.Location = new System.Drawing.Point(0, 0); + this.border1.Name = "border1"; + this.border1.Size = new System.Drawing.Size(494, 1); + this.border1.TabIndex = 17; + // + // middlePanel + // + this.middlePanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.middlePanel.ColumnCount = 1; + this.middlePanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.middlePanel.Controls.Add(this.panel3, 0, 0); + this.middlePanel.Controls.Add(this.panel4, 0, 1); + this.middlePanel.Controls.Add(this.panel5, 0, 2); + this.middlePanel.Location = new System.Drawing.Point(15, 61); + this.middlePanel.Name = "middlePanel"; + this.middlePanel.RowCount = 3; + this.middlePanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); + this.middlePanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); + this.middlePanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); + this.middlePanel.Size = new System.Drawing.Size(479, 248); + this.middlePanel.TabIndex = 20; + // + // panel3 + // + this.panel3.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.panel3.Controls.Add(this.change); + this.panel3.Controls.Add(this.label3); + this.panel3.Location = new System.Drawing.Point(3, 3); + this.panel3.Name = "panel3"; + this.panel3.Size = new System.Drawing.Size(473, 76); + this.panel3.TabIndex = 0; + // + // panel4 + // + this.panel4.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.panel4.Controls.Add(this.repair); + this.panel4.Controls.Add(this.label4); + this.panel4.Location = new System.Drawing.Point(3, 85); + this.panel4.Name = "panel4"; + this.panel4.Size = new System.Drawing.Size(473, 76); + this.panel4.TabIndex = 1; + // + // panel5 + // + this.panel5.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.panel5.Controls.Add(this.remove); + this.panel5.Controls.Add(this.label5); + this.panel5.Location = new System.Drawing.Point(3, 167); + this.panel5.Name = "panel5"; + this.panel5.Size = new System.Drawing.Size(473, 78); + this.panel5.TabIndex = 2; + // + // MaintenanceTypeDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.ClientSize = new System.Drawing.Size(494, 361); + this.Controls.Add(this.middlePanel); + this.Controls.Add(this.topBorder); + this.Controls.Add(this.topPanel); + this.Controls.Add(this.bottomPanel); + this.Name = "MaintenanceTypeDialog"; + this.Load += new System.EventHandler(this.OnLoad); + this.topPanel.ResumeLayout(false); + this.topPanel.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.banner)).EndInit(); + this.bottomPanel.ResumeLayout(false); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.middlePanel.ResumeLayout(false); + this.panel3.ResumeLayout(false); + this.panel3.PerformLayout(); + this.panel4.ResumeLayout(false); + this.panel4.PerformLayout(); + this.panel5.ResumeLayout(false); + this.panel5.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.PictureBox banner; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Panel topPanel; + private System.Windows.Forms.Panel bottomPanel; + private ElevatedButton change; + private ElevatedButton repair; + private ElevatedButton remove; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Panel border1; + private System.Windows.Forms.Panel topBorder; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Button back; + private System.Windows.Forms.Button next; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.TableLayoutPanel middlePanel; + private System.Windows.Forms.Panel panel3; + private System.Windows.Forms.Panel panel4; + private System.Windows.Forms.Panel panel5; + } +} diff --git a/package/AgentWindowsManaged/Dialogs/MaintenanceTypeDialog.cs b/package/AgentWindowsManaged/Dialogs/MaintenanceTypeDialog.cs new file mode 100644 index 000000000..c7debe517 --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/MaintenanceTypeDialog.cs @@ -0,0 +1,70 @@ +using DevolutionsAgent.Dialogs; +using System; +using System.Linq; +using WixSharp; + +namespace WixSharpSetup.Dialogs; + +public partial class MaintenanceTypeDialog : AgentDialog +{ + public MaintenanceTypeDialog() + { + InitializeComponent(); + + label1.MakeTransparentOn(banner); + label2.MakeTransparentOn(banner); + } + + Type ProgressDialog + { + get + { + return Shell.Dialogs + .FirstOrDefault(d => d.GetInterfaces().Contains(typeof(IProgressDialog))); + } + } + + void change_Click(object sender, System.EventArgs e) + { + Runtime.Session["MODIFY_ACTION"] = "Change"; + Shell.GoNext(); + } + + void repair_Click(object sender, System.EventArgs e) + { + Runtime.Session["MODIFY_ACTION"] = "Repair"; + int index = Shell.Dialogs.IndexOf(ProgressDialog); + if (index != -1) + Shell.GoTo(index); + else + Shell.GoNext(); + } + + void remove_Click(object sender, System.EventArgs e) + { + Runtime.Session["REMOVE"] = "ALL"; + Runtime.Session["MODIFY_ACTION"] = "Remove"; + + int index = Shell.Dialogs.IndexOf(ProgressDialog); + if (index != -1) + Shell.GoTo(index); + else + Shell.GoNext(); + } + + // ReSharper disable once RedundantOverriddenMember + protected override void Back_Click(object sender, EventArgs e) => base.Back_Click(sender, e); + + // ReSharper disable once RedundantOverriddenMember + protected override void Next_Click(object sender, EventArgs e) => base.Next_Click(sender, e); + + // ReSharper disable once RedundantOverriddenMember + protected override void Cancel_Click(object sender, EventArgs e) => base.Cancel_Click(sender, e); + + public override void OnLoad(object sender, System.EventArgs e) + { + banner.Image = Runtime.Session.GetResourceBitmap("WixUI_Bmp_Banner"); + + base.OnLoad(sender, e); + } +} diff --git a/package/AgentWindowsManaged/Dialogs/MaintenanceTypeDialog.resx b/package/AgentWindowsManaged/Dialogs/MaintenanceTypeDialog.resx new file mode 100644 index 000000000..1af7de150 --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/MaintenanceTypeDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/package/AgentWindowsManaged/Dialogs/ProgressDialog.Designer.cs b/package/AgentWindowsManaged/Dialogs/ProgressDialog.Designer.cs new file mode 100644 index 000000000..c49b91e16 --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/ProgressDialog.Designer.cs @@ -0,0 +1,292 @@ +using WixSharp; +using WixSharp.UI.Forms; + +namespace WixSharpSetup.Dialogs +{ + partial class ProgressDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.topBorder = new System.Windows.Forms.Panel(); + this.progress = new System.Windows.Forms.ProgressBar(); + this.currentAction = new System.Windows.Forms.Label(); + this.topPanel = new System.Windows.Forms.Panel(); + this.dialogText = new System.Windows.Forms.Label(); + this.banner = new System.Windows.Forms.PictureBox(); + this.bottomPanel = new System.Windows.Forms.Panel(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.back = new System.Windows.Forms.Button(); + this.next = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.bottomBorder = new System.Windows.Forms.Panel(); + this.description = new System.Windows.Forms.Label(); + this.currentActionLabel = new System.Windows.Forms.Label(); + this.waitPrompt = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.topPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.banner)).BeginInit(); + this.bottomPanel.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.SuspendLayout(); + // + // topBorder + // + this.topBorder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.topBorder.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.topBorder.Location = new System.Drawing.Point(0, 58); + this.topBorder.Name = "topBorder"; + this.topBorder.Size = new System.Drawing.Size(494, 1); + this.topBorder.TabIndex = 22; + // + // progress + // + this.progress.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.progress.Location = new System.Drawing.Point(32, 165); + this.progress.Name = "progress"; + this.progress.Size = new System.Drawing.Size(434, 13); + this.progress.Step = 1; + this.progress.TabIndex = 20; + // + // currentAction + // + this.currentAction.AutoSize = true; + this.currentAction.Location = new System.Drawing.Point(34, 144); + this.currentAction.Name = "currentAction"; + this.currentAction.Size = new System.Drawing.Size(0, 13); + this.currentAction.TabIndex = 19; + // + // topPanel + // + this.topPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.topPanel.BackColor = System.Drawing.SystemColors.Control; + this.topPanel.Controls.Add(this.dialogText); + this.topPanel.Controls.Add(this.banner); + this.topPanel.Location = new System.Drawing.Point(0, 0); + this.topPanel.Name = "topPanel"; + this.topPanel.Size = new System.Drawing.Size(494, 58); + this.topPanel.TabIndex = 15; + // + // dialogText + // + this.dialogText.AutoSize = true; + this.dialogText.BackColor = System.Drawing.Color.Transparent; + this.dialogText.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.dialogText.ForeColor = System.Drawing.SystemColors.HighlightText; + this.dialogText.Location = new System.Drawing.Point(11, 22); + this.dialogText.Name = "dialogText"; + this.dialogText.Size = new System.Drawing.Size(159, 13); + this.dialogText.TabIndex = 1; + this.dialogText.Text = "[ProgressDlgTitleInstalling]"; + // + // banner + // + this.banner.BackColor = System.Drawing.Color.White; + this.banner.Location = new System.Drawing.Point(0, 0); + this.banner.Name = "banner"; + this.banner.Size = new System.Drawing.Size(494, 58); + this.banner.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; + this.banner.TabIndex = 0; + this.banner.TabStop = false; + // + // bottomPanel + // + this.bottomPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.bottomPanel.BackColor = System.Drawing.SystemColors.Control; + this.bottomPanel.Controls.Add(this.tableLayoutPanel1); + this.bottomPanel.Controls.Add(this.bottomBorder); + this.bottomPanel.Location = new System.Drawing.Point(0, 312); + this.bottomPanel.Name = "bottomPanel"; + this.bottomPanel.Size = new System.Drawing.Size(494, 49); + this.bottomPanel.TabIndex = 14; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.tableLayoutPanel1.ColumnCount = 5; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 14F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.Controls.Add(this.back, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.next, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.cancel, 4, 0); + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 3); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 1; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(491, 43); + this.tableLayoutPanel1.TabIndex = 7; + // + // back + // + this.back.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.back.AutoSize = true; + this.back.Enabled = false; + this.back.Location = new System.Drawing.Point(222, 10); + this.back.MinimumSize = new System.Drawing.Size(75, 0); + this.back.Name = "back"; + this.back.Size = new System.Drawing.Size(77, 23); + this.back.TabIndex = 0; + this.back.Text = "[WixUIBack]"; + this.back.UseVisualStyleBackColor = true; + // + // next + // + this.next.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.next.AutoSize = true; + this.next.Enabled = false; + this.next.Location = new System.Drawing.Point(305, 10); + this.next.MinimumSize = new System.Drawing.Size(75, 0); + this.next.Name = "next"; + this.next.Size = new System.Drawing.Size(77, 23); + this.next.TabIndex = 1; + this.next.Text = "[WixUINext]"; + this.next.UseVisualStyleBackColor = true; + // + // cancel + // + this.cancel.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.cancel.AutoSize = true; + this.cancel.Location = new System.Drawing.Point(402, 10); + this.cancel.MinimumSize = new System.Drawing.Size(75, 0); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(86, 23); + this.cancel.TabIndex = 2; + this.cancel.Text = "[WixUICancel]"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.Cancel_Click); + // + // bottomBorder + // + this.bottomBorder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.bottomBorder.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.bottomBorder.Location = new System.Drawing.Point(0, 0); + this.bottomBorder.Name = "bottomBorder"; + this.bottomBorder.Size = new System.Drawing.Size(494, 1); + this.bottomBorder.TabIndex = 21; + // + // description + // + this.description.AutoSize = true; + this.description.BackColor = System.Drawing.Color.Transparent; + this.description.Location = new System.Drawing.Point(29, 95); + this.description.Name = "description"; + this.description.Size = new System.Drawing.Size(132, 13); + this.description.TabIndex = 16; + this.description.Text = "[ProgressDlgTextInstalling]"; + // + // currentActionLabel + // + this.currentActionLabel.BackColor = System.Drawing.Color.Transparent; + this.currentActionLabel.Location = new System.Drawing.Point(29, 144); + this.currentActionLabel.Name = "currentActionLabel"; + this.currentActionLabel.Size = new System.Drawing.Size(132, 13); + this.currentActionLabel.TabIndex = 19; + this.currentActionLabel.Text = "[ProgressDlgStatusLabel]"; + this.currentActionLabel.Visible = false; + // + // waitPrompt + // + this.waitPrompt.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.waitPrompt.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.waitPrompt.ForeColor = System.Drawing.Color.Blue; + this.waitPrompt.Location = new System.Drawing.Point(29, 195); + this.waitPrompt.Name = "waitPrompt"; + this.waitPrompt.Size = new System.Drawing.Size(381, 114); + this.waitPrompt.TabIndex = 23; + this.waitPrompt.TabStop = true; + this.waitPrompt.Text = "[UACPromptLabel]"; + this.waitPrompt.Visible = false; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(416, 195); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(50, 50); + this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage; + this.pictureBox1.TabIndex = 24; + this.pictureBox1.TabStop = false; + this.pictureBox1.Visible = false; + // + // ProgressDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.ClientSize = new System.Drawing.Size(494, 361); + this.ControlBox = false; + this.Controls.Add(this.pictureBox1); + this.Controls.Add(this.waitPrompt); + this.Controls.Add(this.topBorder); + this.Controls.Add(this.progress); + this.Controls.Add(this.currentAction); + this.Controls.Add(this.topPanel); + this.Controls.Add(this.bottomPanel); + this.Controls.Add(this.description); + this.Controls.Add(this.currentActionLabel); + this.Name = "ProgressDialog"; + this.Text = "[ProgressDlg_Title]"; + this.Load += new System.EventHandler(this.OnLoad); + this.topPanel.ResumeLayout(false); + this.topPanel.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.banner)).EndInit(); + this.bottomPanel.ResumeLayout(false); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.PictureBox banner; + private System.Windows.Forms.Panel topPanel; + private System.Windows.Forms.Label dialogText; + private System.Windows.Forms.Panel bottomPanel; + private System.Windows.Forms.Label description; + private System.Windows.Forms.ProgressBar progress; + private System.Windows.Forms.Label currentAction; + private System.Windows.Forms.Label currentActionLabel; + private System.Windows.Forms.Panel bottomBorder; + private System.Windows.Forms.Panel topBorder; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Button back; + private System.Windows.Forms.Button next; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Label waitPrompt; + private System.Windows.Forms.PictureBox pictureBox1; + } +} \ No newline at end of file diff --git a/package/AgentWindowsManaged/Dialogs/ProgressDialog.cs b/package/AgentWindowsManaged/Dialogs/ProgressDialog.cs new file mode 100644 index 000000000..f6089c578 --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/ProgressDialog.cs @@ -0,0 +1,200 @@ +using DevolutionsAgent.Dialogs; +using Microsoft.Deployment.WindowsInstaller; +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Security.Principal; +using DevolutionsAgent.Helpers; +using DevolutionsAgent.Properties; +using WixSharp; +using WixSharp.CommonTasks; + +namespace WixSharpSetup.Dialogs; + +public partial class ProgressDialog : AgentDialog, IProgressDialog +{ + private static Icon shieldIcon; + + public static Icon ShieldLarge => shieldIcon ??= StockIcon.GetStockIcon(StockIcon.SIID_SHIELD, StockIcon.SHGSI_LARGEICON); + + public ProgressDialog() + { + InitializeComponent(); + dialogText.MakeTransparentOn(banner); + + pictureBox1.Image = ShieldLarge.ToBitmap(); + + showWaitPromptTimer = new System.Windows.Forms.Timer() { Interval = 4000 }; + showWaitPromptTimer.Tick += (s, e) => + { + this.waitPrompt.Visible = true; + this.pictureBox1.Visible = true; + showWaitPromptTimer.Stop(); + }; + } + + private readonly System.Windows.Forms.Timer showWaitPromptTimer; + + public override void OnLoad(object sender, EventArgs e) + { + banner.Image = Runtime.Session.GetResourceBitmap("WixUI_Bmp_Banner"); + + if (!WindowsIdentity.GetCurrent().IsAdmin() && Uac.IsEnabled()) + { + showWaitPromptTimer.Start(); + } + + AgentProperties properties = new AgentProperties(this.Session()); + + base.OnLoad(sender, e); + + Shell.StartExecute(); + } + + /// + /// Called when Shell is changed. It is a good place to initialize the dialog to reflect the MSI session + /// (e.g. localize the view). + /// + protected override void OnShellChanged() + { + if (Runtime.Session.IsUninstalling()) + { + dialogText.Text = + Text = "[ProgressDlgTitleRemoving]"; + description.Text = "[ProgressDlgTextRemoving]"; + } + else if (Runtime.Session.IsRepairing()) + { + dialogText.Text = + Text = "[ProgressDlgTextRepairing]"; + description.Text = "[ProgressDlgTitleRepairing]"; + } + else if (Runtime.Session.IsInstalling()) + { + dialogText.Text = + Text = "[ProgressDlgTitleInstalling]"; + description.Text = "[ProgressDlgTextInstalling]"; + } + + this.Localize(); + } + + /// + /// Processes the message. + /// + /// Type of the message. + /// The message record. + /// The buttons. + /// The icon. + /// The default button. + /// + public override MessageResult ProcessMessage(InstallMessage messageType, Record messageRecord, MessageButtons buttons, MessageIcon icon, MessageDefaultButton defaultButton) + { + switch (messageType) + { + case InstallMessage.InstallStart: + case InstallMessage.InstallEnd: + { + showWaitPromptTimer.Stop(); + waitPrompt.Visible = false; + pictureBox1.Visible = false; + } + break; + + case InstallMessage.ActionStart: + { + try + { + //messageRecord[0] - is reserved for FormatString value + + string message = null; + + bool simple = true; + if (simple) + { + /* + messageRecord[2] unconditionally contains the string to display + + Examples: + + messageRecord[0] "Action 23:14:50: [1]. [2]" + messageRecord[1] "InstallFiles" + messageRecord[2] "Copying new files" + messageRecord[3] "File: [1], Directory: [9], Size: [6]" + + messageRecord[0] "Action 23:15:21: [1]. [2]" + messageRecord[1] "RegisterUser" + messageRecord[2] "Registering user" + messageRecord[3] "[1]" + + */ + if (messageRecord.FieldCount >= 3) + { + message = messageRecord[2].ToString(); + } + } + else + { + message = messageRecord.FormatString; + if (message.IsNotEmpty()) + { + for (int i = 1; i < messageRecord.FieldCount; i++) + { + message = message.Replace("[" + i + "]", messageRecord[i].ToString()); + } + } + else + { + message = messageRecord[messageRecord.FieldCount - 1].ToString(); + } + } + + if (message.IsNotEmpty()) + currentAction.Text = "{0} {1}".FormatWith(currentActionLabel.Text, message); + } + catch + { + //Catch all, we don't want the installer to crash in an + //attempt to process message. + } + } + break; + } + return MessageResult.OK; + } + + /// + /// Called when MSI execution progress is changed. + /// + /// The progress percentage. + public override void OnProgress(int progressPercentage) + { + progress.Value = progressPercentage; + + if (progressPercentage > 0) + { + waitPrompt.Visible = false; + } + } + + /// + /// Called when MSI execution is complete. + /// + public override void OnExecuteComplete() + { + currentAction.Text = null; + Shell.GoNext(); + } + + protected override void Cancel_Click(object sender, EventArgs e) + { + if (Shell.IsDemoMode) + { + Shell.GoNext(); + } + else + { + Shell.Cancel(); + } + } +} diff --git a/package/AgentWindowsManaged/Dialogs/ProgressDialog.resx b/package/AgentWindowsManaged/Dialogs/ProgressDialog.resx new file mode 100644 index 000000000..1af7de150 --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/ProgressDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/package/AgentWindowsManaged/Dialogs/VerifyReadyDialog.Designer.cs b/package/AgentWindowsManaged/Dialogs/VerifyReadyDialog.Designer.cs new file mode 100644 index 000000000..e7a848cd0 --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/VerifyReadyDialog.Designer.cs @@ -0,0 +1,274 @@ +using DevolutionsAgent.Controls; +using WixSharp; +using WixSharp.UI.Forms; + +namespace WixSharpSetup.Dialogs +{ + partial class VerifyReadyDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); + this.copyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.middlePanel = new System.Windows.Forms.Panel(); + this.label3 = new System.Windows.Forms.Label(); + this.topBorder = new System.Windows.Forms.Panel(); + this.topPanel = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.banner = new System.Windows.Forms.PictureBox(); + this.bottomPanel = new System.Windows.Forms.Panel(); + this.generateCli = new System.Windows.Forms.LinkLabel(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.back = new System.Windows.Forms.Button(); + this.next = new DevolutionsAgent.Controls.ElevatedButton(); + this.cancel = new System.Windows.Forms.Button(); + this.border1 = new System.Windows.Forms.Panel(); + this.contextMenuStrip1.SuspendLayout(); + this.middlePanel.SuspendLayout(); + this.topPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.banner)).BeginInit(); + this.bottomPanel.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // contextMenuStrip1 + // + this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.copyToolStripMenuItem}); + this.contextMenuStrip1.Name = "contextMenuStrip1"; + this.contextMenuStrip1.Size = new System.Drawing.Size(103, 26); + // + // copyToolStripMenuItem + // + this.copyToolStripMenuItem.Name = "copyToolStripMenuItem"; + this.copyToolStripMenuItem.Size = new System.Drawing.Size(102, 22); + this.copyToolStripMenuItem.Text = "Copy"; + // + // middlePanel + // + this.middlePanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.middlePanel.Controls.Add(this.label3); + this.middlePanel.Location = new System.Drawing.Point(22, 75); + this.middlePanel.Name = "middlePanel"; + this.middlePanel.Size = new System.Drawing.Size(449, 139); + this.middlePanel.TabIndex = 16; + // + // label3 + // + this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.label3.BackColor = System.Drawing.Color.Transparent; + this.label3.Location = new System.Drawing.Point(0, 3); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(446, 136); + this.label3.TabIndex = 11; + this.label3.Text = "[VerifyReadyDlgInstallText]"; + // + // topBorder + // + this.topBorder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.topBorder.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.topBorder.Location = new System.Drawing.Point(0, 58); + this.topBorder.Name = "topBorder"; + this.topBorder.Size = new System.Drawing.Size(494, 1); + this.topBorder.TabIndex = 15; + // + // topPanel + // + this.topPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.topPanel.BackColor = System.Drawing.SystemColors.Control; + this.topPanel.Controls.Add(this.label1); + this.topPanel.Controls.Add(this.banner); + this.topPanel.Location = new System.Drawing.Point(0, 0); + this.topPanel.Name = "topPanel"; + this.topPanel.Size = new System.Drawing.Size(494, 58); + this.topPanel.TabIndex = 10; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.BackColor = System.Drawing.Color.Transparent; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.ForeColor = System.Drawing.SystemColors.HighlightText; + this.label1.Location = new System.Drawing.Point(11, 8); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(161, 13); + this.label1.TabIndex = 1; + this.label1.Text = "[VerifyReadyDlgInstallTitle]"; + // + // banner + // + this.banner.BackColor = System.Drawing.Color.White; + this.banner.Location = new System.Drawing.Point(0, 0); + this.banner.Name = "banner"; + this.banner.Size = new System.Drawing.Size(494, 58); + this.banner.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; + this.banner.TabIndex = 0; + this.banner.TabStop = false; + // + // bottomPanel + // + this.bottomPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.bottomPanel.BackColor = System.Drawing.SystemColors.Control; + this.bottomPanel.Controls.Add(this.generateCli); + this.bottomPanel.Controls.Add(this.tableLayoutPanel1); + this.bottomPanel.Controls.Add(this.border1); + this.bottomPanel.Location = new System.Drawing.Point(0, 312); + this.bottomPanel.Name = "bottomPanel"; + this.bottomPanel.Size = new System.Drawing.Size(494, 49); + this.bottomPanel.TabIndex = 9; + // + // generateCli + // + this.generateCli.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.generateCli.AutoSize = true; + this.generateCli.BackColor = System.Drawing.Color.Transparent; + this.generateCli.Location = new System.Drawing.Point(16, 17); + this.generateCli.Name = "generateCli"; + this.generateCli.Size = new System.Drawing.Size(70, 13); + this.generateCli.TabIndex = 3; + this.generateCli.TabStop = true; + this.generateCli.Text = "Generate CLI"; + this.generateCli.Visible = false; + this.generateCli.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.generateCli_LinkClicked); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.tableLayoutPanel1.ColumnCount = 5; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 14F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.Controls.Add(this.back, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.next, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.cancel, 4, 0); + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 3); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 1; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(493, 43); + this.tableLayoutPanel1.TabIndex = 0; + // + // back + // + this.back.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.back.AutoSize = true; + this.back.Location = new System.Drawing.Point(174, 10); + this.back.MinimumSize = new System.Drawing.Size(75, 0); + this.back.Name = "back"; + this.back.Size = new System.Drawing.Size(77, 23); + this.back.TabIndex = 1; + this.back.Text = "[WixUIBack]"; + this.back.UseVisualStyleBackColor = true; + this.back.Click += new System.EventHandler(this.Back_Click); + // + // next + // + this.next.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.next.AutoSize = true; + this.next.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.next.Location = new System.Drawing.Point(257, 10); + this.next.MinimumSize = new System.Drawing.Size(75, 0); + this.next.Name = "next"; + this.next.Size = new System.Drawing.Size(127, 23); + this.next.TabIndex = 0; + this.next.Text = "[VerifyReadyDlgInstall]"; + this.next.UseVisualStyleBackColor = true; + this.next.Click += new System.EventHandler(this.Next_Click); + // + // cancel + // + this.cancel.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.cancel.AutoSize = true; + this.cancel.Location = new System.Drawing.Point(404, 10); + this.cancel.MinimumSize = new System.Drawing.Size(75, 0); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(86, 23); + this.cancel.TabIndex = 2; + this.cancel.Text = "[WixUICancel]"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.Cancel_Click); + // + // border1 + // + this.border1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.border1.Dock = System.Windows.Forms.DockStyle.Top; + this.border1.Location = new System.Drawing.Point(0, 0); + this.border1.Name = "border1"; + this.border1.Size = new System.Drawing.Size(494, 1); + this.border1.TabIndex = 14; + // + // VerifyReadyDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.ClientSize = new System.Drawing.Size(494, 361); + this.Controls.Add(this.middlePanel); + this.Controls.Add(this.topBorder); + this.Controls.Add(this.topPanel); + this.Controls.Add(this.bottomPanel); + this.Name = "VerifyReadyDialog"; + this.Load += new System.EventHandler(this.OnLoad); + this.contextMenuStrip1.ResumeLayout(false); + this.middlePanel.ResumeLayout(false); + this.topPanel.ResumeLayout(false); + this.topPanel.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.banner)).EndInit(); + this.bottomPanel.ResumeLayout(false); + this.bottomPanel.PerformLayout(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.PictureBox banner; + private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; + private System.Windows.Forms.ToolStripMenuItem copyToolStripMenuItem; + private System.Windows.Forms.Panel topPanel; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Panel bottomPanel; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Panel border1; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Button back; + private ElevatedButton next; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Panel topBorder; + private System.Windows.Forms.Panel middlePanel; + private System.Windows.Forms.LinkLabel generateCli; + } +} diff --git a/package/AgentWindowsManaged/Dialogs/VerifyReadyDialog.cs b/package/AgentWindowsManaged/Dialogs/VerifyReadyDialog.cs new file mode 100644 index 000000000..34ed0227a --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/VerifyReadyDialog.cs @@ -0,0 +1,68 @@ +using DevolutionsAgent.Dialogs; +using System; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using DevolutionsAgent.Properties; +using WixSharp; + +namespace WixSharpSetup.Dialogs; + +public partial class VerifyReadyDialog : AgentDialog +{ + public VerifyReadyDialog() + { + InitializeComponent(); + label1.MakeTransparentOn(banner); + +#if DEBUG + this.generateCli.Visible = true; +#endif + } + + public override void OnLoad(object sender, EventArgs e) + { + banner.Image = Runtime.Session.GetResourceBitmap("WixUI_Bmp_Banner"); + + base.OnLoad(sender, e); + } + + // ReSharper disable once RedundantOverriddenMember + protected override void Back_Click(object sender, EventArgs e) => base.Back_Click(sender, e); + + // ReSharper disable once RedundantOverriddenMember + protected override void Next_Click(object sender, EventArgs e) + { + Shell.GoNext(); + } + + // ReSharper disable once RedundantOverriddenMember + protected override void Cancel_Click(object sender, EventArgs e) => base.Cancel_Click(sender, e); + + private void generateCli_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + StringBuilder builder = new(); + builder.Append("msiexec /i DevolutionsAgent.msi /qb /l*v install.log"); + + foreach (IWixProperty property in AgentProperties.Properties.Where(p => p.Public)) + { + string propertyValue = this.Session().Property(property.Id); + + if (propertyValue.Equals(property.DefaultValue)) + { + continue; + } + + builder.Append($" {property.Id}=\"{propertyValue}\""); + } + + builder.AppendLine(); + builder.AppendLine(); + builder.Append("Copy to clipboard?"); + + if (MessageBox.Show(builder.ToString(), "", MessageBoxButtons.YesNo) == DialogResult.Yes) + { + Clipboard.SetText(builder.ToString()); + } + } +} diff --git a/package/AgentWindowsManaged/Dialogs/VerifyReadyDialog.resx b/package/AgentWindowsManaged/Dialogs/VerifyReadyDialog.resx new file mode 100644 index 000000000..ad537526f --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/VerifyReadyDialog.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/package/AgentWindowsManaged/Dialogs/WelcomeDialog.Designer.cs b/package/AgentWindowsManaged/Dialogs/WelcomeDialog.Designer.cs new file mode 100644 index 000000000..9d98e3bca --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/WelcomeDialog.Designer.cs @@ -0,0 +1,230 @@ +using WixSharp; +using WixSharp.UI.Forms; + +namespace WixSharpSetup.Dialogs +{ + partial class WelcomeDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.imgPanel = new System.Windows.Forms.Panel(); + this.textPanel = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.image = new System.Windows.Forms.PictureBox(); + this.bottomPanel = new System.Windows.Forms.Panel(); + this.bottomBorder = new System.Windows.Forms.Panel(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.back = new System.Windows.Forms.Button(); + this.next = new System.Windows.Forms.Button(); + this.cancel = new System.Windows.Forms.Button(); + this.imgPanel.SuspendLayout(); + this.textPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.image)).BeginInit(); + this.bottomPanel.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // imgPanel + // + this.imgPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.imgPanel.Controls.Add(this.textPanel); + this.imgPanel.Controls.Add(this.image); + this.imgPanel.Location = new System.Drawing.Point(0, 0); + this.imgPanel.Name = "imgPanel"; + this.imgPanel.Size = new System.Drawing.Size(494, 312); + this.imgPanel.TabIndex = 4; + // + // textPanel + // + this.textPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textPanel.BackColor = System.Drawing.Color.White; + this.textPanel.Controls.Add(this.label1); + this.textPanel.Controls.Add(this.label2); + this.textPanel.Location = new System.Drawing.Point(162, 12); + this.textPanel.Name = "textPanel"; + this.textPanel.Size = new System.Drawing.Size(326, 294); + this.textPanel.TabIndex = 4; + // + // label1 + // + this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.label1.BackColor = System.Drawing.Color.Transparent; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 14); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(317, 61); + this.label1.TabIndex = 2; + this.label1.Text = "[WelcomeDlgTitle]"; + // + // label2 + // + this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.label2.BackColor = System.Drawing.Color.Transparent; + this.label2.Location = new System.Drawing.Point(4, 75); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(316, 209); + this.label2.TabIndex = 3; + this.label2.Text = "[WelcomeDlgDescription]"; + // + // image + // + this.image.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.image.BackColor = System.Drawing.Color.White; + this.image.Location = new System.Drawing.Point(0, 0); + this.image.Name = "image"; + this.image.Size = new System.Drawing.Size(494, 312); + this.image.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; + this.image.TabIndex = 0; + this.image.TabStop = false; + // + // bottomPanel + // + this.bottomPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.bottomPanel.BackColor = System.Drawing.SystemColors.Control; + this.bottomPanel.Controls.Add(this.bottomBorder); + this.bottomPanel.Controls.Add(this.tableLayoutPanel1); + this.bottomPanel.Location = new System.Drawing.Point(0, 312); + this.bottomPanel.Name = "bottomPanel"; + this.bottomPanel.Size = new System.Drawing.Size(494, 49); + this.bottomPanel.TabIndex = 1; + // + // bottomBorder + // + this.bottomBorder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.bottomBorder.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.bottomBorder.Location = new System.Drawing.Point(0, 0); + this.bottomBorder.Name = "bottomBorder"; + this.bottomBorder.Size = new System.Drawing.Size(494, 1); + this.bottomBorder.TabIndex = 5; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.tableLayoutPanel1.ColumnCount = 5; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 14F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.Controls.Add(this.back, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.next, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.cancel, 4, 0); + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 3); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 1; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(493, 43); + this.tableLayoutPanel1.TabIndex = 0; + // + // back + // + this.back.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.back.AutoSize = true; + this.back.Enabled = false; + this.back.Location = new System.Drawing.Point(224, 10); + this.back.MinimumSize = new System.Drawing.Size(75, 0); + this.back.Name = "back"; + this.back.Size = new System.Drawing.Size(77, 23); + this.back.TabIndex = 1; + this.back.Text = "[WixUIBack]"; + this.back.UseVisualStyleBackColor = true; + this.back.Click += new System.EventHandler(this.Back_Click); + // + // next + // + this.next.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.next.AutoSize = true; + this.next.Location = new System.Drawing.Point(307, 10); + this.next.MinimumSize = new System.Drawing.Size(75, 0); + this.next.Name = "next"; + this.next.Size = new System.Drawing.Size(77, 23); + this.next.TabIndex = 0; + this.next.Text = "[WixUINext]"; + this.next.UseVisualStyleBackColor = true; + this.next.Click += new System.EventHandler(this.Next_Click); + // + // cancel + // + this.cancel.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.cancel.AutoSize = true; + this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancel.Location = new System.Drawing.Point(404, 10); + this.cancel.MinimumSize = new System.Drawing.Size(75, 0); + this.cancel.Name = "cancel"; + this.cancel.Size = new System.Drawing.Size(86, 23); + this.cancel.TabIndex = 2; + this.cancel.Text = "[WixUICancel]"; + this.cancel.UseVisualStyleBackColor = true; + this.cancel.Click += new System.EventHandler(this.Cancel_Click); + // + // WelcomeDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.White; + this.ClientSize = new System.Drawing.Size(494, 361); + this.Controls.Add(this.imgPanel); + this.Controls.Add(this.bottomPanel); + this.Name = "WelcomeDialog"; + this.Text = "[WelcomeDlg_Title]"; + this.Load += new System.EventHandler(this.OnLoad); + this.imgPanel.ResumeLayout(false); + this.textPanel.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.image)).EndInit(); + this.bottomPanel.ResumeLayout(false); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.PictureBox image; + private System.Windows.Forms.Panel bottomPanel; + private System.Windows.Forms.Button back; + private System.Windows.Forms.Button next; + private System.Windows.Forms.Button cancel; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Panel imgPanel; + private System.Windows.Forms.Panel bottomBorder; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Panel textPanel; + } +} \ No newline at end of file diff --git a/package/AgentWindowsManaged/Dialogs/WelcomeDialog.cs b/package/AgentWindowsManaged/Dialogs/WelcomeDialog.cs new file mode 100644 index 000000000..4bc160e7e --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/WelcomeDialog.cs @@ -0,0 +1,32 @@ +using System; +using System.Drawing; + +using DevolutionsAgent.Dialogs; + +namespace WixSharpSetup.Dialogs; + +public partial class WelcomeDialog : AgentDialog +{ + public WelcomeDialog() + { + InitializeComponent(); + + this.textPanel.BackColor = Color.FromArgb(233, 233, 233); + } + + public override void OnLoad(object sender, EventArgs e) + { + image.Image = Runtime.Session.GetResourceBitmap("WixUI_Bmp_Dialog"); + + base.OnLoad(sender, e); + } + + // ReSharper disable once RedundantOverriddenMember + protected override void Back_Click(object sender, EventArgs e) => base.Back_Click(sender, e); + + // ReSharper disable once RedundantOverriddenMember + protected override void Next_Click(object sender, EventArgs e) => base.Next_Click(sender, e); + + // ReSharper disable once RedundantOverriddenMember + protected override void Cancel_Click(object sender, EventArgs e) => base.Cancel_Click(sender, e); +} diff --git a/package/AgentWindowsManaged/Dialogs/WelcomeDialog.resx b/package/AgentWindowsManaged/Dialogs/WelcomeDialog.resx new file mode 100644 index 000000000..1af7de150 --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/WelcomeDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/package/AgentWindowsManaged/Dialogs/Wizard.cs b/package/AgentWindowsManaged/Dialogs/Wizard.cs new file mode 100644 index 000000000..519d1d07a --- /dev/null +++ b/package/AgentWindowsManaged/Dialogs/Wizard.cs @@ -0,0 +1,45 @@ +using DevolutionsAgent.Helpers; +using DevolutionsAgent.Properties; +using Microsoft.Deployment.WindowsInstaller; +using System; +using System.Collections.Generic; +using System.Linq; +using WixSharp; +using WixSharpSetup.Dialogs; + +namespace DevolutionsAgent.Dialogs; + +internal static class Wizard +{ + internal static Dictionary Globals = new Dictionary(); + + private static readonly Type[] Sequence; + + static Wizard() + { + List dialogs = new() + { + typeof(WelcomeDialog), + typeof(InstallDirDialog), + }; + + dialogs.Add(typeof(VerifyReadyDialog)); + + Sequence = dialogs.ToArray(); + } + + internal static IEnumerable Dialogs => Sequence; + + internal static int Move(IManagedDialog current, bool forward) + { + Type t = current.GetType(); + int index = Dialogs.FindIndex(t); + + index = forward ? index + 1 : index - 1; + return index; + } + + internal static int GetNext(IManagedDialog current) => Move(current, true); + + internal static int GetPrevious(IManagedDialog current) => Move(current, false); +} diff --git a/package/AgentWindowsManaged/Helpers/AppSearch.cs b/package/AgentWindowsManaged/Helpers/AppSearch.cs new file mode 100644 index 000000000..3229a12b8 --- /dev/null +++ b/package/AgentWindowsManaged/Helpers/AppSearch.cs @@ -0,0 +1,15 @@ +using System; +using System.Linq; +using DevolutionsAgent.Resources; + +namespace DevolutionsAgent.Helpers +{ + internal static class AppSearch + { + internal static Version InstalledVersion => + WixSharp.CommonTasks.AppSearch.GetRelatedProducts("{" + Includes.UPGRADE_CODE + "}") + .Where(productCode => WixSharp.CommonTasks.AppSearch.GetProductName(productCode)?.Equals(Includes.PRODUCT_NAME) ?? false) + .Select(WixSharp.CommonTasks.AppSearch.GetProductVersion) + .FirstOrDefault(); + } +} diff --git a/package/AgentWindowsManaged/Helpers/Debouncer.cs b/package/AgentWindowsManaged/Helpers/Debouncer.cs new file mode 100644 index 000000000..c8b90d85c --- /dev/null +++ b/package/AgentWindowsManaged/Helpers/Debouncer.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +namespace DevolutionsAgent.Helpers +{ + public class Debouncer : IDisposable + { + private readonly TimeSpan ts; + private readonly Action action; + private readonly object parameter; + private readonly HashSet resets = new(); + private readonly object mutex = new(); + + public Debouncer(TimeSpan timespan, Action action, object parameter) + { + this.ts = timespan; + this.action = action; + this.parameter = parameter; + } + + public void Invoke() + { + var thisReset = new ManualResetEvent(false); + + lock (mutex) + { + while (resets.Count > 0) + { + var otherReset = resets.First(); + resets.Remove(otherReset); + otherReset.Set(); + } + + resets.Add(thisReset); + } + + ThreadPool.QueueUserWorkItem(_ => + { + try + { + if (!thisReset.WaitOne(ts)) + { + this.action(this.parameter); + } + } + finally + { + lock (mutex) + { + using (thisReset) + { + resets.Remove(thisReset); + } + } + } + }); + } + + public void Dispose() + { + lock (mutex) + { + while (resets.Count > 0) + { + var reset = resets.First(); + resets.Remove(reset); + reset.Set(); + reset.Dispose(); + } + } + } + } +} diff --git a/package/AgentWindowsManaged/Helpers/DisplayEnum.cs b/package/AgentWindowsManaged/Helpers/DisplayEnum.cs new file mode 100644 index 000000000..1b43c3f8d --- /dev/null +++ b/package/AgentWindowsManaged/Helpers/DisplayEnum.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using WixSharp; + +namespace DevolutionsAgent.Helpers +{ + internal class NamedItem + { + public NamedItem(string name, T value) + { + this.Name = name; + this.Value = value; + } + + public string Name { get; } + + public T Value { get; } + + public override string ToString() + { + return this.Name; + } + } + + internal class DisplayEnum where T : Enum + { + private readonly MsiRuntime runtime; + + private IEnumerable Values => (T[])Enum.GetValues(typeof(T)); + + internal IEnumerable> Items + { + get + { + Func fnLocalize = this.runtime.Localize; + string enumName = typeof(T).Name; + + foreach (T value in this.Values) + { + string key = $"{enumName}_{value}"; + string name = $"[{key}]".LocalizeWith(fnLocalize); + + if (name.Equals(key)) + { + name = Enum.GetName(typeof(T), value); + } + + yield return new NamedItem(name, value); + } + } + } + + public DisplayEnum(MsiRuntime runtime) + { + this.runtime = runtime; + } + } +} diff --git a/package/AgentWindowsManaged/Helpers/Localization.cs b/package/AgentWindowsManaged/Helpers/Localization.cs new file mode 100644 index 000000000..d4857f454 --- /dev/null +++ b/package/AgentWindowsManaged/Helpers/Localization.cs @@ -0,0 +1,45 @@ +using System; +using System.Linq; +using System.Windows.Forms; +using WixSharp; +using static System.Windows.Forms.LinkLabel; + +namespace DevolutionsAgent.Helpers +{ + internal static class LocalizationExtensions + { + internal static void Source(this ComboBox comboBox, MsiRuntime runtime) where T : Enum + { + comboBox.DisplayMember = nameof(NamedItem.Name); + comboBox.ValueMember = nameof(NamedItem.Value); + comboBox.DataSource = new DisplayEnum(runtime).Items.ToArray(); + } + + internal static T Selected(this ComboBox comboBox) where T : Enum => ((NamedItem)comboBox.SelectedItem).Value; + + internal static void SetSelected(this ComboBox comboBox, T value) where T : Enum => comboBox.SelectedValue = value; + + internal static void SetLink(this LinkLabel label, MsiRuntime runtime, string labelFormat, params string[] linkText) + { + string linkFormat = $"[{labelFormat}]".LocalizeWith(runtime.Localize); + + object[] localizedLinkText = new object[linkText.Length]; + + for (int i = 0; i < linkText.Length; i++) + { + localizedLinkText[i] = $"[{linkText[i]}]".LocalizeWith(runtime.Localize); + } + + label.Text = string.Format(linkFormat, localizedLinkText); + + for (int i = 0; i < localizedLinkText.Length; i++) + { + string localizedLink = localizedLinkText[i].ToString(); + Link link = new Link(label.Text.IndexOf(localizedLink, StringComparison.CurrentCulture), + localizedLink.Length); + link.Tag = linkText[i]; + label.Links.Add(link); + } + } + } +} diff --git a/package/AgentWindowsManaged/Helpers/StockIcon.cs b/package/AgentWindowsManaged/Helpers/StockIcon.cs new file mode 100644 index 000000000..dd2707cd1 --- /dev/null +++ b/package/AgentWindowsManaged/Helpers/StockIcon.cs @@ -0,0 +1,47 @@ +using System; +using System.Drawing; +using System.Runtime.InteropServices; + +namespace DevolutionsAgent.Helpers +{ + internal class StockIcon + { + internal static Icon GetStockIcon(uint type, uint size) + { + var info = new SHSTOCKICONINFO(); + info.cbSize = (uint)Marshal.SizeOf(info); + + SHGetStockIconInfo(type, SHGSI_ICON | size, ref info); + + var icon = (Icon)Icon.FromHandle(info.hIcon).Clone(); // Get a copy that doesn't use the original handle + DestroyIcon(info.hIcon); // Clean up native icon to prevent resource leak + + return icon; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + private struct SHSTOCKICONINFO + { + public uint cbSize; + public IntPtr hIcon; + public int iSysIconIndex; + public int iIcon; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string szPath; + } + + [DllImport("shell32.dll")] + private static extern int SHGetStockIconInfo(uint siid, uint uFlags, ref SHSTOCKICONINFO psii); + + [DllImport("user32.dll")] + private static extern bool DestroyIcon(IntPtr handle); + + internal const uint SIID_HELP = 23; + internal const uint SIID_SHIELD = 77; + internal const uint SIID_WARNING = 78; + internal const uint SIID_INFO = 79; + internal const uint SHGSI_ICON = 0x100; + internal const uint SHGSI_LARGEICON = 0x0; + internal const uint SHGSI_SMALLICON = 0x1; + } +} diff --git a/package/AgentWindowsManaged/Helpers/Validation.cs b/package/AgentWindowsManaged/Helpers/Validation.cs new file mode 100644 index 000000000..52d67e6e7 --- /dev/null +++ b/package/AgentWindowsManaged/Helpers/Validation.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DevolutionsAgent.Helpers +{ + internal class Validation + { + internal static bool IsValidPort(string port, out uint p) + { + p = 0; + + if (uint.TryParse(port, out p)) + { + return p is > 0 and <= 65535; + } + + return false; + } + } +} diff --git a/package/AgentWindowsManaged/Program.cs b/package/AgentWindowsManaged/Program.cs new file mode 100644 index 000000000..b0050aa93 --- /dev/null +++ b/package/AgentWindowsManaged/Program.cs @@ -0,0 +1,364 @@ +using DevolutionsAgent.Actions; +using DevolutionsAgent.Dialogs; +using DevolutionsAgent.Properties; +using DevolutionsAgent.Resources; +using Microsoft.Deployment.WindowsInstaller; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Security.Cryptography; +using System.Text.RegularExpressions; +using System.Windows.Forms; +using System.Xml; +using WixSharp; +using WixSharp.CommonTasks; +using WixSharpSetup.Dialogs; +using Assembly = System.Reflection.Assembly; +using CompressionLevel = WixSharp.CompressionLevel; +using File = WixSharp.File; + +namespace DevolutionsAgent; + +internal class Program +{ + private const string PackageName = "DevolutionsAgent"; + + private static string DevolutionsAgentExePath + { + get + { + string path = Environment.GetEnvironmentVariable("DAGENT_EXECUTABLE"); + + if (string.IsNullOrEmpty(path) || !System.IO.File.Exists(path)) + { +#if DEBUG + path = "..\\..\\target\\x86_64-pc-windows-msvc\\release\\devolutionsagent.exe"; +#else + throw new Exception("The environment variable DAGENT_EXECUTABLE is not specified or the file does not exist"); +#endif + } + + if (!System.IO.File.Exists(path)) + { + throw new FileNotFoundException("The agent executable was not found", path); + } + + return path; + } + } + + private static Version DevolutionsAgentVersion + { + get + { + string versionString = Environment.GetEnvironmentVariable("DAGENT_VERSION"); + + if (string.IsNullOrEmpty(versionString) || !Version.TryParse(versionString, out Version version)) + { +#if DEBUG + versionString = FileVersionInfo.GetVersionInfo(DevolutionsAgentExePath).FileVersion; + + if (versionString.StartsWith("20")) + { + versionString = versionString.Substring(2); + } + + version = Version.Parse(versionString); +#else + throw new Exception("The environment variable DAGENT_VERSION is not specified or is invalid"); +#endif + } + + return version; + } + } + + private static bool SourceOnlyBuild => !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("DAGENT_MSI_SOURCE_ONLY_BUILD")); + + private static string ProjectLangId + { + get + { + string langId = Environment.GetEnvironmentVariable("DAGENT_MSI_LANG_ID"); + + if (string.IsNullOrWhiteSpace(langId)) + { + return "en-US"; + } + + // ReSharper disable once SimplifyLinqExpressionUseAll + if (!Languages.Any(x => x.Key == langId)) + { + throw new Exception($"unrecognized language id: {langId}"); + } + + return langId; + } + } + + private static readonly Dictionary Languages = new() + { + { "en-US", "DevolutionsAgent_en-us.wxl" }, + { "fr-FR", "DevolutionsAgent_fr-fr.wxl" }, + }; + + private static KeyValuePair enUS => Languages.First(x => x.Key == "en-US"); + + private static KeyValuePair frFR => Languages.First(x => x.Key == "fr-FR"); + + static void Main() + { + ManagedProject project = new(Includes.PRODUCT_NAME) + { + UpgradeCode = Includes.UPGRADE_CODE, + Version = DevolutionsAgentVersion, + Description = "!(loc.ProductDescription)", + InstallerVersion = 500, // Windows Installer 5.0; Server 2008 R2 / Windows 7 + InstallScope = InstallScope.perMachine, + InstallPrivileges = InstallPrivileges.elevated, + Platform = Platform.x64, +#if DEBUG + PreserveTempFiles = true, + OutDir = "Debug", +#else + OutDir = "Release", +#endif + BannerImage = "Resources/WixUIBanner.jpg", + BackgroundImage = "Resources/WixUIDialog.jpg", + ValidateBackgroundImage = false, + OutFileName = PackageName, + MajorUpgrade = new MajorUpgrade + { + AllowDowngrades = false, + AllowSameVersionUpgrades = true, + DowngradeErrorMessage = "!(loc.NewerInstalled)", + Schedule = UpgradeSchedule.afterInstallInitialize, + }, + Media = new List + { + new() + { + Cabinet = "dgateway.cab", + EmbedCab = true, + CompressionLevel = CompressionLevel.mszip, + } + }, + ControlPanelInfo = new ProductInfo + { + Manufacturer = Includes.VENDOR_NAME, + NoModify = true, + ProductIcon = "Resources/DevolutionsAgent.ico", + UrlInfoAbout = Includes.INFO_URL, + } + }; + + if (CryptoConfig.AllowOnlyFipsAlgorithms) + { + project.CandleOptions = "-fips"; + } + + project.Dirs = new Dir[] + { + new ("%ProgramFiles%", new Dir(Includes.VENDOR_NAME, new InstallDir(Includes.SHORT_NAME) + { + Files = new File[] + { + new (DevolutionsAgentExePath) + { + TargetFileName = Includes.EXECUTABLE_NAME, + FirewallExceptions = new FirewallException[] + { + new() + { + Name = Includes.SERVICE_DISPLAY_NAME, + Description = $"{Includes.SERVICE_DISPLAY_NAME} TCP", + Protocol = FirewallExceptionProtocol.tcp, + Profile = FirewallExceptionProfile.all, + Scope = FirewallExceptionScope.any, + IgnoreFailure = false + }, + new() + { + Name = Includes.SERVICE_DISPLAY_NAME, + Description = $"{Includes.SERVICE_DISPLAY_NAME} UDP", + Protocol = FirewallExceptionProtocol.udp, + Profile = FirewallExceptionProfile.all, + Scope = FirewallExceptionScope.any, + IgnoreFailure = false + }, + }, + ServiceInstaller = new ServiceInstaller() + { + Type = SvcType.ownProcess, + // In contrast to Devolutions Gateway, Devolutions Agent uses LocalSystem + // accout to be able to perform administrative operations + // such as MSI installation (Updating, restarting DevolutionsGateway). + Interactive = false, + Vital = true, + Name = Includes.SERVICE_NAME, + DisplayName = Includes.SERVICE_DISPLAY_NAME, + Description = Includes.SERVICE_DISPLAY_NAME, + FirstFailureActionType = FailureActionType.restart, + SecondFailureActionType = FailureActionType.restart, + ThirdFailureActionType = FailureActionType.restart, + RestartServiceDelayInSeconds = 900, + ResetPeriodInDays = 1, + RemoveOn = SvcEvent.Uninstall, + StopOn = SvcEvent.InstallUninstall, + }, + }, + } + })), + }; + project.ResolveWildCards(true); + + project.DefaultRefAssemblies.Add(typeof(ZipArchive).Assembly.Location); + project.Actions = GatewayActions.Actions; + project.RegValues = new RegValue[] + { + new (RegistryHive.LocalMachine, $"Software\\{Includes.VENDOR_NAME}\\{Includes.SHORT_NAME}", "InstallDir", $"[{AgentProperties.InstallDir}]") + { + AttributesDefinition = "Type=string; Component:Permanent=yes", + Win64 = project.Platform == Platform.x64, + RegistryKeyAction = RegistryKeyAction.create, + } + }; + project.Properties = AgentProperties.Properties.Select(x => x.ToWixSharpProperty()).ToArray(); + project.ManagedUI = new ManagedUI(); + project.ManagedUI.InstallDialogs.AddRange(Wizard.Dialogs); + project.ManagedUI.InstallDialogs + .Add() + .Add(); + project.ManagedUI.ModifyDialogs + .Add() + .Add() + .Add(); + + project.UnhandledException += Project_UnhandledException; + project.UIInitialized += Project_UIInitialized; + + if (SourceOnlyBuild) + { + project.Language = ProjectLangId; + project.LocalizationFile = $"Resources/{Languages.First(x => x.Key == ProjectLangId).Value}"; + + if (ProjectLangId != enUS.Key) + { + project.OutDir = Path.Combine(project.OutDir, ProjectLangId); + } + + project.BuildMsiCmd(); + } + else + { + // Build the multi-language MSI in the {Debug/Release} directory + + project.Language = enUS.Key; + project.LocalizationFile = $"Resources/{enUS.Value}"; + + string msi = project.BuildMsi(); + + foreach (KeyValuePair language in Languages.Where(x => x.Key != enUS.Key)) + { + project.Language = language.Key; + string mstFile = project.BuildLanguageTransform(msi, project.Language, $"Resources/{language.Value}"); + + msi.EmbedTransform(mstFile); + } + + msi.SetPackageLanguages(string.Join(",", Languages.Keys).ToLcidList()); + } + } + + private static void Project_UnhandledException(ExceptionEventArgs e) + { + string errorMessage = + $"An unhandled error has occurred. If this is recurring, please report the issue to {Includes.EMAIL_SUPPORT} or on {Includes.FORUM_SUPPORT}."; + errorMessage += Environment.NewLine; + errorMessage += Environment.NewLine; + errorMessage += "Error details:"; + errorMessage += Environment.NewLine; + errorMessage += e.Exception; + + MessageBox.Show(errorMessage, Includes.PRODUCT_NAME, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + private static void Project_UIInitialized(SetupEventArgs e) + { + string lcid = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "fr" ? frFR.Key : enUS.Key; + + using Stream stream = Assembly.GetExecutingAssembly() + .GetManifestResourceStream($"DevolutionsAgent.Resources.{Languages[lcid]}"); + + XmlDocument xml = new(); + xml.Load(stream); + + Dictionary strings = new(); + + foreach (XmlNode s in xml.GetElementsByTagName("String")) + { + strings.Add(s.Attributes["Id"].Value, s.InnerText); + } + + string I18n(string key) + { + if (!strings.TryGetValue(key, out string result)) + { + return key; + } + + return Regex.Replace(result, @"\[(.*?)]", (match) => + { + string property = match.Groups[1].Value; + string value = e.Session[property]; + + return string.IsNullOrEmpty(value) ? property : value; + }); + } + + if (!Environment.Is64BitOperatingSystem) + { + MessageBox.Show(I18n(Strings.x86VersionRequired), I18n(Strings.AgentDlg_Title)); + + e.ManagedUI.Shell.ErrorDetected = true; + e.Result = ActionResult.UserExit; + } + + Version thisVersion = e.Session.QueryProductVersion(); + Version installedVersion = Helpers.AppSearch.InstalledVersion; + + + if (thisVersion < installedVersion) + { + MessageBox.Show($"{I18n(Strings.NewerInstalled)} ({installedVersion})"); + + e.ManagedUI.Shell.ErrorDetected = true; + e.Result = ActionResult.UserExit; + } + + if (!CustomActions.TryGetInstalledNetFx45Version(out uint netfx45Version) || netfx45Version < 394802) + { + if (MessageBox.Show(I18n(Strings.Dotnet462IsRequired), I18n(Strings.AgentDlg_Title), + MessageBoxButtons.YesNo) == DialogResult.Yes) + { + Process.Start("https://go.microsoft.com/fwlink/?LinkId=2085155"); + } + + e.ManagedUI.Shell.ErrorDetected = true; + e.Result = ActionResult.UserExit; + } + + if (netfx45Version < 528040) + { + if (MessageBox.Show(I18n(Strings.DotNet48IsStrongRecommendedDownloadNow), I18n(Strings.AgentDlg_Title), + MessageBoxButtons.YesNo) == DialogResult.Yes) + { + Process.Start("https://go.microsoft.com/fwlink/?LinkId=2085155"); + } + } + } +} diff --git a/package/AgentWindowsManaged/Properties/AgentProperties.cs b/package/AgentWindowsManaged/Properties/AgentProperties.cs new file mode 100644 index 000000000..1010fb969 --- /dev/null +++ b/package/AgentWindowsManaged/Properties/AgentProperties.cs @@ -0,0 +1,41 @@ +using System; +using WixSharp; + +namespace DevolutionsAgent.Properties +{ + internal partial class AgentProperties + { + private readonly Microsoft.Deployment.WindowsInstaller.Session installerSession; + + private readonly ISession runtimeSession; + + private Func FnGetPropValue { get; } + + /// + /// The default WiX INSTALLDIR property name + /// + public static string InstallDir = "INSTALLDIR"; + + public AgentProperties(ISession runtimeSession) + { + this.runtimeSession = runtimeSession; + this.FnGetPropValue = GetPropertyValueRuntimeSession; + } + + public AgentProperties(Microsoft.Deployment.WindowsInstaller.Session installerSession) + { + this.installerSession = installerSession; + this.FnGetPropValue = GetPropertyValueInstallerSession; + } + + private string GetPropertyValueRuntimeSession(string name) + { + return this.runtimeSession.Property(name); + } + + private string GetPropertyValueInstallerSession(string name) + { + return this.installerSession.Property(name); + } + } +} diff --git a/package/AgentWindowsManaged/Properties/AgentProperties.g.cs b/package/AgentWindowsManaged/Properties/AgentProperties.g.cs new file mode 100644 index 000000000..6d844621f --- /dev/null +++ b/package/AgentWindowsManaged/Properties/AgentProperties.g.cs @@ -0,0 +1,314 @@ + +using StoreLocation = System.Security.Cryptography.X509Certificates.StoreLocation; +using StoreName = System.Security.Cryptography.X509Certificates.StoreName; +using ServiceStartMode = System.ServiceProcess.ServiceStartMode; +using System; +using static DevolutionsAgent.Properties.Constants; + +namespace DevolutionsAgent.Properties +{ + /// + /// do not modify the contents of this class with the code editor. + /// + public partial class Constants + { + + public const string HttpProtocol = "http"; + + public const string HttpsProtocol = "https"; + + public const string TcpProtocol = "tcp"; + + + public enum AuthenticationMode + { + None, + Custom, + } + + public enum CertificateMode + { + External, + System, + } + + public enum CertificateFindType + { + Thumbprint, + SubjectName, + } + + public enum CustomizeMode + { + Now, + Later, + } + } + + /// + /// do not modify the contents of this class with the code editor. + /// + internal partial class AgentProperties + { + + internal static readonly WixProperty configureAgent = new() + { + Id = "P.CONFIGUREAGENT", + Default = false, + Name = "ConfigureAgent", + Secure = false, + Hidden = false, + Public = true + }; + + /// `true` to configure the Gateway interactively + public Boolean ConfigureAgent + { + get + { + string stringValue = this.FnGetPropValue(configureAgent.Id); + return WixProperties.GetPropertyValue(stringValue); + } + set + { + if (this.runtimeSession is not null) + { + this.runtimeSession.Set(configureAgent, value); + } + } + } + + internal static readonly WixProperty debugPowerShell = new() + { + Id = "P.DEBUGPOWERSHELL", + Default = false, + Name = "DebugPowerShell", + Secure = true, + Hidden = false, + Public = true + }; + + public Boolean DebugPowerShell + { + get + { + string stringValue = this.FnGetPropValue(debugPowerShell.Id); + return WixProperties.GetPropertyValue(stringValue); + } + set + { + if (this.runtimeSession is not null) + { + this.runtimeSession.Set(debugPowerShell, value); + } + } + } + + internal static readonly WixProperty installId = new() + { + Id = "P.InstallId", + Default = new Guid("00000000-0000-0000-0000-000000000000"), + Name = "InstallId", + Secure = false, + Hidden = false, + Public = false + }; + + public Guid InstallId + { + get + { + string stringValue = this.FnGetPropValue(installId.Id); + return WixProperties.GetPropertyValue(stringValue); + } + set + { + if (this.runtimeSession is not null) + { + this.runtimeSession.Set(installId, value); + } + } + } + + internal static readonly WixProperty netFx45Version = new() + { + Id = "P.NetFx45Version", + Default = 0, + Name = "NetFx45Version", + Secure = false, + Hidden = false, + Public = false + }; + + public UInt32 NetFx45Version + { + get + { + string stringValue = this.FnGetPropValue(netFx45Version.Id); + return WixProperties.GetPropertyValue(stringValue); + } + set + { + if (this.runtimeSession is not null) + { + this.runtimeSession.Set(netFx45Version, value); + } + } + } + + internal static readonly WixProperty firstInstall = new() + { + Id = "P.FirstInstall", + Default = false, + Name = "FirstInstall", + Secure = false, + Hidden = false, + Public = false + }; + + public Boolean FirstInstall + { + get + { + string stringValue = this.FnGetPropValue(firstInstall.Id); + return WixProperties.GetPropertyValue(stringValue); + } + set + { + if (this.runtimeSession is not null) + { + this.runtimeSession.Set(firstInstall, value); + } + } + } + + internal static readonly WixProperty upgrading = new() + { + Id = "P.Upgrading", + Default = false, + Name = "Upgrading", + Secure = false, + Hidden = false, + Public = false + }; + + public Boolean Upgrading + { + get + { + string stringValue = this.FnGetPropValue(upgrading.Id); + return WixProperties.GetPropertyValue(stringValue); + } + set + { + if (this.runtimeSession is not null) + { + this.runtimeSession.Set(upgrading, value); + } + } + } + + internal static readonly WixProperty removingForUpgrade = new() + { + Id = "P.RemovingForUpgrade", + Default = false, + Name = "RemovingForUpgrade", + Secure = false, + Hidden = false, + Public = false + }; + + public Boolean RemovingForUpgrade + { + get + { + string stringValue = this.FnGetPropValue(removingForUpgrade.Id); + return WixProperties.GetPropertyValue(stringValue); + } + set + { + if (this.runtimeSession is not null) + { + this.runtimeSession.Set(removingForUpgrade, value); + } + } + } + + internal static readonly WixProperty uninstalling = new() + { + Id = "P.Uninstalling", + Default = false, + Name = "Uninstalling", + Secure = false, + Hidden = false, + Public = false + }; + + public Boolean Uninstalling + { + get + { + string stringValue = this.FnGetPropValue(uninstalling.Id); + return WixProperties.GetPropertyValue(stringValue); + } + set + { + if (this.runtimeSession is not null) + { + this.runtimeSession.Set(uninstalling, value); + } + } + } + + internal static readonly WixProperty maintenance = new() + { + Id = "P.Maintenance", + Default = false, + Name = "Maintenance", + Secure = false, + Hidden = false, + Public = false + }; + + public Boolean Maintenance + { + get + { + string stringValue = this.FnGetPropValue(maintenance.Id); + return WixProperties.GetPropertyValue(stringValue); + } + set + { + if (this.runtimeSession is not null) + { + this.runtimeSession.Set(maintenance, value); + } + } + } + + + public static IWixProperty[] Properties = + { + + configureAgent, + + debugPowerShell, + + installId, + + netFx45Version, + + firstInstall, + + upgrading, + + removingForUpgrade, + + uninstalling, + + maintenance, + + }; + } +} + diff --git a/package/AgentWindowsManaged/Properties/AgentProperties.g.tt b/package/AgentWindowsManaged/Properties/AgentProperties.g.tt new file mode 100644 index 000000000..94ad1932f --- /dev/null +++ b/package/AgentWindowsManaged/Properties/AgentProperties.g.tt @@ -0,0 +1,217 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ assembly name="System.ServiceProcess" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.ServiceProcess" #> +<#@ import namespace="System.Security.Cryptography.X509Certificates" #> +<#@ output extension=".cs" #> + +using StoreLocation = System.Security.Cryptography.X509Certificates.StoreLocation; +using StoreName = System.Security.Cryptography.X509Certificates.StoreName; +using ServiceStartMode = System.ServiceProcess.ServiceStartMode; +using System; +using static DevolutionsAgent.Properties.Constants; + +namespace DevolutionsAgent.Properties +{ + /// + /// do not modify the contents of this class with the code editor. + /// + public partial class Constants + { +<# for (int idx = 0; idx < this.StaticStrings.GetLength(0); idx++) { #> + public const string <#=this.StaticStrings[idx, 0]#> = "<#=this.StaticStrings[idx, 1]#>"; +<# } #> + +<# for (int idx = 0; idx < this.Enums.GetLength(0); idx++) { #> + public enum <#=this.Enums[idx].Name #> + { + <# foreach (var value in Enum.GetValues(this.Enums[idx])) { #> + <#=value#>, + <# } #> + } +<# } #> + } + + /// + /// do not modify the contents of this class with the code editor. + /// + internal partial class AgentProperties + { +<# for (int idx = 0; idx < this.properties.GetLength(0); idx++) { #> + internal static readonly WixProperty<<#= this.properties[idx].TypeName #>> <#= this.properties[idx].PrivateName #> = new() + { +<# string id = string.IsNullOrEmpty(this.properties[idx].Id) ? this.properties[idx].Name : this.properties[idx].Id; #> +<# if (this.properties[idx].Public) { id = id.ToUpper(); } #> + Id = "P.<#=id#>", +<# if (this.properties[idx].PropertyType == typeof(string)) { #> + Default = "<#= this.properties[idx].Default #>", +<# } else if (this.properties[idx].PropertyType == typeof(Guid)) { #> + Default = new Guid("<#= this.properties[idx].Default.ToString() #>"), +<# } else { #> + Default = <#= this.properties[idx].Default #>, +<# } #> + Name = "<#= this.properties[idx].Name #>", + Secure = <#= this.properties[idx].Secure.ToString().ToLower() #>, + Hidden = <#= this.properties[idx].Hidden.ToString().ToLower() #>, + Public = <#= this.properties[idx].Public.ToString().ToLower() #> + }; + +<# if (!string.IsNullOrEmpty(this.properties[idx].Comment)) { #> + /// <#= this.properties[idx].Comment #> +<# } #> + public <#= this.properties[idx].TypeName #> <#= this.properties[idx].Name #> + { + get + { + string stringValue = this.FnGetPropValue(<#= this.properties[idx].PrivateName #>.Id); + return WixProperties.GetPropertyValue<<#= this.properties[idx].TypeName #>>(stringValue); + } + set + { + if (this.runtimeSession is not null) + { + this.runtimeSession.Set(<#= this.properties[idx].PrivateName #>, value); + } + } + } +<# } #> + + public static IWixProperty[] Properties = + { +<# for (int idx = 0; idx < this.properties.GetLength(0); idx++) { #> + <#= this.properties[idx].PrivateName #>, +<# } #> + }; + } +} + +<#+ + public class Statics + { + internal const string HttpProtocol = "http"; + + internal const string HttpsProtocol = "https"; + + internal const string TcpProtocol = "tcp"; + + public enum AuthenticationMode + { + None, + Custom, + } + + public enum CertificateMode + { + External, + System + } + + public enum CertificateFindType + { + Thumbprint, + SubjectName + } + + public enum CustomizeMode + { + Now, + Later, + } + } + + public string[,] StaticStrings = new[,] + { + {nameof(Statics.HttpProtocol), Statics.HttpProtocol}, + {nameof(Statics.HttpsProtocol), Statics.HttpsProtocol}, + {nameof(Statics.TcpProtocol), Statics.TcpProtocol}, + }; + + public Type[] Enums = new[] + { + typeof(Statics.AuthenticationMode), + typeof(Statics.CertificateMode), + typeof(Statics.CertificateFindType), + typeof(Statics.CustomizeMode), + }; + + public abstract class BasePropertyDefinition + { + public abstract string Comment { get; set; } + public abstract bool Hidden { get; set; } + public abstract string Id { get; set; } + public abstract string Name { get; set; } + public abstract string PrivateName { get; } + public abstract bool Public { get; set; } + public abstract bool Secure { get; set; } + public abstract string TypeName { get; } + + public abstract string Default { get; } + public abstract Type PropertyType { get; } + } + + public class PropertyDefinition : BasePropertyDefinition + { + public override string Name { get; set; } + public T DefaultValue { get; set; } + public override bool Public { get; set; } + public override bool Secure { get; set; } + public override bool Hidden { get; set; } + public override string Id { get; set; } + public override string Comment { get; set; } + public bool Private => !this.Public; + + public PropertyDefinition(string name, T defaultValue, bool isPublic = true, bool secure = true, bool hidden = false, string id = null, string comment = null) + { + this.Name = name; + this.DefaultValue = defaultValue; + this.Public = isPublic; + this.Secure = secure; + this.Hidden = hidden; + this.Id = id; + this.Comment = comment; + } + + public override string Default + { + get + { + if (PropertyType == typeof(bool)) + { + return this.DefaultValue.ToString().ToLower(); + } + + if (PropertyType.IsEnum) + { + return $"{TypeName}.{this.DefaultValue.ToString()}"; + } + + return this.DefaultValue.ToString(); + } + } + + public override string PrivateName => char.ToLower(this.Name[0]) + this.Name.Substring(1); + + public override Type PropertyType => typeof(T); + + public override string TypeName => typeof(T).Name; + } + + private static uint DefaultHttpPort = 7171; + + BasePropertyDefinition[] properties = { + new PropertyDefinition("ConfigureAgent", false, secure: false, comment: "`true` to configure the Gateway interactively"), + + new PropertyDefinition("DebugPowerShell", false), + + new PropertyDefinition("InstallId", Guid.Empty, isPublic: false, secure: false), + new PropertyDefinition("NetFx45Version", 0, isPublic: false, secure: false), + new PropertyDefinition("FirstInstall", false, isPublic: false, secure: false), + new PropertyDefinition("Upgrading", false, isPublic: false, secure: false), + new PropertyDefinition("RemovingForUpgrade", false, isPublic: false, secure: false), + new PropertyDefinition("Uninstalling", false, isPublic: false, secure: false), + new PropertyDefinition("Maintenance", false, isPublic: false, secure: false), + }; +#> diff --git a/package/AgentWindowsManaged/Properties/WixProperty.cs b/package/AgentWindowsManaged/Properties/WixProperty.cs new file mode 100644 index 000000000..1ef97e8af --- /dev/null +++ b/package/AgentWindowsManaged/Properties/WixProperty.cs @@ -0,0 +1,114 @@ +using System; +using System.ComponentModel; +using System.Diagnostics; +using WixSharp; + +namespace DevolutionsAgent.Properties +{ + internal static class WixProperties + { + public static T Get(this Microsoft.Deployment.WindowsInstaller.Session session, WixProperty prop) + { + Debug.Assert(session is not null); + + string propertyValue = session.Property(prop.Id); + + return GetPropertyValue(propertyValue); + } + + public static T Get(this ISession session, WixProperty prop) + { + Debug.Assert(session is not null); + + string propertyValue = session.Property(prop.Id); + + return GetPropertyValue(propertyValue); + } + + public static void Set(this Microsoft.Deployment.WindowsInstaller.Session session, WixProperty prop, T value) + { + session[prop.Id] = value.ToString(); + } + + public static void Set(this ISession session, WixProperty prop, T value) + { + session[prop.Id] = value?.ToString(); + } + + public static Property ToWixSharpProperty(this IWixProperty property) + { + return new(property.Id) + { + Value = property.DefaultValue, + Hidden = property.Hidden, + Secure = property.Secure, + }; + } + + internal static T GetPropertyValue(string propertyValue) + { + if (string.IsNullOrWhiteSpace(propertyValue)) + { + return default; + } + + if (typeof(T).IsEnum) + { + return (T) Enum.Parse(typeof(T), propertyValue); + } + + return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromInvariantString(propertyValue); + } + } + + internal static class WixPropertyExtensions + { + // https://www.firegiant.com/docs/wix/v3/tutorial/com-expression-syntax-miscellanea/expression-syntax/ + + internal static Condition Equal(this WixProperty property, T value) + { + return new Condition($"{property.Id}=\"{value}\""); + } + + internal static Condition NotEqual(this WixProperty property, T value) + { + return new Condition($"{property.Id}<>\"{value}\""); + } + } + + internal interface IWixProperty + { + public string DefaultValue { get; } + + public bool Hidden { get; } + + public string Id { get; } + + public string Name { get; } + + public bool Secure { get; } + + public bool Public { get; } + + public Type PropertyType { get; } + } + + internal class WixProperty : IWixProperty + { + public T Default { get; set; } + + public string DefaultValue => Default.ToString(); + + public bool Hidden { get; set; } + + public string Id { get; set; } + + public string Name { get; set; } + + public bool Secure { get; set; } = false; + + public bool Public { get; set; } + + public Type PropertyType => typeof(T); + } +} diff --git a/package/AgentWindowsManaged/README.md b/package/AgentWindowsManaged/README.md new file mode 100644 index 000000000..49b79ff58 --- /dev/null +++ b/package/AgentWindowsManaged/README.md @@ -0,0 +1,23 @@ +# Devolutions Agent Windows Installer + +Windows Installer project for Devolutions Agent. + +## Overview + +Project structure is the same as Devolutions Gateway, see [README.md](../WindowsManaged/README.md) for more info. + +## Build + +### MSBuild + +`msbuild` must be in your PATH; it's easiest to use the Developer Command Prompt for VS 2022. + +The following environment variables should be defined: + +`DAGENT_EXECUTABLE` +The absolute path to the main executable (DevolutionsAgent.exe) to be packaged + +`DAGENT_VERSION` +The version to use for the installer. Note that in Windows Installer, the product version is restricted as follows: + +[0-255].[0-255].[0-65535] diff --git a/package/AgentWindowsManaged/Resources/DevolutionsAgent.ico b/package/AgentWindowsManaged/Resources/DevolutionsAgent.ico new file mode 100644 index 0000000000000000000000000000000000000000..88cb15dd8faa4645aeac0ae140ac1aef42063ae3 GIT binary patch literal 112498 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 R;M*OLmARc+h3VDE{{f0oLnQzJ literal 0 HcmV?d00001 diff --git a/package/AgentWindowsManaged/Resources/DevolutionsAgent_en-us.wxl b/package/AgentWindowsManaged/Resources/DevolutionsAgent_en-us.wxl new file mode 100644 index 000000000..9d84ea98d --- /dev/null +++ b/package/AgentWindowsManaged/Resources/DevolutionsAgent_en-us.wxl @@ -0,0 +1,52 @@ + + + + 1033 + System-wide service for extending Devolutions Gateway functionality. + Devolutions Inc. + Devolutions + + The product requires Microsoft .NET Framework 4.6.2. Microsoft .NET Framework 4.8 is strongly recommended. Would you like to download it now? + Microsoft .NET Framework 4.8 is strongly recommended. Would you like to download it now? + A newer version of this product is already installed. + This product requires at least Windows 8 / Windows Server 2012 R2 + There is a problem with the entered data. Please correct the issue and try again. + You need to install the 64-bit version of this product on 64-bit Windows. + You need to install the 32-bit version of this product on 32-bit Windows. + + Search + View + View Log + + Install Location + + Directory + + Please wait for UAC prompt to appear. + +If it appears minimized then active it from the taskbar. + + All Files + + [ProductName] Setup + + Browse to the destination folder + Change destination folder + + Click Next to install to the default folder or click Change to choose another. + Destination Folder + + Changing [ProductName] + Installing [ProductName] + Removing [ProductName] + Repairing [ProductName] + Updating [ProductName] + + Ready to change [ProductName] + Ready to install [ProductName] + Ready to remove [ProductName] + Ready to repair [ProductName] + Ready to update [ProductName] + + Welcome to the [ProductName] 20[ProductVersion] Setup Wizard + diff --git a/package/AgentWindowsManaged/Resources/DevolutionsAgent_fr-fr.wxl b/package/AgentWindowsManaged/Resources/DevolutionsAgent_fr-fr.wxl new file mode 100644 index 000000000..0082d6472 --- /dev/null +++ b/package/AgentWindowsManaged/Resources/DevolutionsAgent_fr-fr.wxl @@ -0,0 +1,60 @@ + + + + 1036 + Service à l’échelle du système pour étendre les fonctionnalités de Devolutions Gateway. + Devolutions Inc. + Devolutions + + Le produit nécessite Microsoft .NET Framework 4.6.2. Microsoft .NET Framework 4.8 est fortement conseillé. Souhaiteriez-vous le télécharger maintenant? + Microsoft .NET Framework 4.8 est fortement conseillé. Souhaiteriez-vous le télécharger maintenant? + Une version plus récente de ce produit est déjà installée. + Ce produit requiert Windows 8 ou Windows Server 2012 R2 au minimum. + Il y a un problème avec les informations fournies, veuillez les modifier et essayer de nouveau. + Vous devez installer la version 64 bits de ce produit avec l'environnement 64 bits de Windows. + Vous devez installer la version 32 bits de ce produit avec l'environnement 32 bits de Windows. + + Rechercher + Afficher + Afficher le journal + + Emplacement de l'installation + + Répertoire + + Veuillez attendre que l'invite UAC apparaisse. + +Si elle apparaît en mode réduit, alors vous devez l'activer à partir de la barre de tâches. + + Tous les fichiers + + Installation de [ProductName] + + Sélectionnez le dossier de destination + Modifier le dossier de destination + + Cliquez sur Suivant pour effectuer l'installation dans le dossier par défaut, ou cliquez sur Modifier pour choisir un autre dossier. + Dossier de destination + + Modification de [ProductName] + Installation de [ProductName] + Suppression de [ProductName] + Réparation de [ProductName] + Mise à jour de [ProductName] + + Prêt pour la modification de [ProductName] + Prêt pour l'installation de [ProductName] + Prêt pour la suppression de [ProductName] + Prêt pour la réparation de [ProductName] + Prêt pour la mise à jour de [ProductName] + + Bienvenue dans l'assistant d'installation de [ProductName] 20[ProductVersion] + + Impossible de se connecter au Pare-feu Windows. ([2] [3] [4] [5]) + Installation de la configuration du Pare-feu Windows + Désinstallation de la configuration du Pare-feu Windows + Restauration de la configuration du Pare-feu Windows + Restauration de la configuration du Pare-feu Windows + Configuration du Pare-feu Windows + Configuration du Pare-feu Windows + diff --git a/package/AgentWindowsManaged/Resources/Includes.cs b/package/AgentWindowsManaged/Resources/Includes.cs new file mode 100644 index 000000000..ae719023c --- /dev/null +++ b/package/AgentWindowsManaged/Resources/Includes.cs @@ -0,0 +1,55 @@ +using System; +namespace DevolutionsAgent.Resources +{ + internal static class Includes + { + internal static string VENDOR_NAME = "Devolutions"; + + internal static string PRODUCT_NAME = "Devolutions Agent"; + + internal static string SHORT_NAME = "Agent"; + + internal static string SERVICE_NAME = "DevolutionsAgent"; + + internal static string SERVICE_DISPLAY_NAME = "Devolutions Agent Service"; + + internal static string SERVICE_DESCRIPTION = "Devolutions Agent Service"; + + internal static string EXECUTABLE_NAME = "DevolutionsAgent.exe"; + + internal static string EMAIL_SUPPORT = "support@devolutions.net"; + + internal static string FORUM_SUPPORT = "forum.devolutions.net"; + + internal static Guid UPGRADE_CODE = new("82318D3C-811F-4D5D-9A82-B7C31B076755"); + + internal static string INFO_URL = "https://server.devolutions.net"; + + /// + /// SDDL string representing desired %programdata%\devolutions\agent ACL + /// Easiest way to generate an SDDL is to configure the required access, and then query the path with PowerShell: `Get-Acl | Format-List` + /// + /// + /// Owner : NT AUTHORITY\SYSTEM + /// Group : NT AUTHORITY\SYSTEM + /// Access : + /// NT AUTHORITY\SYSTEM Allow FullControl + /// NT AUTHORITY\LOCAL SERVICE Allow Write, ReadAndExecute, Synchronize + /// NT AUTHORITY\NETWORK SERVICE Allow Modify, Synchronize + /// BUILTIN\Administrators Allow FullControl + /// BUILTIN\Users Allow ReadAndExecute, Synchronize + /// + internal static string PROGRAM_DATA_SDDL = "O:SYG:SYD:PAI(A;OICI;FA;;;SY)(A;OICI;0x1201bf;;;LS)(A;OICI;0x1301bf;;;NS)(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;BU)"; + + /// + /// Owner : NT AUTHORITY\SYSTEM + /// Group : NT AUTHORITY\SYSTEM + /// Access : + /// NT AUTHORITY\SYSTEM Allow FullControl + /// NT AUTHORITY\LOCAL SERVICE Allow Write, ReadAndExecute, Synchronize + /// NT AUTHORITY\NETWORK SERVICE Allow Write, ReadAndExecute, Synchronize + /// BUILTIN\Administrators Allow FullControl + /// + internal static string USERS_FILE_SDDL = "O:SYG:SYD:PAI(A;;FA;;;SY)(A;;0x1201bf;;;LS)(A;;0x1201bf;;;NS)(A;;FA;;;BA)"; + } +} diff --git a/package/AgentWindowsManaged/Resources/Newtonsoft.Json.dll b/package/AgentWindowsManaged/Resources/Newtonsoft.Json.dll new file mode 100644 index 0000000000000000000000000000000000000000..341d08fc8b7ea5cda1f12b3d2756b539e1f94ed0 GIT binary patch literal 711952 zcmb@v2YejG`98kd-P*wQ-}rY2MlA%&TtRIPbiT^A3I4%jaF_zv7(1_V&!4A?ah+Sk}=k zj&;$V2Yxil?Fnnayw;W_mi6|GWu+9m`ZI9PgZl|U%jy)jt$Y*V@rQr&S}hCsclE2LoZSId*>+E7N zD8OZ#yCFo|9;2tQ_WFE_We0givAUPI)>~0BFqlmYcw28`K@wY;er?9{=A7-gJI-30 zaT9A>k+Rz@X9bxRY*tWNH@WO-&nsrvk03>{RX`tp?HcnlH-luGmpY@}9%G*AJGv9) zN;WZ8b1l~!nhDn!T#lPC-Qcp43K1p100G?zF!pFq$r4`g1g{WR)){E6g6E4ycYyj9 z$A2S0#)}HzX#raEZRM_H5`{?lw;;kcD>s;thq zqrKdp7EY5x`Lr@R%fA)0DoOR0(!ynXdfLPt zqcRO5{{-_Fi6;i_d?}wP_t>5Y*m1qJJBafNXgwNd*^>n};zi)Qw7_-(aiFX0OG%*> z6Gmu+n_=Rku7rdpN+=E>_KFbV00O)zX>kBiju7Giq7os*0mL#Q4nVea*gXctVy0t5 z5uT11et>v#FOFq)4GCQHCJJUB+Znwbj)15Vf}lYs1`)-eB@DVFO7&)@B8ovv8T6JY z2+ceb5LK5%y}z4zRo4xO7_P(>(`>tm1yXigGzF>w`ZAZo` zxch)K$B>^J>T|J0Udhho^M5-IV$Lz-i`J+q(&AWF2O=SD5{Lr#NW-g4U25$~(n1p|jN~o}vQcW{-AA_>{v)c_8(xxhW9~wkEz$p2g&WrD29H!xWJr17j>j#p;Ob` z$)a$qQkCq75Ir-aMLi#er#0y%RSc5;B&I)phBcmtGHH?aLvIYg>9JGVmk%btcAchUOLuy zG4O!dESsoQo9%xb(TvZI=Jk%OVWCiy*1z%03E^HLL~9oO!6AHU2+^0zz?19B^w5{~bT}re`5Q?Ns*8ztMdVWnu4x zLC|6jRQEXX$Eo`tbVIo?&OhlsRNbJ~Vqj?z{|w#7s{3DbuTl4NbRVW}h^ECHuI~TP zeT2IIOZU<0o=-QF3u!K-`*^xTk?-7tnEf@hpYE57dlQ7;Vop{(n}<13-Fwpg61uCe zk6Kz-aZjRZM?b-u?zek-%-M{DhS6iz(F=F{N8 zF&jZ(C;g>nJc%fteQh4fLAmcvmMFC3+K0F-Gt_g|t{&C`RnqC9SfPmg8j3cM^a}nM zMK$^mWs#*2yrkn_3*3Z3Ps==DwGKyRM)}YuskfLHnxfSatE&kVuLMQ6vdHzn4@ad~ z&Lw^HF0XH}Em=LD%T4O`_T0~UHj6Nw&5D4YYts2=vqwIO1ciA3mmzn zOo9LC4G*Kyp?Z;rYf8_UX97!j={CG=;>wgv* zY$u|0Sc4VE{~Ys0*{)_iz<`DgDw9ztW==q5&WfJ%c|@zURxiY&htAkwN=sDO;Edzh zZXiiO>I)%B2-%~}U7rzrkpV<}i4L?_*L=C|`U+i#5%&S4>$Kg`TSAs#s;ccS4)Azj zgb>F`(QKWDxn~z>gvTc!T5U*nP?Ss@Fxd|V!hDtaat`na905_Z<}pIL21Bt1eVsu> zF$jaCYrYW$eUm{%QHWCF`jp7rLq_TXxDFya5)o1U`wR@{`Z*L@svu*DV`EylvV%x5?nk@v86$~cQ9ek z9B(NVPYOG{#5s$X)U;d!wc&(Vckv#{#^4IV$Je*F^7ulKMsh74&(gq zkm*3XTfGZX6`LLC>b-Ff#(32uj-mPpBiWVZwx0*zl>xW<=_rcr7l7LScW`6-7;MyO zo@%6P&Z%^EvFrN3A@;&YFT$#Vc>;WGW6PjI7$M}$SsXyDixA=f;*4j@jA5aK|W zIR=Ocy@@FHrk643=g3g{;4k0^h+@#O3_=^$pf@v!C}SHr2{A;ue8oF4(zgp18x1b7$9aYCN5}v)E!WXRuB45 zmt}rULieCTyRce#uRtewyTDxVHi0z_HZJIOS>iLlWtMeXxfrcGLt1YQY26jldW+K1 zV3SZ<^eHXy>(%+qVAwNBcv*~89i1RLO*HtA%7KQ>a{P|vxJ~k~gEt9ug4+e=f?EaF zG}vT70p+02{DI{_Cav{MI+#ftGXc82j_xEHomJ2?>gYD2F$|YcMShB&Y#0YRHI%+h zG}M(Oh1f&1OEe^h(nMuJk7QZ?$g_C_T$UpLwQUC$k|R zbZM;L{S9paYcQ_YH&40&(fwic0M5+yEqVVL4*(o5NAdRaRBiOA~rhtWv(-GX{%WEIy`}jgdxr=8*zFk z#*r|@c~v7$VPYH!L!9%X%*6o&re3l>Zd*f;*(}Jy$>dv62khp_aT{h4Jb#Agj7n$q zPf(u9;KzW0VHQYw&^euS0M8&iQsKe@r@yCUJ}%WFb<8}_@-b#_K)i(mZhyXIdFFM|?osDoA*avKn>#W*FpgXNTrjLb(tNB_5eS^K~D5eKH)P->qg($b=2U=`)Vf0zaTDXzX!Yi$|$JPsPUcXqRQkle*?o+NSeU3V0R9%eqewM|N!51b2 z$(DAh8EoeRwhMcv)T3PpSXDr>Htga-qoP46LD*w(b57%051@=_zggSNLI;VLv>o=> zdl4;=JT%{dR(td>C`3Re6s0`)e+4$4z^*YTsmjXnw*OBMDy-E8kO}L1+^*O45_{}i zL?1BVnhv{Kbi=V#(`?@xg49tKlL>E&@cs_)KBr#4m6yf@m0xfCjT7#KMS3 zPs%k_AUbiT+#MyCx*yLi#B4BLrjnLPxkFV;3)TkQ5;ou@WY5BQ(y}Gk=GGTa%jsS}GmM zorG;Esq#{C^Pa?ZCU+9Hrwsx#ZP0^BT@3sOy}SClJ-d2&B=w}oI_sIGl(s!5C6-Ix z^uimG)jVaftJ{S=XdHxLCG(3B4R1SIza6VI+F_P;snu~ciVs%U{?OJrug*H=7wC(X z9=q7uquH?guz1OmGsia1p?qjAXh)e!&Yq*@lScHkn07IZWk)h-SLY|1Ei-8|&MnuVRks5UL*=0&dMFy7qk0=&fr*FXRk3|=zFqxR z#EWXiiB)G8vkk_jv58>X(=zQDNSj`aQkWUcNn<|n8kjznRya^;86Z<>^KWs1N*3!N ztfQ=3ibRa}Xx`?2$gEP#dOPW0f0DFGrbvb+gY#Q79mZaHS`UZ!}L*AUh zZZwv`{<5*Fi##+rX=jeM675{$pzA-6pFE~hd9ZsSL3Iq;WCX7WE)X#4EIXOl@pZ9B)DeCLc zC+zEUy%-N($U>~I_a%4N*S`X4(A)L?(~u(!PTApbG9YXdk>eIXK9p(s(k^E_ho;z< z4#mj+ta`LN>xv?6OXW8hSe2R9GAuvpU#)R;Ft(&jay!@iRdh*tn^|g-T&qY<63Ux_ zFiQ2ps63NINjaVd?!eb>0=j%RvS!gB>!M$8u$V`M4Yt9I zr_4A$syma{O%kY{ZBUVgA!*!`;>HKEha-mL)x&mhH+b8tUN?9Jjd3hl=>;4dDW8YU z7g!aCYQi>*&cU|w+Ae!Mw+alW!sgtb-V6=XQkk8WcRm@r*a@yMy~=FIh!YA@TlG#N zMj^mVbNij4BJEDUfIdrD_kmaF)7{9CY(b7T=ain#8R`R!~}tY2~j6gMQv)YQ%Q4meb_bKD9jd^!qxd1 zG#e@#Gz_-qtH3}1BFlvOv~r5d#=jfGW%V4UfZ39%9NH8KZ=o4kBVCM0vwY-|sbHl> zjhsg4^>q?0al@f7aS#R{oUiW#m|*?ONIh#PspY^54@Yj9JXU&j9)QuDubnDsSXi9v zEc+ARrSufU){)mLp_6pK2)g{kgq9OoEYqI>;1ovIU^Rp97RB~20v?HH1D^HMJc|)F zjOXB3oDwF1XOVQlzyN`j)TiFw&|6H0Q^m;NZu+E|6+0rwjL^}mF=g)JiqWU*;&#H! zWRiE-1}x(xht0(;NWh*eDaz=za}eNE;NaY{v?ZJXy%da9I@MD<3Z8{W7S^6nqRbQW z4iHknnDacSVM=M{BCR*Lz^=ZTwTC{LaiqSy?XV|oT|t#mYXT#wkP%plvdZ)9SyG6SJv)TOOz2Qzb~Vu&auf0)E5O3X@wSR$z%906 zAwbjp$|<&wi0#gdn5oB%yh5qBG!u>Sk%*iYE;eggX&3x?;8hIx3|{Pxv1aGUI)ift zr0dUT@z&XMBqvNdrBU@rqbi*g_Pw^S%gH-Z5Wzd0J&|mn8P7WKtUrLW0f@=1ge|`0 z3^@6+=Zsy5_YII4<%EtQCKL>R>wG)`o)J7(pE<`1A32S2reT4>!TwhRO ziZ8EbU2|*QbsJq**iJ`6kkA}VW+wttvpd{CsNpSMj~GU!MLtTq#af0u zus#BwUIXmr!_fNC%CIsSBJM$zwk9jdYc~f73t=0?ZQ4}kCE0g10{=xw)HTOZY27*Dz6%1h& ziv!D)R|3J&Utf#Oh9e+~E0{3TT#kSmG?zg{QKz(UJ)i3>Xtz-CE|+6JW+B`)|r4P23^A-ES6~y zc1UFcq8M}?gZ7GoN(>^3EASYu@OHrKuik`CBRiOh!BR9PfD+;LVT7%3CxW6PhEm;5 zNWH%jWk>n{$M+dbj(n~s74Uci;DncsMAd`JWS{!SwxBWPufFuprj4xf`)6ELBLcVqBDJT32ST~nzLh|1Q?j_x}iRoBA*ZK|gFHDTA!#Eui$$-71fZG^wf&_em0n4L++Zk|@ z1bmzU`$Pe6X27Wu@G%DL8wK3SfVC3v5e5uL0e3NAodkTE0V|?__c7pf3HTHPRz?92 zGvFKvcz^+HM-<5zeF2-8<9`~#A|^Wg0~y)BKf=-BuNT~hiG$hT)8S(r&G@S&;G+zn zoayin7u*LT93B3Vg8O8Iqr*RpxZ1nO6--m*iayaePVLM z;l3f7%tP!C)yJWvbRTB*50TTxl3!N~cg%FTSa*Lt659St@LTCn=)WEsf7W8y)!372 zZbArN_l#~#Vj&(~)b4j&-xm+wz(~SbS=1Mdg&u!9Qeq!$GL_EaoQv5`ro=Y8maYch z;>Ef;A;+qk?DlV8PWeeX(sVvk0ExAL!LI~HIDxWp-WU-mn~x>=-Lu|#=yc3 zu*Vpq94lXQkP?>k97X*-LG>0~0K(zVxsnU)g|2Q;szZ>D&Ms4d}w2kFx6PO31^|HEg zdPHTL5?nAvYdIWlV z#*w8h9wIscUYRSp*6+|a_7&ap??~ZvS>w6=r6Gy2D60EF$A1TQz1w3%b*H*gVnd>; zQ=;0>7?|5-X%s`76762n_Q5UN<@v(*0HG;Xrf7Z8e3#H9syp447UL5%(=sZMW{XDQ zIuU6~+Bu~8ZY1Qow|1pQ7X{7xgeFnlnXXLKM>Am`o!f;`j8P0bX;c4v4{2hZceU^- z=B5l8pn1R0B&xf$t5vK}@YyOu3i)(2ilJ2snv#~Ls{a6KqGKK+G_L~z9aKQ`y+V_y z?zXNrF<(KmO~OeNeV$Pa%~{Ztw6t;k_W|3}od~trqDY&45NOqAJ&sRLcOueeACeTP zJ?UYK9RCAA#CsT05Y_EP9qd6y*v%;8IiqKy0d*(s(YI0=Cv}iX_;-n~G0G(Uv+AP^ zEPiQ7?RuY1nB6cLHSQ6NOB92|#D(RkK}Ru& zC^Gt4LNY22AU+o%#Bro;Xrt=BFtJoix^`1WePM2r9UbQZ4$ld2T+_9^F#pbIs_%XNsBhe*bZ8r zEX|Z#dd#Q7{Jt2G7YFkDQiKpknBQE$Rm^XUCO_xgwEA&~4$a5q9Qvh^=#HW27>QF} z+Q7|IUScp6rIpMzW}2%5sc_dA7M^IeLVPm!MEU*?b6rwE1|-aSrI9~jk%1@%y_-Q) z;nLoy*8`&J>jg`ty!Qdc@^V0)a*crz>S>lqn)ifhI6oM$r&wcP1hVj6zzGWpL^0@n z45E!B+{y9-Q4Ep^0rjs0Q7H#RQQ-FjQs9jx1Y($obFc}Mexge2E@u^v=fJt1+T8Yb zqS30+jWYJ=%Rt`T!^T1#w5j8h@W(foSz)cAS{DGSi7BR!k ziU{}vH>)j>x(lZ(tU%z|UIJ+};Vgz^oVBRs0!IAOIBEH{+op~HuO685Z581|hm7j(2cL}U!JHq!ac$o%{w-<1eMeIe3 z_W73}v;Dz0I@}+r<$ol5l1tAVzba zgzk&qSD+KD5ayyaz(n8+%mo_+)-<{)YCiNC ztP;ic^dB7K`6NkW2RPM(a{^AVR$wkTQD9Aj%{-+=U#$F;DH;*W|7+IMt&+wLZWD+- z`U2hHO#*YlEdpyA&CFMt^wrNCxU#l+2x!{&*f-I(ai0c(UBIiOU&UHl7YrXZHB&&# z-imPtThKi7R6~ZWn?qx~oi2IX!I=V`;0%Ge;530X4K@puC;I9<>6wZ(O6x?{$5E2T z4vrS+1TPhs3yu_6(_piQ(xT6tO!c}riwE^;}~fjy#9U*`yPgL4HYf>#R61!oJaX>_xv@mNa&N zSK~r^slZ$?D6po%W}(ueFGh=P;qaKe$E8o&!Se!fV~)^p11z+Hb}sm@_F0W)`jsYq z6XbqGjOMP8=HnsFCj{n##{||i*ep_7^u=gVjz`959V&d-!C?ZO;3Xk{hYL&uYXs(k zLj=||x-m+dz8Gzm{iv86pA~-W;4cE5;I9JR;BNwR!Jh@zG@98kT_43vLG}SU=8tU0z+%$x7Bl%ccC{_{0AL4p7!AD9 zwqTn)CdOk*IL=A_KmgeOfXB**uiXpPS7XN4dw{U z1v3TKG@2Pun)Dg0fQR~%b$x7%rjayuP!i|_dkJ)dvcO!hNMKE)@tPXe3w;x49v7pD z9p%!;_Ymj=3k15so&s~hJb^WhW)>?=`X9iv`v+*q|d)Ui8Iiu}zOCE$JukkTiDiPJvGFZh^4U z1?GaC0&5!0?5i~Co6!E&#c19jY3$$)0-fL{fo^c4z+7;>z?w$miE;8tpVEw*x<3j{ z-4-sNM(ywv&Fd8Q^LLBpJcO0WI8*a@1@riV z(6R%Z{1F@FO9FGj=LOa@n73Oo5Bkix6Z6;==Ft|<<1EeNmCWO-Ldy;w5$FV86POFW zBCw{xyj+KQ&}Yt@n8)K`9v$&K&el9OFpqBvEj##@Kqq)qU@rKEz?ue|gESBN%mowk zcp}WBGoHtK&ErDm@m-;12j3Iu1m72!3%(<;rojfY0hWV4<1>%vZ1Z1`<_&#(|5v~7 z;cFNGu&DAVfh}hrAH^7H>&+KfsLBLiMi7^;Fd5awGI0R$JtEeTA{OZy6a{^=UHcA# z&9`{!DG$4!*m>NxIQl410Ru}9IhCS4_H{Vbh~81m40RyxH-U-f2LGkd#+73Aith z`R|3B55tGw3Wr_$E;2MfLkR{_a$>2qye@_;?m4ulddKtUBf5V#1PDV#&NI>R9Q>{2 zMPZVkfKKTAX~RkC<|5Qt>@-nn64lsAFsxsmNjd1T+sb9`>>;<%UY_C38FG8u@xE4$ zmk&3pd&=MP-APUl)-oYrK1>CAXDQg=RS`q%*MF*cGgt~40zSz!ya zlL2RJQA)b=ck;z_-V~+9&~{B&SRJ&^5#AWX#XL`WEQ}nOM37TiA7OgeWxV_{$15ku z=H<$yySA{_RHZhh4rG#>7JWNxShWtDu7w5LflO1SJW z?OcKs;@xgKzci=eT5g<~OYbIwtSdR-+}+et4AbpiifNG|`-nQ1C-o)Q3gINq+7$wliwdL_l`L)4vPCoRWohO$pEbv8 zM}!}YeAc;AcXfWGT}5d|a3N;C)pot)h}3yos**~kaBHWx?CGSp82lHceLf31uOuDQ zSaZWJ=60Z|Gp-9^8z3LzB7FA*|6KToufmUQa2XL+-OK?3@@9X)%Dsh>ozI(jbdT8P zFhE?kfCN<*C;jfhBEJ+QtM7mu|4Z(IU?l8Oay7;qd{PcQN`;R~$Y z?E~5s(B28-w-1Zbv&|yRBfr^qU8stitWo6rYf6L#qVr3OfK!hz`^+SQDta=aNForfDPg@1s z5pd(G2L+A|`ok53*k%agSaA;eyPXe@9fQl%OfwUBJy)^`wsc>+S6LMuCqZSr=rglv z)t@*Kq^&&~77Kd(RF32pGQ&@egMsE4yYDX$J8%QBk1kep3v4auA>6c45I5*RX;ZUxCCnlm+9vbbs0)quk=c%v5mU)D>k zMPpODv}niR`ara&LP2#Dw(D?!ikjYz;t_Ih$w?B)F^2iC#v+0BPjrYpAfMmEjS)n9G&V+A zJMmy4QXt*YGZk(C;hb6U!Iq%9JHqV_N>5G|&t0D57{ne7&#I4uiu7a*NP|bE(0$)Y z3>0eAeP_i7q*%dm1%q_sNoKlfZ448!`1x4R>`rFGi$HSWp?@A}509|7Pd>uljZcn!{trF4D;m&Z zWsXLpuqS`3(UZS0soM^JU=k`E*xtl|h?Ml?SQald!2P{hUxdT&V9_qN>8M_;h>nY* zjmU&%i_L9u`o7V%E=HTZVCufaaS-=p%J@haxg&ghd}J8bTnJbn6>-MaVv9Z)DG1}wRJe~I-g$W5g6F;v()~iuFgqLu!sRnC zJ`Nra*WjArcEO#t>TVFgI5=1y{x@8qImhYS)PvtZ)98VFtHV)6N0wsm~r|B z_i*M8?pHn;S0HgcA+|gsxBkfOOc+Ea1)`q3?JGHyOc9u7;ED<3%s%!UoukMAw2$4V z^B1A9k3Cm?l1S%>rzo3f+o4HwKzw*8tld%EjC0%O;-7%>Oj4D}7E`P-cg^(5!G5t~XE!C(@WF zHkl*&(mk5@KoJ4;g*sVFki`KF&0}>0{Ie|ofG)U@x<$-6O!H{f;f^u_9C_s8Ufz6@ z>laccZz1N{JGf68=5sH0OAo@8S+x~Zh*~uQNBMhjE?9My;ufu{spHha{mXd8&N;tV z7#X@BKN)BtDbGE&r&6w-Fe5tnJHfEn5=F^tM%B&S;aOuuxM!&+u`V><5W0k z-1Ac*!{Zx51sq?%Lk4)z`G(T8BG| zrw*MTM`$-#n@x15!nPCjf#$phW{32nOjCvKWI3q};DA32DU93}UD{xx@I*Gb z@;s_uZB^yLs65pNqWZ1=5iTrNz7OlD<^lf17BxcZiIBS2HXLveMy+ZyDacGUq&|FB zQ<#|QQP5!w(soc7+2I_tPZ%|sGW&pSu621FTjkG93>6>~wim-)qxCHxzb}o&Za1tZtyX+$2O8JlA&WG-Az}+B(yTn!DlpaaAQ_*r0Zb&PZjbpw8 zD#|N|O$_R|Cm&0v_Rfz2V3{fQ{fI6tt4_cC;o3`l7nlu8x(4u#^83M-h^q!I?s@ z(jwcS%U=O4*ZO-Xf2X*1^)|SjVtHiEY&Zt+EDqsBH~rH+Ot-sdcj>^upeY-tc-F`o z59w(=P4zg%ONY23>Pl;LWWqNv(ed_Q&2*yZU|#^e!~> zi4&$R=Qe3s9X4~AI@wsSbgef-EBGSkMrH57^)q`dKaeVTp;tK*chKlO@oBJ&fz;{= zpZ~%?JOdsN%Vm$jgJi32A@Vu#EMA`Tuvqp!^{m>#kSD}*+Te(Xg6ubp#a63n2+D^; z(8}}Ky^C$ZS!jb0g|>sRbq^oIz$TPkY!`cX;)oSl?UV5{qWBIj(Ui3=XXW2v#Y~DR zrt%~fzD9#5w3!~JTc_|`F7u?qD&5T-4&N@Zeho44)`Ncn@*)%3;apwq&xRG~O&pk{ zKRgaWO;t})MML!}x&+avZmtWTGXZ4{8C5ihYlIdL9!|lR%bS{{-Wm=3cTX&5`G!cc z&82~q>PyL?6wYF$s1#0`R6oOq0JR&z7)f|ws4o*1c9mPO3Bz|hTFq>e3wU4Rq9K|d zv9%$5hD%@LCFM0<`Cx1WJcv4=6YKRFVh)C%#?k$ekmKrU$3%f+^_A&)^$(z0nr8QJ z;CysDuPrsuKglM=*UKj3B3mxj--;^-!CbN{Yys0V*Hn0WmM&c34Hc>yuj}>$$A!{K zj=2OXcL3i6I%CjtihIfp>$JzvovcZEH54(FD=gY?)RjK&3=383kL>DO$eq3t2$iv> z@?ztd=-<_p-8&=u?{wtHKTKb=Qkp9-UkbGwG}+3b*$AXq!a*lKM54V_z3$!VI2|)S z1Va#OSd-e#F{`fy2j~IImv}3_B+gKyEkuf0crfzFgV*(<%wRNH!&vi%XYultv^F1- zWQ+7I@J;ai8IAOQJcN9>rZYEmo6kebOzgzN zPgC`Hx}pO~n&YLF*ycoqyPMBovc4IMv@)Y9?~^Q^L(fZ)ro2~JQ(?;qmr;Em#!#Fe9@rM5okLefdM^lPGU5?kJeQs#*f}xnG=-f=DhSJ2jVrO_07g4 zbogol_;8hEa*X=u5GuEK(XdnHb|;-p`3zqN)NYiUFF}{GH$sICP=yWRJzTwDp2}`x zGsV!Sn;Bj`F|2?Cp^$=Xenv;5y|f9fYl`uqf?MMh$`5LZ%lC2gNQDLLZ04qXy}bA2`c0L6ueyo7l&fTQq>JrCor-HkyOENdmd1vW84*(Iv3x{Y z$LT_ZZl+U87Y!;(v&UT!)kh=slb$qSgc>m!SB5W6^BZdZI^`w-O_57%*i%(+&s6;n z?YOch5*h8fG6Xcr`p^+DF8KN++^j?`@wmM?0uJ62ov@a{E=604_Ps#1tTG(#WY&q8 zu6GTV>oUAsjWZb8=u(i&$lKiFlA#T8^wLv9--I!g-F>-V4q(#_{a>hx7`D9PBm5_UmUR%hMnHIQJRUg+0`Ie4ep zf)e(ZaTmc{b?zF$G1ux16f{k3+(XSdCFZe5LVjDa?mRB8OAx*z;;_r3a_iA2^_jdY`pr5I^*G$rYX&PY8i)E?lHo#<*i_*JDtVOpiDNSbjv4Z)4f9|;czY#$h0Ml z66HGw`N&BII_e9)hTeUjdUaVBw!>d&@*>+`&CT5-7B<|wXJ-2oEQ)rK=O2NHg50nOf zhaWUz_Lt&)6uv2o*OBdLE?!5ycpXvka5yicbaqv{XV}$$){EDn#hVc?UaUdI8gHjG z-byChsgzi*I-M6P*Hh?osYf*?O6>QJI9u zuG!sPvEG!;YH_->ILP9B4mVS(ix4YKxyM}2rre$FE)31c_S6~Vd$(+N@6g&wEH0FL z$LFCfT1z2_qw_DOwTl)o`1+TW{!!GZe#W9Obk+;=n3IO3JZ7@FuyJjDv5jj|31U^7 zkE#~Ch*&kO+83vx=C+p6Ti(Lb>%c5?4Xn6?+yC(L=O7f*`m@zvXqmkPUTleM8F(~u zf7D+5D?7yB>MZ`E6S_CJr8m0bJ_hDkxHE6;6Tk6Mz|Bz=_Yh!PZGw?*!$| z9HW{BY`dVFp?J;Y6AdooBVVyJg!=`{Fgs#)2v$(sg2 zd1tCp1YCu2(;EI2ez5y!)x82sgGUZy?Y!B5Ulh%?J|q`q#!BE|TP8#-6Tu8epqtDH=SYuLKmD9&1O@j=HiLPWDLB2=4+l)TD}dW6>}V;G*2+GdL(=Q9NAYv8yqWh%lC4kRWs~z<4!l(rn4b*JFutMa{=%0^66N@ zTi(XCJ)~QV4@10^t9zIZ2SbM-`>0WKIWWcvOef4HN`v?+^J2+g&(p%z&xEe@P)7lL z>GZmSlH^No)$>_moa3^lVrJ`F966Mip1bv;h+sH`KJ&a>-_PB}N|B9BT;Ob3Z&W_JdO_VSx#JyQm^
0O;K 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

D|0>6~5=A-sN(m`K7i$%JNQC>RPJqcXmv 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%0DXabbnX(CCbe`!iZ2g0u|Gex- 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

(T}H@mdam_OzOC!_o9?4{p$!2lpS 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 DhO?6O literal 0 HcmV?d00001 diff --git a/package/AgentWindowsManaged/Resources/Strings.g.cs b/package/AgentWindowsManaged/Resources/Strings.g.cs new file mode 100644 index 000000000..dfdc8588d --- /dev/null +++ b/package/AgentWindowsManaged/Resources/Strings.g.cs @@ -0,0 +1,148 @@ +using WixSharp; + +namespace DevolutionsAgent.Resources +{ + public static class Strings + { + public static string I18n(this MsiRuntime runtime, string res) + { + return $"[{res}]".LocalizeWith(runtime.Localize); + } + /// + /// 1033 + /// + public const string Language = "Language"; + /// + /// Devolutions + /// + public const string VendorName = "VendorName"; + /// + /// Devolutions Inc. + /// + public const string VendorFullName = "VendorFullName"; + /// + /// System-wide service for extending Devolutions Gateway functionality. + /// + public const string ProductDescription = "ProductDescription"; + /// + /// There is a problem with the entered data. Please correct the issue and try again. + /// + public const string ThereIsAProblemWithTheEnteredData = "ThereIsAProblemWithTheEnteredData"; + /// + /// This product requires at least Windows 8 / Windows Server 2012 R2 + /// + public const string OS2Old = "OS2Old"; + /// + /// A newer version of this product is already installed. + /// + public const string NewerInstalled = "NewerInstalled"; + /// + /// You need to install the 64-bit version of this product on 64-bit Windows. + /// + public const string x64VersionRequired = "x64VersionRequired"; + /// + /// You need to install the 32-bit version of this product on 32-bit Windows. + /// + public const string x86VersionRequired = "x86VersionRequired"; + /// + /// Microsoft .NET Framework 4.8 is strongly recommended. Would you like to download it now? + /// + public const string DotNet48IsStrongRecommendedDownloadNow = "DotNet48IsStrongRecommendedDownloadNow"; + /// + /// The product requires Microsoft .NET Framework 4.6.2. Microsoft .NET Framework 4.8 is strongly recommended. Would you like to download it now? + /// + public const string Dotnet462IsRequired = "Dotnet462IsRequired"; + /// + /// View + /// + public const string ViewButton = "ViewButton"; + /// + /// Search + /// + public const string SearchButton = "SearchButton"; + /// + /// View Log + /// + public const string ViewLogButton = "ViewLogButton"; + /// + /// Install Location + /// + public const string Group_InstallLocation = "Group_InstallLocation"; + /// + /// Directory + /// + public const string Property_Directory = "Property_Directory"; + /// + /// Please wait for UAC prompt to appear.If it appears minimized then active it from the taskbar. + /// + public const string UACPromptLabel = "UACPromptLabel"; + /// + /// All Files + /// + public const string Filter_AllFiles = "Filter_AllFiles"; + /// + /// [ProductName] Setup + /// + public const string AgentDlg_Title = "AgentDlg_Title"; + /// + /// Change destination folder + /// + public const string BrowseDlgTitle = "BrowseDlgTitle"; + /// + /// Browse to the destination folder + /// + public const string BrowseDlgDescription = "BrowseDlgDescription"; + /// + /// Destination Folder + /// + public const string InstallDirDlgTitle = "InstallDirDlgTitle"; + /// + /// Click Next to install to the default folder or click Change to choose another. + /// + public const string InstallDirDlgDescription = "InstallDirDlgDescription"; + /// + /// Installing [ProductName] + /// + public const string ProgressDlgTitleInstalling = "ProgressDlgTitleInstalling"; + /// + /// Changing [ProductName] + /// + public const string ProgressDlgTitleChanging = "ProgressDlgTitleChanging"; + /// + /// Repairing [ProductName] + /// + public const string ProgressDlgTitleRepairing = "ProgressDlgTitleRepairing"; + /// + /// Removing [ProductName] + /// + public const string ProgressDlgTitleRemoving = "ProgressDlgTitleRemoving"; + /// + /// Updating [ProductName] + /// + public const string ProgressDlgTitleUpdating = "ProgressDlgTitleUpdating"; + /// + /// Ready to install [ProductName] + /// + public const string VerifyReadyDlgInstallTitle = "VerifyReadyDlgInstallTitle"; + /// + /// Ready to change [ProductName] + /// + public const string VerifyReadyDlgChangeTitle = "VerifyReadyDlgChangeTitle"; + /// + /// Ready to repair [ProductName] + /// + public const string VerifyReadyDlgRepairTitle = "VerifyReadyDlgRepairTitle"; + /// + /// Ready to remove [ProductName] + /// + public const string VerifyReadyDlgRemoveTitle = "VerifyReadyDlgRemoveTitle"; + /// + /// Ready to update [ProductName] + /// + public const string VerifyReadyDlgUpdateTitle = "VerifyReadyDlgUpdateTitle"; + /// + /// Welcome to the [ProductName] 20[ProductVersion] Setup Wizard + /// + public const string WelcomeDlgTitle = "WelcomeDlgTitle"; + } +} diff --git a/package/AgentWindowsManaged/Resources/Strings.g.tt b/package/AgentWindowsManaged/Resources/Strings.g.tt new file mode 100644 index 000000000..0cd07f79b --- /dev/null +++ b/package/AgentWindowsManaged/Resources/Strings.g.tt @@ -0,0 +1,213 @@ +<#@ template debug="false" hostspecific="true" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ assembly name="System.Xml" #> +<#@ assembly name="System.Xml.Linq" #> +<#@ assembly name="$(SolutionDir)Resources\Newtonsoft.Json.dll" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Xml" #> +<#@ import namespace="System.Text.RegularExpressions" #> +<#@ import namespace="System.Xml.Linq" #> +<#@ import namespace="System.IO" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="Newtonsoft.Json.Linq" #> +<#@ output extension=".cs" #> +<# + const string mainCulture = "en-US"; + string[] cultures = new string[] { mainCulture, "fr-FR" }; + + JToken mainCultureJson = JToken.Parse(File.ReadAllText(GetI18nFile(mainCulture, Host.TemplateFile))); + List mainCultureTokens = null; + + foreach (string culture in cultures) + { + string sourceFile = GetI18nFile(culture, Host.TemplateFile); + + if (!File.Exists(sourceFile)) + { + continue; + } + + JToken root = JToken.Parse(File.ReadAllText(sourceFile)); + JObject diff = null; + + if (!culture.Equals(mainCulture)) + { + diff = FindDiff(root, mainCultureJson); #> + <#= diff #> + <# SaveOutput($"DevolutionsAgent_{culture}_missing.json"); + } + + List idTextPairs = new List(); + FindIdTextPairs(root, idTextPairs); + + if (culture.Equals(mainCulture)) + { + mainCultureTokens = idTextPairs; + } + else + { + List missing = new List(); + FindIdTextPairs((JObject)diff, missing); + idTextPairs.AddRange(missing); + } + #> + +" xmlns="http://schemas.microsoft.com/wix/2006/localization"> + <# foreach (var tokenGroup in idTextPairs.GroupBy(x => x.path)) { #> + + <# foreach (var token in tokenGroup.OrderBy(x => x.id)) { #> + <#= token.text.Replace("\\n", " ") #> + <# } #> + <# } #> + +<# SaveOutput($"DevolutionsAgent_{culture.ToLower()}.wxl"); +} #> +using WixSharp; + +namespace DevolutionsAgent.Resources +{ + public static class Strings + { + public static string I18n(this MsiRuntime runtime, string res) + { + return $"[{res}]".LocalizeWith(runtime.Localize); + } +<# foreach (LocalizationToken token in mainCultureTokens) { #> + /// + /// <#= Regex.Replace(token.text.ToString(), @"\t|\n|\r", "") #> + /// + public const string <#= token.id #> = "<#= token.id #>"; +<# } #> + } +} +<#+ +class LocalizationToken +{ + public string id { get; set; } + public string text { get; set; } + public string path { get; set; } + public string fullPath { get; set; } +} + +static string GetI18nFile(string culture, string templateFile) +{ + string file = $"Strings_{culture}.json"; + string directory = System.IO.Path.GetDirectoryName(templateFile); + return Path.Combine(directory, file); +} + +static void FindIdTextPairs(JToken token, List pairs) +{ + string rootPath = "WixLocalization.strings."; + if (token.Type == JTokenType.Object) + { + var obj = (JObject)token; + var id = obj["id"]; + var text = obj["text"]; + + if (id != null && text != null) + { + pairs.Add(new LocalizationToken { + id = id.ToString(), + text = text.ToString(), + path = obj.Parent.Path.Replace(rootPath, ""), + fullPath = obj.Path + }); + } + + foreach (var property in obj.Properties()) + { + FindIdTextPairs(property.Value, pairs); + } + } + else if (token.Type == JTokenType.Array) + { + foreach (var item in token.Children()) + { + FindIdTextPairs(item, pairs); + } + } +} + +private void SaveOutput(string outputFileName) +{ + string templateDirectory = Path.GetDirectoryName(Host.TemplateFile); + string outputFilePath = Path.Combine(templateDirectory, outputFileName); + File.WriteAllText(outputFilePath, this.GenerationEnvironment.ToString()); + this.GenerationEnvironment.Remove(0, this.GenerationEnvironment.Length); +} + +private class LanguageTokenEqualityComparer : IEqualityComparer +{ + public bool Equals(JToken x, JToken y) + { + return ((JObject)x)["id"].Equals(((JObject)y)["id"]); + } + + public int GetHashCode(JToken obj) + { + if (obj == null) + { + return 0; + } + + return ((JObject)obj)["id"].GetHashCode(); + } +} + +private JObject FindDiff(JToken Current, JToken Model) +{ + var diff = new JObject(); + if (JToken.DeepEquals(Current, Model)) return diff; + + switch(Current.Type) + { + case JTokenType.Object: + { + var current = Current as JObject; + var model = Model as JObject; + var addedKeys = current.Properties().Select(c => c.Name).Except(model.Properties().Select(c => c.Name)); + var removedKeys = model.Properties().Select(c => c.Name).Except(current.Properties().Select(c => c.Name)); + var unchangedKeys = current.Properties().Where(c => JToken.DeepEquals(c.Value, Model[c.Name])).Select(c => c.Name); + foreach (var k in addedKeys) + { + diff[k] = new JObject + { + //["+"] = Current[k] + }; + } + foreach (var k in removedKeys) + { + diff[k] = new JObject + { + ["-"] = Model[k] + }; + } + var potentiallyModifiedKeys = current.Properties().Select(c => c.Name).Except(addedKeys).Except(unchangedKeys); + foreach (var k in potentiallyModifiedKeys) + { + var foundDiff = FindDiff(current[k], model[k]); + if(foundDiff.HasValues) diff[k] = foundDiff; + } + } + break; + case JTokenType.Array: + { + var current = Current as JArray; + var model = Model as JArray; + var plus = new JArray(current.Except(model, new LanguageTokenEqualityComparer())); + var minus = new JArray(model.Except(current, new LanguageTokenEqualityComparer())); + if (plus.HasValues) diff["+"] = plus; + if (minus.HasValues) diff["-"] = minus; + } + break; + default: + diff["+"] = Current; + diff["-"] = Model; + break; + } + + return diff; +} +#> diff --git a/package/AgentWindowsManaged/Resources/Strings_en-US.json b/package/AgentWindowsManaged/Resources/Strings_en-US.json new file mode 100644 index 000000000..24bdda455 --- /dev/null +++ b/package/AgentWindowsManaged/Resources/Strings_en-US.json @@ -0,0 +1,171 @@ +{ + "WixLocalization": { + "strings": { + "metadata": [ + { + "id": "Language", + "text": "1033" + }, + { + "id": "VendorName", + "text": "Devolutions" + }, + { + "id": "VendorFullName", + "text": "Devolutions Inc." + }, + { + "id": "ProductDescription", + "text": "System-wide service for extending Devolutions Gateway functionality." + } + ], + "messages": [ + { + "id": "ThereIsAProblemWithTheEnteredData", + "text": "There is a problem with the entered data. Please correct the issue and try again." + }, + { + "id": "OS2Old", + "text": "This product requires at least Windows 8 / Windows Server 2012 R2" + }, + { + "id": "NewerInstalled", + "text": "A newer version of this product is already installed." + }, + { + "id": "x64VersionRequired", + "text": "You need to install the 64-bit version of this product on 64-bit Windows." + }, + { + "id": "x86VersionRequired", + "text": "You need to install the 32-bit version of this product on 32-bit Windows." + }, + { + "id": "DotNet48IsStrongRecommendedDownloadNow", + "text": "Microsoft .NET Framework 4.8 is strongly recommended. Would you like to download it now?" + }, + { + "id": "Dotnet462IsRequired", + "text": "The product requires Microsoft .NET Framework 4.6.2. Microsoft .NET Framework 4.8 is strongly recommended. Would you like to download it now?" + } + ], + "buttons": [ + { + "id": "ViewButton", + "text": "View" + }, + { + "id": "SearchButton", + "text": "Search" + }, + { + "id": "ViewLogButton", + "text": "View Log" + } + ], + "propertyGroups": [ + { + "id": "Group_InstallLocation", + "text": "Install Location" + } + ], + "properties": [ + { + "id": "Property_Directory", + "text": "Directory" + } + ], + "static": [ + { + "id": "UACPromptLabel", + "text": "Please wait for UAC prompt to appear.\n\nIf it appears minimized then active it from the taskbar." + } + ], + "filters": [ + { + "id": "Filter_AllFiles", + "text": "All Files" + } + ], + "dialogs": { + "static": [ + { + "id": "AgentDlg_Title", + "text": "[ProductName] Setup" + } + ], + "browse": [ + { + "id": "BrowseDlgTitle", + "text": "Change destination folder" + }, + { + "id": "BrowseDlgDescription", + "text": "Browse to the destination folder" + } + ], + "installDir": [ + { + "id": "InstallDirDlgTitle", + "text": "Destination Folder" + }, + { + "id": "InstallDirDlgDescription", + "text": "Click Next to install to the default folder or click Change to choose another." + } + ], + "progress": [ + { + "id": "ProgressDlgTitleInstalling", + "text": "Installing [ProductName]" + }, + { + "id": "ProgressDlgTitleChanging", + "text": "Changing [ProductName]" + }, + { + "id": "ProgressDlgTitleRepairing", + "text": "Repairing [ProductName]" + }, + { + "id": "ProgressDlgTitleRemoving", + "text": "Removing [ProductName]" + }, + { + "id": "ProgressDlgTitleUpdating", + "text": "Updating [ProductName]" + } + ], + "verifyReady": [ + { + "id": "VerifyReadyDlgInstallTitle", + "text": "Ready to install [ProductName]" + }, + { + "id": "VerifyReadyDlgChangeTitle", + "text": "Ready to change [ProductName]" + }, + { + "id": "VerifyReadyDlgRepairTitle", + "text": "Ready to repair [ProductName]" + }, + { + "id": "VerifyReadyDlgRemoveTitle", + "text": "Ready to remove [ProductName]" + }, + { + "id": "VerifyReadyDlgUpdateTitle", + "text": "Ready to update [ProductName]" + } + ], + "welcome": [ + { + "id": "WelcomeDlgTitle", + "overridable": "yes", + "text": "Welcome to the [ProductName] 20[ProductVersion] Setup Wizard" + } + ] + } + } + } +} diff --git a/package/AgentWindowsManaged/Resources/Strings_fr-FR.json b/package/AgentWindowsManaged/Resources/Strings_fr-FR.json new file mode 100644 index 000000000..dc40c7ab8 --- /dev/null +++ b/package/AgentWindowsManaged/Resources/Strings_fr-FR.json @@ -0,0 +1,201 @@ +{ + "WixLocalization": { + "strings": { + "metadata": [ + { + "id": "Language", + "text": "1036" + }, + { + "id": "VendorName", + "text": "Devolutions" + }, + { + "id": "VendorFullName", + "text": "Devolutions Inc." + }, + { + "id": "ProductDescription", + "text": "Service à l’échelle du système pour étendre les fonctionnalités de Devolutions Gateway." + } + ], + "messages": [ + { + "id": "ThereIsAProblemWithTheEnteredData", + "text": "Il y a un problème avec les informations fournies, veuillez les modifier et essayer de nouveau." + }, + { + "id": "OS2Old", + "text": "Ce produit requiert Windows 8 ou Windows Server 2012 R2 au minimum." + }, + { + "id": "NewerInstalled", + "text": "Une version plus récente de ce produit est déjà installée." + }, + { + "id": "x64VersionRequired", + "text": "Vous devez installer la version 64 bits de ce produit avec l'environnement 64 bits de Windows." + }, + { + "id": "x86VersionRequired", + "text": "Vous devez installer la version 32 bits de ce produit avec l'environnement 32 bits de Windows." + }, + { + "id": "DotNet48IsStrongRecommendedDownloadNow", + "text": "Microsoft .NET Framework 4.8 est fortement conseillé. Souhaiteriez-vous le télécharger maintenant?" + }, + { + "id": "Dotnet462IsRequired", + "text": "Le produit nécessite Microsoft .NET Framework 4.6.2. Microsoft .NET Framework 4.8 est fortement conseillé. Souhaiteriez-vous le télécharger maintenant?" + } + ], + "buttons": [ + { + "id": "ViewButton", + "text": "Afficher" + }, + { + "id": "SearchButton", + "text": "Rechercher" + }, + { + "id": "ViewLogButton", + "text": "Afficher le journal" + } + ], + "propertyGroups": [ + { + "id": "Group_InstallLocation", + "text": "Emplacement de l'installation" + } + ], + "properties": [ + { + "id": "Property_Directory", + "text": "Répertoire" + } + ], + "static": [ + { + "id": "UACPromptLabel", + "text": "Veuillez attendre que l'invite UAC apparaisse.\n\nSi elle apparaît en mode réduit, alors vous devez l'activer à partir de la barre de tâches." + } + ], + "filters": [ + { + "id": "Filter_AllFiles", + "text": "Tous les fichiers" + } + ], + "dialogs": { + "static": [ + { + "id": "AgentDlg_Title", + "text": "Installation de [ProductName]" + } + ], + "browse": [ + { + "id": "BrowseDlgTitle", + "text": "Modifier le dossier de destination" + }, + { + "id": "BrowseDlgDescription", + "text": "Sélectionnez le dossier de destination" + } + ], + "installDir": [ + { + "id": "InstallDirDlgTitle", + "text": "Dossier de destination" + }, + { + "id": "InstallDirDlgDescription", + "text": "Cliquez sur Suivant pour effectuer l'installation dans le dossier par défaut, ou cliquez sur Modifier pour choisir un autre dossier." + } + ], + "progress": [ + { + "id": "ProgressDlgTitleInstalling", + "text": "Installation de [ProductName]" + }, + { + "id": "ProgressDlgTitleChanging", + "text": "Modification de [ProductName]" + }, + { + "id": "ProgressDlgTitleRepairing", + "text": "Réparation de [ProductName]" + }, + { + "id": "ProgressDlgTitleRemoving", + "text": "Suppression de [ProductName]" + }, + { + "id": "ProgressDlgTitleUpdating", + "text": "Mise à jour de [ProductName]" + } + ], + "verifyReady": [ + { + "id": "VerifyReadyDlgInstallTitle", + "text": "Prêt pour l'installation de [ProductName]" + }, + { + "id": "VerifyReadyDlgChangeTitle", + "text": "Prêt pour la modification de [ProductName]" + }, + { + "id": "VerifyReadyDlgRepairTitle", + "text": "Prêt pour la réparation de [ProductName]" + }, + { + "id": "VerifyReadyDlgRemoveTitle", + "text": "Prêt pour la suppression de [ProductName]" + }, + { + "id": "VerifyReadyDlgUpdateTitle", + "text": "Prêt pour la mise à jour de [ProductName]" + } + ], + "welcome": [ + { + "id": "WelcomeDlgTitle", + "overridable": "yes", + "text": "Bienvenue dans l'assistant d'installation de [ProductName] 20[ProductVersion]" + } + ], + "firewall": [ + { + "id": "msierrFirewallCannotConnect", + "text": "Impossible de se connecter au Pare-feu Windows. ([2] [3] [4] [5])" + }, + { + "id": "WixSchedFirewallExceptionsInstall", + "text": "Configuration du Pare-feu Windows" + }, + { + "id": "WixSchedFirewallExceptionsUninstall", + "text": "Configuration du Pare-feu Windows" + }, + { + "id": "WixRollbackFirewallExceptionsInstall", + "text": "Restauration de la configuration du Pare-feu Windows" + }, + { + "id": "WixExecFirewallExceptionsInstall", + "text": "Installation de la configuration du Pare-feu Windows" + }, + { + "id": "WixRollbackFirewallExceptionsUninstall", + "text": "Restauration de la configuration du Pare-feu Windows" + }, + { + "id": "WixExecFirewallExceptionsUninstall", + "text": "Désinstallation de la configuration du Pare-feu Windows" + } + ] + } + } + } +} diff --git a/package/AgentWindowsManaged/Resources/WixUIBanner.jpg b/package/AgentWindowsManaged/Resources/WixUIBanner.jpg new file mode 100644 index 0000000000000000000000000000000000000000..732caf6e6f9a9b9c43ec13d65ee232336687e352 GIT binary patch literal 5411 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 Mb882oov7)*07>;DI{*Lx literal 0 HcmV?d00001 diff --git a/package/AgentWindowsManaged/Resources/WixUIDialog.jpg b/package/AgentWindowsManaged/Resources/WixUIDialog.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2cc9af97c35c734c8d29405769dff7ba36d892df GIT binary patch literal 20983 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∈5@e2sOr@BU*O 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 gbf}2+EWdr;e})FB8*5JY9du Date: Sat, 1 Jun 2024 15:47:24 +0300 Subject: [PATCH 3/3] Upgraded ceviche --- Cargo.lock | 82 ++---------------------------------- devolutions-agent/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 79 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d4ef32970..3c876aaad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -550,26 +550,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ceviche" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd1a44085e79b3b02c2a5d18a54c8cb943bc7ae85207f76679b89f1e1c3c68c9" -dependencies = [ - "cfg-if", - "chrono", - "core-foundation", - "core-foundation-sys", - "ctrlc", - "libc", - "log", - "system-configuration-sys", - "systemd-rs", - "timer", - "widestring 0.4.3", - "winapi", -] - [[package]] name = "ceviche" version = "0.6.0" @@ -781,7 +761,7 @@ version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b" dependencies = [ - "nix 0.27.1", + "nix", "windows-sys 0.52.0", ] @@ -926,7 +906,7 @@ version = "2024.1.2" dependencies = [ "anyhow", "camino", - "ceviche 0.5.2", + "ceviche", "cfg-if", "ctrlc", "devolutions-gateway-task", @@ -954,7 +934,7 @@ dependencies = [ "backoff", "bytes", "camino", - "ceviche 0.6.0", + "ceviche", "cfg-if", "devolutions-gateway-generators", "devolutions-gateway-task", @@ -1204,16 +1184,6 @@ dependencies = [ "winreg 0.52.0", ] -[[package]] -name = "epoll" -version = "4.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79" -dependencies = [ - "bitflags 2.4.2", - "libc", -] - [[package]] name = "equivalent" version = "1.0.1" @@ -2099,15 +2069,6 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - [[package]] name = "mime" version = "0.3.17" @@ -2361,18 +2322,6 @@ dependencies = [ "windows-sys 0.45.0", ] -[[package]] -name = "nix" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset", -] - [[package]] name = "nix" version = "0.27.1" @@ -3421,7 +3370,7 @@ dependencies = [ "netlink-packet-utils", "netlink-proto", "netlink-sys", - "nix 0.27.1", + "nix", "thiserror", "tokio", ] @@ -4049,29 +3998,6 @@ dependencies = [ "libc", ] -[[package]] -name = "systemd-rs" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba28cdec1491faf8b9820305aa6cf9df1dff3378c1adfe41d5bd1261d4a17b59" -dependencies = [ - "epoll", - "libc", - "log", - "nix 0.24.3", - "systemd-sys", -] - -[[package]] -name = "systemd-sys" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4816f770cdd05c0a0b8bd048178c3eb3048221016945469c6124a1dd1afc6400" -dependencies = [ - "libc", - "pkg-config", -] - [[package]] name = "tap" version = "1.0.1" diff --git a/devolutions-agent/Cargo.toml b/devolutions-agent/Cargo.toml index 8685fface..b5b832239 100644 --- a/devolutions-agent/Cargo.toml +++ b/devolutions-agent/Cargo.toml @@ -13,7 +13,7 @@ devolutions-gateway-task = { path = "../crates/devolutions-gateway-task" } devolutions-log = { path = "../crates/devolutions-log" } # Lifecycle -ceviche = "0.5" +ceviche = "0.6" ctrlc = "3.1" # Serialization