"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MacTargetHelper = void 0;
const builder_util_1 = require("builder-util");
const dynamicImport_1 = require("../util/dynamicImport");
const path = require("path");
const macCodeSign_1 = require("../codeSign/macCodeSign");
const pathManager_1 = require("../util/pathManager");
class MacTargetHelper {
    constructor(packager) {
        this.packager = packager;
    }
    handleNullIdentity(fallBackToAdhoc) {
        if (this.packager.forceCodeSigning) {
            throw new builder_util_1.InvalidConfigurationError("identity explicitly is set to null, but forceCodeSigning is set to true");
        }
        builder_util_1.log.info({ reason: "identity explicitly is set to null" }, "skipped macOS code signing");
        if (fallBackToAdhoc) {
            builder_util_1.log.warn("arm64 requires signing, but identity is set to null and signing is being skipped");
        }
        return false;
    }
    async findSigningIdentity(isMas, isDevelopment, qualifier, keychainFile, config, fallBackToAdhoc) {
        const certificateTypes = MacTargetHelper.getCertificateTypes(isMas, isDevelopment);
        let identity = null;
        for (const certificateType of certificateTypes) {
            identity = await (0, macCodeSign_1.findIdentity)(certificateType, qualifier, keychainFile);
            if (identity != null) {
                break;
            }
        }
        if (identity == null) {
            if (!isMas && !isDevelopment && config.type !== "distribution") {
                identity = await (0, macCodeSign_1.findIdentity)("Mac Developer", qualifier, keychainFile);
                if (identity != null) {
                    builder_util_1.log.warn("Mac Developer is used to sign app — it is only for development and testing, not for production");
                }
            }
            const noIdentity = !config.sign && identity == null;
            if (qualifier === "-") {
                const { Identity: IdentityClass } = await (0, dynamicImport_1.dynamicImport)("@electron/osx-sign/dist/cjs/util-identities");
                identity = new IdentityClass("-", undefined);
            }
            else if (noIdentity && fallBackToAdhoc) {
                builder_util_1.log.warn(null, "falling back to ad-hoc signature for macOS application code signing");
                const { Identity: IdentityClass } = await (0, dynamicImport_1.dynamicImport)("@electron/osx-sign/dist/cjs/util-identities");
                identity = new IdentityClass("-", undefined);
            }
            else if (noIdentity) {
                await (0, macCodeSign_1.reportError)(isMas, certificateTypes, qualifier, keychainFile, this.packager.forceCodeSigning);
                return null;
            }
        }
        return identity;
    }
    async buildSignOptions(appPath, identity, type, isMas, config, keychainFile, arch) {
        let filter = config.signIgnore;
        if (Array.isArray(filter)) {
            if (filter.length == 0) {
                filter = null;
            }
        }
        else if (filter != null) {
            filter = filter.length === 0 ? null : [filter];
        }
        const filterRe = filter == null
            ? null
            : filter.map(it => {
                try {
                    return new RegExp(it);
                }
                catch (e) {
                    throw new builder_util_1.InvalidConfigurationError(`Invalid regex filter pattern: ${it}. ${e.message}`);
                }
            });
        let binaries = config.binaries || undefined;
        if (binaries) {
            // Accept absolute paths for external binaries, else resolve relative paths from the artifact's app Contents path.
            binaries = (await Promise.all(binaries.flatMap(async (destination) => {
                const expandedDestination = this.packager.expandArch(destination, arch);
                return await Promise.all(expandedDestination.map(async (d) => {
                    if (await (0, builder_util_1.statOrNull)(d)) {
                        return d;
                    }
                    return path.resolve(appPath, d);
                }));
            }))).flat();
            builder_util_1.log.info({ binaries, arch: arch == null ? null : builder_util_1.Arch[arch] }, "signing additional user-defined binaries for arch");
        }
        const signOptions = {
            identityValidation: false,
            // https://github.com/electron-userland/electron-builder/issues/1699
            // kext are signed by the chipset manufacturers. You need a special certificate (only available on request) from Apple to be able to sign kext.
            ignore: (file) => {
                if (filterRe != null) {
                    for (const regExp of filterRe) {
                        if (regExp.test(file)) {
                            return true;
                        }
                    }
                }
                return (file.endsWith(".kext") ||
                    file.startsWith("/Contents/PlugIns", appPath.length) ||
                    file.includes("/node_modules/puppeteer/.local-chromium") ||
                    file.includes("/node_modules/playwright-firefox/.local-browsers") ||
                    file.includes("/node_modules/playwright/.local-browsers"));
                /* Those are browser automating modules, browser (chromium, nightly) cannot be signed
                  https://github.com/electron-userland/electron-builder/issues/2010
                  https://github.com/electron-userland/electron-builder/issues/5383
                  */
            },
            identity: identity ? identity.hash || identity.name : undefined,
            type,
            platform: isMas ? "mas" : "darwin",
            version: this.packager.config.electronVersion || undefined,
            app: appPath,
            keychain: keychainFile || undefined,
            binaries,
            // https://github.com/electron-userland/electron-builder/issues/1480
            strictVerify: config.strictVerify,
            preAutoEntitlements: config.preAutoEntitlements,
            optionsForFile: await this.getOptionsForFile(appPath, isMas, config),
            provisioningProfile: config.provisioningProfile || undefined,
        };
        return signOptions;
    }
    async createMasInstaller(appPath, outDir, masOptions, keychainFile, isDevelopment, arch) {
        const certType = isDevelopment ? "Mac Developer" : "3rd Party Mac Developer Installer";
        const masInstallerIdentity = await (0, macCodeSign_1.findIdentity)(certType, masOptions.identity, keychainFile);
        if (masInstallerIdentity == null) {
            throw new builder_util_1.InvalidConfigurationError(`Cannot find valid "${certType}" identity to sign MAS installer, please see https://electron.build/code-signing`);
        }
        MacTargetHelper.assertSafePathForCommandUsage(outDir, "output directory");
        // mas uploaded to AppStore, so, use "-" instead of space for name
        // path.basename prevents path traversal if a crafted artifactName contains "../"
        const artifactName = path.basename(this.packager.expandArtifactNamePattern(masOptions, "pkg", arch));
        MacTargetHelper.assertSafePathForCommandUsage(artifactName, "artifact name");
        const artifactPath = path.resolve(outDir, artifactName);
        await this.packager.doFlat(appPath, artifactPath, masInstallerIdentity, keychainFile);
        await this.packager.info.emitArtifactBuildCompleted({
            file: artifactPath,
            target: null,
            arch: builder_util_1.Arch.x64,
            safeArtifactName: this.packager.computeSafeArtifactName(artifactName, "pkg", arch, true, this.packager.platformSpecificBuildOptions.defaultArch),
            packager: this.packager,
        });
    }
    async getOptionsForFile(appPath, isMas, customSignOptions) {
        const resourceList = await this.packager.resourceList;
        const entitlementsSuffix = isMas ? "mas" : "mac";
        const getEntitlements = (filePath) => {
            if (filePath === appPath) {
                if (customSignOptions.entitlements) {
                    return customSignOptions.entitlements;
                }
                const p = `entitlements.${entitlementsSuffix}.plist`;
                if (resourceList.includes(p)) {
                    return path.join(this.packager.info.buildResourcesDir, p);
                }
                else {
                    return (0, pathManager_1.getTemplatePath)("entitlements.mac.plist");
                }
            }
            if (filePath.includes("Library/LoginItems")) {
                return customSignOptions.entitlementsLoginHelper;
            }
            if (customSignOptions.entitlementsInherit) {
                return customSignOptions.entitlementsInherit;
            }
            const p = `entitlements.${entitlementsSuffix}.inherit.plist`;
            if (resourceList.includes(p)) {
                return path.join(this.packager.info.buildResourcesDir, p);
            }
            else {
                return (0, pathManager_1.getTemplatePath)("entitlements.mac.plist");
            }
        };
        const requirements = isMas || this.packager.platformSpecificBuildOptions.requirements == null
            ? undefined
            : await this.packager.getResource(this.packager.platformSpecificBuildOptions.requirements);
        // harden by default for mac builds. Only harden mas builds if explicitly true (backward compatibility)
        const hardenedRuntime = isMas ? customSignOptions.hardenedRuntime === true : customSignOptions.hardenedRuntime !== false;
        return (filePath) => {
            const entitlements = getEntitlements(filePath);
            return {
                entitlements: entitlements || undefined,
                hardenedRuntime: hardenedRuntime !== null && hardenedRuntime !== void 0 ? hardenedRuntime : undefined,
                timestamp: customSignOptions.timestamp || undefined,
                requirements: requirements || undefined,
                additionalArguments: customSignOptions.additionalArguments || [],
            };
        };
    }
    static getCertificateTypes(isMas, isDevelopment) {
        if (isDevelopment) {
            return isMas ? ["Mac Developer", "Apple Development"] : ["Mac Developer", "Developer ID Application"];
        }
        return isMas ? ["Apple Distribution", "3rd Party Mac Developer Application"] : ["Developer ID Application"];
    }
    static isMasTarget(targetName) {
        return targetName === "mas" || targetName === "mas-dev";
    }
    static getPlatformTypeFromTarget(targetName) {
        if (targetName === "mas")
            return "mas";
        if (targetName === "mas-dev")
            return "mas-dev";
        return "mac";
    }
    static assertSafePathForCommandUsage(pathValue, description) {
        if (/[\0\r\n"'`$;&|<>]/.test(pathValue)) {
            throw new builder_util_1.InvalidConfigurationError(`Invalid ${description}: contains unsupported shell-special characters`);
        }
    }
    static getNotarizeOptions(appPath) {
        const tool = "notarytool";
        const teamId = process.env.APPLE_TEAM_ID;
        const appleId = process.env.APPLE_ID;
        const appleIdPassword = process.env.APPLE_APP_SPECIFIC_PASSWORD;
        if (appleId || appleIdPassword) {
            if (!appleId) {
                throw new builder_util_1.InvalidConfigurationError(`APPLE_ID env var needs to be set`);
            }
            if (!appleIdPassword) {
                throw new builder_util_1.InvalidConfigurationError(`APPLE_APP_SPECIFIC_PASSWORD env var needs to be set`);
            }
            if (!teamId) {
                throw new builder_util_1.InvalidConfigurationError(`APPLE_TEAM_ID env var needs to be set`);
            }
            return { tool, appPath, appleId, appleIdPassword, teamId };
        }
        const appleApiKey = process.env.APPLE_API_KEY;
        const appleApiKeyId = process.env.APPLE_API_KEY_ID;
        const appleApiIssuer = process.env.APPLE_API_ISSUER;
        if (appleApiKey || appleApiKeyId || appleApiIssuer) {
            if (!appleApiKey || !appleApiKeyId || !appleApiIssuer) {
                throw new builder_util_1.InvalidConfigurationError(`Env vars APPLE_API_KEY, APPLE_API_KEY_ID and APPLE_API_ISSUER need to be set`);
            }
            return { tool, appPath, appleApiKey, appleApiKeyId, appleApiIssuer };
        }
        const keychain = process.env.APPLE_KEYCHAIN;
        const keychainProfile = process.env.APPLE_KEYCHAIN_PROFILE;
        if (keychainProfile) {
            let args = { keychainProfile };
            if (keychain) {
                args = { ...args, keychain };
            }
            return { tool, appPath, ...args };
        }
        return undefined;
    }
    async notarizeIfProvided(appPath) {
        const notarizeOptions = this.packager.platformSpecificBuildOptions.notarize;
        if (notarizeOptions === false) {
            builder_util_1.log.info({ reason: "`notarize` options were set explicitly `false`" }, "skipped macOS notarization");
            return;
        }
        const options = MacTargetHelper.getNotarizeOptions(appPath);
        if (!options) {
            builder_util_1.log.warn({ reason: "`notarize` options were unable to be generated" }, "skipped macOS notarization");
            return;
        }
        const { notarize } = await (0, dynamicImport_1.dynamicImport)("@electron/notarize");
        await notarize(options);
        builder_util_1.log.info(null, "notarization successful");
    }
}
exports.MacTargetHelper = MacTargetHelper;
//# sourceMappingURL=MacTargetHelper.js.map