#!/bin/bash

########################################################################
#
#PROJECT:      Unified Sessions Manager
#AUTHOR:       Arno-Can Uestuensoez - acue@UnifiedSessionsManager.org
#MAINTAINER:   Arno-Can Uestuensoez - acue_sf1@sourceforge.net
#SHORT:        ctys
#CALLFULLNAME: Commutate To Your Session
#LICENCE:      GPL3
#VERSION:      01_02_007a17
#
########################################################################
#
# Copyright (C) 2007 Arno-Can Uestuensoez (UnifiedSessionsManager.org)
#
########################################################################

_myPKGNAME_DIGGER="${BASH_SOURCE}"
_myPKGVERS_DIGGER="01.02.002c01"
hookInfoAdd "$_myPKGNAME_DIGGER" "$_myPKGVERS_DIGGER"




#FUNCBEG###############################################################
#NAME:
#  digLocalPort
#
#TYPE:
#  bash-function
#
#DESCRIPTION:
#  Supports a local port to the listener port given by <host>:<label>.
#  <label> is a free defined label by user, which has to be unique on 
#  the target host.
#
#  The port is forwarded as an encrypted channel by usage of ssh port 
#  forwarding.
#
#  For the evaluation of the port value from the label a package 
#  specific functio is called:
#
#    "digLocalPort<package-id> <host>:<label>"
#
#  Local label access is supported by a simple trick, the ssh call will 
#  be performed as a one-shot emulation anyway, so the label will be 
#  supported as a remote call comment, e.g. like:
#
#  Defined format:
#    CALL:
#      "ssh -f -L 6001:localhost:5901 host01 sleep <timeout> \&\&\
#        echo "# DYNREMAP:<package>:<host>:<port>:<label>:<rport>:<myhost>:">/dev/null 
#              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
#      <timeout>=${SSH_ONESHOT_TIMEOUT}
#
#  The tunnel has to be used before the timer expires, but once in usage it
#  will remain until the client is closed. During the whole lifetime of the
#  tunnel the label and any additional text will be shown and could be 
#  accessed by ps/grep/awk.
#
#  The particularly important part here is the simplified dynamic decision
#  of the port to be used for mapping. Therefor the highest port incremented by 1
#  will be tried. The initial port is provided by LOCAL_PORTREMAP
#
#EXAMPLE:
#
#PARAMETERS:
#  $1: <package>
#  $2: <host>
#  $3: <label>
#  $4: [<id>|<pname>]
#
#GLOBALS:
#  LOCAL_PORTREMAP
#    The base for local port remapping.
#  SSH_ONESHOT_TIMEOUT
#
#OUTPUT:
#  RETURN:
#  VALUES:
#    <local-port>
#      The local port number for accessing <label>@<host>
#
#FUNCEND###############################################################
function digLocalPort () {
    printVerboseDebug ${DBG_LVL_MAINT} $LINENO $BASH_SOURCE "$FUNCNAME CALL=<${@}>"
    local remoteClientTP=`eval digGetClientTPfromServer $3 $2 $1 $4`
    if [ -z "${remoteClientTP}" ];then
        ABORT=2
        printERR $LINENO $BASH_SOURCE ${ABORT} "Cannot get remote port for server access."
        printERR $LINENO $BASH_SOURCE ${ABORT} "Check following hint."
	eval noClientServerSplitSupportedMessage${CTRL_SESSIONTYPE}
        gotoHell ${ABORT} 
    fi

    #somewhat simplified, will be refined soon.
    local localClientAccess=`ps ${PSEF} |grep -v sed |sed -n '/# DYNREMAP:/!d;s/.* \([0-9]*:localhost:[0-9]*\) .*/\1/;p'|awk -F':' 'BEGIN{max=0;}{if(max<$1)max=$1;}END{print max+1;}'`

    #if this is the first
    if [ "${localClientAccess}" == "1" ];then
	localClientAccess=${LOCAL_PORTREMAP}
    fi

    printVerboseDebug ${DBG_LVL_MAINT} $LINENO $BASH_SOURCE "$FUNCNAME Using for port forwarding - aka ConnectionForwarding:"
    printVerboseDebug ${DBG_LVL_MAINT} $LINENO $BASH_SOURCE "$FUNCNAME   Keyword for dynamic port evaluation of forwarding processes:"
    printVerboseDebug ${DBG_LVL_MAINT} $LINENO $BASH_SOURCE "$FUNCNAME   grep-keyword         = \"# DYNREMAP\""
    printVerboseDebug ${DBG_LVL_MAINT} $LINENO $BASH_SOURCE "$FUNCNAME   timeout for connect  = ${SSH_ONESHOT_TIMEOUT}"
    printVerboseDebug ${DBG_LVL_MAINT} $LINENO $BASH_SOURCE "$FUNCNAME "
    printVerboseDebug ${DBG_LVL_MAINT} $LINENO $BASH_SOURCE "$FUNCNAME   localClientAccess    = ${localClientAccess}"
    printVerboseDebug ${DBG_LVL_MAINT} $LINENO $BASH_SOURCE "$FUNCNAME   remoteClientTP       = ${remoteClientTP}"
    printVerboseDebug ${DBG_LVL_MAINT} $LINENO $BASH_SOURCE "$FUNCNAME "


    local _tunnel="ssh -f -L ${localClientAccess}:localhost:${remoteClientTP} $2 sleep ${SSH_ONESHOT_TIMEOUT} \# DYNREMAP:$1:$2:${localClientAccess}:$3:${remoteClientTP}:${MYHOST}:"

    printVerboseDebug ${DBG_LVL_MAINT} $LINENO $BASH_SOURCE "$FUNCNAME "
    printVerboseDebug ${DBG_LVL_MAINT} $LINENO $BASH_SOURCE "$FUNCNAME Assembled tunnel-call:"
    printVerboseDebug ${DBG_LVL_MAINT} $LINENO $BASH_SOURCE "$FUNCNAME _tunnel=${_tunnel}"
    printVerboseDebug ${DBG_LVL_MAINT} $LINENO $BASH_SOURCE "$FUNCNAME "
    eval $_tunnel

    if [ $? -ne 0 ];then
        ABORT=1
        printERR $LINENO $BASH_SOURCE ${ABORT} "openning port forwarding tunnel failed"
        printERR $LINENO $BASH_SOURCE ${ABORT} "  CALLED:<${_tunnel}>"
        gotoHell ${ABORT} 
    fi
    echo ${localClientAccess}
}


