diff --git a/afterInstall.ps1 b/afterInstall.ps1 new file mode 100644 index 0000000..fcaf232 --- /dev/null +++ b/afterInstall.ps1 @@ -0,0 +1,297 @@ +# ================================ +# CONFIGURATION +# ================================ +$AppName = "welsonjs" +$TargetDir = Join-Path $env:APPDATA $AppName +$TmpDir = Join-Path $env:TEMP "$AppName-downloads" + +Write-Host "" +Write-Host "[*] Target directory : $TargetDir" +Write-Host "[*] Temporary directory: $TmpDir" +Write-Host "" + +# Ensure base directories exist +New-Item -ItemType Directory -Path $TargetDir -Force | Out-Null +New-Item -ItemType Directory -Path $TmpDir -Force | Out-Null + + +# ================================ +# ARCHITECTURE DETECTION +# ================================ +$arch = "x86" +if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64") { $arch = "x64" } +elseif ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") { $arch = "arm64" } +if ($env:PROCESSOR_ARCHITEW6432) { $arch = "x64" } + +Write-Host "[*] Detected architecture: $arch" +Write-Host "" + + +# ================================ +# HELPER FUNCTIONS +# ================================ + +function Ensure-EmptyDirectory { + param( + [Parameter(Mandatory=$true)] + [string]$Path + ) + + # If a file exists at the path, remove it + if (Test-Path $Path -PathType Leaf) { + Write-Host "[WARN] A file exists at path '$Path'. Deleting it..." + Remove-Item -Path $Path -Force + } + + # Ensure directory exists + if (-not (Test-Path $Path -PathType Container)) { + Write-Host "[*] Creating directory: $Path" + New-Item -ItemType Directory -Path $Path -Force | Out-Null + } +} + +function Download-File { + param( + [Parameter(Mandatory=$true)] + [string]$Url, + [Parameter(Mandatory=$true)] + [string]$Destination + ) + + Write-Host "[*] Downloading file..." + Write-Host " URL : $Url" + Write-Host " OUT : $Destination" + + try { + Invoke-WebRequest -Uri $Url -OutFile $Destination -UseBasicParsing -ErrorAction Stop + Write-Host "[OK] Download completed." + Write-Host "" + } + catch { + Write-Host "[ERROR] Failed to download: $Url" + Write-Host $_.Exception.Message + throw + } +} + +function Extract-Zip { + param( + [Parameter(Mandatory=$true)] + [string]$ZipPath, + [Parameter(Mandatory=$true)] + [string]$DestDir + ) + + Write-Host "[*] Extracting ZIP:" + Write-Host " $ZipPath" + Write-Host " -> $DestDir" + + # Make sure destination directory exists and is a directory + Ensure-EmptyDirectory -Path $DestDir + + # Temporary extraction folder inside destination + $TmpExtract = Join-Path $DestDir "__tmp_extract__" + Ensure-EmptyDirectory -Path $TmpExtract + + Write-Host "[DEBUG] PowerShell command:" + Write-Host " Expand-Archive -LiteralPath `"$ZipPath`" -DestinationPath `"$TmpExtract`" -Force" + + try { + Expand-Archive -LiteralPath $ZipPath -DestinationPath $TmpExtract -Force -ErrorAction Stop + } + catch { + Write-Host "[ERROR] Failed to extract ZIP with Expand-Archive: $ZipPath" + Write-Host $_.Exception.Message + throw + } + + # Check extracted entries + $entries = Get-ChildItem -LiteralPath $TmpExtract + + if (-not $entries -or $entries.Count -eq 0) { + Write-Host "[ERROR] No entries were extracted from ZIP: $ZipPath" + throw "ZIP appears to be empty or extraction failed." + } + + if ($entries.Count -eq 1 -and $entries[0].PSIsContainer) { + Write-Host "[*] Detected single root directory inside ZIP. Flattening..." + + # Move children of the single root directory into destination + $innerItems = Get-ChildItem -LiteralPath $entries[0].FullName + foreach ($item in $innerItems) { + Move-Item -LiteralPath $item.FullName -Destination $DestDir -Force + } + } + else { + Write-Host "[*] ZIP has multiple top-level entries. Copying all..." + + foreach ($item in $entries) { + Move-Item -LiteralPath $item.FullName -Destination $DestDir -Force + } + } + + # Clean up temp extraction folder + Remove-Item -LiteralPath $TmpExtract -Recurse -Force + + Write-Host "[OK] ZIP extraction completed." + Write-Host "" +} + +function Extract-TarGz { + param( + [Parameter(Mandatory=$true)] + [string]$TarGzPath, + [Parameter(Mandatory=$true)] + [string]$DestDir + ) + + Write-Host "[*] Extracting TAR.GZ:" + Write-Host " $TarGzPath" + Write-Host " -> $DestDir" + + Ensure-EmptyDirectory -Path $DestDir + + # Modern Windows ships with tar, but we validate its presence + $tarCmd = Get-Command tar -ErrorAction SilentlyContinue + if (-not $tarCmd) { + Write-Host "[ERROR] 'tar' command not found. Cannot extract TAR.GZ." + throw "tar command not found." + } + + Write-Host "[DEBUG] tar command:" + Write-Host " tar -xzf `"$TarGzPath`" -C `"$DestDir`"" + + try { + & tar -xzf "$TarGzPath" -C "$DestDir" + if ($LASTEXITCODE -ne 0) { + throw "tar exit code $LASTEXITCODE" + } + Write-Host "[OK] TAR.GZ extraction completed." + Write-Host "" + } + catch { + Write-Host "[ERROR] Failed to extract TAR.GZ: $TarGzPath" + Write-Host $_.Exception.Message + throw + } +} + + +# ================================ +# SET DOWNLOAD URLS BASED ON ARCH +# ================================ +$PythonUrl = $null +$CurlUrl = $null +$YaraUrl = $null +$WamrUrl = $null + +switch ($arch) { + "x64" { + # Python embeddable (x64) + $PythonUrl = "https://www.python.org/ftp/python/3.13.9/python-3.13.9-embeddable-amd64.zip" + + # curl (x64, mingw) + $CurlUrl = "https://curl.se/windows/latest.cgi?p=win64-mingw.zip" + + # YARA (x64, GitHub — as you specified) + $YaraUrl = "https://github.com/VirusTotal/yara/releases/download/v4.5.5/yara-4.5.5-2368-win64.zip" + + # WAMR (x64 only) + $WamrUrl = "https://github.com/bytecodealliance/wasm-micro-runtime/releases/download/WAMR-2.4.3/iwasm-2.4.3-x86_64-windows-2022.tar.gz" + } + + "arm64" { + # Python embeddable (ARM64) + $PythonUrl = "https://www.python.org/ftp/python/3.13.9/python-3.13.9-embeddable-arm64.zip" + + # curl (ARM64) + $CurlUrl = "https://curl.se/windows/latest.cgi?p=win64a-mingw.zip" + + # DO NOT install YARA/WAMR on ARM64 + $YaraUrl = $null + $WamrUrl = $null + } + + default { + # Treat anything else as x86 + # Python embeddable (x86) + $PythonUrl = "https://www.python.org/ftp/python/3.13.9/python-3.13.9-embeddable-win32.zip" + + # curl (x86) + $CurlUrl = "https://downloads.sourceforge.net/project/muldersoft/cURL/curl-8.17.0-win-x86-full.2025-11-09.zip"; + + # Do NOT install YARA/WAMR on x86 (same policy as before) + $YaraUrl = $null + $WamrUrl = $null + } +} + +Write-Host "[*] Python URL: $PythonUrl" +Write-Host "[*] curl URL : $CurlUrl" +if ($YaraUrl) { + Write-Host "[*] YARA URL : $YaraUrl" +} else { + Write-Host "[*] YARA : skipped on this architecture" +} +if ($WamrUrl) { + Write-Host "[*] WAMR URL : $WamrUrl" +} else { + Write-Host "[*] WAMR : skipped on this architecture" +} +Write-Host "" + + +# ================================ +# DOWNLOAD FILES +# ================================ +$PythonZip = Join-Path $TmpDir "python.zip" +$CurlZip = Join-Path $TmpDir "curl.zip" +$YaraZip = Join-Path $TmpDir "yara.zip" +$WamrTgz = Join-Path $TmpDir "wamr.tar.gz" + +try { + Download-File -Url $PythonUrl -Destination $PythonZip + Download-File -Url $CurlUrl -Destination $CurlZip + + if ($YaraUrl) { + Download-File -Url $YaraUrl -Destination $YaraZip + } + + if ($WamrUrl) { + Download-File -Url $WamrUrl -Destination $WamrTgz + } +} +catch { + Write-Host "[FATAL] Download phase failed." + exit 1 +} + + +# ================================ +# EXTRACT FILES +# ================================ +try { + Extract-Zip -ZipPath $PythonZip -DestDir (Join-Path $TargetDir "python") + Extract-Zip -ZipPath $CurlZip -DestDir (Join-Path $TargetDir "curl") + + if ($YaraUrl) { + Extract-Zip -ZipPath $YaraZip -DestDir (Join-Path $TargetDir "yara") + } + + if ($WamrUrl) { + Extract-TarGz -TarGzPath $WamrTgz -DestDir (Join-Path $TargetDir "wamr") + } +} +catch { + Write-Host "[FATAL] Extraction phase failed." + exit 1 +} + + +# ================================ +# FINISH +# ================================ +Write-Host "[*] All tools installed successfully." +Write-Host "[*] Installed into: $TargetDir" +Write-Host "" +exit 0 diff --git a/lib/http.js b/lib/http.js index 47605e9..cdebb6b 100644 --- a/lib/http.js +++ b/lib/http.js @@ -119,16 +119,21 @@ var HTTPObject = function(engine) { } else if (this.engine == "CURL") { this._interface = SHELL.create(); - // the location of cURL binary - var arch = SYS.getArch(); - if (arch.toLowerCase().indexOf("arm") > -1) { - this.setBinPath("bin\\arm64\\curl-" + this.curlVersion + "-win64a-mingw\\bin\\curl.exe"); - } else if (arch.indexOf("64") > -1) { - this.setBinPath("bin\\x64\\curl-" + this.curlVersion + "-win64-mingw\\bin\\curl.exe"); + // set cURL executable file location + var default_curl_path = SYS.getAppDataDir() + "\\curl\\curl.exe"; + if (FILE.fileExists(default_curl_path)) { + this.setBinPath(default_curl_path); } else { - this.setBinPath("bin\\x86\\curl-" + this.curlVersion + "-win32-mingw\\bin\\curl.exe"); + var arch = SYS.getArch(); + if (arch.toLowerCase().indexOf("arm") > -1) { + this.setBinPath("bin\\arm64\\curl-" + this.curlVersion + "-win64a-mingw\\bin\\curl.exe"); + } else if (arch.indexOf("64") > -1) { + this.setBinPath("bin\\x64\\curl-" + this.curlVersion + "-win64-mingw\\bin\\curl.exe"); + } else { + this.setBinPath("bin\\x86\\curl-" + this.curlVersion + "-win32-mingw\\bin\\curl.exe"); + } } - + // do not clear after calling the `exec` this._interface.setIsPreventClear(true); } else if (this.engine == "BITS") { diff --git a/lib/ovftool.js b/lib/ovftool.js index 311b6f0..679d542 100644 --- a/lib/ovftool.js +++ b/lib/ovftool.js @@ -1,16 +1,17 @@ // ovftool.js -// Copyright 2019-2025, Namhyeon Go and the WelsonJS contributors. +// Namhyeon Go and the WelsonJS contributors. // SPDX-License-Identifier: GPL-3.0-or-later // https://github.com/gnh1201/welsonjs // // Download OVFTool (Open Virtualization Format (OVF) Tool): // https://developer.broadcom.com/tools/open-virtualization-format-ovf-tool/latest // +var SYS = require("lib/system"); var SHELL = require("lib/shell"); var CRED = require("lib/credentials"); function OVFObject() { - this.binPath = "bin\\x64\\VMware-ovftool-4.6.3-24031167-win.x86_64\\ovftool\\ovftool.exe"; + this.binPath = SYS.getAppDataDir() + "\\ovftool\\ovftool.exe"; this.hostname = ""; this.port = 443; this.resourceName = ""; @@ -66,7 +67,7 @@ function create() { exports.setCredential = setCredential; exports.create = create; -exports.VERSIONINFO = "Broadcom/VMware OVF Tool interface (ovftool.js) version 0.1.2"; +exports.VERSIONINFO = "Broadcom/VMware OVF Tool interface (ovftool.js) version 0.1.3"; exports.AUTHOR = "gnh1201@catswords.re.kr"; exports.global = global; exports.require = global.require; diff --git a/lib/python3.js b/lib/python3.js index 37635c0..5231bfe 100644 --- a/lib/python3.js +++ b/lib/python3.js @@ -13,13 +13,18 @@ function PythonObject() { this.version = "3.13.2"; this.create = function() { - var arch = SYS.getArch(); - if (arch.toLowerCase().indexOf("arm") > -1) { - this.setBinPath("bin\\arm64\\python-" + this.version + "-embed-arm64\\python.exe"); - } else if (arch.indexOf("64") > -1) { - this.setBinPath("bin\\x64\\python-" + this.version + "-embed-amd64\\python.exe"); + var default_python3_path = SYS.getAppDataDir() + "\\python\\python.exe"; + if (FILE.fileExists(default_python3_path)) { + this.setBinPath(default_python3_path); } else { - this.setBinPath("bin\\x86\\python-" + this.version + "-embed-win32\\python.exe"); + var arch = SYS.getArch(); + if (arch.toLowerCase().indexOf("arm") > -1) { + this.setBinPath("bin\\arm64\\python-" + this.version + "-embed-arm64\\python.exe"); + } else if (arch.indexOf("64") > -1) { + this.setBinPath("bin\\x64\\python-" + this.version + "-embed-amd64\\python.exe"); + } else { + this.setBinPath("bin\\x86\\python-" + this.version + "-embed-win32\\python.exe"); + } } }; @@ -57,6 +62,6 @@ exports.execScript = function(scriptName, args) { return (new PythonObject()).execScript(scriptName, args); }; -exports.VERSIONINFO = "Python Interface (python3.js) version 0.2.1"; +exports.VERSIONINFO = "Python Interface (python3.js) version 0.2.2"; exports.global = global; exports.require = global.require; diff --git a/lib/system.js b/lib/system.js index 37f8a0f..cd4fb90 100644 --- a/lib/system.js +++ b/lib/system.js @@ -37,6 +37,10 @@ function getEnvString(envName) { })(envName.toUpperCase()); } +function getAppDataDir() { + return getEnvString("APPDATA") + "\\WelsonJS"; +} + function get32BitFolder() { var base = getEnvString("WINDIR"); var syswow64 = base + "\\SysWOW64\\"; @@ -198,6 +202,7 @@ function getProcessVersion() { exports.createProcess = createProcess; exports.getEnvString = getEnvString; +exports.getAppDataDir = getAppDataDir; exports.get32BitFolder = get32BitFolder; exports.isElevated = isElevated; exports.getOS = getOS; @@ -219,6 +224,6 @@ exports.createShortcut = createShortcut; exports.ping = ping; exports.getProcessVersion = getProcessVersion; -exports.VERSIONINFO = "System Module (system.js) version 0.1.5"; +exports.VERSIONINFO = "System Module (system.js) version 0.1.6"; exports.global = global; exports.require = global.require; diff --git a/lib/wamr.js b/lib/wamr.js index 83d2573..0e3b550 100644 --- a/lib/wamr.js +++ b/lib/wamr.js @@ -1,15 +1,16 @@ // wamr.js -// Copyright 2019-2025, Namhyeon Go and the WelsonJS contributors. +// Namhyeon Go and the WelsonJS contributors. // SPDX-License-Identifier: GPL-3.0-or-later // https://github.com/gnh1201/welsonjs // -// WAMR(WebAssembly Micro Runtime) integration for WelsonJS framework +// WAMR(WebAssembly Micro Runtime) integration // https://github.com/bytecodealliance/wasm-micro-runtime // +var SYS = require("lib/system"); var SHELL = require("lib/shell"); var WAMRObject = function() { - this.binPath = "bin\\iwasm"; + this.binPath = SYS.getAppDataDir() + "\\wamr\\iwasm.exe"; this.verbose = 0; this.stackSize = 0; @@ -60,3 +61,7 @@ var WAMRObject = function() { exports.create = function() { return new WAMRObject(); }; + +exports.VERSIONINFO = "WAMR(WebAssembly Micro Runtime) integration (wamr.js) version 0.1.1"; +exports.global = global; +exports.require = global.require; diff --git a/setup.iss b/setup.iss index 205ea96..51fc7e9 100644 --- a/setup.iss +++ b/setup.iss @@ -84,6 +84,7 @@ Name: "{group}\Uninstall {cm:AppName}"; Filename: "{uninstallexe}"; AfterInstall ; Filename: {app}\bin\gtk2-runtime-2.24.33-2021-01-30-ts-win64.exe; ; Filename: {app}\bin\nmap-7.92\VC_redist.x86.exe; ; Filename: {app}\bin\nmap-7.92\npcap-1.50.exe; +Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -NoProfile -File ""{app}\afterInstall.ps1"""; WorkingDir: "{app}"; Flags: nowait Filename: {app}\installService.bat; Flags: nowait Filename: {app}\bin\x86\WelsonJS.Launcher.exe; Flags: nowait