#!/usr/bin/env bash

# Copyright 1999-2025. WebPros International GmbH. All rights reserved.

# This script could work in two different modes:
# - installer - default mode, used when script is called without any arguments,
#   or with "--url" and/or with "--version" arguments.
# - repo-config-updater - that mode is used for gradual rollouts of WP Agent,
#   the script is switched to it when the "--generate-configs" and optional "--build-path"
#   arguments is found.
CURRENT_MODE="installer"
LOG_DIR=${HOME}
if [ ! -d "$LOG_DIR" ]; then
  LOG_DIR="/root"
fi
LOG_FILE_PATH="${LOG_DIR}/wp-agent-install.log"

DEFAULT_REPO_BASE_URL="https://get.wpguardian.io/wp-agent"
DEFAULT_PLATFORM_API_URL="https://app.wpguardian.io"
PATTERN_HOST_WITH_DEV_BUILDS="wpt-builds"
HELLO_RESPONSE_FILE_PATH='/tmp/wpg-response.tmp'
DEFAULT_PLATFORM_360_CPANEL_PLUGIN_DOWNLOAD_URL='https://repo.platform360.io'
USER_ID=$(id -u)

REPO_BASE_URL=""
PACKAGE_VERSION=""
WP_AGENT_VERSION="latest"
WP_AGENT_BUILD=""
AUTH_TOKEN=""
PLATFORM_API_URL=""
USE_WGET=0
ADDITIONAL_IPS=""
CONFIGURE_FIREWALL=0
ALLOW_PANEL=0
WP_AGENT_KEYRING_FILE="/etc/apt/keyrings/wp-agent.gpg"

if [[ "${USER_ID}" != "0" ]]; then
  echo "Running under root user is required"
  exit 1
fi

parse_args() {
  while [ "$#" -gt 0 ]; do
    # shellcheck disable=SC2015
    case "$1" in
    --url)
      REPO_BASE_URL="${2}"
      [ "$#" -ge 2 ] && shift 2 || break
      ;;
    --version)
      PACKAGE_VERSION="${2}"
      IFS=- read -r WP_AGENT_VERSION WP_AGENT_BUILD <<< "$PACKAGE_VERSION"; WP_AGENT_VERSION="${WP_AGENT_VERSION:-latest}"
      [ "$#" -ge 2 ] && shift 2 || break
      ;;
    --platform-url)
      PLATFORM_API_URL="${2}"
      [ "$#" -ge 2 ] && shift 2 || break
      ;;
    --token)
      AUTH_TOKEN="${2}"
      [ "$#" -ge 2 ] && shift 2 || break
      ;;
    --additional-ips)
      ADDITIONAL_IPS="${2}"
      [ "$#" -ge 2 ] && shift 2 || break
      ;;
    --configure-firewall)
      CONFIGURE_FIREWALL=1
      [ "$#" -ge 1 ] && shift 1 || break
      ;;
    --generate-configs)
      CURRENT_MODE="repo-config-updater"
      LOG_FILE_PATH="${LOG_DIR}/wp-agent-repo-config-updater.log"
      [ "$#" -ge 1 ] && shift 1 || break
      ;;
    --self-update)
      CURRENT_MODE="self-updater"
      LOG_FILE_PATH="${LOG_DIR}/wp-agent-updater.log"
      [ "$#" -ge 1 ] && shift 1 || break
      ;;
    --allow-panel)
      ALLOW_PANEL=1
      [ "$#" -ge 1 ] && shift 1 || break
      ;;
    --cpanel-plugin-download-url)
      PLATFORM_360_CPANEL_PLUGIN_DOWNLOAD_URL="${2}"
      [ "$#" -ge 2 ] && shift 2 || break
      ;;
    *)
      shift
      ;;
    esac
  done
}

# Sets the value for REPO_BASE_URL and PLATFORM_API_URL variables to default when it's empty.
fill_repo_base_url_variable() {
  if [[ -z "${REPO_BASE_URL}" ]]; then
    REPO_BASE_URL=${DEFAULT_REPO_BASE_URL}
  fi

  if [[ -z "${PLATFORM_API_URL}" ]]; then
    PLATFORM_API_URL=${DEFAULT_PLATFORM_API_URL}
  fi
}

print_header() {
  echo
  echo -e "\n\n\n" >> "${LOG_FILE_PATH}" 2>&1
  echo -e "*** Starting WP Guardian agent installation script on $(date) *** \n" >> "${LOG_FILE_PATH}" 2>&1
}

print_footer() {
  echo -e "\n*** WP Guardian agent installation script finished on $(date) ***" >> "${LOG_FILE_PATH}" 2>&1
}

