#!/bin/bash
#
#  Copyright (c) 2020, Oracle and/or its affiliates.
#

#
# Deploys the members of an SGD array on the gateway.
# If the SGD members are already deployed, updates the
# certificates if they have changed.
#

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

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

CERT_NO_MATCH=0
CERT_EXISTS=1
CERT_UPDATE_REQD=2

KEYSTORE_PASS="changeit"
GW_PEM="${INSTDIR}/proxy/etc/gw.pem"
OUTPUT_DIR="${INSTDIR}/proxy/var/info"
ARRAYS="${OUTPUT_DIR}/arrays"

PRIVUSR="sgdgsys"
PRIVGRP="sgdgserv"

#
# Creates a keystore for the Java client to connect to 
# the SGD webservices.
#
createKeystore()
{
    sgdurl="$1"
    dest="$(echo "${sgdurl}" | sed "s|https://||")"

    # If there is no port on the URL add the default port for https
    if [[ ${dest} != *:* ]]
    then
        dest="${dest}:443"
    fi
 
    echo -n | openssl s_client -connect "${dest}" </dev/null 2>&1 | \
        openssl x509 -outform PEM > "${INSTDIR}/proxy/etc/cert.pem" 2> /dev/null
    status=$?
    if [ $status -ne 0 ]
    then
        echo "${i18n_serveraddarray_cantcontactserver_gw}"
        exit 1
    fi

    # Remove any existing tmp keystore, left over from a previous
    # call to the script
    rm -f "${INSTDIR}/proxy/etc/temp.keystore" > /dev/null 2>&1
    ${GW_JAVA_DIR}/keytool -importcert \
                           -file "${INSTDIR}/proxy/etc/cert.pem" \
                           -alias sgd \
                           -noprompt \
                           -keystore "${INSTDIR}/proxy/etc/temp.keystore" \
                           -storepass "${KEYSTORE_PASS}" > /dev/null 2>&1
    rm -f "${INSTDIR}/proxy/etc/cert.pem"
}

#
# Uses the Java client to call the gateway deploy webservice on
# the given SGD server.
#
callDeploy() 
{
    is_secure="$1"
    auth_type="$2"
    auth_data="$3"

    gw_name="$(${INSTDIR}/bin/bin/ttahostname)"

    ${INSTDIR}/bin/gateway cert export --certfile "${GW_PEM}" > /dev/null 2>&1

    JAVA_ARGS="-classpath ${INSTDIR}/proxy/sgd-gw-deploy.jar:${INSTDIR}/proxy/sgd-webservices.jar"

    if [ -n "${is_secure}" ]
    then
        JAVA_ARGS="${JAVA_ARGS} \
            -Djavax.net.ssl.trustStore=${INSTDIR}/proxy/etc/temp.keystore \
            -Djavax.net.ssl.trustStorePassword=${KEYSTORE_PASS} \
            -Djava.util.logging.config.file=${INSTDIR}/proxy/etc/gateway-cmd-logging.properties"
    fi

    JAVA_ARGS="${JAVA_ARGS} com.oracle.sgd.deploy.GatewayDeploy"

    if [ -z "${auth_type}" ]
    then
        ${GW_JAVA_DIR}/java ${JAVA_ARGS} "${gw_name}" \
            "${sgdurl}" "${INSTDIR}" "${GW_PEM}" 2> /dev/null
    else
        ${GW_JAVA_DIR}/java ${JAVA_ARGS} "${gw_name}" \
            "${sgdurl}" "${INSTDIR}" "${GW_PEM}" "${auth_type}" \
            "${auth_data}" 2> /dev/null
    fi
    
    status=$?
    if [ ${status} == 0 ]
    then
        echo "${i18n_serveraddarray_recv_info_gw}"
    elif [ ${status} == 1 ]
    then
        displayError "${i18n_serveraddarray_error_remote_gw}"
    else
        echo "${i18n_serveraddarray_error_gw}"
    fi

    # Tidy up the files created
    rm -f "${INSTDIR}/proxy/etc/temp.keystore" > /dev/null 2>&1
    rm -f "${GW_PEM}" > /dev/null 2>&1
    rm -f SGDWebServiceCalls.log > /dev/null 2>&1
}

