#!/usr/bin/env python
"""Configuration parameters for client builder and server packaging."""

import os
import time

from grr_response_core.lib import config_lib
from grr_response_core.lib import type_info

config_lib.DEFINE_string(
    name="ClientBuilder.output_extension",
    default=None,
    help="The file extension for the client (OS dependent).")

config_lib.DEFINE_string(
    name="ClientBuilder.package_dir",
    default="%(ClientBuilder.build_root_dir)/%(Client.name)-pkg",
    help="OSX package name.")

config_lib.DEFINE_bool(
    "ClientBuilder.fleetspeak_bundled", False,
    "Whether to bundle a Fleetspeak installation into the client package.")

config_lib.DEFINE_string(
    "ClientBuilder.fleetspeak_install_dir",
    "%(fleetspeak-client-bin@fleetspeak-client-bin|resource)",
    "Location of the bundled Fleetspeak installation.")

config_lib.DEFINE_string(
    "ClientBuilder.fleetspeak_client_config", "",
    "Location of the fleetspeak client configuration generated by "
    "fleetspeak-config.")

config_lib.DEFINE_string(
    "Client.fleetspeak_service_name", "FleetspeakService",
    "Name of the Fleetspeak (upstart, systemd or Windows) service. Used to "
    "restart the Fleetspeak service during GRR client installation.")

config_lib.DEFINE_string(
    "ClientBuilder.client_path",
    default="grr_response_client.client",
    help="Full module path for GRR client's main file.")

config_lib.DEFINE_string(
    "ClientBuilder.fleetspeak_service_dir", "/etc/fleetspeak/textservices",
    "Directory where Fleetspeak expects service configs to be. Only applies "
    "if Client.fleetspeak_enabled is true.")

config_lib.DEFINE_string(
    "ClientBuilder.fleetspeak_plist_path", None,
    "Path where the Fleetspeak client installs its plist file. Only applies "
    "if Client.fleetspeak_enabled is true.")

config_lib.DEFINE_string(
    "ClientBuilder.fleetspeak_config_path",
    "%(install_data/fleetspeak/%(Client.platform)/"
    "grr_service_config.txt.in@grr-response-core|resource)",
    "Path to GRR's Fleetspeak service configuration.")

config_lib.DEFINE_string(
    "ClientBuilder.version_ini_path", None,
    "Path to the version.ini file to be used when building a client template. "
    "If not specified, version.ini packaged with grr-response-core package "
    "will be used.")


class PathTypeInfo(type_info.String):
  """A path to a file or a directory."""

  def __init__(self, must_exist=True, **kwargs):
    self.must_exist = must_exist
    super().__init__(**kwargs)

  def Validate(self, value):
    value = super().Validate(value)
    if self.must_exist and not os.access(value, os.R_OK):
      raise type_info.TypeValueError(
          "Path %s does not exist for %s" % (value, self.name))

    return value

  def FromString(self, string):
    return os.path.normpath(string)


# PyInstaller build configuration.
config_lib.DEFINE_string(
    name="PyInstaller.spec",
    help="The spec file contents to use for building the client.",
    default=r"""
import glob
import os
import platform
import sys

# By default build in one dir mode.
client_path = r"%(%(%(ClientBuilder.client_path)|module_path)|fixpathsep)"

WINDOWS_IMPORTS = []
if platform.system\(\).lower\(\) == 'windows':
  WINDOWS_IMPORTS = ["win32process", "win32timezone"]

a = Analysis\(
    [client_path],
    # TODO\(https://github.com/pypa/setuptools/issues/1963\): py2_warn is
    # a workaround. Revisit in the future, whether this is needed.
    hiddenimports=WINDOWS_IMPORTS + ["pkg_resources.py2_warn"],
    hookspath=None\)

# Remove some optional libraries that would be packed but serve no purpose.
for prefix in ["IPython"]:
  for collection in [a.binaries, a.datas, a.pure]:
    for item in collection[:]:
      if item[0].startswith\(prefix\):
        collection.remove\(item\)

pyz = PYZ\(
    a.pure\)
exe = EXE\(
    pyz,
    a.scripts,
    exclude_binaries=1,
    name=os.path.join\("build", "grr-client"\),
    debug=False,
    strip=False,
    upx=False,
    embed_manifest=False,
    console=True,
    target_arch=r"%(PyInstaller.target_arch)" or None,
    version=os.path.join\(r"%(PyInstaller.build_dir)", "version.txt"\),
    icon=os.path.join\(r"%(PyInstaller.build_dir)", "grr.ico"\)\)

RESOURCES_PREFIX = os.path.join\(sys.prefix, "resources"\)

coll = COLLECT\(
    exe,
    a.binaries,
    a.zipfiles,
    a.datas,
    strip=False,
    upx=False,
    name="grr-client"
\)
""",
)

