#!/bin/bash

# Copyright 2012 Álvaro Justen
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; version 3 dated June, 2007.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.

SBC=$0
SBC_PATH="$HOME/.sbc"
SBC_KEY="$HOME/.ssh/sbc_rsa"
SBC_CONTROL_PATH=/tmp/sbc_master_%h_%p_%r
AUTH_KEYS="$HOME/.ssh/authorized_keys"
SSH_LOCAL_PORT=22

get_username_and_hostname() {
    args=`getopt :1246AaCfgKkMNnqsTtVvXxYyb:c:D:e:F:I:i:L:l:m:O:o:p:R:S:W:w: "$@"`
    username=""
    port=22
    last=""
    for arg in $args; do
        if [ "$last" = "-l" ]; then
            username=$arg
        elif [ "$last" = "-p" ]; then
            port=$arg
        fi
        last=$arg
    done
    user_host=$last
    hostname=${user_host#*@}
    if [ -z "$username" ]; then
        username=${user_host%@*}
    fi
    echo "$username@$hostname:$port"
}

if [ -z "$1" ]; then
    echo "Usage: $0 <plugin_name> [--help] [options]"
    exit 1
fi

sbc_command=$1
shift

case "$sbc_command" in
    install)
        echo 'Deprecated. Please install sbc on the server by hand.'
        echo 'After installing it on the server, run "sbc setup user@host".'
        exit 1
    ;;

    configure)
        # Running on remote - should be run only by sbc itself
        mkdir -p $HOME/.ssh

        key=$(cat -)
        echo "$key" > $SBC_KEY

        chmod 700 $HOME/.ssh
        chmod 600 $SBC_KEY
    ;;

    setup)
        # Running on client
        set -e
        if [ -z "$1" ]; then
            echo "Usage: $0 setup [ssh options] <user@remote-server>"
            exit 1
        fi

        if [ ! -e "$SBC_KEY" ]; then
            echo -n "[sbc] Generating SSH RSA key without password ($SBC_KEY)..."
            ssh-keygen -t rsa -f $SBC_KEY -N '' &> /dev/null
            chmod 600 $SBC_KEY
            echo ' [OK]'
        else
            echo "[sbc] Using existing key $SBC_KEY"
        fi

        # TODO: replace private_key with variable name
        echo -n "[sbc] Copying private key to remote server..."
        cat $SBC_KEY | ssh $@ 'sbc configure'
        echo ' [OK]'

        echo -n "[sbc] Updating local authorized_keys (if needed)..."
        if [ ! -e "$HOME/.ssh" ]; then
            mkdir -p "$HOME/.ssh"
            chmod 700 "$HOME/.ssh"
        fi
        if [ "$(grep -s "$(cat $SBC_KEY.pub)" $AUTH_KEYS | wc -l)" = 0 ]; then
            cat $SBC_KEY.pub >> $AUTH_KEYS
        fi
        echo ' [OK]'

        echo "[sbc] Done! Try now: sbc ssh $@"

        set +e
    ;;

    ssh)
        # Running on client to start SSH connection
        set -e

        #TODO: warn user if sbc is not installed on server

        data=$(get_username_and_hostname $@)
        username=${data%@*}
        hostname=${data#*@}; hostname=${hostname%:*}
        port=${data#*:}

        # GUI applications need local environment
        env_file=/tmp/sbc-$USER-env-$hostname-$port-$username
        touch $env_file; chmod 600 $env_file
        env | sed 's/^/export /; s/=/="/; s/$/"/' >> $env_file

        SBC_PORT=$(($RANDOM + 1024))

        echo -n '[sbc] Creating back-channel and connecting...'
        ssh -CtR 127.0.0.1:$SBC_PORT:127.0.0.1:$SSH_LOCAL_PORT $@ \
            "SBC_USER=$USER SBC_PORT=$SBC_PORT \
            SBC_REMOTE_USER=$username SBC_REMOTE_HOST=$hostname \
            SBC_REMOTE_PORT=$port sbc shell"

        set +e
    ;;

    shell)
        # Running on remote host, to open a new SSH shell session

        #TODO: verify if is already inside a sbc remote session

        SBC_CMD="ssh -qNf -p $SBC_PORT -o StrictHostKeyChecking=no \
                     -o ControlMaster=auto -o ControlPath=$SBC_CONTROL_PATH \
                     -i $SBC_KEY $SBC_USER@localhost"
        $SBC_CMD

        SBC_BACKCHANNEL_PID=$(ps -Ao pid,cmd | grep "$(echo $SBC_CMD)" | awk '($2 != "grep") {print $1}')
        echo ' [OK]'

        # Open the user's default shell
        $SHELL

        echo -n '[sbc] Closing back-channel...'
        kill -9 $SBC_BACKCHANNEL_PID
        echo ' [OK]'
    ;;

    exec-plugin)
        # Running on client (called by remote `sbc plugin ...`)

        plugin=$1
        shift

        # Update environment to the process which called 'sbc ssh'
        source /tmp/sbc-$USER-env-$SBC_REMOTE_HOST-$SBC_REMOTE_PORT-$SBC_REMOTE_USER

        if [ ! -x "$SBC_PATH/plugins/$plugin" ]; then
            echo "ERROR: plugin '$plugin' doesn't exist or isn't executable."
            exit 1
        fi

        # Run the plugin
        $SBC_PATH/plugins/$plugin $@
    ;;

    *)
        # Running on remote (try to run a plugin on local machine)
        # $sbc_command is the plugin to be executed
        env_vars="SBC_REMOTE_PWD=$PWD $(env | grep SBC_ | tr '\n' ' ')"
        ssh -qtp $SBC_PORT -o StrictHostKeyChecking=no \
            -i $SBC_KEY -o ControlMaster=auto \
            -o ControlPath=$SBC_CONTROL_PATH \
            $SBC_USER@localhost \
            "/bin/sh -lc \"$env_vars sbc exec-plugin $sbc_command '$@'\""
    ;;

esac