#FUNCBEG###############################################################
#NAME:
#  digGetLocalPort
#
#TYPE:
#  bash-function
#
#DESCRIPTION:
#  Finds the local port for a remote accesspoint. This is an forwarded 
#  clientTP established by digLocalPort to be used for client access.
#  
#  When multiple would match, the first is returned.
#
#  ATTENTION: 
#    The so called tunnel is established as a one-shot channel with an
#    timeout, there might be closed already when applying this function,
#    or might be closed after successful detection, when the following 
#    access has too long delay.
#    Thus do not shorten the value of SSH_ONESHOT_TIMEOUT dramatically!
#    Give it almost half a minute for starting, because a misusage might
#    be in single(?)-user working environment with localhost access not
#    really to be expected.
#
#  Defined format:
#   ....ssh -f -L 4801:localhost:4801 host sleep <timeout> \
#      # DYNREMAP:<package>:<host>:<port>:<label>:<rport>:<myhost>
#
#EXAMPLE:
#
#PARAMETERS:
#  $1: <label>
#  $2: [<host>]
#  $3: [<package>]
#
#OUTPUT:
#  RETURN:
#  VALUES:
#    <local-port>
#      The local port number for accessing <label>@<host>
#
#FUNCEND###############################################################
function digGetLocalPort () {
    printVerboseDebug ${DBG_LVL_MAINT} $LINENO $BASH_SOURCE "$FUNCNAME CALL=<${@}>"

        #
        #....ssh -f -L 4801:localhost:4801 host sleep 1000 \
        #             # DYNREMAP:<package>:<host>:<port>:<label>:<rport>:<myhost>
        #                         \1        \2     \3      \4     \5       \6
        #
    local _localClientAccess=`ps ${PSEF} |grep -v sed |sed -n '
        /# DYNREMAP/!d;
        s/.*# DYNREMAP:\([^:]*\):\([^:]*\):\([0-9]*\):\([^:]*\):\([^:]*\):\([^:]*\):[^:]*$/\1:\2:\3:\4:\5:\6/p
        '|awk -F':' -v l="$1" -v h="$2" -v p="$3" '
           BEGIN{port=0;}
           $4~l&&$2~h&&$1~p{print $3;exit;}
           $4~l&&$2~h&&p==/^$/{print $3;exit;}
           $4~l&&h~/^$/&&p~/^$/{print $3;exit;}
        '`

    printVerboseDebug ${DBG_LVL_MAINT} $LINENO $BASH_SOURCE "$FUNCNAME _localClientAccess=${_localClientAccess}"
    echo ${_localClientAccess}
}


