{"version":3,"file":"appImageUtil.js","sourceRoot":"","sources":["../../../src/targets/appimage/appImageUtil.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arch, copyDir, copyFile, exec, exists, InvalidConfigurationError, log } from \"builder-util\"\nimport * as fs from \"fs-extra\"\nimport * as path from \"path\"\nimport { FileAssociation } from \"../../options/FileAssociation\"\nimport { getAppImageTools } from \"../../toolsets/linux\"\nimport { copyIcons, copyMimeTypes } from \"./appLauncher\"\nimport { appendBlockmap } from \"../differentialUpdateInfoBuilder\"\nimport { BlockMapDataHolder } from \"builder-util-runtime\"\nimport { APP_RUN_ENTRYPOINT } from \"./AppImageTarget\"\nimport { ToolsetConfig } from \"../../configuration\"\nimport { IconInfo } from \"../../util/iconConverter\"\n\ninterface Options {\n  productName: string\n  productFilename: string\n  executableName: string\n  desktopEntry: string\n  icons: IconInfo[]\n  license?: string | null\n  fileAssociations: FileAssociation[]\n  /**\n   * The compression type available for static runtime is limited as it's only compiled with support for gzip and zstd.\n   * \"xz\" is only valid for the legacy FUSE2 (0.0.0) toolset.\n   *\n   * [stderr] Squashfs image uses lzo compression, this version supports only zlib, zstd.\n   * Failed to open squashfs image\n   * Failed to extract AppImage\n   *\n   */\n  compression?: \"gzip\" | \"zstd\" | \"xz\"\n  /** Pre-computed desktop basename (already validated). When absent, falls back to `executableName`. */\n  desktopBaseName?: string\n}\n\nexport interface AppImageBuilderOptions {\n  appDir: string\n  stageDir: string\n  arch: Arch\n  output: string\n  options: Options\n}\n\nexport async function buildStaticRuntimeAppImage(appimageToolVersion: ToolsetConfig[\"appimage\"], opts: AppImageBuilderOptions): Promise<BlockMapDataHolder> {\n  const { stageDir, output, appDir, options, arch } = opts\n\n  try {\n    await fs.remove(output)\n\n    // Write AppRun launcher and related files\n    await writeAppLauncherAndRelatedFiles(opts)\n\n    const { runtimeLibraries: libraries, runtime, mksquashfs } = await getAppImageTools(appimageToolVersion, arch)\n    await copyDir(libraries, path.join(stageDir, \"usr\", \"lib\"))\n\n    // Copy app directory to stage\n    // mksquashfs doesn't support merging, so we copy the entire app dir\n    await copyDir(appDir, stageDir)\n\n    const runtimeData = await fs.readFile(runtime)\n\n    // Create squashfs with offset for runtime\n    const args: string[] = [stageDir, output, \"-offset\", runtimeData.length.toString(), \"-all-root\", \"-noappend\", \"-no-progress\", \"-quiet\", \"-no-xattrs\", \"-no-fragments\"]\n    if (options.compression) {\n      args.push(\"-comp\", options.compression)\n    }\n    await exec(mksquashfs, args, {\n      cwd: stageDir,\n    })\n\n    // Write runtime data at the beginning of the file\n    await writeRuntimeData(output, runtimeData)\n\n    // Make executable\n    await fs.chmod(output, 0o755)\n\n    // Append blockmap inside try block to ensure cleanup on failure\n    const updateInfo = await appendBlockmap(output)\n    return updateInfo\n  } catch (error) {\n    // Clean up partial build on failure\n    await fs.remove(output).catch(() => {})\n    throw error\n  }\n}\n\nexport async function buildLegacyFuse2AppImage(opts: AppImageBuilderOptions): Promise<BlockMapDataHolder> {\n  const { stageDir, output, appDir, options, arch } = opts\n\n  try {\n    await fs.remove(output)\n\n    await writeAppLauncherAndRelatedFiles(opts)\n\n    const { runtime, mksquashfs, runtimeLibraries } = await getAppImageTools(\"0.0.0\", arch)\n    // Mirror the app-builder-lib Go implementation: bundle lib/<arch> into usr/lib for x64 and ia32.\n    // arm targets don't have a dedicated lib dir in the FUSE2 toolset.\n    if (arch === Arch.x64 || arch === Arch.ia32) {\n      await copyDir(runtimeLibraries, path.join(stageDir, \"usr\", \"lib\"))\n    }\n    await copyDir(appDir, stageDir)\n\n    const runtimeData = await fs.readFile(runtime)\n\n    const args: string[] = [stageDir, output, \"-offset\", runtimeData.length.toString(), \"-all-root\", \"-noappend\", \"-no-progress\", \"-quiet\", \"-no-xattrs\", \"-no-fragments\"]\n    if (options.compression) {\n      args.push(\"-comp\", options.compression)\n      if (options.compression === \"xz\") {\n        // Match the dictionary/block-size settings used by the original app-builder Go implementation\n        args.push(\"-Xdict-size\", \"100%\", \"-b\", \"1048576\")\n      }\n    }\n    await exec(mksquashfs, args, { cwd: stageDir })\n\n    await writeRuntimeData(output, runtimeData)\n    await fs.chmod(output, 0o755)\n\n    const updateInfo = await appendBlockmap(output)\n    return updateInfo\n  } catch (error) {\n    await fs.remove(output).catch(() => {})\n    throw error\n  }\n}\n\nasync function writeRuntimeData(filePath: string, runtimeData: Buffer): Promise<void> {\n  const fd = await fs.open(filePath, \"r+\")\n  try {\n    await fs.write(fd, runtimeData, 0, runtimeData.length, 0)\n  } finally {\n    try {\n      await fs.close(fd)\n    } catch (closeError: any) {\n      // Log but don't throw - preserve original error if any\n      log.warn({ message: closeError.message, file: filePath }, `failed to close file descriptor`)\n    }\n  }\n}\n\n/**\n * Escapes a string for safe use in shell scripts by wrapping in single quotes\n * and escaping any single quotes within the string.\n *\n * This allows strings with spaces, special characters, etc. to be safely used.\n */\nfunction escapeShellString(str: string): string {\n  // Escape single quotes by replacing ' with '\\''\n  // Then wrap the whole string in single quotes\n  return `'${str.replace(/'/g, \"'\\\\''\")}'`\n}\n\n/**\n * Validates that critical path fields (executable name, product filename, license filename)\n * contain only characters that are safe for use in filesystem paths and embedded bash strings.\n * Allowed: Unicode letters, digits, dots, underscores, hyphens, and spaces.\n */\nexport function validateCriticalPathString(str: string, fieldName: string): void {\n  if (!/^[\\p{L}\\p{N}._\\- ]+$/u.test(str)) {\n    throw new InvalidConfigurationError(\n      `${fieldName} contains characters that cannot be safely used in file paths: ${str}. Please use only letters, digits, hyphens, underscores, dots, and spaces.`\n    )\n  }\n}\n\nasync function writeAppLauncherAndRelatedFiles(opts: AppImageBuilderOptions): Promise<void> {\n  const {\n    stageDir,\n    options: { license, executableName, productFilename, productName, desktopEntry, desktopBaseName },\n  } = opts\n\n  // executableName and productFilename are embedded directly into double-quoted bash strings\n  // and used as filesystem paths, so they must pass the whitelist.\n  // productName is not validated here because it is only ever passed through escapeShellString().\n  validateCriticalPathString(executableName, \"executableName\")\n  validateCriticalPathString(productFilename, \"productFilename\")\n\n  // Write desktop file\n  const desktopFileName = `${desktopBaseName ?? executableName}.desktop`\n  await fs.writeFile(path.join(stageDir, desktopFileName), desktopEntry, { mode: 0o644 })\n  await copyIcons(opts)\n\n  const templateConfig: AppRunScriptBase = {\n    DesktopFileName: desktopFileName,\n    ExecutableName: executableName,\n    ProductName: productName,\n    ProductFilename: productFilename,\n    ResourceName: `appimagekit-${executableName}`,\n  }\n\n  const mimeTypeFile = await copyMimeTypes(stageDir, opts.options)\n  if (mimeTypeFile) {\n    templateConfig.MimeTypeFile = mimeTypeFile\n  }\n\n  let finalConfig: AppRunScript = templateConfig\n\n  // Copy license file if provided\n  if (license) {\n    // Validate license file exists\n    if (!(await exists(license))) {\n      throw new InvalidConfigurationError(`License file not found: ${license}`)\n    }\n\n    const licenseBaseName = path.basename(license)\n    const ext = path.extname(license).toLowerCase()\n\n    // Validate license filename for path safety\n    validateCriticalPathString(licenseBaseName, \"licenseBaseName\")\n\n    // Validate extension\n    if (![\".txt\", \".html\"].includes(ext)) {\n      log.warn({ license, expected: \".txt or .html\" }, `license file has unexpected extension`)\n    }\n\n    await copyFile(license, path.join(stageDir, licenseBaseName))\n\n    finalConfig = {\n      ...templateConfig,\n      EulaFile: licenseBaseName,\n      IsHtmlEula: ext === \".html\",\n    }\n  }\n\n  const appRunContent = generateAppRunScript(finalConfig)\n  await fs.writeFile(path.join(stageDir, APP_RUN_ENTRYPOINT), appRunContent, { mode: 0o755 })\n}\n\nexport type AppRunScriptBase = {\n  ExecutableName: string\n  DesktopFileName: string\n  ProductFilename: string\n  ProductName: string\n  ResourceName: string\n  MimeTypeFile?: string\n}\n\nexport type AppRunScriptWithEula = AppRunScriptBase & {\n  EulaFile: string\n  IsHtmlEula: boolean\n}\n\nexport type AppRunScript = AppRunScriptBase | AppRunScriptWithEula\n\nfunction hasEula(config: AppRunScript): config is AppRunScriptWithEula {\n  return \"EulaFile\" in config && typeof config.EulaFile === \"string\"\n}\n\nexport function generateAppRunScript(config: AppRunScript): string {\n  const eulaEnabled = hasEula(config)\n\n  return `#!/usr/bin/env bash\nset -e\n\nTHIS=\"$0\"\n# http://stackoverflow.com/questions/3190818/\nargs=(\"$@\")\nNUMBER_OF_ARGS=\"$#\"\n\nif [ -z \"$APPDIR\" ] ; then\n  # Find the AppDir. It is the directory that contains AppRun.\n  # This assumes that this script resides inside the AppDir or a subdirectory.\n  # If this script is run inside an AppImage, then the AppImage runtime likely has already set $APPDIR\n  path=\"$(dirname \"$(readlink -f \"\\${THIS}\")\")\"\n  while [[ \"$path\" != \"\" && ! -e \"$path/${APP_RUN_ENTRYPOINT}\" ]]; do\n    path=\\${path%/*}\n  done\n  APPDIR=\"$path\"\nfi\n\nif [ -z \"$APPDIR\" ] ; then\n  echo \"ERROR: could not locate the AppDir. Ensure this script is run from within a properly structured AppImage.\" >&2\n  exit 1\nfi\n\nexport PATH=\"\\${APPDIR}:\\${APPDIR}/usr/sbin\\${PATH:+:\\${PATH}}\"\nexport XDG_DATA_DIRS=\"\\${APPDIR}/usr/share/\\${XDG_DATA_DIRS:+:\\${XDG_DATA_DIRS}}:/usr/share/gnome:/usr/local/share/:/usr/share/\"\nexport LD_LIBRARY_PATH=\"\\${APPDIR}/usr/lib\\${LD_LIBRARY_PATH:+:\\${LD_LIBRARY_PATH}}\"\nexport GSETTINGS_SCHEMA_DIR=\"\\${APPDIR}/usr/share/glib-2.0/schemas\\${GSETTINGS_SCHEMA_DIR:+:\\${GSETTINGS_SCHEMA_DIR}}\"\n\nBIN=\"$APPDIR/${config.ExecutableName}\"\n\nif [ -z \"$APPIMAGE_EXIT_AFTER_INSTALL\" ] ; then\n  trap atexit EXIT\nfi\n\nisEulaAccepted=1\n\nHAVE_NO_SANDBOX=0\nfor arg in \"\\${args[@]}\" ; do\n  if [ \"$arg\" = --no-sandbox ] ; then\n    HAVE_NO_SANDBOX=1\n    break\n  fi\ndone\nNO_SANDBOX=()\n# Use 'unshare -Ur true' as a heuristic to detect whether user namespaces are available.\n# Notes:\n#   - When running as root, this check will always succeed even if the sandbox configuration\n#     actually relies on unprivileged user namespaces. In practice, Chrome/Electron usually\n#     disables or adjusts the sandbox separately when running as root, so this probe is mostly\n#     a no-op in that scenario.\n#   - On minimal systems (e.g. Alpine or stripped-down containers) 'unshare' may not exist.\n#     In that case the shell will return exit code 127 (\"command not found\"), which will cause\n#     us to add '--no-sandbox'. This is an intentional fail-safe: we prefer the app to start\n#     without sandboxing rather than crash on startup.\nif [ $HAVE_NO_SANDBOX -eq 0 ] && ! unshare -Ur true 2>/dev/null ; then\n  NO_SANDBOX=(--no-sandbox)\nfi\n\natexit()\n{\n  if [ $isEulaAccepted == 1 ] ; then\n    if [ $NUMBER_OF_ARGS -eq 0 ] ; then\n      exec \"$BIN\" \"\\${NO_SANDBOX[@]}\"\n    else\n      exec \"$BIN\" \"\\${NO_SANDBOX[@]}\" \"\\${args[@]}\"\n    fi\n  fi\n}\n\nerror()\n{\n  if [ -x /usr/bin/zenity ] ; then\n    LD_LIBRARY_PATH=\"\" zenity --error --text \"\\${1}\" 2>/dev/null\n  elif [ -x /usr/bin/kdialog ] ; then\n    LD_LIBRARY_PATH=\"\" kdialog --msgbox \"\\${1}\" 2>/dev/null\n  elif [ -x /usr/bin/Xdialog ] ; then\n    LD_LIBRARY_PATH=\"\" Xdialog --msgbox \"\\${1}\" 2>/dev/null\n  else\n    echo \"\\${1}\"\n  fi\n  exit 1\n}\n\nyesno()\n{\n  TITLE=$1\n  TEXT=$2\n  if [ -x /usr/bin/zenity ] ; then\n    LD_LIBRARY_PATH=\"\" zenity --question --title=\"$TITLE\" --text=\"$TEXT\" 2>/dev/null || exit 0\n  elif [ -x /usr/bin/kdialog ] ; then\n    LD_LIBRARY_PATH=\"\" kdialog --title \"$TITLE\" --yesno \"$TEXT\" || exit 0\n  elif [ -x /usr/bin/Xdialog ] ; then\n    LD_LIBRARY_PATH=\"\" Xdialog --title \"$TITLE\" --clear --yesno \"$TEXT\" 10 80 || exit 0\n  else\n    echo \"zenity, kdialog, Xdialog missing. Skipping \\${THIS}.\"\n    exit 0\n  fi\n}\n\ncheck_dep()\n{\n  DEP=$1\n  if ! command -v \"$DEP\" &>/dev/null ; then\n    echo \"$DEP is missing. Skipping \\${THIS}.\"\n    exit 0\n  fi\n}\n\nif [ -z \"$APPIMAGE\" ] ; then\n  APPIMAGE=\"$APPDIR/${APP_RUN_ENTRYPOINT}\"\n  # not running from within an AppImage; hence using the AppRun for Exec=\nfi\n\n${\n  eulaEnabled\n    ? `if [ -z \"$APPIMAGE_SILENT_INSTALL\" ] ; then\n  EULA_MARK_DIR=\"\\${XDG_CONFIG_HOME:-$HOME/.config}/${config.ProductFilename}\"\n  EULA_MARK_FILE=\"$EULA_MARK_DIR/eulaAccepted\"\n  # show EULA only if desktop file doesn't exist\n  if [ ! -e \"$EULA_MARK_FILE\" ] ; then\n    if [ -x /usr/bin/zenity ] ; then\n      # on cancel simply exits and our trap handler launches app, so, $isEulaAccepted is set here to 0 and then to 1 if EULA accepted\n      isEulaAccepted=0\n      LD_LIBRARY_PATH=\"\" zenity --text-info --title=${escapeShellString(config.ProductName)} --filename=\"$APPDIR/${config.EulaFile}\" --ok-label=Agree --cancel-label=Disagree ${config.IsHtmlEula ? \"--html\" : \"\"}\n    elif [ -x /usr/bin/Xdialog ] ; then\n      isEulaAccepted=0\n      LD_LIBRARY_PATH=\"\" Xdialog --title ${escapeShellString(config.ProductName)} --textbox \"$APPDIR/${config.EulaFile}\" 30 80 --ok-label Agree --cancel-label Disagree\n    elif [ -x /usr/bin/kdialog ] ; then\n      # cannot find any option to force Agree/Disagree buttons for kdialog. And official example exactly with OK button https://techbase.kde.org/Development/Tutorials/Shell_Scripting_with_KDE_Dialogs#Example_21._--textbox_dialog_box\n      # in any case we pass labels text\n      isEulaAccepted=0\n      LD_LIBRARY_PATH=\"\" kdialog --textbox \"$APPDIR/${config.EulaFile}\" --yes-label Agree --cancel-label \"Disagree\"\n    fi\n\n    case $? in\n      0)\n          isEulaAccepted=1\n          echo \"License accepted\"\n          mkdir -p \"$EULA_MARK_DIR\"\n          touch \"$EULA_MARK_FILE\"\n      ;;\n        1)\n          echo \"License not accepted\"\n          exit 0\n      ;;\n        -1)\n          echo \"An unexpected error has occurred.\"\n          isEulaAccepted=1\n      ;;\n    esac\n  fi\nfi`\n    : \"\"\n}\n`\n}\n"]}