#!/bin/sh

set -e

. "/opt/rudder/share/lib/common.sh"

if [ -x "/opt/rudder/bin/curl" ]; then
  CURL="/opt/rudder/bin/curl"
else
  CURL="curl"
fi

# Always on options
CURL_OPTS="--tlsv1.3 --location --fail --connect-timeout 10"
ERROR_OPT="--show-error"
CURL_VERBOSITY="--silent"
EXEC="exec"
TAGFILE=""
if is_cert_validated; then
  CLIENT_CERT="true"
else
  CLIENT_CERT="false"
fi

## Get options
while [ "$1" != "" ]
do
  if [ "$1" = "-h" ]; then
    echo "Usage $0 -e <endpoint> [-u <user:password>] [-t <tagfile>] [-c] [-n] [-h] -- <curl_options>"
    echo "  -e <endpoint>: rudder endpoint, example /inventory-updates"
    echo "  -u <user:password>: force user and password, default is to take it from rudder.json"
    echo "  -t <tagfile>: use ETag file to avoid redownloading unchanged file"
    echo "  -c: use agent certificate for client authentication"
    echo "  -r: do not show any result, just HTTP code"
    echo "  -h: help"
    echo "  -n: do not run, display curl command instead"
    echo "  -v: verbose"
    cat <<EOF

Notes:
  - rudder-client uses policy_server.dat to find it policy-server
  - rudder-client uses agent.conf get its proxy parameters
  - rudder-client uses rudder.json for server validation parameters

Server validation:
  - when no parameter is present, rudder-client uses the policy_server_hash if it exists to check the server key
  - when the POLICY_SERVER_KEY_HASH parameter is defined, rudder-client only accepts servers having one of these hash
      POLICY_SERVER_KEY_HASH must be of the form sha256//<hash1>;sha256//<hash2>;...
  - when POLICY_SERVER_SECURE_VALIDATION is true
      rudder-client applies POLICY_SERVER_KEY_HASH rules if defined
      rudder-client checks that the server certificate is signed by a trusted CA
      rudder-client checks that the server certificate valid and its subject CN matches the URL
  - when POLICY_SERVER_CERT_CA is defined, the default trust store is replaced by this CA certificate
      POLICY_SERVER_SECURE_VALIDATION must be true for this option to be useful

EOF
    exit
  elif [ "$1" = "-e" ]; then
    ENDPOINT="$2"
    shift 2
  elif [ "$1" = "-u" ]; then
    AUTH="--user $2"
    shift 2
  elif [ "$1" = "-t" ]; then
    TAGFILE="$2"
    shift 2
  elif [ "$1" = "-c" ]; then
    CLIENT_CERT="true"
    shift
  elif [ "$1" = "-r" ]; then
    ERROR_OPT="--output /dev/null --write-out %{http_code}"
    shift
  elif [ "$1" = "-n" ]; then
    EXEC="echo"
    shift
  elif [ "$1" = "-v" ]; then
    CURL_VERBOSITY=""
    shift
  elif [ "$1" = "--" ]; then
    shift
    break
  else
    echo "Unknown parameter $1, try -h for help" >&2
    # codes before are taken by curl
    exit 127
  fi
done
if [ "${ENDPOINT}" = "" ]; then
  echo "You need to pass an endpoint, try -h for help" >&2
  exit 127
fi

## Get configuration

# proxy options
PROXY=$(agent_conf https_proxy)
if [ "${PROXY}" = "" ]; then
  CURL_OPTS="${CURL_OPTS} --noproxy *"
elif [ "${PROXY}" != "system" ]; then
  CURL_OPTS="${CURL_OPTS} --proxy ${PROXY}"
fi

# Create URL
SERVER=$(agent_conf server)
if [ "${SERVER}" = "" ]; then
  # get server from policy_server.dat
  SERVER=$(cut -d: -f1 "${RUDDER_VAR}/cfengine-community/policy_server.dat")
fi

PORT=$(get_https_port)
URL="https://${SERVER}${PORT}${ENDPOINT}"

if [ -f "${RUDDER_JSON}" ]; then
  # Read hash from policies
  INPUTS_PINNED_HASHES=$(rudder_json_value 'POLICY_SERVER_KEY_HASH')
  POLICY_SERVER_SECURE_VALIDATION=$(rudder_json_value 'POLICY_SERVER_SECURE_VALIDATION')
fi

# Handle pinning
if [ "${INPUTS_PINNED_HASHES}" != "" ]; then
  PINNED_HASH="${INPUTS_PINNED_HASHES}"
elif [ -f "${SERVER_HASH_FILE}" ]; then
  PINNED_HASH=$(cat ${SERVER_HASH_FILE})
fi

# We set th root server's policy server to 127.0.0.1
if [ "${SERVER}" = "127.0.0.1" ]; then
  CURL_OPTS="${CURL_OPTS} --insecure"
fi

if ! is_cert_validated; then
  if [ "${PINNED_HASH}" != "" ]; then
    CURL_OPTS="${CURL_OPTS} --pinnedpubkey ${PINNED_HASH}"
  fi

  CURL_OPTS="${CURL_OPTS} --insecure"
fi

# file created by policies
POLICY_SERVER_CERT_CA="/var/rudder/lib/ssl/policy_server_ca.pem"
if [ -f "${POLICY_SERVER_CERT_CA}" ]; then
  # we have to provide --capath so that curl doesn't look in default truststore
  CURL_OPTS="${CURL_OPTS} --capath ${POLICY_SERVER_CERT_CA} --cacert ${POLICY_SERVER_CERT_CA}"
fi

# get server authentication from rudder.json if available and not overridden
if [ "${CLIENT_CERT}" = "true" ]; then
  # handle client certificate
  if [ -f "/opt/rudder/etc/ssl/agent-http.cert" ]; then
    # Use specific key/cert if available
    AUTH="--cert /opt/rudder/etc/ssl/agent-http.cert --key /opt/rudder/etc/ssl/agent-http.key"
  else
    # Use standard paths
    AUTH="--cert /opt/rudder/etc/ssl/agent.cert --key /var/rudder/cfengine-community/ppkeys/localhost.priv"
  fi
elif [ "${AUTH}" = "" ]; then
  # handle client password
  if [ -f "${RUDDER_JSON}" ]; then
    DAVUSER=$(rudder_json_value 'DAVUSER')
    DAVPW=$(rudder_json_value 'DAVPASSWORD')
    AUTH="--user ${DAVUSER}:${DAVPW}"
  fi
fi

# handle etags
if [ "${TAGFILE}" != "" ]; then
  CURL_OPTS="${CURL_OPTS} --etag-save ${TAGFILE} --etag-compare ${TAGFILE}"
fi

# disable globbing to avoid * expansion (POSIX)
set -f
# advantage of exec:
# - one process less
# - don't care if there is some code after it
# - keeps return code
# Other arguments are curl arguments
${EXEC} ${CURL} ${CURL_OPTS} ${CURL_VERBOSITY} ${ERROR_OPT} ${AUTH} ${URL} "$@"
