#!/bin/bash
PROGNAME=${0##*/}

#  Notes to users:
#
#      (C) 2021-2025 Alexis Huxley - distributed under GPL3 - absolutely no warranty!
#
#  Notes to self:
#
#      That webpage advertises this script as being simple. For this reason I avoid use 
#      of miniade, which would require the user to do more than just download *this* script.

main()
{
    #  Defaults for options
    VERBOSELEVEL=2

    #  Option processing
    while :; do
        case $1 in
           -v) VERBOSELEVEL=3 ;;
           --) shift; break ;;
           -*) usage ;;
           *)  break ;;
        esac
        shift
    done

    #  Argument processing
    [ $# -ge 1 -a $# -le 3 ] || usage
    REPO_URL=$1
    #  (further argument analysis is left until after the repo URL is validated)

    #  Sanity checks and derivations
    [[ $REPO_URL =~ ^(https?)://([^/:]+)(:([^/]+))? ]] || error "$REPO_URL: doesn't look like a repo URL (hint: <repo-url> should look like <prot>://<host>[:<port>][/path] where <prot> is 'http' or 'https' and stuff in square brackets is optional)"
    REPO_PROT=${BASH_REMATCH[1]}
    REPO_HOST=${BASH_REMATCH[2]}
    REPO_PORT=${BASH_REMATCH[4]}
    if [ ${#REPO_PORT} != 0 ]; then
        :
    elif [ $REPO_PROT = http ]; then
        REPO_PORT=80
    else
        REPO_PORT=443
    fi
    for COMMAND in wget openssl; do
        type -p $COMMAND > /dev/null || error "$COMMAND: could not locate this command (hint: can you install it?)"
    done

    #  Argument processing
    if [ $# = 1 ]; then
        read -p "Username: " REPO_USERNAME
        read -sp "Password: " REPO_PASSWORD
	echo
    elif [ $# = 2 ]; then
        REPO_USERNAME=$2
        read -sp "Password: " REPO_PASSWORD
	echo
    else
        REPO_USERNAME=$2
        REPO_PASSWORD=$3
    fi

    #  Get realm (ignore authentication errors)
    info "attempting to get authentication realm ..."
    WGET_OUTPUT=$(wget -O /dev/null -S $REPO_URL 2>&1) || true
    if [[ $WGET_OUTPUT =~ Basic\ realm=\"(.*)\" ]]; then
	REPO_REALM=${BASH_REMATCH[1]}
    else
	info "failed to get authentication realm; prompting ..."
        DFLT_REPO_REALM='Subversion Service'
	read -p "Realm [$DFLT_REPO_REALM]: " REPO_REALM
        [ ${#REPO_REALM} != 0 ] || REPO_REALM=$DFLT_REPO_REALM
    fi
    #  Construct the Subversion realmstring from the protocol, host, port and the actual authenticaton realm.
    REPO_REALMSTRING="<$REPO_PROT://$REPO_HOST:$REPO_PORT> $REPO_REALM"
	   
    #  Get password cache filename (named after the repo URL and realm name)
    OPENSSL_OUTPUT=$(echo -n "$REPO_REALMSTRING" | openssl md5 2>&1)
    [[ $OPENSSL_OUTPUT =~ ^(|MD5)\(stdin\)=\ (.*) ]] || error "failed to parse openssl output (hint: openssl said: $OPENSSL_OUTPUT)"
    REPO_CACHE_FILENAME=$HOME/.subversion/auth/svn.simple/${BASH_REMATCH[2]}

    #  Write temporary password cache file
    mkdir -p "${REPO_CACHE_FILENAME%/*}"
    ( umask 077; touch $REPO_CACHE_FILE.tmp )
    {
        echo "K 8"
        echo "passtype"
        echo "V 6"
        echo "simple"
        echo "K 15"
        echo "svn:realmstring"
        echo "V ${#REPO_REALMSTRING}"
        echo "$REPO_REALMSTRING"
        echo "K 8"
        echo "username"
        echo "V ${#REPO_USERNAME}"
        echo "$REPO_USERNAME"
        echo "K 8"
        echo "password"
        echo "V ${#REPO_PASSWORD}"
        echo "$REPO_PASSWORD"
        echo "END"
    } > $REPO_CACHE_FILENAME.tmp

    #  Save cache file to right name (or discard)
    if [ ! -f $REPO_CACHE_FILENAME ]; then
        info "creating password cache ..."
	mv $REPO_CACHE_FILENAME.tmp $REPO_CACHE_FILENAME
    elif cmp -s $REPO_CACHE_FILENAME.tmp $REPO_CACHE_FILENAME; then
	info "password cache does not need updating"
	rm $REPO_CACHE_FILENAME.tmp
    else
	warning "password cache will be modified (old version will be saved to /tmp)"
        mv $REPO_CACHE_FILENAME /tmp
	mv $REPO_CACHE_FILENAME.tmp $REPO_CACHE_FILENAME
    fi
}

usage()
{
    echo "Usage: $PROGNAME [ -v ] <repo-url> [ <repo-username> [ <repo-passwd> ] ]" >&2
    exit 1
}

info()
{
    [ $VERBOSELEVEL -lt 3 ] || echo "$PROGNAME: INFO: $1" >&2
}

warning()
{
    [ $VERBOSELEVEL -lt 2 ] || echo "$PROGNAME: WARNING: $1" >&2
}

error()
{
    [ $VERBOSELEVEL -lt 1 ] || echo "$PROGNAME: ERROR: $1" >&2
    exit 1
}

main "$@"