# $1 <string> - step ID
# $2 <bool> - status
# $3 <string> - message
function sendStepStatus() {
  if [[ -z "${AUTH_TOKEN}" ]]; then
      printf "Auth token is not specified. Connection to WP Guardian will not be established.\n" >> "${LOG_FILE_PATH}" 2>&1
      return
  fi

  if [[ "${SERVER_GUID}" == "" ]]; then
    if [[ -e "$HELLO_RESPONSE_FILE_PATH" ]]; then
      SERVER_GUID=$(sed 's/{"uid":"//' "$HELLO_RESPONSE_FILE_PATH" | sed 's/".*//')
    else
      return
    fi
  fi

  URL="${PLATFORM_API_URL}/api/v1/security-dashboard/servers/hello/${SERVER_GUID}"
  B64_MESSAGE=$(echo -n "$3" | base64 -)

  if [[ $3 == "" ]]; then
    PAYLOAD="{\"id\":\"$1\",\"status\":$2}"
  else
    PAYLOAD="{\"id\":\"$1\",\"status\":$2,\"message\":\"$B64_MESSAGE\"}"
  fi

  # Timeout in seconds
  TIMEOUT=5

  printf "Sending installation step status to WP Guardian.\n" >> "${LOG_FILE_PATH}" 2>&1
  if [[ "${USE_WGET}" == 0 ]]; then
    curl \
      -H 'Content-type: application/json' \
      -H "Authorization: Bearer $AUTH_TOKEN" \
      -d "$PAYLOAD" \
      -m "$TIMEOUT" \
      "$URL" >> "${LOG_FILE_PATH}" 2>&1 &
    else
      wget -O- \
        --header 'Content-Type: application/json' \
        --header "Authorization: Bearer $AUTH_TOKEN" \
        --post-data "$PAYLOAD" \
        --connect-timeout "$TIMEOUT" \
        "$URL" >> "${LOG_FILE_PATH}" 2>&1 &
    fi
}

function displayHelpMessage() {
  printf "
To address the issues with the installation process of WP Guardian agent, we'd
like you to share the installation log located at '/root/wp-agent-install.log'
with WP Guardian team.  You can do this through LiveChat widget in WP Guardian
UI or through the following contact form:
https://pt-research.typeform.com/to/inhgEltN

Thank you for helping us make our product better!
"
}

# $1 <string> - step ID
function reportStepDone() {
  sendStepStatus "$1" true
}

# $1 <string> - step ID
# $2 <string> - sub step ID
function reportStepError() {
  sendStepStatus "$1" false "$2 ${DIST_ID}-${OS_VERSION}"
}

function exitWithError() {
  echo "ERROR: $*" >&2
  displayHelpMessage
  exit 1
}

# $1 <integer> - Expected exit code
# $2 <string> - Step ID
# $3 <string> - Sub step ID
# $4 <string> - Exit message
function checkExitCode() {
  if [ $? -ne "$1" ]; then
    reportStepError "$2" "$3"
    exitWithError "$4"
  fi
}

function wait_for_locks() {
    local lock_files
    local wait_time=10
    local max_retries=10
    local retry_count=0

    if [[ "${OS_NAME}" == "Ubuntu" || "${OS_NAME}" == "Debian" ]]; then
        lock_files=("/var/lib/dpkg/lock-frontend" "/var/lib/dpkg/lock" "/var/lib/apt/lists/lock" "/var/cache/apt/archives/lock")
    else
        return
    fi

    function check_for_locks() {
        for lock_file in "${lock_files[@]}"; do
            if lsof "$lock_file" >/dev/null 2>&1; then
                echo "Lock file $lock_file is currently in use. Waiting..." >> "${LOG_FILE_PATH}"
                return 1
            fi
        done
        return 0
    }

    while ! check_for_locks; do
        if (( retry_count >= max_retries )); then
            exitWithError "$PACKAGE_MANAGER is locked, maximum retries ($max_retries) reached"
        fi
        sleep $wait_time
        ((retry_count++))
    done
}

function install_gpg() {
  if [[ "${OS_NAME}" == "Ubuntu" || "${OS_NAME}" == "Debian" ]]; then
    DEBIAN_FRONTEND=noninteractive LANG=C "${PACKAGE_MANAGER}" install -y gnupg >> "${LOG_FILE_PATH}" 2>&1
    checkExitCode 0 'importRepoKeys' 'installGnupg' "Failed to install gnupg package, see installation log above for details"
  else
    echo "Installing GPG ..." >> "${LOG_FILE_PATH}"
    "$PACKAGE_MANAGER" -y install gnupg >> "${LOG_FILE_PATH}" 2>&1
    checkExitCode 0 'importRepoKeys' 'installGnupg' "Failed to install gnupg package, see installation log above for details"
  fi
}

function configure_apt_keys() {
  if [[ "${OS_NAME}" != "Ubuntu" && "${OS_NAME}" != "Debian" ]]; then
    return
  fi

  if [[ ! -d /etc/apt/keyrings ]]; then
    mkdir -m 0755 /etc/apt/keyrings
  fi

  if [[ "${OS_NAME}" == "Debian" && "${OS_VERSION}" == "13" ]]; then
    GPG_KEY_FILENAME="wp-toolkit.gpg.bin"
  else
    GPG_KEY_FILENAME="wp-toolkit-cpanel.gpg.bin"
  fi

  (wget -qO "${WP_AGENT_KEYRING_FILE}" "${REPO_BASE_URL}/${GPG_KEY_FILENAME}" || curl -L -f -o "${WP_AGENT_KEYRING_FILE}" "${REPO_BASE_URL}/${GPG_KEY_FILENAME}")
  checkExitCode 0 'importRepoKeys' 'downloadWpgKey' "Unable to download WP Guardian gpg key."

  chmod 0644 "${WP_AGENT_KEYRING_FILE}"
  checkExitCode 0 'importRepoKeys' 'wpgKeyPermissions' "Unable to change permissions for WP Guardian gpg key."
}

function configure_rpm_keys() {
  if [[ "${OS_NAME}" != "CentOS" ]]; then
    return
  fi

  # Import new WP Toolkit GPG key
  rpm --import ${REPO_BASE_URL}/wp-toolkit.gpg >> "${LOG_FILE_PATH}" 2>&1 || true

  # For rhel-based systems < 10 need to import two old keys
  if [ "${OS_VERSION}" -lt 10 ]; then
    # Thirdparties are signed with Plesk GPG
    rpm --import "${REPO_BASE_URL}"/plesk.gpg >> "${LOG_FILE_PATH}" 2>&1 || true

    # WP Agent are signed with own GPG
    rpm --import "${REPO_BASE_URL}"/wp-toolkit-cpanel.gpg >> "${LOG_FILE_PATH}" 2>&1 || true
  fi
}