#
# Gets a fingerprint from the given PEM file.
#
getFingerprintFromPem()
{
    pem_file="$1"
    alg="$2"

    # The algorithm from the list has a - whereas the printcert
    # does not, so it must be removed
    reqd_alg="$(echo "${alg}" | sed "s|-||")"
    fingerprint="$(${GW_JAVA_DIR}/keytool -printcert \
        -file "${pem_file}" | grep "${reqd_alg}: " | cut -d' ' -f3)"
    echo "${fingerprint}"
}

#
# Compares a certificate installed on the Gateway with one
# returned by the call to gateway deploy and returns a
# status code to indicate whether the certificate has changed.
#
checkCert()
{
    alias="$1"
    pem_file="$2"

    # Check whether this alias already exists in the keystore
    fingerprint="$(${GW_JAVA_DIR}/keytool -list \
        -keystore "${INSTDIR}/proxy/etc/keystore" \
        -storepass "$(cat ${INSTDIR}/etc/password)" \
        -alias "${alias}" | grep fingerprint)"
    if [ -z "${fingerprint}" ]
    then
        # No match
        echo "${CERT_NO_MATCH}" 
        return
    fi

    # Compare the fingerprint with the one from the new cert
    comps=($fingerprint)
    alg="$(echo "${comps[2]}" | sed 's|(||;s|)||;s|:||')"
    existingdata="${comps[3]}"

    newdata="$(getFingerprintFromPem "${pem_file}" "${alg}")"
       
    if [ "${existingdata}" != "${newdata}" ]
    then
        # Cert needs to be updated
        echo "${CERT_UPDATE_REQD}"
    else
        # Cert exists and is unchanged
        echo "${CERT_EXISTS}"
    fi
}

#
# Checks the peer and ssl certificates returned by the gateway deploy
# against those currently installed on the gateway.
#
checkStatus()
{
    array_member="$1"

    # Check the peer cert status
    peerStatus="$(checkCert "${array_member}" "${OUTPUT_DIR}/${array_member}-peer.pem")" 
    if [ "${peerStatus}" == "${CERT_NO_MATCH}" ] || [ "${peerStatus}" == "${CERT_UPDATE_REQD}" ]
    then
        echo "${peerStatus}"
        return
    fi

    # If the peer status is unchanged, check the ssl cert
    if [ -f "${OUTPUT_DIR}/${array_member}-ssl.pem" ]
    then
        sslStatus="$(checkCert "${array_member}-ssl" "${OUTPUT_DIR}/${array_member}-ssl.pem")"
        echo "${sslStatus}"
    else
        echo "${peerStatus}"
    fi
}

#
# Given the info from gateway deploy, adds the SGD servers to
# the gateway as required.
#
addToGateway()
{
    if [ ! -f "${OUTPUT_DIR}/array-members" ]
    then
        echo "${i18n_serveraddarray_deploy_failed_gw}"
        exit 1 
    fi

    while read line; do
        member=($line)
 
        status=$(checkStatus "${member[0]}")

        if [ "${status}" == "${CERT_EXISTS}" ]
        then
            printf "${i18n_serveraddarray_exists_gw}" "${member[0]}"
            rm -f "${OUTPUT_DIR}/${member[0]}-peer.pem"
            rm -f "${OUTPUT_DIR}/${member[0]}-ssl.pem"
            continue
        elif [ "${status}" == "${CERT_UPDATE_REQD}" ]
        then
            printf "${i18n_serveraddarray_removing_gw}" "${member[0]}"
            # Delete the existing entry before adding the new one
            "${INSTDIR}/bin/gateway" server remove --server "${member[0]}" > /dev/null 2>&1
        fi

        chown ${PRIVUSR}:${PRIVGRP} "${OUTPUT_DIR}/${member[0]}-peer.pem"

        # Call the gateway server add command for each array member
        printf "${i18n_serveraddarray_adding_gw}" "${member[0]}"
        if [ -f "${OUTPUT_DIR}/${member[0]}-ssl.pem" ]
        then
            chown ${PRIVUSR}:${PRIVGRP} "${OUTPUT_DIR}/${member[0]}-ssl.pem"
            # Server with SSL cert
            "${INSTDIR}/bin/gateway" server add \
                --server "${member[0]}" \
                --url "${member[1]}" \
                --certfile "${OUTPUT_DIR}/${member[0]}-peer.pem" \
                --ssl-certfile "${OUTPUT_DIR}/${member[0]}-ssl.pem" > /dev/null 2>&1
            rm -f "${OUTPUT_DIR}/${member[0]}-ssl.pem"
        else
            # Server with no SSL cert
            "${INSTDIR}/bin/gateway" server add \
                --server "${member[0]}" \
                --url "${member[1]}" \
                --certfile "${OUTPUT_DIR}/${member[0]}-peer.pem" > /dev/null 2>&1
        fi

        rm -f "${OUTPUT_DIR}/${member[0]}-peer.pem"
    done < "${OUTPUT_DIR}/array-members"

}

