#!/bin/bash
# ##########################################################
# Download a Sonatype Nexus Repository version 2 support zip using REST APIs
#
# Tested only with Nexus Repository 2
#
# Latest version:
#   https://support.sonatype.com/hc/en-us/articles/213465878

# Requirements:
#   Either 'base64' or 'openssl' command must be available for encoding
#   curl
#   awk
# Author:
#   Sonatype Support Team
# ##########################################################

set -e

LIMIT_SIZE=0

# Help function
show_help() {
	cat <<EOF

Usage: $0 [--url URL] [--user USER] [--pass PASS] [--debug] [--limit-size]


Options:
	--url        Nexus base URL (or set NEXUS_URL env var)
				 Default: http://localhost:8081
	--user       Nexus username (or set NEXUS_USER env var)
				 Default: admin
	--pass       Nexus password (or set NEXUS_PASS env var)
				 Default: admin123
	--debug      Enable debug output
	--limit-size Limit the size of the support zip (sets limitFileSizes and limitZipSize in payload)
	-h, --help   Show this help message

If no arguments or env vars are provided, defaults are used as shown above.
EOF
}

# Parse named arguments
while [[ $# -gt 0 ]]; do
	case "$1" in
		--url)
			NEXUS_URL="$2"; shift 2;;
		--user)
			NEXUS_USER="$2"; shift 2;;
		--pass)
			NEXUS_PASS="$2"; shift 2;;
		--debug)
			DEBUG=1; shift;;
		--limit-size)
			LIMIT_SIZE=1; shift;;
		-h|--help)
			show_help; exit 0;;
		*)
			echo "Unknown argument: $1"; show_help; exit 1;;
	esac
done

# Config
NEXUS_URL="${NEXUS_URL:-http://localhost:8081}"


# Accept username and password from env or defaults
NEXUS_USER="${NEXUS_USER:-admin}"
NEXUS_PASS="${NEXUS_PASS:-admin123}"

# Error if username or password is missing
if [[ -z "$NEXUS_USER" || -z "$NEXUS_PASS" ]]; then
	echo "ERROR: Nexus username and password must be provided via arguments or environment variables." >&2
	exit 2
fi


# Base64 encode function for fetch_single_use_token
base64_encode(){
	local to_encode="$1"
	local result="NOT-BASE64-ENCODED"
	if command -v base64 &> /dev/null; then
			result="$(echo -n "$to_encode" | base64 | tr -d '\n')"
	elif command -v openssl &> /dev/null; then
		result="$(echo -n "$to_encode" | openssl enc -A -base64)"
	fi
	echo "$result"
}

# Function to determine supported URL for obtaining single use access token
get_single_use_token_url() {
	local repo_url="$1"
	local urls=("/service/rest/wonderland/authenticate" "/service/siesta/wonderland/authenticate" "/service/local/usertoken/authenticate")
	local furl=''
	for url in "${urls[@]}"; do
		furl="$repo_url$url"
		resp_code="$(curl -sI "$furl" | awk '/^HTTP/{print $2}')"
		if [[ "$resp_code" != '404' ]]; then
			break;
		fi
	done
	echo "$furl"
}

# fetch a single use time-limited access token
function fetch_single_use_token(){
	local base_url username password creds u_b64 p_b64 payload furl http_resp auth_token_raw auth_token_encoded
	base_url="$1"
	username="$2"
	password="$3"
	creds="$username:$password"
	u_b64=$(base64_encode "$username")
	p_b64=$(base64_encode "$password")
	payload="{\"u\":\"$u_b64\",\"p\":\"$p_b64\"}"
	furl=$(get_single_use_token_url $base_url)
	http_resp=$(curl "$furl" -H "Accept: application/json" -H "Content-Type: application/json" \
		-u "$creds" --data-raw "$payload" 2>/dev/null)
	auth_token_raw=$(echo -n "$http_resp" | awk -F'"' '{printf $4}')
	auth_token_encoded=$(base64_encode "$auth_token_raw")
	echo -n "$auth_token_encoded"
}

fetch_support_zip() {
	local base_url username password creds support_response zip_name token download_base_url http_code
	base_url="$1"
	username="$2"
	password="$3"
	creds="$username:$password"

	# Request support zip
	echo "Requesting support zip..."
		if [[ $LIMIT_SIZE -eq 1 ]]; then
			payload='{"systemInformation":true,"threadDump":true,"configuration":true,"security":true,"log":true,"metrics":true,"jmxinfo":true,"limitFileSizes":true,"limitZipSize":true}'
		else
			payload='{"systemInformation":true,"threadDump":true,"configuration":true,"security":true,"log":true,"metrics":true,"jmxinfo":true}'
		fi
		support_response=$(curl -s -w "%{http_code}" -o /tmp/support_response.json -u "$creds" -H "Accept: application/json" -H "Content-Type: application/json" --data-raw "$payload" "$base_url/nexus/service/siesta/atlas/support-zip")
	http_code="${support_response: -3}"
	support_response=$(cat /tmp/support_response.json)
	if [[ $DEBUG -eq 1 ]]; then
		echo "Support zip API raw response: $support_response"
	fi
	if [[ "$http_code" != "200" ]]; then
		echo "ERROR: Support zip API request failed with status $http_code" >&2
		exit 3
	fi

	# Parse file name
	zip_name=$(echo "$support_response" | grep -o '"name"[ ]*:[ ]*"[^" ]*"' | sed 's/.*: *"\([^"]*\)".*/\1/')
	if [[ $DEBUG -eq 1 ]]; then
		echo "DEBUG: Support zip name: $zip_name"
	fi

	# Get single-use token
	token=$(fetch_single_use_token "$base_url/nexus" "$username" "$password")
	if [[ $DEBUG -eq 1 ]]; then
		echo "DEBUG: Token: $token"
	fi

	# Download support zip using token
	download_base_url="$base_url/nexus/service/siesta/wonderland/download/$zip_name"
	echo "Downloading support zip from $download_base_url with token..."
	http_code=$(curl -s -w "%{http_code}" -o "$zip_name" -u "$creds" -G --data-urlencode "t=$token" "$download_base_url")
	if [[ "$http_code" != "200" ]]; then
		echo "ERROR: Download request failed with status $http_code" >&2
		exit 4
	fi
	echo "Download complete: $zip_name"
}

fetch_support_zip "$NEXUS_URL" "$NEXUS_USER" "$NEXUS_PASS"