function configure_keys() {
  wait_for_locks

  if [[ "${OS_NAME}" == "Ubuntu" || "${OS_NAME}" == "Debian" ]]; then
    configure_apt_keys
  else
    configure_rpm_keys
  fi

  reportStepDone 'importRepoKeys'
}

function create_apt_sources_list() {
  DEB_REPO="deb"
  if [[ -e "${WP_AGENT_KEYRING_FILE}" ]]; then
    DEB_REPO="deb [signed-by=${WP_AGENT_KEYRING_FILE}]"
  fi

  cat <<EOF >/etc/apt/sources.list.d/wp-agent.list
# WP Guardian agent
${DEB_REPO} ${REPO_BASE_URL}/${OS_NAME}-${OS_VERSION}-x86_64/${WP_AGENT_VERSION}/wp-agent/ ./

# WP Guardian agent - third parties
${DEB_REPO} ${REPO_BASE_URL}/${OS_NAME}-${OS_VERSION}-x86_64/${WP_AGENT_VERSION}/thirdparty/ ./
EOF
}

function create_yum_repo_config() {
  cat <<EOF >/etc/yum.repos.d/wp-agent.repo
[wp-agent]
name=WP Guardian agent
baseurl=${REPO_BASE_URL}/${OS_NAME}-${OS_VERSION}-x86_64/${WP_AGENT_VERSION}/wp-agent/
enabled=1
gpgcheck=1

[wp-agent-thirdparties]
name=WP Guardian agent
baseurl=${REPO_BASE_URL}/${OS_NAME}-${OS_VERSION}-x86_64/${WP_AGENT_VERSION}/thirdparty/
enabled=1
gpgcheck=1
EOF
}

function create_repo_config() {
  if [[ "${OS_NAME}" == "Ubuntu" || "${OS_NAME}" == "Debian" ]]; then
    create_apt_sources_list
  else
    create_yum_repo_config
  fi
}

function clean_repo_cache() {
  wait_for_locks
  echo "Cleaning repo cache..." >> "${LOG_FILE_PATH}"
  if [[ "${OS_NAME}" == "Ubuntu" || "${OS_NAME}" == "Debian" ]]; then
    DEBIAN_FRONTEND=noninteractive LANG=C "$PACKAGE_MANAGER" update >> "${LOG_FILE_PATH}" 2>&1
  else
    "$PACKAGE_MANAGER" clean all --disablerepo="*" --enablerepo=epel --enablerepo=wp-agent --enablerepo=wp-agent-thirdparties >> "${LOG_FILE_PATH}" 2>&1
  fi
}

function detect_os() {
  if [ -e /etc/os-release ]; then
    source /etc/os-release

    DIST_ID="${ID}"
    OS_VERSION="${VERSION_ID}"
  elif [ "$(which lsb_release 2>/dev/null)" ]; then
    DIST_ID="$(lsb_release -s -i)"
    OS_VERSION="$(lsb_release -s -r)"
  elif [ -e /etc/redhat-release ]; then
    DIST_ID="$(sed -n -e 's/\([[:alnum:]]*\)[[:space:]].*/\1/p' /etc/redhat-release)"
    OS_VERSION="$(sed -n -e 's/[^0-9]*\([0-9\.]\+\).*/\1/p' /etc/redhat-release)"
  elif [ -e /etc/issue ]; then
    DIST_ID="$(sed -n -e 's/\([[:alnum:]]*\)[[:space:]].*/\1/p' /etc/issue)"
    OS_VERSION="$(sed -n -e 's/[^0-9]*\([0-9\.]\+\).*/\1/p' /etc/issue)"
  fi
  # Technically, after the end of this you can end with a version that is not a number (e.g. 7.9.2009),
  # so some of the code that relies on this may break.

  IS_KUSANAGI=$(test -x /usr/local/bin/kusanagi && echo 1 || echo 0)

  if [ -n "${DIST_ID}" ]; then
    DIST_ID="$(echo "${DIST_ID}" | tr '[:upper:]' '[:lower:]')"
  fi

  case "$DIST_ID" in
  red*)
    # We use the same RPMs for RedHat as for CentOS
    OS_NAME="CentOS"
    ;;
  centos)
    OS_NAME="CentOS"
    ;;
  rhel | almalinux | rocky | cloudlinux | cloudlinuxserver)
    # We use the same RPMs for RHEL, AlmaLinux and RockyLinux as for CentOS
    OS_NAME="CentOS"
    ;;
  ubuntu)
    OS_NAME="Ubuntu"
    ;;
  debian)
    OS_NAME="Debian"
    ;;
  *)
    OS_NAME="${DIST_ID}"
    ;;
  esac

  check_supported_os_and_version

  case "$DIST_ID$OS_VERSION" in
  rhel7|centos7|cloudlinux7)
    PACKAGE_MANAGER="yum"
    ;;
  rhel*|centos*|cloudlinux*|almalinux*|rocky*)
    PACKAGE_MANAGER="dnf"
    ;;
  debian*|ubuntu*)
    PACKAGE_MANAGER="apt-get"
    ;;
  esac
}