config_lib.DEFINE_string(
    name="PyInstaller.distpath",
    help=("Passed to PyInstaller as the --distpath flag. This sets the output "
          "directory for PyInstaller."),
    default="./dist")

config_lib.DEFINE_string(
    name="PyInstaller.version",
    help="The version.txt file contents to use for building the client.",
    default=r"""
VSVersionInfo\(
  ffi=FixedFileInfo\(
    filevers=\(%(Source.version_major), %(Source.version_minor),
               %(Source.version_revision), %(Source.version_release)\),
    prodvers=\(%(Source.version_major), %(Source.version_minor),
               %(Source.version_revision), %(Source.version_release)\),
    mask=0x3f,
    flags=0x0,
    OS=0x40004,
    fileType=0x1,
    subtype=0x0,
    date=\(0, 0\)
    \),
  kids=[
  VarFileInfo\([VarStruct\('Translation', [1033, 1200]\)]\)
  ]
\)
""")

config_lib.DEFINE_string("PyInstaller.icon_path",
                         "%(install_data/grr.ico@grr-response-core|resource)",
                         "A path to the icon to use for building the client.")

config_lib.DEFINE_string(
    "PyInstaller.build_dir",
    default="%(ClientBuilder.build_root_dir)/%(ClientBuilder.build_dest)",
    help="The path to the build directory.")

config_lib.DEFINE_string(
    "PyInstaller.dpkg_root",
    default="%(ClientBuilder.build_root_dir)/dist",
    help="Pyinstaller dpkg root.")

config_lib.DEFINE_string(
    "PyInstaller.workpath_dir",
    default="%(ClientBuilder.build_root_dir)/workpath",
    help="Pyinstaller working directory.")

config_lib.DEFINE_string(
    "PyInstaller.target_arch",
    default="",
    help=(
        "Pyinstaller target architecture (for macOS builds only). Defaults to "
        "a default PyInstaller behavior: producing non-unversal binary with "
        "the current machine's architecture. Set this option to 'universal2' "
        "to make PyInstaller produce universal binaries (you have to run on "
        "universal Python binary then). Set to 'x86_64' or 'arm64' to force "
        "a particular architecture. For details see: "
        "https://pyinstaller.org/en/stable/man/pyinstaller.html?highlight=universal2#mac-os-specific-options"
    ),
)

config_lib.DEFINE_string(
    name="Client.prefix",
    default="",
    help="A prefix for the client name, usually dbg_ for debug builds.")

config_lib.DEFINE_string(
    name="ClientBuilder.output_basename",
    default=("%(Client.prefix)%(Client.name)_"
             "%(Source.version_string)_%(Client.arch)"),
    help="The base name of the output package.")

# Windows client specific options.
config_lib.DEFINE_bool(
    "ClientBuilder.console",
    default=False,
    help="Should the application be built as a console program. "
    "This aids debugging in windows.")

config_lib.DEFINE_choice(
    name="ClientBuilder.build_type",
    default="Release",
    choices=["Release", "Debug"],
    help="Type of build (Debug, Release)")

config_lib.DEFINE_string(
    name="ClientBuilder.template_extension",
    default=".zip",
    help="The extension to appear on templates.")

config_lib.DEFINE_string(
    name="PyInstaller.template_basename",
    default=("%(Client.name)_%(Source.version_string)_%(Client.arch)"),
    help="The template name of the output package.")

config_lib.DEFINE_string(
    name="PyInstaller.template_filename",
    default=(
        "%(PyInstaller.template_basename)%(ClientBuilder.template_extension)"),
    help="The template file name of the output package.")

