zip-content/zip-install.sh
#!/system/bin/sh
# @name ZIP install
# @brief It can execute a flashable ZIP directly without the need of a recovery.
# @author ale5000
# Get the latest version from here: https://github.com/micro5k/microg-unofficial-installer/blob/HEAD/zip-content/zip-install.sh

# SPDX-FileCopyrightText: (c) 2022 ale5000
# SPDX-License-Identifier: GPL-3.0-or-later
# shellcheck enable=all

readonly ZIPINSTALL_VERSION='1.4.1'

PATH="${PATH:-/system/bin}:."
umask 022 || :

### PREVENTIVE CHECKS ###

command 1> /dev/null -v 'echo' || {
  echo || :
  exit 100
}

command 1> /dev/null -v 'test' || {
  echo 1>&2 'ERROR: Missing => test'
  exit 100
}

case "$(:)" in '') ;; *)
  echo 1>&2 'ERROR: Command substitution NOT supported by your shell'
  exit 100
  ;;
esac

_is_busybox_available()
{
  if test -f './busybox'; then test -x './busybox' || chmod 0755 './busybox' || echo 1>&2 'WARNING: Chmod failed on busybox'; fi

  command 1> /dev/null -v 'busybox' || {
    echo 'false'
    return
  }

  PROPAGATE_BUSYBOX='true'
  echo 'true'
}

_is_head_functional()
{
  command 1> /dev/null -v 'head' || return 1
  case "$(echo 2> /dev/null 'ABCD' | head 2> /dev/null -c 2 || :)" in 'AB') return 0 ;; *) ;; esac # Some versions of head are broken or incomplete
  return 2
}

_redirect_command()
{
  eval " ${1:?}() { busybox '${1:?}' \"\${@}\"; } " || {
    echo 1>&2 "ERROR: Replacing ${1?} failed"
    exit 100
  }
}

_is_head_functional || {
  if "${IS_BUSYBOX_AVAILABLE:=$(_is_busybox_available || :)}"; then _redirect_command 'head'; fi
}

command 1> /dev/null -v 'printf' || {
  if "${IS_BUSYBOX_AVAILABLE:=$(_is_busybox_available || :)}"; then
    _redirect_command 'printf'
  else
    NO_COLOR=1

    # shellcheck disable=SC2329 # IGNORE: Detection bug
    printf()
    {
      case "${1-unset}" in
        '%s')
          _printf_backup_ifs="${IFS-unset}"
          if _is_head_functional; then
            shift && IFS='' && echo "${*}" | head -c '-1'
          else
            shift && IFS='' && echo "${*}"
          fi
          if test "${_printf_backup_ifs}" = 'unset'; then unset IFS; else IFS="${_printf_backup_ifs}"; fi
          unset _printf_backup_ifs
          ;;
        '%s\n')
          shift && for _printf_val in "${@}"; do echo "${_printf_val}"; done
          ;;
        '%s\n\n')
          shift && for _printf_val in "${@}"; do echo "${_printf_val}" && echo ''; done
          ;;
        '\n') echo '' ;;
        '\n\n') echo '' && echo '' ;;
        '') ;;

        *)
          echo 1>&2 'ERROR: Unsupported printf parameter'
          return 2
          ;;
      esac

      unset _printf_val || :
      return 0
    }
  fi
}

command 1> /dev/null -v 'unzip' || {
  if "${IS_BUSYBOX_AVAILABLE:=$(_is_busybox_available || :)}"; then
    _redirect_command 'unzip'
  else
    echo 1>&2 'ERROR: "unzip" is missing'
    exit 100
  fi
}

### FUNCTIONS AND CODE ###

ui_info_msg()
{
  if test -n "${NO_COLOR-}"; then
    printf '%s\n' "${1}"
  elif test "${CI:-false}" = 'false'; then
    printf '\033[1;32m\r%s\n\033[0m\r    \r' "${1}"
  else
    printf '\033[1;32m%s\033[0m\n' "${1}"
  fi
}

ui_error_msg()
{
  if test -n "${NO_COLOR-}"; then
    printf 1>&2 '%s\n' "ERROR: ${1}"
  elif test "${CI:-false}" = 'false'; then
    printf 1>&2 '\033[1;31m\r%s\n\033[0m\r    \r' "ERROR: ${1}"
  else
    printf 1>&2 '\033[1;31m%s\033[0m\n' "ERROR: ${1}"
  fi
}