function check_supported_os_and_version() {
  if [ -z "${OS_NAME}" ]; then
    reportStepError 'checkCompatibility' 'osNotDetected'
    exitWithError "Cannot determine OS name"
  fi

  if [ "${OS_NAME}" != "CentOS" ] && [ "${OS_NAME}" != "Ubuntu" ] && [ "${OS_NAME}" != "Debian" ]; then
    reportStepError 'checkCompatibility' 'unsupportedOs'
    exitWithError "Only CentOS-based distributions, Ubuntu, and Debian are supported at the moment"
  fi

  if [ "${OS_NAME}" == "Ubuntu" ]; then
    # In Ubuntu take two parts of OS_VERSION, e.g. for "Ubuntu 20.04.2 LTS" we consider OS_VERSION=20.04
    OS_VERSION=$(echo "$OS_VERSION" | sed -n -e 's/\([0-9]\+\.[0-9]\+\).*/\1/p')

    if [ "${OS_VERSION}" != "18.04" ] && [ "${OS_VERSION}" != "20.04" ] && [ "${OS_VERSION}" != "22.04" ] && [ "${OS_VERSION}" != "24.04" ]; then
      reportStepError 'checkCompatibility' 'unsupportedOs'
      exitWithError "Only Ubuntu 18.04, 20.04, 22.04, and 24.04 are supported at the moment"
    fi
  elif [ "${OS_NAME}" == "Debian" ]; then
    OS_VERSION=$(echo "$OS_VERSION" | sed -n -e 's/\([0-9]\+\).*/\1/p')

    if [ "${OS_VERSION}" != "10" ] && [ "${OS_VERSION}" != "11" ] && [ "${OS_VERSION}" != "12" ] && [ "${OS_VERSION}" != "13" ]; then
      reportStepError 'checkCompatibility' 'unsupportedOs'
      exitWithError "Only Debian 10, 11, 12, and 13 are supported at the moment"
    fi
  else
    # In CentOS based OS take only first part of OS_VERSION, e.g. for CentOS 7.7 we consider OS_VERSION=7
    OS_VERSION=$(echo "$OS_VERSION" | sed -n -e 's/\([0-9]\+\).*/\1/p')

    if [ "${OS_VERSION}" -lt 7 ] || [ "${OS_VERSION}" -gt 10 ]; then
      reportStepError 'checkCompatibility' 'unsupportedOs'
      exitWithError "Only CentOS 7, CloudLinux 7, 8 or 9, AlmaLinux 8, 9 or 10, and RockyLinux 8, 9 or 10 are supported at the moment"
    fi
  fi

  reportStepDone checkCompatibility
}

function check_plesk() {
  # Check if Plesk installed on the server
  for dir in "/usr/local/psa" "/opt/psa" "/etc/psa"
  do
    if [ -d "$dir" ]; then
      CURRENT_MODE="plesk-connection"
      break
    fi
  done

  if [[ "${ALLOW_PANEL}" -eq 0 && $CURRENT_MODE == "plesk-connection" ]]; then
    echo "Connecting Plesk servers to WP Guardian platform is not supported at the moment. Please check back later" | tee -a "${LOG_FILE_PATH}"
    reportStepError 'checkConflicts' 'plesk'
    print_footer
    exit 1
  fi
}

function check_cpanel() {
  # Check if cPanel installed on the server
  for dir in "/usr/local/cpanel" "/var/cpanel" "/etc/cpanel"
  do
    if [ -d "$dir" ]; then
      CURRENT_MODE="cpanel-connection"
      break
    fi
  done
  if [[ "${ALLOW_PANEL}" -eq 0 && $CURRENT_MODE == "cpanel-connection" ]]; then
    echo "Connecting cPanel servers to WP Guardian platform is not supported at the moment. Please check back later" | tee -a "${LOG_FILE_PATH}"
    reportStepError 'checkConflicts' 'cpanel'
    print_footer
    exit 1
  fi
}

function install_wp_agent() {
  wait_for_locks
  # The option for Dpkg is required in case when some configs are left on the server
  # from previous versions of package.
  # Installation of WP Guardian agent package should always overwrite configs,
  # this allows to deliver fixes for these files without manual actions from server admin.

  if [ -z "${WP_AGENT_BUILD}" ]; then
    echo "(4/5) Installing WP Guardian agent ..." | tee -a "${LOG_FILE_PATH}"

    if [[ "${OS_NAME}" == "Ubuntu" || "${OS_NAME}" == "Debian" ]]; then
      DEBIAN_FRONTEND=noninteractive LANG=C "${PACKAGE_MANAGER}" install -o Dpkg::Options::="--force-confnew" -y wp-agent >> "${LOG_FILE_PATH}" 2>&1
    else
      "$PACKAGE_MANAGER" install --enablerepo=epel -y wp-agent >> "${LOG_FILE_PATH}" 2>&1
    fi
  else
    echo "(4/5) Installing WP Guardian agent version ${PACKAGE_VERSION}..." | tee -a "${LOG_FILE_PATH}"

    if [[ "${OS_NAME}" == "Ubuntu" || "${OS_NAME}" == "Debian" ]]; then
      DEBIAN_FRONTEND=noninteractive LANG=C "${PACKAGE_MANAGER}" install -o Dpkg::Options::="--force-confnew" -y "wp-agent=${PACKAGE_VERSION}*" >> "${LOG_FILE_PATH}" 2>&1
    else
      "$PACKAGE_MANAGER" install --enablerepo=epel -y "wp-agent-${PACKAGE_VERSION}" >> "${LOG_FILE_PATH}" 2>&1
    fi
  fi

  checkExitCode 0 'installAgent' '' "Failed to install WP Guardian agent, see installation log in ${LOG_FILE_PATH}"
  reportStepDone 'installAgent'
}

