#!/bin/sh
#  $Id: gateway_server.sh 146617 2018-01-12 14:45:05Z matt.dean@oracle.com $
#  Copyright (c) 2009, 2020, Oracle and/or its affiliates.
#  This software code contains proprietary information
#  and should be treated as confidential.
#
#  Component: Secure Global Desktop Gateway
#  Author: Bill Weir

. "${INSTDIR}/bin/scripts/common"
. "${INSTDIR}/bin/scripts/gateway_serverarray"
. "${INSTDIR}/bin/scripts/gateway_serverlistarray"

privusr="sgdgsys"
privgrp="sgdgserv"


[ "${trace}" = "x" ] && set -x

[ -s "${INSTDIR}/etc/version" ] && . ${INSTDIR}/etc/version

SERVERIDMAP_FILE="${INSTDIR}/etc/serveridmap.properties"
SERVERIDMAP_TMPFILE="${SERVERIDMAP_FILE}.tmp"

apachepath="${INSTDIR}/httpd/httpd-${VERSION_APACHE}"
fApachePid="${apachepath}/logs/httpd.pid"
fProxyPid="${INSTDIR}/proxy/var/run/proxy.pid"

GetApachePid() {
    _pid=""
    [ -s "${fApachePid}" ] && _pid=`tr -cd '0123456789' <"${fApachePid}"`
    [ -n "${_pid}" ] && [ -n "`ps -p${_pid} | grep \"${_pid}\"`" ] && echo "${_pid}"
    return 0
}

GetProxyPid() {
    _pid=""
    [ -s "${fProxyPid}" ] && _pid=`tr -cd '0123456789' <"${fProxyPid}"`
    [ -n "${_pid}" ] && [ -n "`ps -p${_pid} | grep \"${_pid}.*java\"`" ] && echo "${_pid}"
    return 0
}


# Detect which version of awk to use
which nawk 1>/dev/null 2>&1
if [ $? -eq 0 ]
then
    AWK="nawk"
else
    AWK="awk"
fi

usage() {
    echo "${i18n_serverusage10_gw}"
    exit 1
}

usage_add() {
    case "$1" in '' | --help) ;; *) printf "${i18n_badarg01_gw}" "$1" ;; esac
    echo "${i18n_serveraddusage10_gw}"
    exit 1
}

usage_remove() {
    case "$1" in '' | --help) ;; *) printf "${i18n_badarg01_gw}" "$1" ;; esac
    echo "${i18n_serverremoveusage10_gw}"
    exit 1
}

usage_list() {
    case "$1" in '' | --help) ;; *) printf "${i18n_badarg01_gw}" "$1" ;; esac
    echo "${i18n_serverlistusage10_gw}"
    exit 1
}

usage_add_array()
{
    case "$1" in '' | --help) ;; *) printf "${i18n_badarg01_gw}" "$1" ;; esac
    echo "${i18n_serveraddarrayusage_gw}"
    exit 1
}

# Check whether the gateway is currently active or not
# return: "t" if the gateway is active, "f" otherwise.
is_gateway_up()
{
    ApachePid=`GetApachePid`
    ProxyPid=`GetProxyPid`
    if [ -n "${ApachePid}" -a -n "${ProxyPid}" ]; then
	    echo "t"
    else
	    echo "f"
    fi
}

# Show a message suggesting what to do next, after a configuration change.
suggest_next_action()
{
    gatewayUp=`is_gateway_up`
    if [ "${gatewayUp}" = "t" ]
    then
          echo "${i18n_waitforrefresh10_gw}"
    else
          echo "${i18n_needtostartgw10_gw}"
    fi
}

fail_if_colocated() {
    LOCAL_SGD_SERVER="LocalSGDServer"
    server_name="$1"

    # Operations using ${LOCAL_SGD_SERVER} are allowed to proceed
    # to avoid breaking the rename logic
    islocal=`echo "${server_name}" | grep -i "${LOCAL_SGD_SERVER}"`

    if [ -z "${islocal}" ]
    then
        # Get the output of server list
        password=`get_password`
        results=`${GW_JAVA_DIR}/keytool -list \
       -storepass "${password}" \
       -keystore "${INSTDIR}/proxy/etc/keystore" | grep -i "${LOCAL_SGD_SERVER}" `
        
        if [ -n "${results}" ]
        then
            # Keystore has an entry for a local sgd server
            echo "${i18n_server_localsgd}"
            exit 1
        fi
    fi
}

get_password() {
    cat ${INSTDIR}/etc/password
    return 0
}