#
# Updates the arrays file which contains info about the SGD
# servers in each deployed array.
#
updateArrayFile()
{
    array_name="$1"

    # Does this array name already exist?
    listed_members=""
    if [ -f "${ARRAYS}" ] 
    then
        listed_members="$(grep "${array_name}=" "${ARRAYS}"|\
                           cut -d'=' -f2)"
    fi

    if [ -n "${listed_members}" ]
    then
        # Compare the listed members with the current list
        listed_array=(${listed_members})
        for i in "${listed_array[@]}"
        do
            # Is this server in the array members?
            inArray="$(grep "$i" "${OUTPUT_DIR}/array-members")"
            if [ -z "${inArray}" ]
            then
                # The server has been removed from this array.
                # Check whether it is still a member of another
                # array. If not, remove it from the servers list.
                other_arrays="$(grep "$i" "${ARRAYS}" | \
                    grep -v "${array_name}=")"
                if [ -z "${other_arrays}" ]
                then
                    # Server is not in any other arrays
                    printf "${i18n_serveraddarray_removing_gw}" "$i"
                    "${INSTDIR}/bin/gateway" server remove \
                        --server "$i" > /dev/null 2>&1
                fi
            fi
        done

        # Remove the invalid entry for this array_name
        grep -v "${array_name}=" "${ARRAYS}" > "${ARRAYS}.temp"
        mv -f "${ARRAYS}.temp" "${ARRAYS}"
    fi

    # Create an entry for this array name
    current_members="$(cat "${OUTPUT_DIR}/array-members" | \
                         awk '{print $1}' | tr '\012' ' ')"
    echo "${array_name}=${current_members}" >> "${ARRAYS}"

    # Tidy up the array-members file
    rm -f "${OUTPUT_DIR}/array-members"

    echo "${i18n_serveraddarray_complete_gw}"
}

#
# Display any error returned by the Java client.
#
displayError()
{
    errorMsg="$1"

    if [ -f "${OUTPUT_DIR}/error" ]
    then
        remoteErr="$(cat "${OUTPUT_DIR}/error")"
        printf "${errorMsg}" "${remoteErr}"
        rm -f "${OUTPUT_DIR}/error"
        return
    fi

    printf "${errorMsg}" ""
}

#
# Function to run the server add-array implementation
#
server_add_array_impl()
{
    sgdurl="$1"
    array_name="$2"
    auth_type="$3"
    auth_data="$4"

    is_secure="$(echo "${sgdurl}" | grep "https://")"
    if [ -n "${is_secure}" ]
    then
        createKeystore "${sgdurl}"
    fi

    if [ -z "${auth_type}" ]
    then
        callDeploy "${is_secure}"
    else
        callDeploy "${is_secure}" "${auth_type}" "${auth_data}"
    fi
    addToGateway
    updateArrayFile "${array_name}"
}