config_lib.DEFINE_option(
    PathTypeInfo(
        name="ClientBuilder.template_dir",
        must_exist=False,
        default=("%(grr-response-templates@grr-response-templates|resource)/"
                 "templates"),
        description="The directory holding executable template files."))

config_lib.DEFINE_option(
    PathTypeInfo(
        name="ClientBuilder.template_path",
        must_exist=False,
        default=(
            "%(ClientBuilder.template_dir)/%(PyInstaller.template_filename)"),
        description="The full path to the executable template files for "
        "building."))

config_lib.DEFINE_option(
    PathTypeInfo(
        name="ClientBuilder.executables_dir",
        must_exist=False,
        default="%(executables@grr-response-core|resource)",
        description="The path to the grr executables directory."))

config_lib.DEFINE_string(
    name="ClientBuilder.config_filename",
    default="%(Client.binary_name).yaml",
    help=("The name of the configuration file which will be embedded in the "
          "deployable binary."))

config_lib.DEFINE_string(
    name="ClientBuilder.maintainer",
    default="GRR <grr-dev@googlegroups.com>",
    help="The client package's maintainer.")

config_lib.DEFINE_string(
    name="ClientBuilder.debian_build_time",
    default=time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()),
    help="The build time put into the debian package. Needs to be formatted"
    " like the output of 'date -R'.")

config_lib.DEFINE_string(
    name="ClientBuilder.rpm_build_time",
    default=time.strftime("%a %b %d %Y", time.gmtime()),
    help="The build time put into the rpm package. Needs to be formatted"
    " according to the rpm specs.")

config_lib.DEFINE_string(
    name="ClientBuilder.debian_version",
    default="%(Template.version_numeric)-1",
    help="The version of the debian package.")

config_lib.DEFINE_string(
    name="ClientBuilder.debian_package_base",
    default=("%(ClientBuilder.package_name)_"
             "%(ClientBuilder.debian_version)_%(Template.arch)"),
    help="The filename of the debian package without extension.")

config_lib.DEFINE_string(
    name="ClientBuilder.package_name",
    default="%(Client.name)",
    help="The debian package name.")

config_lib.DEFINE_string(
    "ClientBuilder.vs_arch",
    default=None,
    help="Visual studio architecture string.")

config_lib.DEFINE_string(
    "ClientBuilder.vs_env_script",
    default=None,
    help="Path to visual studio environment variables bat file.")

config_lib.DEFINE_string(
    "ClientBuilder.vs_dir",
    default=r"%{C:\Program Files (x86)\Microsoft Visual Studio 14.0}",
    help="Path to visual studio installation dir.")

config_lib.DEFINE_string(
    "ClientBuilder.build_root_dir",
    default=None,
    help="Root directory for client builds.")

config_lib.DEFINE_string(
    "ClientBuilder.build_dest",
    default="%(Client.name)-build",
    help="Output directory for client building.")

config_lib.DEFINE_string(
    "ClientBuilder.install_dir",
    default="/usr/lib/%(Client.name)",
    help="Target installation directory for client builds.")

# Needed for package maker. Do not touch.
config_lib.DEFINE_string(
    "ClientBuilder.mangled_output_basename",
    default="%(Client.name)_%(Source.version_major)."
    "%(Source.version_minor).%(Source.version_revision)",
    help="OS X package maker mangled name.")

config_lib.DEFINE_string(
    "ClientBuilder.package_maker_organization",
    default=None,
    help="OS X package maker organization name.")

config_lib.DEFINE_string(
    "ClientBuilder.signing_cert_name",
    default=None,
    help="Name of a Darwin signing cert.")

config_lib.DEFINE_string(
    "ClientBuilder.signing_keychain_file",
    default="%(HOME|env)/Library/Keychains/MacApplicationSigning.keychain",
    help="Path to a keychain file to be used to sign Darwin binaries.")

config_lib.DEFINE_string(
    "ClientBuilder.target_dir",
    default=None,
    help="ClientBuilder target directory.")

config_lib.DEFINE_string(
    "ClientBuilder.daemon_link",
    default="/usr/sbin/%(Client.binary_name)",
    help="The installer package will create a link in the system to the "
    "installed binary.")