serveridmapentry_remove() {
    if [ $# -ne 1 ]
    then
        return # Do nothing
    fi

    sgdServer=$1

    # Remove old entry
    if [ -f  "${SERVERIDMAP_FILE}" ]
    then
        cp "${SERVERIDMAP_FILE}" "${SERVERIDMAP_TMPFILE}"
        sed "/\=${sgdServer}/d" "${SERVERIDMAP_TMPFILE}" > "${SERVERIDMAP_FILE}"
        rm "${SERVERIDMAP_TMPFILE}"
    fi
}

server_add_array() 
{
    sgdurl="" creds_file="" array_name="" token=""
    while [ -n "$1" ]
    do
	case "$1" in
        --serverurl)
            # Check the next parameter is available
            # and starts with http
            if [ $# -lt 2 ]
            then
                echo "${i18n_serveraddarray_error_missingserverurl_gw}"
                usage_add_array
            fi
            if [[ $2 == http* ]]
            then
                sgdurl="$2"
            else
                echo "${i18n_serveraddarray_error_invalidserverurl_gw}"
                usage_add_array
            fi
        ;;
        --file)
            # Check a file is provided and that the
            # file exists
            if [ $# -lt 2 ]
            then
                echo "${i18n_serveraddarray_error_missingfile_gw}"
                usage_add_array
            fi
            if [ -f "$2" ]
            then
                creds_file="$2"
            else
                echo "${i18n_serveraddarray_error_invalidfile_gw}"
                usage_add_array
            fi
        ;;
        --name)
            # The name for the array
            if [ $# -lt 2 ]
            then
                echo "${i18n_serveraddarray_error_missingname_gw}"
                usage_add_array
            fi
            # Check the name does not start with --
            if [[ $2 == --* ]]
            then
                echo "${i18n_serveraddarray_error_missingname_gw}"
                usage_add_array
            fi 
            array_name="$2"
        ;;
        --token)
            # A one-time token
            if [ $# -lt 2 ]
            then
                echo "${i18n_serveraddarray_error_missingtoken_gw}"
                usage_add_array
            fi
            # Check the token does not start with --
            if [[ $2 == --* ]]
            then
                echo "${i18n_serveraddarray_error_missingtoken_gw}"
                usage_add_array
            fi 
            token="$2"
        ;;
        *)  usage_add_array "$1"
        esac
        shift; shift
    done

    if [ -z "${sgdurl}" ] || [ -z "${array_name}" ]
    then
        usage_add_array
    fi

    # Cannot supply both a creds file and a token
    if [ -n "${creds_file}" ] && [ -n "${token}" ]
    then
        echo "${i18n_serveraddarray_error_tokenorfile_gw}"
        usage_add_array
    fi

    # If auth was supplied, set the appropriate type
    if [ -n "${creds_file}" ]
    then
        auth_type="file"
        auth_data="${creds_file}"
    elif [ -n "${token}" ]
    then
        auth_type="token"
        auth_data="${token}"
    fi

    server_add_array_impl "${sgdurl}" "${array_name}" "${auth_type}" \
        "${auth_data}"
    # If the gateway is up, restart apache gracefully to pick up the 
    # updated configuration for the server(s) added
    gatewayUp=`is_gateway_up`
    if [ "${gatewayUp}" = "t" ]
    then
        ${apachepath}/bin/apachectl -d ${apachepath} -D SSL -k graceful
    fi
    return 0
}