is_root()
{
  if command 1> /dev/null -v 'whoami'; then
    case "$(whoami || :)" in 'root') return 0 ;; *) ;; esac
  else
    command 1> /dev/null -v 'grep' || {
      if "${IS_BUSYBOX_AVAILABLE:=$(_is_busybox_available || :)}"; then
        _redirect_command 'grep'
      else
        echo 1>&2 'ERROR: "grep" is missing'
        exit 100
      fi
    }

    if id | grep -m '1' -q -e "uid=0[ (]"; then
      return 0
    fi
  fi

  return 1
}

if test "${#}" -gt 0; then
  for _param in "${@}"; do
    shift || {
      ui_error_msg 'shift failed'
      exit 6
    }
    # Skip empty parameters or parameters that may get passed due to buggy su implementation
    if test -z "${_param?}" || test "${_param:?}" = '--' || test "${_param:?}" = '[su]zip-install.sh'; then continue; fi

    test -e "${_param:?}" || {
      ui_error_msg "ZIP file doesn't exist => '${_param:-}'"
      exit 7
    }

    _param_copy="${_param:?}"
    _param="$(readlink 2> /dev/null -f "${_param_copy:?}")" || _param="$(realpath "${_param_copy:?}")" || {
      ui_error_msg "Canonicalization failed => '${_param_copy:-}'"
      exit 8
    }

    set -- "${@}" "${_param:?}" || {
      ui_error_msg 'set failed'
      exit 6
    }
  done
  unset _param _param_copy
fi

if test "${#}" -eq 0; then
  ui_error_msg 'You must specify the ZIP file to install'
  exit 5
fi

if ! is_root; then
  if test "${AUTO_ELEVATED:-false}" = 'false'; then
    printf '%s\n' 'Auto-rooting attempt...'

    # First verify that "su" is working (user 0 is root)
    su 0 sh -c 'command' '[su]verification' || {
      _status="${?}" # Usually it return 1 or 255 when root is present but disabled
      ui_error_msg 'Auto-rooting failed, you must execute this as root!!!'
      exit "${_status:-2}"
    }

    ZIP_INSTALL_SCRIPT="$(readlink 2> /dev/null -f "${0:?}")" || ZIP_INSTALL_SCRIPT="$(realpath "${0:?}")" || {
      ui_error_msg 'Unable to find myself'
      exit 3
    }
    exec su 0 sh -c "AUTO_ELEVATED=true DEBUG_LOG='${DEBUG_LOG-}' TMPDIR='${TMPDIR-}' LD_LIBRARY_PATH='${LD_LIBRARY_PATH-}' DRY_RUN='${DRY_RUN-}' KEY_TEST_ONLY='${KEY_TEST_ONLY-}' BYPASS_LOCK_CHECK='${BYPASS_LOCK_CHECK-}' INPUT_TYPE='${INPUT_TYPE-}' FORCE_HW_KEYS='${FORCE_HW_KEYS-}' CI='${CI:-false}' sh -- '${ZIP_INSTALL_SCRIPT:?}' \"\${@}\"" '[su]zip-install.sh' "${@}" || ui_error_msg 'failed: exec'
    exit "${?}"
  fi

  ui_error_msg 'You must execute this as root!!!'
  exit 4
fi
export LD_LIBRARY_PATH

test -n "${DEBUG_LOG-unset}" || unset DEBUG_LOG
test -n "${TMPDIR-unset}" || unset TMPDIR
test -n "${LD_LIBRARY_PATH-unset}" || unset LD_LIBRARY_PATH

test -n "${DRY_RUN-unset}" || unset DRY_RUN
test -n "${KEY_TEST_ONLY-unset}" || unset KEY_TEST_ONLY
test -n "${BYPASS_LOCK_CHECK-unset}" || unset BYPASS_LOCK_CHECK

test -n "${INPUT_TYPE-unset}" || unset INPUT_TYPE
test -n "${FORCE_HW_KEYS-unset}" || unset FORCE_HW_KEYS

propagate_busybox()
{
  mkdir -p "${TMPDIR:?}/bb-applets" || {
    ui_error_msg 'Failed to create a temp folder for busybox applets'
    return
  }
  busybox 2> /dev/null --install -s "${TMPDIR:?}/bb-applets" || {
    ui_error_msg 'Failed to propagate busybox'
    return
  }
  PATH="${PATH?}:${TMPDIR:?}/bb-applets"
  export PATH
}