# These options will be used by client.client_build when running buildandrepack
# and can be used to customize what is built for each client label.
config_lib.DEFINE_multichoice(
    name="ClientBuilder.target_platforms",
    default=[],
    choices=[
        "darwin_amd64_dmg", "linux_amd64_deb", "linux_i386_deb",
        "linux_amd64_rpm", "linux_i386_rpm", "windows_amd64_exe",
        "windows_i386_exe"
    ],
    help="Platforms that will be built by client_build buildandrepack")

config_lib.DEFINE_string(
    "ClientBuilder.rpm_signing_key_public_keyfile",
    default="/etc/alternatives/grr_rpm_signing_key",
    help="Public key file for post-signing verification.")

config_lib.DEFINE_string(
    "ClientBuilder.rpm_gpg_name",
    default="GRR Team",
    help="The gpg name should match your gpg key name.")

# Parameters for signing Windows binaries on Linux hosts.
config_lib.DEFINE_string(
    "ClientBuilder.windows_signing_key",
    default="/etc/alternatives/grr_windows_signing_key",
    help="Path to GRR signing key. Should symlink "
    "to actual key.")

config_lib.DEFINE_string(
    "ClientBuilder.windows_signing_cert",
    default="/etc/alternatives/grr_windows_signing_cert",
    help="Path to GRR signing cert. Should symlink "
    "to actual cert.")

config_lib.DEFINE_string(
    "ClientBuilder.windows_signing_application_name",
    default="GRR",
    help="Signing cert application name.")

# Parameters for signing Windows binaries on Windows hosts.
config_lib.DEFINE_string(
    "ClientBuilder.signtool_signing_cmd",
    default=None,
    help="Full commandline for signtool to sign files.")

config_lib.DEFINE_string(
    "ClientBuilder.signtool_verification_cmd",
    default=None,
    help="Full commandline for signtool to verify signatures.")

config_lib.DEFINE_string(
    "ClientBuilder.extra_package_dependencies",
    default="",
    help="Extra dependencies, put verbatim into \"Depends:\" in debian/control "
    "or into \"Requires:\" in the RPM spec file.")

config_lib.DEFINE_string(
    "ClientBuilder.wix_tools_path",
    default="",
    help="Path to install location of wix toolset.")

config_lib.DEFINE_bool(
    "ClientBuilder.build_msi",
    default=False,
    help="If true, an MSI template is built on Windows.")

config_lib.DEFINE_string(
    name="ClientRepacker.output_basename",
    default=("%(Client.prefix)%(Client.name)_"
             "%(Template.version_string)_%(Template.arch)"),
    help="The base name of the output package.")

config_lib.DEFINE_option(
    PathTypeInfo(
        name="ClientRepacker.output_filename",
        must_exist=False,
        default=(
            "%(ClientRepacker.output_basename)%(ClientBuilder.output_extension)"
        ),
        description="The filename of the generated installer file."))

config_lib.DEFINE_option(
    PathTypeInfo(
        name="ClientRepacker.output_path",
        must_exist=False,
        default=("%(ClientBuilder.executables_dir)"
                 "/installers/%(ClientRepacker.output_filename)"),
        description="The full path to the generated installer file."))

# These values are determined from the template at repack time.
config_lib.DEFINE_choice(
    name="Template.build_type",
    default="Release",
    choices=["Release", "Debug"],
    help="Type of build (Debug, Release)")

config_lib.DEFINE_list(
    name="Template.build_context",
    default=[],
    help="List of build contexts that should be reapplied at repack.")

config_lib.DEFINE_integer("Template.version_major", None,
                          "Major version number of client template.")

config_lib.DEFINE_integer("Template.version_minor", None,
                          "Minor version number of client template.")

config_lib.DEFINE_integer("Template.version_revision", None,
                          "Revision number of client template.")

config_lib.DEFINE_integer("Template.version_release", None,
                          "Release number of client template.")

config_lib.DEFINE_string(
    "Template.version_string", "%(version_major).%(version_minor)."
    "%(version_revision).%(version_release)",
    "Version string of the client template.")

config_lib.DEFINE_integer(
    "Template.version_numeric", "%(version_major)%(version_minor)"
    "%(version_revision)%(version_release)",
    "Version string of the template as an integer.")

config_lib.DEFINE_string("Template.arch", None,
                         "The architecture of the client template.")