server_add() {
    SERVER="" CERTFILE="" URL="" SSLCERTFILE=""
    while [ -n "$1" ]; do
	case "$1" in
	--server)
	    case "$2" in
	    "" | --*) printf "${i18n_invalidarg01_gw}" "$1"; usage_add
	    esac
	    SERVER="$2"
	    ;;
	--certfile)
	    case "$2" in
	    "" | --*) printf "${i18n_invalidarg01_gw}" "$1"; usage_add
	    esac
	    CERTFILE="$2"
	    # deal with relative filename
	    case "${CERTFILE}" in /*) ;; *) CERTFILE="${ORIGDIR}/${CERTFILE}" ;; esac
	    [ ! -s "${CERTFILE}" ] && { printf "${i18n_invalidarg01_gw}" "$1"; usage_add; }
	    ;;
	--url)
	    case "$2" in
	    *://*) ;;
	    *) printf "${i18n_invalidarg01_gw}" "$1"; usage_add
	    esac
	    URL="$2"
	    ;; 
	--ssl-certfile)
	    case "$2" in
	    "" | --*) printf "${i18n_invalidarg01_gw}" "$1"; usage_add
	    esac
	    SSLCERTFILE="$2"
	    # deal with relative filename
	    case "${SSLCERTFILE}" in /*) ;; *) SSLCERTFILE="${ORIGDIR}/${SSLCERTFILE}" ;; esac
	    [ ! -s "${SSLCERTFILE}" ] && { printf "${i18n_invalidarg01_gw}" "$1"; usage_add; }
	    ;;
	*)  usage_add "$1"
	esac
	shift; shift
    done

    [ -z "${SERVER}" -o -z "${CERTFILE}" -o -z "${URL}" ] && usage_add

    fail_if_colocated "${SERVER}"

    PASSWORD=`get_password`
    ${GW_JAVA_DIR}/keytool \
	-importcert -noprompt \
	-keypass "${PASSWORD}" \
	-keystore "${INSTDIR}/proxy/etc/keystore" \
	-storepass "${PASSWORD}" \
	-file "${CERTFILE}" \
	-alias "${SERVER}"
    status=$?
    [ "${status}" -ne 0 ] && {
	printf "${i18n_certimportfailed01_gw}" "${status} (${CERTFILE})"
	exit 1
    }
    [ -n "${SSLCERTFILE}" ] && {
	${GW_JAVA_DIR}/keytool \
	    -importcert -noprompt \
	    -keypass "${PASSWORD}" \
	    -keystore "${INSTDIR}/proxy/etc/keystore" \
	    -storepass "${PASSWORD}" \
	    -file "${SSLCERTFILE}" \
	    -alias "${SERVER}-ssl"
	status=$?
	[ "${status}" -ne 0 ] && {
	    printf "${i18n_certimportfailed01_gw}" "${status} (${SSLCERTFILE})"
	    exit 1
	}
    }

    # Get the SGD server name (and port) from the URL
    url_server_name="`echo "${URL}" | sed 's|.*://||;s|/.*||g'`"

    # Get the name of the route for the SGD server name
    route_name="`get_route_name "${url_server_name}"`"

    templates_path="${INSTDIR}/etc/templates"
    httpd_config_path="httpd/httpd-${VERSION_APACHE}/conf/extra/gateway"
    server_conf_file="${httpd_config_path}/server.domain.conf"

    mkdir -p ${INSTDIR}/${httpd_config_path}/servers 2>/dev/null
    sed "s|%SGDWEBSERVERURL%|${URL}|g;s|%SGDROUTE%|${route_name}|g" \
        "${templates_path}/${httpd_config_path}/server.domain.conf.template" \
            >${INSTDIR}/${httpd_config_path}/servers/${SERVER}.conf 

    if [ ! -f "${SERVERIDMAP_FILE}" ]
    then
        echo "# Server Tag - Server map. Generated by gateway server add" >> "${SERVERIDMAP_FILE}"
    else
        # Remove any old entries for this server name
        serveridmapentry_remove "${url_server_name}"
    fi

    # Add a mapping from the route name to the server name
    echo "${route_name}=${url_server_name}" >> "${SERVERIDMAP_FILE}"
    chown ${privusr}:${privgrp} "${SERVERIDMAP_FILE}"
    chmod 640 "${SERVERIDMAP_FILE}"
    echo
    suggest_next_action
    return 0
}

server_remove() {
    SERVER=""
    while [ -n "$1" ]; do
	case "$1" in
	--server)
	    case "$2" in
	    "" | --*) printf "${i18n_invalidarg01_gw}" "$1"; usage_remove
	    esac
	    SERVER="$2"
	    ;;
	*)  usage_remove "$1"
	esac
	shift; shift
    done

    [ -z "${SERVER}" ] && usage_remove

    fail_if_colocated "${SERVER}"

    PASSWORD=`get_password`
    ${GW_JAVA_DIR}/keytool \
	-delete \
	-keypass "${PASSWORD}" \
	-keystore "${INSTDIR}/proxy/etc/keystore" \
	-storepass "${PASSWORD}" \
	-alias "${SERVER}"
    status=$?
    [ "${status}" -ne 0 ] && {
	printf "${i18n_deletefailed01_gw}" "${status} (${SERVER})"
	exit 1
    }
    
    # check for presence of ssl alias, silence is golden.
    ${GW_JAVA_DIR}/keytool \
	-list \
	-keypass "${PASSWORD}" \
	-keystore "${INSTDIR}/proxy/etc/keystore" \
	-storepass "${PASSWORD}" \
	-alias "${SERVER}-ssl" >/dev/null 2>&1
    status=$?
    [ "${status}" -eq 0 ] && {
        #delete the alias.
        ${GW_JAVA_DIR}/keytool \
	    -delete \
	    -keypass "${PASSWORD}" \
	    -keystore "${INSTDIR}/proxy/etc/keystore" \
	    -storepass "${PASSWORD}" \
	    -alias "${SERVER}-ssl"
        status=$?
        [ "${status}" -ne 0 ] && {
	    printf "${i18n_deletefailed01_gw}" "${status} (${SERVER}-ssl)"
	    exit 1
        }
    }
    
    rm -f ${INSTDIR}/httpd/httpd-${VERSION_APACHE}/conf/extra/gateway/servers/${SERVER}.conf 2>/dev/null

    # Remove Server from the serverid Map
    serveridmapentry_remove ${SERVER}

    echo
    suggest_next_action
    return 0
}

server_list() {
    DELIMITER=""
    while [ -n "$1" ]; do
	case "$1" in
	    --delimiter)
		case "$2" in
		    "" | --*) usage_list
		esac
	       DELIMITER="$2"
		;;
	    *) usage_list "$1"
	esac
	shift; shift
    done

    # Note: This keytool code is repeated and should be placed in common
    # location which can be shared by the scripts.
    # The keytool command can be used without the password specified as
    # an argument to list the keystore contents. When used this way, 
    # keytool asks for a password which can be answered with a carriage
    # return. The keytool will still present the results but displays a
    # warning message which is sent to /dev/null
    password="`get_password`"
    LANG=C LC_ALL=C ${GW_JAVA_DIR}/keytool \
	-list -v \
        -storepass "${password}" \
	-keystore "${INSTDIR}/proxy/etc/keystore" << EOF 2> /dev/null |
EOF
    while read data; do
	case "${data}" in
	Alias*[nN]ame:*)
	    alias=`expr "${data}" : 'Alias[^:]*: *\(.*\)'`
	    ;;
	Entry*[tT]ype:*)
	    type=`expr "${data}" : 'Entry[^:]*: *\(.*\)'`
	    [ "${type}" = "trustedCertEntry" ] && echo "${alias}"
	    ;;
	esac
    done |
    LANG=C LC_ALL=C sort -u |
    while read alias; do
	(
	    SERVERCONF="${INSTDIR}/httpd/httpd-${VERSION_APACHE}/conf/extra/gateway/servers/${alias}.conf"
	    [ -s "${SERVERCONF}" ] && ${AWK} '$1 == "BalancerMember" {print "URL: " $2}' ${SERVERCONF}
            # Note: This keytool code is repeated and should be placed in common
            # location which can be shared by the scripts.
            password="`get_password`"
	    LANG=C LC_ALL=C ${GW_JAVA_DIR}/keytool \
		-list -v -alias "${alias}" \
                -storepass "${password}" \
		-keystore "${INSTDIR}/proxy/etc/keystore" << EOF 2> /dev/null
EOF
	) done |
    if [ -z "${DELIMITER}" ]; then
        ${AWK} '/^URL:/ {
	    URL=$0
	}
	/^Alias name:.*-ssl *$/ {
            doneHEAD = 0
	    HEAD="\nSSL Certificate:"
	    next
	}
	/^Alias name:/ {
            doneHEAD = 0
	    HEAD="\n\nSGD Server: " $3 "\n" URL "\n\nCA Certificate:"
	}
	/^(Owner|Issuer|Serial number|Valid from):/ {
            if (!doneHEAD) print HEAD
            doneHEAD = 1
	    print "   " $0
	}'
    else
	${AWK} -v DEL="${DELIMITER}" '
        function addNA() {
            if (!SSL) printf DEL "n/a" DEL "n/a" DEL "n/a"
            print ""
        }
        function getValidity(DATES) {
            split(DATES,time)

            from = time[3]" "time[4]" "time[5]" "time[6]" "time[7]" "time[8]
            to = time[10]" "time[11]" "time[12]" "time[13]" "time[14]" "time[15]

            printf from DEL to
        }
        /^Alias name:.*-ssl *$/ {
	    HEAD=DEL
            SSL = 1
	    next
	}
	/^Alias name:/ {
            if (OUTPUT) {
                addNA()
            }

            SSL = 0
            OUTPUT = 1
	    HEAD=$3 DEL
	}
        /^Serial number:/ {
            SERIAL=$3 DEL
        }
	/^Valid from:/ {
	    printf HEAD SERIAL
            getValidity($0)
        }
        END {
            addNA()
        }'
    fi

    return 0
}

# Get the route name for the provided SGD server name
#
# The route name must be less than 64 characters for it to work with the
# reverse proxy
#
# Args:   $1 - SGD server name
# Output: The route name for the provided SGD server name
get_route_name()
{
    server_name="$1"

    # Generate the route name as a SHA224 hash of the server name and trim the
    # leading output from the "openssl dgst" command
    echo "${server_name}" | \
        openssl dgst -sha224 | \
        ${AWK} '{print $2}'
}


case "$1" in
add)
    shift
    server_add "$@"
    ;;
remove)
    shift
    server_remove "$@"
    ;;
list)
    shift
    server_list "$@"
    ;;
add-array)
    shift
    server_add_array "$@"
    ;;
list-array)
    shift
    server_list_array "$@"
    ;;
*)  usage
esac

exit 0