#FUNCBEG###############################################################
#NAME:
#  digGetClientTPfromServer
#
#TYPE:
#  bash-function
#
#DESCRIPTION:
#  Gets remote port, of course some performance tuning could be done....
#
#EXAMPLE:
#
#PARAMETERS:
#  $1: <label>
#  $2: [<host>]
#  $3: [<package>]
#  $4: [<id>|<pname>]
#
#OUTPUT:
#  RETURN:
#  VALUES:
#    <local-port>
#      The local port number for accessing <label>@<host>
#
#FUNCEND###############################################################
function digGetClientTPfromServer () {
    printVerboseDebug ${DBG_LVL_MAINT} $LINENO $BASH_SOURCE "$FUNCNAME CALL=<${*}>"
    local _rcall="${MYCALLPATHNAME} -t ${3:-$CTRL_SESSIONTYPE} -a getclientport=$1${4:+,$4} ${2:-$R_HOSTS}"
    printVerboseDebug ${DBG_LVL_MAINT} $LINENO $BASH_SOURCE "$FUNCNAME __rcall=${_rcall}"
    local _cPort=`$_rcall`
    printVerboseDebug ${DBG_LVL_MAINT} $LINENO $BASH_SOURCE "$FUNCNAME _cPort=${_cPort}"
    echo ${_cPort#*=}
}


#FUNCBEG###############################################################
#NAME:
#  digGetExecLink
#
#TYPE:
#  bash-function
#
#DESCRIPTION:
#  Sets the complete remote ssh call prefix, including the host. This is 
#  in distinction to the function "getExecTarget" the setup of the tunnel
#  call or local operation respectively. The "getExecTarget" is the 
#  interface for the name service of ctys in order of target location.
#
#  The own user on localhost is just dropped for local execution without
#  ssh-call to ${USER}@localhost.
#
#EXAMPLE:
#
#PARAMETERS:
#  $1: <host>
#
#OUTPUT:
#  RETURN:
#    0: remote-config: ssh...
#    1: local-config:  cd $HOME&&
#  VALUES:
#    <ssh-exec-prefix>  string a prefix for call
#
#FUNCEND###############################################################
function digGetExecLink () {
    printVerboseDebug ${DBG_LVL_UID} $LINENO $BASH_SOURCE "$FUNCNAME:TARGET(\$1-only!!!)=${*}"
    local _EXECLINK=""

    if [ -z "${1}" ];then
        ABORT=1
	printERR $LINENO $BASH_SOURCE ${ABORT} "$FUNCNAME:Missing target host."
	gotoHell ${ABORT}
    fi

    local _target="${1// }";_target="${_target#$USER@localhost}"
    if [ -z "${_target}" ];then
        ABORT=1
	printVerboseDebug ${DBG_LVL_UID} $LINENO $BASH_SOURCE "$FUNCNAME:local native execution for USER=$USER"
	printVerboseDebug ${DBG_LVL_UID} $LINENO $BASH_SOURCE "$FUNCNAME:for unified PATH semantics:"
	printVerboseDebug ${DBG_LVL_UID} $LINENO $BASH_SOURCE "$FUNCNAME:   \"cd \$HOME&&\" "
	echo -n "cd $HOME&&"
        return 1
    fi

    #OK. even though it might to be executed on localhost, it is not the same USER!
    #So use SSH.
    if [ -n "${CTRL_SSH}" ];then
	_EXECLINK="ssh -X "
	if [ -n "${CTRL_SSH_PSEUDOTTY}" ];then
	    _EXECLINK="${_EXECLINK} -t "
	fi
	if [ -n "${CTRL_ASYNC}" -a "${CTRL_ASYNC}" == 1 ];then
	    _EXECLINK="${_EXECLINK} -f "
	fi
        _EXECLINK="${_EXECLINK} ${_target}"
	printVerboseDebug ${DBG_LVL_UID} $LINENO $BASH_SOURCE "$FUNCNAME:_EXECLINK=${_EXECLINK}"
    else
        ABORT=1
	printERR $LINENO $BASH_SOURCE ${ABORT} "$FUNCNAME:Only SSH sessions are supported for remote execution."
	gotoHell ${ABORT}
    fi
    echo -n "${_EXECLINK}"
}