function update_wp_agent() {
  # The option for Dpkg is required in case when some configs are left on the server
  # from previous versions of package.
  # Update of WP Guardian agent package should always overwrite configs,
  # this allows to deliver fixes for these files without manual actions from server admin.

  echo "(3/3) Updating WP Guardian agent ..." | tee -a "${LOG_FILE_PATH}"

  if [[ "${OS_NAME}" == "Ubuntu" || "${OS_NAME}" == "Debian" ]]; then
    DEBIAN_FRONTEND=noninteractive LANG=C "${PACKAGE_MANAGER}" --assume-yes --no-reinstall -o Dpkg::Options::="--force-confnew" install -y wp-agent >> "${LOG_FILE_PATH}" 2>&1
  else
    "$PACKAGE_MANAGER" update --enablerepo=epel -y wp-agent >> "${LOG_FILE_PATH}" 2>&1
  fi

  checkExitCode 0 'updateAgent' '' "Failed to update WP Guardian agent, see update log in ${LOG_FILE_PATH}"
  reportStepDone 'updateAgent'
}

function update_system() {
  wait_for_locks
  echo "Update system..." >> "${LOG_FILE_PATH}"
  if [[ "${OS_NAME}" == "Ubuntu" || "${OS_NAME}" == "Debian" ]]; then
    DEBIAN_FRONTEND=noninteractive LANG=C "$PACKAGE_MANAGER" update >> "${LOG_FILE_PATH}" 2>&1
  fi
}

function install_required_packages() {
  wait_for_locks

  if [ "${IS_KUSANAGI}" = 1 ]; then
    echo "Kusanagi 9 detected, installing system libzip package ..." | tee -a "${LOG_FILE_PATH}"
    "$PACKAGE_MANAGER" install -y libzip >> "${LOG_FILE_PATH}" 2>&1
  fi

  if has_disabled_repo "epel" || has_enabled_repo "epel"; then
    return
  fi

  echo "Install EPEL repository ..." | tee -a "${LOG_FILE_PATH}"
  "$PACKAGE_MANAGER" install -y epel-release >> "${LOG_FILE_PATH}" 2>&1 \
    || "$PACKAGE_MANAGER" install -y "https://dl.fedoraproject.org/pub/epel/epel-release-latest-$OS_VERSION.noarch.rpm" >> "${LOG_FILE_PATH}" 2>&1
  "$PACKAGE_MANAGER" update -y 'epel-release' --setopt='*.skip_if_unavailable=1' >> "${LOG_FILE_PATH}" 2>&1
  echo "Disable EPEL repository ..." | tee -a "${LOG_FILE_PATH}"
  disable_repo epel
  disable_repo epel-cisco-openh264 || :
}

function sendHello() {
  if [ -e $HELLO_RESPONSE_FILE_PATH ]; then
    printf "Response file already exists. Connection to WP Guardian will not be established.\n" >> "${LOG_FILE_PATH}" 2>&1
    return
  fi

  if [[ -z "${AUTH_TOKEN}" ]]; then
      printf "Auth token is not specified. Connection to WP Guardian will not be established.\n" >> "${LOG_FILE_PATH}" 2>&1
      return
  fi

  URL="${PLATFORM_API_URL}/api/v1/security-dashboard/servers/hello"
  BASE64_HOSTNAME=$(echo -n "$HOSTNAME" | base64 -)
  HELLO_PAYLOAD="{\"product\":\"wp\",\"hostnameBase64\":\"$BASE64_HOSTNAME\"}"
  # Timeout in seconds
  TIMEOUT=10

  printf "Connecting to WP Guardian.\n" >> "${LOG_FILE_PATH}" 2>&1
  curl -f -o "$HELLO_RESPONSE_FILE_PATH" \
    -H 'Content-type: application/json' \
    -H "Authorization: Bearer $AUTH_TOKEN" \
    -d "$HELLO_PAYLOAD" \
    -m "$TIMEOUT" \
    "$URL" >> "${LOG_FILE_PATH}" 2>&1
  curlExitCode=$?

  if [ $curlExitCode -ne "0" ]; then
    {
      echo "Failed to connect to WP Guardian using 'curl': exit code: $curlExitCode."
      printf "Trying with 'wget'\n"
      wget -O "$HELLO_RESPONSE_FILE_PATH" \
        --header 'Content-Type: application/json' \
        --header "Authorization: Bearer $AUTH_TOKEN" \
        --post-data "$HELLO_PAYLOAD" \
        --connect-timeout "$TIMEOUT" \
        "$URL"
    } >> "${LOG_FILE_PATH}" 2>&1
    wgetExitCode=$?

    if [ $wgetExitCode -ne "0" ]; then
        {
          echo "Failed to connect to WP Guardian using 'wget': exit code: $wgetExitCode"
          rm -f "$HELLO_RESPONSE_FILE_PATH"
        } >> "${LOG_FILE_PATH}" 2>&1

        {
          echo 'Installation stopped: Failed to connect to WP Guardian.'
          echo "You can see detailed installation log in ${LOG_FILE_PATH}"
          echo ""
          echo "Check that $PLATFORM_API_URL is accessible from this server, refresh and copy installation snippet, and try again."
        }  | tee -a "${LOG_FILE_PATH}"
        displayHelpMessage
        exit 1
    fi
    USE_WGET=1
  fi
}