unset SCRIPT_NAME
_clean_at_exit()
{
  if test -n "${SCRIPT_NAME-}" && test -f "${SCRIPT_NAME:?}"; then
    # Legacy versions of rm don't accept any parameter (except -r and -R)
    rm "${SCRIPT_NAME:?}" || :
  fi
  if test -n "${UPD_SCRIPT_NAME-}" && test -f "${UPD_SCRIPT_NAME:?}"; then
    rm "${UPD_SCRIPT_NAME:?}" || :
  fi
  if test -n "${TMPDIR-}" && test -d "${TMPDIR:?}/bb-applets"; then
    rm -r "${TMPDIR:?}/bb-applets" || :
  fi

  unset SCRIPT_NAME
  if test "${TMPDIR:-}" = '/dev/tmp'; then
    if test -d "${TMPDIR:?}"; then
      # Legacy versions of rmdir don't accept any parameter (not even --)
      rmdir "${TMPDIR:?}" 2> /dev/null || :
    fi
    unset TMPDIR
  fi
}
trap ' _clean_at_exit' 0 2 3 6 15

if test -n "${TMPDIR-}" && test "${TMPDIR:?}" != '/data/local/tmp' && test -w "${TMPDIR:?}"; then
  : # Already ready
elif test -w '/tmp'; then
  TMPDIR='/tmp'
elif test -w '/postinstall/tmp'; then
  TMPDIR='/postinstall/tmp'
elif test -d '/dev'; then
  mkdir -p '/dev/tmp' || {
    ui_error_msg 'Failed to create a temp folder'
    exit 9
  }
  chmod 01775 '/dev/tmp' || {
    ui_error_msg "chmod failed on '/dev/tmp'"
    rmdir 2> /dev/null '/dev/tmp' || :
    exit 10
  }
  TMPDIR='/dev/tmp'
else
  unset TMPDIR
fi

if test -z "${TMPDIR-}" || test ! -w "${TMPDIR:?}"; then
  ui_error_msg 'Unable to find a temp folder'
  exit 11
fi
export TMPDIR

test "${PROPAGATE_BUSYBOX:-false}" = 'false' || propagate_busybox

SCRIPT_NAME="${TMPDIR:?}/update-binary" || exit 12
UPD_SCRIPT_NAME="${TMPDIR:?}/updater-script" || exit 12
ZIPFILE="${1:?}"

unzip -p -qq "${ZIPFILE:?}" 'META-INF/com/google/android/update-binary' 1> "${SCRIPT_NAME:?}" || {
  ui_error_msg "Failed to extract update-binary from => '${ZIPFILE:-}'"
  exit 13
}
test -s "${SCRIPT_NAME:?}" || {
  ui_error_msg "Failed to extract update-binary (2) from => '${ZIPFILE:-}'"
  exit 14
}

unzip -p -qq "${ZIPFILE:?}" 'META-INF/com/google/android/updater-script' 1> "${UPD_SCRIPT_NAME:?}" || : # Not strictly needed

STATUS=0
if ! _is_head_functional || test '#!' = "$(head -c 2 -- "${SCRIPT_NAME:?}" || :)"; then
  printf '%s\n' 'Executing script...'

  # Use STDERR (2) for recovery messages to avoid possible problems with subshells intercepting output
  sh -- "${SCRIPT_NAME:?}" 3 2 "${ZIPFILE:?}" 'zip-install' "${ZIPINSTALL_VERSION:?}" || STATUS="${?}"
else
  printf '%s\n' 'Executing binary...'

  # Legacy versions of chmod don't support +x and --
  chmod 0755 "${SCRIPT_NAME:?}" || {
    ui_error_msg "chmod failed on '${SCRIPT_NAME:?}'"
    exit 15
  }
  "${SCRIPT_NAME:?}" 3 2 "${ZIPFILE:?}" 'zip-install' "${ZIPINSTALL_VERSION:?}" || STATUS="${?}"
fi

_clean_at_exit
trap - 0 2 3 6 15 || : # Already cleaned, so unset traps

case "${STATUS?}" in
  '0')
    ui_info_msg 'The ZIP installation has been successfully completed. Please restart your device now!!!'
    exit 0
    ;;
  '251') ui_info_msg 'To continue, restart the device and flash this again!!!' ;;
  '252') ui_info_msg 'To continue, restart the emulator and flash this again!!!' ;;
  *) ui_error_msg "ZIP installation failed with error ${STATUS?}" ;;
esac

exit "${STATUS:-254}"
