#!/bin/bash
BOOTSTRAP_VERSION=1.0

BIN_DIR="${ZMOBILE_PREFIX:-/mit/zmobile}/bin"
export PATH="$PATH:$BIN_DIR"

DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/zephyr-server/"
# This should be safe as dtach makes its own socket and will fail if one
# exists. Dtach's socket has safe permissions regardless of umask.
INFO="$DATA_DIR/info"
LOCK="$DATA_DIR/lock"

#SHARE_PATH="$BIN_DIR/../data"

SERVER_PRIV="$DATA_DIR/certs/server_private.pem" 
SERVER_PUB="$DATA_DIR/certs/server_public.pem" 

echo "!ZSERV HELLO $BOOTSTRAP_VERSION"
trap "echo '!ZSERV BYE'" EXIT

die() {
    echo "!ZSERV ERROR $*"
    exit 1
}

if [[ ! -d $DATA_DIR ]]; then
    mkdir --mode=700 "$DATA_DIR"
fi

[[ "$(stat -c '%a %u %F' $DATA_DIR)" == "700 $UID directory" ]] || die "Unsecure data directory."


isrunning() {
    [[ -e "$LOCK" ]] && ! flock -xn $LOCK true
}

isrunninghere() {
    if isrunning; then
        PID=$(<$LOCK)
        [[ -n $PID ]] && kill -0 $PID 2>/dev/null
        return
    fi
    return 1
}

dokill() {
    PID=${1}
    TIME=${2:-5}
    kill -INT $PID 2>/dev/null || return # Not running
    while [[ $TIME -gt 0 ]] && kill -0 $PID 2>/dev/null; do
        let TIME--
        sleep 1
    done
    # Really kill it.
    kill -TERM $PID 2>/dev/null && sleep 1 # Wait a second if still alive.
    kill -KILL $PID 2>/dev/null
}

stopserver() {
    # Kill the server
    isrunninghere || return 1
    PID=$(<$LOCK)
    dokill $PID
     
    [[ -e $LOCK ]] && rm -f $LOCK # make sure that the lock is removed
    [[ -e $INFO ]] && rm -f $INFO # make sure that info is removed
    return 0
}

resetserver() {
    pkill -INT -u $UID -f '^zserv ' && {
        sleep 5
        pkill -9 -u $UID -f '^zserv '
    }
    rm -rf "$DATA_DIR/certs" 2>/dev/null || true
    rm -f "$LOCK" 2>/dev/null || true
    rm -f "$INFO" 2>/dev/null || true
}

cleanup_certs() {
    for file in "server_public.pem" "server_private.pem" "server_public.bks"; do
        f="$DATA_DIR/certs/$file"
        [[ -e $f ]] && rm -f "$f"
    done
    [[ -d "$DATA_DIR/certs/" ]] || mkdir -p --mode=0700 "$DATA_DIR/certs"
}

mkcerts() {
    openssl genrsa -out $SERVER_PRIV 2048 >/dev/null 2>&1
    openssl req -subj "/CN=ZSERV/" -new -x509 -key $SERVER_PRIV -out $SERVER_PUB >/dev/null 2>&1
}

runserver() {
    if ! isrunning; then
        if [[ ! -r "$DATA_DIR/certs/server_public.pem" ]]; then
            cleanup_certs
            mkcerts
        fi
        (
            cd $BIN_DIR
            ./zserv-newpag --fork --ssl --key=$SERVER_PRIV --cert=$SERVER_PUB || die "Failed to start server."
        )
    fi
}

info() {
    if ! isrunning; then
        echo "!ZSERV INFO FAILED"
        return
    fi
    . $INFO
    echo "!ZSERV INFO BEGIN"
    echo "!ZSERV VERSION $VERSION"
    echo "!ZSERV HOST $HOST"
    echo "!ZSERV PORT $PORT"
    getpem $CERT_PUB
    echo "!ZSERV INFO END"
}

getcert() {
    [[ -e "$1" ]] || die "Public cert not found."
    case $2 in 
        #"BKS") getbks $1 ;;
        "PEM") getpem $1 ;;
        *) echo "!ZSERV GET CERT $2 INVALID" ;;
    esac
}
            
getpem() {
    echo "!ZSERV CERT BEGIN"
    cat $1
    echo "!ZSERV CERT END"
}

#getbks() {
#    KEYSTORE="$1.bks"
#
#    if [[ ! -e "$KEYSTORE" ]]; then
#        yes | keytool -importcert -trustcacerts -keystore $KEYSTORE \
#            -storetype bks -storepass "<BLANK>" -file <(openssl x509 -in $1) \
#            -provider org.bouncycastle.jce.provider.BouncyCastleProvider \
#            -providerpath $SHARE_PATH/bcprov/bcprov.jar >/dev/null 2>&1
#
#    fi
#    echo "!ZSERV CERT BEGIN"
#    base64 "$KEYSTORE"
#    echo "!ZSERV CERT END"
#}


get() {
    if ! isrunning; then
        echo "!ZSERV GET FAILED"
        return
    fi
    . $INFO
    case $1 in
        "VERSION")
            echo "!ZSERV VERSION $VERSION" ;;
        "HOST")
            echo "!ZSERV HOST $HOST" ;;
        "PORT")
            echo "!ZSERV PORT $PORT" ;;
        "CERT") getcert $CERT_PUB $2 ;;
        *)
            echo "!ZSERV GET $1 INVALID"
            ;;
    esac
}

while read line; do
    set $line
    case "$1" in
        "START")
            # For testing
            if [[ -e $HOME/.zserv_fail_start ]]; then
                echo "!ZSERV START FAILURE"
            else
                runserver && echo "!ZSERV START SUCCESS" || echo "!ZSERV START FAILURE"
            fi
            ;;
        "INFO")
            info
            ;;
        "STOP")
            stopserver && echo "!ZSERV STOP SUCCESS" || echo "!ZSERV STOP FAILURE"
            ;;
        "ISRUNNING")
            isrunning && echo "!ZSERV ISRUNNING TRUE" || echo "!ZSERV ISRUNNING FALSE"
            ;;
        "ISRUNNINGHERE")
            isrunninghere && echo "!ZSERV ISRUNNINGHERE TRUE" || echo "!ZSERV ISRUNNINGHERE FALSE"
            ;;
        "RESTART")
            {
                stopserver && runserver
            } && echo "!ZSERV RESTART SUCCESS" || echo "!ZSERV RESTART FAILURE"
            ;;
        "GET")
            shift
            get $@ ;;
        "RESET")
            resetserver
            echo "!ZSERV RESET DONE"
            ;;
        "BYE")
            break;
            ;;
        *)
            echo "!ZSERV INVALID_INPUT"
            ;;
    esac
done