has_enabled_repo() {
  local repo="$1"

  # Try to get list of repos from cache first.
  # If some repo was enabled after last cache creation
  # or some repo is unavailable the query from cache will fail.
  # Try to fetch actual metadata in this case.
  case "$PACKAGE_MANAGER" in
    yum)
      # Repo-id may end with OS version and/or architecture
      # if baseurl of the repo refers to $releasever and/or $basearch variables
      # eg 'epel/7/x86_64', 'epel/7', 'epel/x86_64'
      {
        "$PACKAGE_MANAGER" repolist --verbose --cacheonly -q 2>/dev/null \
        || "$PACKAGE_MANAGER" repolist --verbose -q --setopt='*.skip_if_unavailable=1'
      } | grep -E -q "^Repo-id\s*:\s$repo(/.+)?\s*$"
    ;;
    dnf)
      # note: --noplugins may cause failure and empty output on RedHat
      {
        "$PACKAGE_MANAGER" repoinfo --cacheonly -q 2>/dev/null \
        || "$PACKAGE_MANAGER" repoinfo -q --setopt='*.skip_if_unavailable=1'
      } | grep -E -q "^Repo-id\s*:\s$repo(/.+)?\s*$"
    ;;
  esac
}

has_disabled_repo() {
  local repo="$1"

  # Try to get list of repos from cache first.
  # If some repo was disabled after last cache creation
  # or some repo is unavailable the query from cache will fail.
  # Try to fetch actual metadata in this case.
  case "$PACKAGE_MANAGER" in
    yum)
      # Repo-id may end with OS version and/or architecture
      # if baseurl of the repo refers to $releasever and/or $basearch variables
      # eg 'epel/7/x86_64', 'epel/7', 'epel/x86_64'
      {
        "$PACKAGE_MANAGER" repolist disabled --verbose --cacheonly -q 2>/dev/null \
        || "$PACKAGE_MANAGER" repolist disabled --verbose -q --setopt='*.skip_if_unavailable=1'
      } | grep -E -q "^Repo-id\s*:\s$repo(/.+)?\s*$"
    ;;
    dnf)
      # note: --noplugins may cause failure and empty output on RedHat
      {
        "$PACKAGE_MANAGER" repoinfo --disabled --cacheonly -q 2>/dev/null \
        || "$PACKAGE_MANAGER" repoinfo --disabled -q --setopt='*.skip_if_unavailable=1'
      } | grep -E -q "^Repo-id\s*:\s$repo(/.+)?\s*$"
    ;;
  esac
}

disable_repo() {
  case "$PACKAGE_MANAGER" in
    yum) yum-config-manager --disable "$@" >> "${LOG_FILE_PATH}" 2>&1 && has_disabled_repo "$@" ;;
    dnf) dnf config-manager --set-disabled "$@" >> "${LOG_FILE_PATH}" 2>&1 && has_disabled_repo "$@" ;;
  esac
}

has_rh_config_manager() {
  case "$PACKAGE_MANAGER" in
    yum) yum-config-manager --help >/dev/null 2>&1 ;;
    dnf) dnf config-manager --help >/dev/null 2>&1 ;;
  esac
}

install_rh_config_manager() {
  case "$PACKAGE_MANAGER" in
    yum) "$PACKAGE_MANAGER" install -q -y 'yum-utils' --setopt='*.skip_if_unavailable=1' ;;
    dnf) "$PACKAGE_MANAGER" install -q -y 'dnf-command(config-manager)' --setopt='*.skip_if_unavailable=1' ;;
  esac
}

check_rh_config_manager() {
  case "$OS_NAME" in
    CentOS)
      if ! has_rh_config_manager && ! install_rh_config_manager; then
        reportStepError 'checkConfigManager' 'wp-agent'
        exitWithError "Failed to install 'yum-utils' or 'dnf-command(config-manager)' package, see log above for details"
      fi
    ;;
  esac
}

save_token_to_file() {
  API_KEY_FILE=$(mktemp /tmp/platform_XXXXXX.key)

  echo "Save API key to file ${API_KEY_FILE}"
  echo "${AUTH_TOKEN}" > "${API_KEY_FILE}"
  ret=$?
  if [ $ret -ne "0" ]; then
    echo "Failed to save API key to file ${API_KEY_FILE}" | tee -a "${LOG_FILE_PATH}"
    print_footer
    exit 1
  fi
}

delete_token_file() {
    if ! rm -f "${API_KEY_FILE}"; then
       echo "Failed to remove file ${API_KEY_FILE}, please remove it manually" | tee -a "${LOG_FILE_PATH}"
    fi
}

add_plesk_server_to_platform_360() {
  if [[ -z "${AUTH_TOKEN}" ]]; then
    printf "Auth token is not specified. Connection to platform will not be established.\n" | tee -a "${LOG_FILE_PATH}" 2>&1
    print_footer
    exit 1
  fi

  save_token_to_file

  echo "execute 'plesk ext platform360 --connect-server -allow-unsecure-connection true -p360-key-file ${API_KEY_FILE}'" | tee -a "${LOG_FILE_PATH}"
  OUTPUT=$(plesk ext platform360 --connect-server -p360-key-file "${API_KEY_FILE}" -allow-unsecure-connection true 2>&1)
  ret=$?

  echo "$OUTPUT" >> "${LOG_FILE_PATH}"
  if [ $ret -eq "0" ]; then
    echo "Server successfully added to the platform" | tee -a "${LOG_FILE_PATH}"
  elif [ $ret -eq "5" ]; then
    echo "This server is already connected to the platform" | tee -a "${LOG_FILE_PATH}"
  elif [ $ret -eq "6" ]; then
    echo "Failed to connect the server to the platform" | tee -a "${LOG_FILE_PATH}"
    echo
    echo $OUTPUT
    echo
    echo "See log ${LOG_FILE_PATH} for details"
    delete_token_file
    print_footer
    exit 1
  else
    echo "Failed to connect the server to the platform" | tee -a "${LOG_FILE_PATH}"
    echo "See log ${LOG_FILE_PATH} for details"
    delete_token_file
    print_footer
    exit 1
  fi
}

