#!/bin/sh
# Original: https://stackoverflow.com/a/61241535
# Modified to work on an Airport Express
set -e -u

get_aws_config() {
	PROFILE_KEY="${AWS_DEFAULT_PROFILE:-default}"
	if [ "${PROFILE_KEY}" != "default" ]
	then
		PROFILE_KEY="profile ${PROFILE_KEY}"
	fi
	SKIPPING=1
	while read rawline
	do
		line=`echo $rawline`  # trim
		if [ "$SKIPPING" = 1 ]
		then
			if [ "x${line}" = "x[$PROFILE_KEY]" ]
			then
				SKIPPING=0
			fi
			continue
		fi
		
		if [ "`echo $line | dd count=1 bs=1 2>/dev/null`" = "[" ] || [ "`echo $line | dd count=1 bs=1 2>/dev/null`" = "#" ]
		then
			break
		fi
		
		eval $line
	done < $HOME/.aws/config
}

log() {
	message="$1"
	details="$2"

	printf "${message}\n" >&2
	printf "${details}\n\n" | sed 's/^/    /' >&2
}

sha256Hash() {
	printf "$1" | openssl dgst -sha256 -binary -hex
}

hmac_sha256_nohex() {
	printf "$2" | openssl dgst -binary -hex -sha256 -hmac "$1"
}

hmac_sha256() {
	escaped=`printf "$1"| sed -e 's/../\\\x&/g'`
	dehexed=`printf "${escaped}"`
	printf "$2" | openssl dgst -binary -hex -sha256 -hmac "${dehexed}"
}

## http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
sig4_create_canonical_request() {
	http_request_method="$1"
	canonical_uri="/${api_uri}"
	canonical_query=""
	header_host="host:${api_host}"
	canonical_headers="${header_host}\n${header_x_amz_date}"
	request_payload=`sha256Hash "$metricjson"`
	canonical_request="${http_request_method}\n${canonical_uri}\n${canonical_query}\n${canonical_headers}\n\n${signed_headers}\n${request_payload}"

	printf "`sha256Hash ${canonical_request}`"
}

## http://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
sigv4_create_string_to_sign() {
	hashed_canonical_request="$1"
	sts="${algorithm}\n${timestamp}\n${credential_scope}\n${hashed_canonical_request}"

	printf "${sts}"
}

## http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
sigv4_calculate_signature() {
	secret="AWS4${aws_secret_key}"
	k_date=`hmac_sha256_nohex "${secret}" "${today}"`
	k_region=`hmac_sha256 "${k_date}" "${aws_region}"`
	k_service=`hmac_sha256 "${k_region}" "${aws_service}"`
	k_signing=`hmac_sha256 "${k_service}" "aws4_request"`
	string_to_sign="$1"
	signature=`hmac_sha256 "${k_signing}" "${string_to_sign}"`

	printf "${signature}"
}

sigv4_add_signature_to_request() {
	credential="Credential=${aws_access_key}/${credential_scope}"
	s_headers="SignedHeaders=${signed_headers}"
	signature="Signature=$1"
	authorization_header="Authorization: ${algorithm} ${credential}, ${s_headers}, ${signature}"

	printf "${authorization_header}"
}

# https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html
sign_it() {
	method="$1"
	hashed_canonical_request=`sig4_create_canonical_request "${method}"`
	string_to_sign=`sigv4_create_string_to_sign "${hashed_canonical_request}"`
	signature=`sigv4_calculate_signature "${string_to_sign}"`
	authorization_header=`sigv4_add_signature_to_request "${signature}"`

	printf "${authorization_header}"
}

## https://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/making-api-requests.html#CloudWatch-API-requests-using-post-method
put_metric_data() {
	http_method="$1"
	authorization_header=`sign_it "${http_method}"`
	content_length=`printf "$metricjson" | dd of=/dev/null 2>&1 | tail -1 | sed -e 's@ bytes transferred.*$@@'`

	printf "> ${http_method}-ing ${api_url}\n"

	# NB this uses -k to get around the fact that curl doesn't
	# recognise AWS' cert authority. I should fix that.
	curl -k -i -X ${http_method} "${api_url}" \
		 -H 'Accept: application/json' \
		 -H 'Content-Encoding: amz-1.0' \
		 -H 'Content-Type: application/json' \
		 -H 'X-Amz-Target: GraniteServiceVersion20100801.PutMetricData' \
		 -H "${authorization_header}" \
		 -H "${header_x_amz_date}" \
		 -H "Content-Length: ${content_length}" \
		 -d "$metricjson"
}

main() {
	if [ -e "$HOME/.aws/config" ]
	then
		get_aws_config
	fi
	while [ -n "${1-}" ]; do
		case "$1" in
			-credentials) credentials="$2"; shift ;;
			-url)         url="$2";         shift ;;
			-metricjson)  metricjson="$2";  shift ;;
			*)            echo "Option $1 not recognized"; exit 1 ;;
		esac
		shift
	done

	set +u
	if [ -z "${credentials}" ] && [ -n "${aws_access_key_id}" ] && [ -n "${aws_secret_access_key}" ]
	then
		credentials="${aws_access_key_id}:${aws_secret_access_key}"
	fi
	if [ -z "${url}" ] && [ -n "${cloudwatch_region_endpoint}" ]
	then
		url="https://${cloudwatch_region_endpoint}/"
	fi
	if [ -z "${credentials}" ] || [ -z "${url}" ] || [ -z "${metricjson}" ] ; then
		log "Usage:" "$0 -metricjson <metricjson> -credentials <aws_access_key>:<aws_secret_key> -url <c_url>"
		exit 1
	fi
	set -u

	method="POST"
	aws_access_key=`echo $credentials|sed -e 's/:.*$//'`
	aws_secret_key=`echo $credentials|sed -e 's/^.*://'`
	api_url="${url}"
	timestamp=${timestamp-`date -u +"%Y%m%dT%H%M%SZ"`} # 20171226T112335Z
	today=${today-`date -u +"%Y%m%d"`}                 # 20171226
	api_host=`echo ${api_url} | sed -e 's@.*/.*/\(.*\)/@\1@'`
	api_uri=`echo ${api_url} | sed -e 's@^.*'${api_host}'/@@'`
	aws_region=`echo ${api_url} | sed -e 's@^[^.]*\.\([^.]*\)\..*$@\1@'`
	aws_service=`echo ${api_url} | sed -e 's@\.'${aws_region}'\..*$@@' -e 's@^.*/@@'`
	algorithm="AWS4-HMAC-SHA256"
	credential_scope="${today}/${aws_region}/${aws_service}/aws4_request"
	signed_headers="host;x-amz-date"
	header_x_amz_date="x-amz-date:${timestamp}"

	put_metric_data "${method}"
}

main "$@"

