#!/usr/bin/env bash
VERSION="1.0"
PROG="$(basename "$0")"

QUIET=n

# shellcheck disable=SC2034
while getopts 'dhp:t:z:i:qv' opt; do
    case $opt in
        d) DEBUG="Y"                                   ;;
        p) AWS_CLI_PROFILE="$OPTARG"                   ;;
        q) QUIET=                                      ;;
        v) echo "dns_route53 version $VERSION"; exit 0 ;;
        z) ROUTE53_HOSTED_ZONE_NAME="$OPTARG"          ;;
        i) ROUTE53_HOSTED_ZONE_ID="$OPTARG"            ;;
        *)
            cat <<EOF
Usage
    $PROG [-dt -q] add name data [ttl]
    $PROG [-dt -h -p "aws-profile-name" -q] del name data

Add or delete TXT records from Route53 Hosted Zone

You must have the AWS CLI installed and a profile configured for this script to work.
	The IAM user that the profile uses requires the following action permissions in AWS:
	- route53:ListHostedZones - Not necessary if zone ID is available to this script
	- route53:ChangeResourceRecordSets

With getssl, this script is called from the dns_add_route53 and
dns_del_route53 wrapper scripts.

Arguments:
    add - add the specified record to the domain

    del - remove the specified record from the domain

    name is the fully qualified record name to create the challenge for e.g. www.example.org. Note that trailing '.' is necessary.  Also note that _acme-challenge. will automatically be prepended by this script

    data is the record data, e.g. "myverificationtoken"

    ttl is optional and will default to 120 if not specified

    If it is necessary to turn on debugging externally, define
    ROUTE53_DEBUG="y" (any non-null string will do).
    For minimal trace output (to override -q), define ROUTE53_TRACE="y".

Options
    -d    Provide debugging output - all requests and responses
    -h    This help.
    -i:   The hosted zone ID
    -p:   The AWS CLI profile to use.  Will use default if not specified
    -q:   Quiet - omit normal success messages
    -z:   The hosted zone name. Will be used to determine the zone ID if ID was not provided

    All output, except for this help text, is to stderr.

Environment variables
    ROUTE53_SCRIPT   location of this script
    ROUTE53_HOSTED_ZONE_NAME The name of the hosted zone name.  If not specified, then the name will be determined from the record name provided to this script
    ROUTE53_HOSTED_ZONE_ID The id of the hosted zone to be used instead of trying to automatically determine the ID
    AWS_CLI_PROFILE  the aws cli profile to use if not using default

BUGS
    Report any issues to https://github.com/xyide/getssl/issues
EOF
            exit 0
            ;;
    esac
done
shift $((OPTIND-1))

if [ -z "$AWS_CLI_PROFILE" ]; then
    echo "AWS_CLI_PROFILE not defined.  Using default" >&2
    AWS_CLI_PROFILE=default
fi

op="$1"
if ! [[ "$op" =~ ^(add|del)$ ]]; then
    echo "Operation must be \"add\" or \"del\"" >&2
    exit 3
fi
name="$2"
if [ -z "$name" ]; then
    echo "'name' parameter is required, see -h" >&2
    exit 3
fi
data="$3"
if [ -z "$data" ]; then
    echo "'data' parameter is required, see -h" >&2
    exit 3
fi

if [ "$op" = 'del' ]; then
    ttl=120
elif [ -z "$5" ]; then
    ttl="120"
elif ! [[ "$5" =~ ^[0-9]+$ ]]; then
    echo "TTL $5 is not numeric" >&2
    exit 3
elif [ "$5" -lt 120 ]; then
    [ -n "$VERB" ] && \
        echo "$5 is too small.  Using TTL of 120 instead" >&2
    ttl="120"
else
    ttl="$5"
fi

# end processing parameters

[ -n "$DEBUG" ] && \
    echo "$PROG: $op $name \"$data\" $ttl" >&2

# Determine what actual hosted zone to use.

HOSTED_ZONE_NAME=$ROUTE53_HOSTED_ZONE_NAME
HOSTED_ZONE_ID=$ROUTE53_HOSTED_ZONE_ID
RR_NAME="_acme-challenge.${name}"
RR_VALUE="${data}"

# Function to parse through the segments in the supplied name
# to determine the zone and its id
function determine_hosted_zone_name_and_id() {
    TMP_NAME=$name
    TMP_RR_NAME=
	while [[ "$TMP_NAME" =~ ^([^.]+)\.([^.]+.*) ]]; do
		if [ -n "${TMP_RR_NAME}" ]; then
			TMP_RR_NAME="${TMP_RR_NAME}.";
		fi
		TMP_RR_NAME="${TMP_RR_NAME}${BASH_REMATCH[1]}"
		testdomain="${BASH_REMATCH[2]}"
		[ -n "$DEBUG" ] && echo "Testing hosted zone ${testdomain}"
		TMP_NAME=$testdomain
		if [[ ! "$TMP_NAME" =~ [^.]+\.[^.]+ ]]; then
			[ -n "$DEBUG" ] && echo "No segments left"
				exit 1
		fi

		TMP_ZONE_ID=$(aws --profile="${AWS_CLI_PROFILE}" route53 list-hosted-zones --query "HostedZones[?Name=='${testdomain}'].Id | [0]" | sed -e 's/^"//' -e 's/"$//')


		if [ "${TMP_ZONE_ID}" != "null" ]; then
			[ -n "$DEBUG" ] && echo "Found hosted zone ${testdomain}"
			HOSTED_ZONE_NAME=${testdomain}
			HOSTED_ZONE_ID=$TMP_ZONE_ID
			break
		fi
	done
}

# If zone ID is specified, then use it to determine the hosted zone name
if [ -n "${HOSTED_ZONE_ID}" ]; then
	HOSTED_ZONE_NAME=$(aws --profile="${AWS_CLI_PROFILE}" route53 list-hosted-zones --query "HostedZones[?Id=='${ZONE_ID}'].Name | [0]" | sed -e 's/^"//' -e 's/"$//')
# If zone name is specified, then use it to get the zone id
elif [ -n "${HOSTED_ZONE_NAME}" ]; then
	HOSTED_ZONE_ID=$(aws --profile="${AWS_CLI_PROFILE}" route53 list-hosted-zones --query "HostedZones[?Name=='${HOSTED_ZONE_NAME}'].Id | [0]" | sed -e 's/^"//' -e 's/"$//')
else
	determine_hosted_zone_name_and_id
fi


	if [ -z "${HOSTED_ZONE_ID}" ]; then
	echo "Hosted zone id not specified or determined" >&2
    exit 3
fi

if [ "$op" = "add" ]; then
	ACTION="UPSERT"
elif [ "$op" = "del" ]; then
	ACTION="DELETE"
else
	echo "Unsupported Operation: $op" >&2
fi

CHANGE_BATCH='
{
    "Comment": "GetSSL LetsEncrypt DNS Challenge",
    "Changes": [{
      "Action"             : "'"$ACTION"'",
      "ResourceRecordSet"  : {
        "Name"             : "'"$RR_NAME"'",
        "Type"             : "TXT",
        "TTL"              : '${ttl}',
        "ResourceRecords"  : [{
            "Value"         : "\"'$RR_VALUE'\""
        }]
      }
    }]
  }
'


[ -n "$DEBUG" ] && echo "${CHANGE_BATCH}" >&2

aws \
	--profile="${AWS_CLI_PROFILE}" \
	route53 \
	change-resource-record-sets \
	--hosted-zone-id="${HOSTED_ZONE_ID}" \
	--change-batch "${CHANGE_BATCH}"
exit $?