add_cpanel_server_to_platform_360() {
  if [[ -z "${AUTH_TOKEN}" ]]; then
    echo "Auth token is not specified. Connection to platform will not be established" | tee -a "${LOG_FILE_PATH}" 2>&1
    print_footer
    exit 1
  fi

  if [[ -z "${PLATFORM_360_CPANEL_PLUGIN_DOWNLOAD_URL}" ]]; then
    PLATFORM_360_CPANEL_PLUGIN_DOWNLOAD_URL=${DEFAULT_PLATFORM_360_CPANEL_PLUGIN_DOWNLOAD_URL}
  fi

  echo "Download and install platform360-cpanel-plugin from ${PLATFORM_360_CPANEL_PLUGIN_DOWNLOAD_URL}" | tee -a "${LOG_FILE_PATH}"
  sh <(curl "${PLATFORM_360_CPANEL_PLUGIN_DOWNLOAD_URL}/cpanel-plugin-installer.sh" || wget -O - "$PLATFORM_360_CPANEL_PLUGIN_DOWNLOAD_URL/cpanel-plugin-installer.sh") >> "${LOG_FILE_PATH}" 2>&1

  ret=$?
  if [ $ret -ne "0" ]; then
    echo "Failed to download cpanel-plugin" | tee -a "${LOG_FILE_PATH}"
    echo "See log ${LOG_FILE_PATH} for details"
    print_footer
    exit 1
  fi

  save_token_to_file

  PLATFORM_API_FULL_URL=$PLATFORM_API_URL/api/v1
  echo "execute 'platform360-cpanel-plugin connect-server --base-api-url ${PLATFORM_API_FULL_URL} --allow-unsecure-connection --api-key-file ${API_KEY_FILE}'" | tee -a "${LOG_FILE_PATH}"
  platform360-cpanel-plugin connect-server --base-api-url "${PLATFORM_API_FULL_URL}" --allow-unsecure-connection --api-key-file "${API_KEY_FILE}" >> "${LOG_FILE_PATH}" 2>&1
  ret=$?
  if [ $ret -eq "0" ]; then
    echo 'Server successfully connected to the platform' | tee -a "${LOG_FILE_PATH}"
  else
    echo 'An error was encountered when connecting the server to the platform. If the error is not critical, the process of connection will continue' | tee -a "${LOG_FILE_PATH}"
    echo "See log ${LOG_FILE_PATH} for details"
  fi
}

add_plesk_server_to_wp_guardian() {
  if [ ! -e "${API_KEY_FILE}" ]; then
    echo "File with API key ${API_KEY_FILE} does not exists. Connection to the WP Guardian module will not be established" | tee -a "${LOG_FILE_PATH}" 2>&1
    print_footer
    exit 1
  fi

  echo "execute 'plesk ext platform360 --wpguardian-setup -p360-key-file ${API_KEY_FILE}'" | tee -a "${LOG_FILE_PATH}"
  plesk ext platform360 --wpguardian-setup -p360-key-file "${API_KEY_FILE}" >> "${LOG_FILE_PATH}" 2>&1
  ret=$?
  if [ $ret -eq "0" ]; then
    echo "Server successfully added to the WP Guardian module" | tee -a "${LOG_FILE_PATH}"
  else
    echo "Failed to connect the server to the WP Guardian module" | tee -a "${LOG_FILE_PATH}"
    echo "Try reconnecting the server using a fresh connection snippet" | tee -a "${LOG_FILE_PATH}"
    echo "See log ${LOG_FILE_PATH} for details"
    delete_token_file
    print_footer
    exit 1
  fi
}

add_cpanel_server_to_wp_guardian() {
  if [ ! -e "${API_KEY_FILE}" ]; then
    echo "File with API key ${API_KEY_FILE} does not exists. Connection to the WP Guardian module will not be established" | tee -a "${LOG_FILE_PATH}" 2>&1
    print_footer
    exit 1
  fi

  PLATFORM_API_FULL_URL=$PLATFORM_API_URL/api/v1
  echo "execute 'platform360-cpanel-plugin wpguardian-setup --base-api-url ${PLATFORM_API_FULL_URL}' --api-key-file ${API_KEY_FILE}" | tee -a "${LOG_FILE_PATH}"
  platform360-cpanel-plugin wpguardian-setup --base-api-url "${PLATFORM_API_FULL_URL}" --api-key-file "${API_KEY_FILE}" >> "${LOG_FILE_PATH}" 2>&1
  ret=$?
  if [ $ret -eq "0" ]; then
    echo 'Server successfully connected to the WP Guardian module'| tee -a "${LOG_FILE_PATH}"
  else
    echo "Failed to connect the server to the WP Guardian module" | tee -a "${LOG_FILE_PATH}"
    echo "Try to reconnect the server using a fresh connection snippet" | tee -a "${LOG_FILE_PATH}"
    echo "See log ${LOG_FILE_PATH} for details"
    delete_token_file
    print_footer
    exit 1
  fi
}

connect_wp_agent() {
  if [[ -z "${AUTH_TOKEN}" ]]; then
    echo "Auth token is not specified. Connection to WP Guardian will not be established" | tee -a "${LOG_FILE_PATH}"
    exit 0
  fi

  if [[ -n "${PLATFORM_API_URL}" ]]; then
    escaped_platform_api_url=$(printf "%q" "${PLATFORM_API_URL}")
    connect_args+=(-base-api-url "${escaped_platform_api_url}")
  fi

  if [[ -n "${ADDITIONAL_IPS}" ]]; then
    escaped_additional_ips=$(printf "%q" "${ADDITIONAL_IPS}")
    connect_args+=(-additional-ips "${escaped_additional_ips}")
  fi

  if [[ "${CONFIGURE_FIREWALL}" -eq 1 ]]; then
    connect_args+=(-configure-firewall yes)
  fi

  echo "(5/5) Connecting WP Guardian agent ..." | tee -a "${LOG_FILE_PATH}"

  escaped_auth_token=$(printf "%q" "${AUTH_TOKEN}")
  wp-agent --connect-server -token "${escaped_auth_token}" "${connect_args[@]}" | tee -a "${LOG_FILE_PATH}" 2>&1
  ret=$?
  if [ $ret -ne "0" ]; then
    echo "Failed to connect WP Guardian agent to the platform" | tee -a "${LOG_FILE_PATH}"
    exit 1
  fi
}

check_repo_base_url() {
  # When the installer script is working as "repo-config-updater", it's possible to override
  # the repo base URL to the host with repositories.
  # This is allowed only for development purposes and the file with override is removed
  # when the value doesn't match specific pattern. This is required to avoid possible
  # problems with updates on production servers.
  if [ -e /root/.wp-agent-installer-config-for-development-purposes ]; then
    source /root/.wp-agent-installer-config-for-development-purposes

    if [[ -n "${REPO_BASE_URL}" && "${REPO_BASE_URL}" != *"${PATTERN_HOST_WITH_DEV_BUILDS}"* ]]; then
      rm -f /root/.wp-agent-installer-config-for-development-purposes
      REPO_BASE_URL=""
    fi
  fi
}

parse_args "$@"

print_header
check_plesk
check_cpanel

case "$CURRENT_MODE" in
  installer)
    fill_repo_base_url_variable

    # The installer script defines the default URL to the host with repositories. You can
    # override it in "--url" argument during installation process. For example, to install specific
    # version which isn't yet delivered to all servers you can use installer as following:
    # "./installer.sh --url https://get.wpguardian.io/wp-agent".
    #
    # The same version of installer script is used for development purposes and it's required
    # to override the default URL to the host with repositories in such case, both when installer works
    # in "installer" mode and in "repo-config-updater" mode.
    # When the passed value for "--url" argument matches specific pattern, then the
    # file "/root/.wp-agent-installer-config-for-development-purposes" is created and used
    # in "repo-config-updater" mode.
    # Mentioned file shouldn't be used on production servers, because if it contains
    # incorrect URL to the host with repositories, then receiving of updates can be broken.
    if [[ "${REPO_BASE_URL}" == *"${PATTERN_HOST_WITH_DEV_BUILDS}"* ]]; then
      cat <<EOF >/root/.wp-agent-installer-config-for-development-purposes
    REPO_BASE_URL=${REPO_BASE_URL}
EOF
    else
      rm -f /root/.wp-agent-installer-config-for-development-purposes
    fi

    echo "(1/5) Starting ..." | tee -a "${LOG_FILE_PATH}"
    sendHello

    echo "(2/5) Checking compatibility ..." | tee -a "${LOG_FILE_PATH}"
    detect_os
    check_rh_config_manager
    update_system
    install_required_packages

    echo "(3/5) Configuring installer ..." | tee -a "${LOG_FILE_PATH}"
    install_gpg
    configure_keys
    create_repo_config
    clean_repo_cache

    install_wp_agent

    echo "Installation complete!" | tee -a "${LOG_FILE_PATH}"

    connect_wp_agent
  ;;
  repo-config-updater)
    check_repo_base_url

    fill_repo_base_url_variable
    detect_os
    configure_keys
    create_repo_config
    clean_repo_cache
  ;;
  self-updater)
    check_repo_base_url

    fill_repo_base_url_variable

    echo "(1/3) Checking compatibility ..." | tee -a "${LOG_FILE_PATH}"
    detect_os

    echo "(2/3) Configuring updater ..." | tee -a "${LOG_FILE_PATH}"
    check_rh_config_manager
    install_required_packages

    configure_keys
    create_repo_config

    update_wp_agent

    echo "Update complete!" | tee -a "${LOG_FILE_PATH}"
  ;;
  plesk-connection)
    echo "Plesk detected." | tee -a "${LOG_FILE_PATH}"
    echo -e "\n(1/2) Adding server to the platform" | tee -a "${LOG_FILE_PATH}"
    add_plesk_server_to_platform_360

    echo -e "\n(2/2) Adding server to the WP Guardian module" | tee -a "${LOG_FILE_PATH}"
    add_plesk_server_to_wp_guardian
    delete_token_file

    echo "Connection complete!" | tee -a "${LOG_FILE_PATH}"
  ;;
  cpanel-connection)
    echo "cPanel detected." | tee -a "${LOG_FILE_PATH}"
    echo -e "\n(1/2) Adding server to the platform" | tee -a "${LOG_FILE_PATH}"
    fill_repo_base_url_variable
    add_cpanel_server_to_platform_360

    echo -e "\n(2/2) Adding server to the WP Guardian module" | tee -a "${LOG_FILE_PATH}"
    add_cpanel_server_to_wp_guardian
    delete_token_file
  ;;
esac

print_footer
