#!/bin/bash

###########################################################################
#
# MODULE:       Shared
# AUTHOR(S):    CacheGuard Development Team
# COPYRIGHT:    (C) 2009-2025 by CacheGuard Technologies Ltd (UK)
# COPYRIGHT:    (C) 2026-2026 by CacheGuard Technologies SAS (FR)
#
# 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, either version 3 of the License, or
# (at your option) any later version.
#
# 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/>.
#
###########################################################################

round-float() {

    if test -z "${1}" ; then
	echo 0
	return 1
    fi

    local float=${1}
    local decimals=${2}
    test -n "${decimals}" || decimals=0

    local rounded=$(bc -l <<< "a=${float}; if(a>0) a += 5/10 ^ (decimals+1) else if (a<0) a -= 5/10 ^(decimals+1); scale=decimals; a/1")

    printf '%.*f\n' "${decimals}" "${rounded}"
}

get-files-size()
{
    local files=${@}
    local file sz total=0

    for file in ${files}
    do
	sz=$(du -k ${file} | expand)
	sz=${sz/ *}
	((total += sz))
    done
    echo ${total}
}

get-cmdline-attribute()
{
    test -n "${1}" || return 1
    local attribute=${1}

    local assertion var val
    local command_line=$(cat /proc/cmdline 2> /dev/null)

    for assertion in ${command_line}
    do
	var=${assertion/=*}
	val=${assertion/*=}

	test "${var}" != ${attribute} || break
    done

    test "${var}" == "${val}" -o "${var}" != ${attribute} || echo ${val}
}

get-console()
{
    if test -n "${CLOUD_CONSOLE}" ; then
	echo ${CLOUD_CONSOLE}
	return 0
    fi

    get-cmdline-attribute console
}

is-console-user()
{
    local tty=$(tty 2> /dev/null)
    tty=${tty:5}

    test "${tty:0:3}" == tty -o "${tty:0:4}" == ttyS
}

is-serial-console()
{
    test -n "${1}" || return 1
    local tty=${1}

    test "${tty:0:4}" == ttyS
}

set-first-startup-action()
{
    local action=${1}

    echo ${action} > ${HARD_DIR}/${FIRST_STARTUP}

    test "${USER}" != ${ADMIN_NAME} || return 0

    chown ${ADMIN_NAME}:${GROUP_NAME} ${HARD_DIR}/${FIRST_STARTUP}
    chmod 644 ${HARD_DIR}/${FIRST_STARTUP}
}

get-first-startup-action()
{
    test "${USER}" == ${ADMIN_NAME} || return 11
    test -f ${HARD_DIR}/${FIRST_STARTUP} || return 13
    local action
    read action < ${HARD_DIR}/${FIRST_STARTUP}
    echo ${action}
}

member()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 1
    local list=${1}
    local elt=${2}

    local exp="^${elt}$|[ ]${elt}$|^${elt}[ ]|[ ]${elt}[ ]"
    [[ "${list}" =~ ${exp} ]]
}

uniq-elt()
{
    test -n "${1}" || return 0

    local list=${*}
    local elt uniq_list

    for elt in ${list}
    do
	! member "${uniq_list}" "${elt}" || continue
        uniq_list="${uniq_list} ${elt}"
    done

    echo ${uniq_list:1}
    return 0
}

mono-elt()
{
    test -n "${1}" || return 1
    test -z "${2}" || return 3
}

get-nth-arg()
{
    test -n "${1}" || return 1
    local n=${1}

    local i

    for ((i=0 ; i<n ; i++))
    do
	shift
    done

    echo ${1}
}

minus-list()
{
    test -n "${1}" || return 0
    local list1=${1}

    if test -z "${2}" ; then
	echo ${list1}
	return 0
    fi

    local list2=${2}

    local elt1 elt2
    local difference found

    for elt1 in ${list1}
    do
	found=1
 	for elt2 in ${list2}
	do
	    if test ${elt1} == ${elt2} ; then
		found=0
		break
	    fi
	done
	test ${found} -eq 0 || difference="${difference} ${elt1}"
    done

    echo ${difference:1}
}

get-file-length()
{
    if test -z "${1}" ; then
	echo 0
	return 1
    fi

    local file=${1}

    if test ! -f ${file} ; then
	echo 0
	return 1
    fi

    local ls_file=$(ls -l ${file})
    local len=$(get-nth-arg 5 ${ls_file})

    echo ${len}
}

get-fs-available-sz()
{
    if test -z "${1}" ; then
	echo 0
	return 1
    fi

    local fs=${1}

    if test ! -d ${fs} ; then
	echo 0
	return 11
    fi

    local sz=$(df --block-size=1 --output=avail ${fs} 2> /dev/null | tail -1 2> /dev/null)

    echo ${sz}
}

record-length-list()
{
    test -n "${1}" || return 1
    local record_len=${1}
    local list=${2}

    if test -z "${list}" ; then
	echo -n 0
	return 0
    fi

    list=${list//[^ ]}
    local len=${#list}
    len=$[(${len} + 1) / ${record_len}]

    echo -n ${len}
    return 0
}

unordered-list-equal()
{
    local list1=${1}
    local list2=${2}

    local elt

    for elt in ${list1}
    do
	member "${list2}" ${elt} || return 1
    done

    for elt in ${list2}
    do
	member "${list1}" ${elt} || return 3
    done

    return 0
}

file-basename()
{
    test -n "${1}" || return 1
    local name=${1}

    local base=${name##*/}
    if test -n "${2}" ; then
	base=${base/${2}/}
    fi
    echo ${base}
}

file-dirname()
{
    test -n "${1}" || return 1
    local name=${1}

    local len=${#name} ; ((len--))
    test ${name:${len}:1} != '/' || name=${name:0:${len}}
    
    local dir=${name%\/*}

    if test "${dir}" == "${name}" ; then
	echo .
    else
	echo ${dir}
    fi
}

get-first-line()
{
    test -n "${1}" || return 1
    local file=${1}
    test -f ${file} || return 2

    local line
    
    while read line
    do
	break
    done < ${file}

    echo -n "${line}"
}

private-key-is-unencrypted()
{
    test -n "${1}" || return 11
    local file=${1}
    test -f ${file} || return 13

    local encrypted_tag="-----BEGIN ENCRYPTED"
    local len=${#encrypted_tag}
    local head=$(get-first-line ${file})
    test "${head:0:${len}}" != "${encrypted_tag}"
}

colon2space()
{
    test -n "${1}" || return 0
    local text=${1}

    echo ${text//\:/\ }
}

space2colon()
{
    test -n "${1}" || return 0
    local text=${1}

    echo ${text//\ /\:}
}

underscore2space()
{
    test -n "${1}" || return 0
    local text=${1}

    echo ${text//_/\ }
}

space2underscore()
{
    test -n "${1}" || return 0
    local text=${1}

    echo ${text//\ /_}
}

check-expr-ip()
{
    [[ "${1}" =~ (^[1-9][0-9]{0,2}|^0)[.]([1-9][0-9]{0,2}|0)[.]([1-9][0-9]{0,2}|0)[.]([1-9][0-9]{0,2}|0)$ ]]
}

check-ip()
{
    test -n "${1}" || return 1

    check-expr-ip ${1} || return 3
    ipcalc -s ${1} 255.255.255.0 -n > /dev/null || return 5

    return 0
}

check-mask()
{
    test -n "${1}" || return 1

    check-expr-ip ${1} || return 3
    ipcalc -s 10.0.0.0 ${1} -n > /dev/null
}

check-digit()
{
    [[ "${1}" =~ ^[[:digit:]]+$ ]]
}

get-file-type()
{
    test -n "${1}" || return 1
    local file="${1}"

    test -f ${file} || return 11

    local ftype=$(/bin/file -i ${file})

    ftype=${ftype/*: /}
    ftype=${ftype/;*/}

    echo ${ftype}
}

check-ascii-file()
{
    test -n "${1}" || return 1
    local file="${1}"

    test -f ${file} || return 11
    test -s ${file} || return 0

    local ftype=$(get-file-type ${file})
    test "${ftype}" == "text/plain"
}

check-gzip-file()
{
    test -n "${1}" || return 1
    local file="${1}"

    test -f ${file} || return 11
    local ftype=$(get-file-type ${file})

    case ${ftype} in
	application/x-gzip|application/gzip)
	    return 0
	    ;;
	*)
	    return 13
	    ;;
    esac
}

check-xz-file()
{
    test -n "${1}" || return 1
    local file="${1}"

    test -f ${file} || return 11
    local ftype=$(get-file-type ${file})

    case ${ftype} in
	application/x-xz)
	    return 0
	    ;;
	*)
	    return 13
	    ;;
    esac
}

min-value()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local val1=${1}
    local val2=${2}

    echo $((val1 < val2 ? val1 : val2))
}

max-value()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local val1=${1}
    local val2=${2}

    echo $((val1 > val2 ? val1 : val2))
}

remove-dgst-prefix()
{
    test -n "${1}" || return 0
    local dgst=${1}

    dgst=${dgst/*(/}
    dgst=${dgst/*)= /}

    echo ${dgst}
}

digit-ip()
{
    test -n "${1}" || return 1
    local ip=${1}

    local b1 b2 b3 b4

    IFS='.' read -r b1 b2 b3 b4 <<< ${ip}

    printf "%d" $(((b1 * 256 ** 3) + (b2 * 256 **2) + (b3 * 256) + b4))
}

ip-is-included()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    test -n "${3}" || return 3
    test -n "${4}" || return 4
    local ip=${1}
    local net=${2}
    local brd=${3}
    local mk=${4}

    check-expr-ip ${ip}  || return 11
    check-expr-ip ${net} || return 13
    check-expr-ip ${brd} || return 15
    check-expr-ip ${mk}  || return 17

    local dip=$(digit-ip ${ip})
    local dnet=$(digit-ip ${net})
    local dbrd=$(digit-ip ${brd})

    test -n "${dip}" || dip=0
    test -n "${dnet}" || dnet=0
    test -n "${dbrd}" || dbrd=0

    if test ${mk} == '255.255.255.254' ; then
	test ${dip} -ge ${dnet} -a ${dip} -le ${dbrd}
    else
	test ${dip} -gt ${dnet} -a ${dip} -lt ${dbrd}
    fi
}

ip-belongs2-network()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    test -n "${3}" || return 3
    local ip=${1}
    local net=${2}
    local msk=${3}

    local dip=$(digit-ip ${ip})
    local dnet=$(digit-ip ${net})
    local dmsk=$(digit-ip ${msk})

    local result=$((dip & dmsk))

    test ${result} == ${dnet}
}

get-my-public-ip()
{
    local timeout=${1}
    local maxtime=${2}
    test -n "${timeout}" || timeout=7
    test -n "${maxtime}" || maxtime=15

    local ret ip

    ip=$(curl --disable --fail -4 --no-show-error --silent \
	      --location --connect-timeout ${timeout} --max-time ${maxtime} \
	      --cacert ${SSL_VAR_DIR}/ca-bundle.crt --capath ${RUNTIME_ENV_DIR}/${SSL_LOCAL_CA_RDIR} \
	      --url ${MY_PUBLIC_IP_URL} 2> /dev/null)
    ret=${?}

    test ${ret} -eq 0 || return ${ret}
    echo ${ip}
}

get-azure-first-standard-ip()
{
    local network_json retval ip
    local api_version='2023-07-01'

    network_json=$(curl --disable --fail -4 --no-show-error --silent \
			--connect-timeout ${timeout} --max-time ${max_time} \
			--header "Metadata:True" \
			--url "http://${CLOUD_METADATA_IP}/metadata/loadbalancer?api-version=${api_version}&format=json" 2> /dev/null)
    retval=${?}

    test ${retval} -eq 0 || return 11
    test -n "${network_json}" || return 13

    ip=$(php ${APPLIANCE_PHP_DIR}/get-azure-first-standard-public-ip.php "${network_json}" 2> /dev/null)
    retval=${?}

    test ${retval} -eq 0 || return 21
    test -n "${ip}" || return 23

    echo ${ip}
}

get-azure-first-basic-ip()
{
    local network_json retval ip
    local api_version='2023-07-01'

    network_json=$(curl --disable --fail -4 --no-show-error --silent \
			--connect-timeout ${timeout} --max-time ${max_time} \
			--header "Metadata:True" \
			--url "http://${CLOUD_METADATA_IP}/metadata/instance/network?api-version=${api_version}&format=json" 2> /dev/null)
    retval=${?}

    test ${retval} -eq 0 || return 11
    test -n "${network_json}" || return 13

    ip=$(php ${APPLIANCE_PHP_DIR}/get-azure-first-basic-public-ip.php "${network_json}" 2> /dev/null)
    retval=${?}

    test ${retval} -eq 0 || return 21
    test -n "${ip}" || return 23

    echo ${ip}
}

get-cloud-my-first-public-ip()
{
    local ip retval
    local timeout=7 max_time=15

    case ${CLOUD_NAME} in
	aws)
	    local token token_validity=600
	    token=$(curl --disable --fail -4 --no-show-error --silent \
			 --connect-timeout ${timeout} --max-time ${max_time} \
			 --request PUT \
			 --header "X-aws-ec2-metadata-token-ttl-seconds: ${token_validity}" \
			 --url http://${CLOUD_METADATA_IP}/latest/api/token 2> /dev/null)
	    retval=${?}

	    test ${retval} -eq 0 || return ${retval}
	    test -n "${token}" || return 201

	    ip=$(curl --disable --fail -4 --no-show-error --silent \
		      --connect-timeout ${timeout} --max-time ${max_time} \
		      --header "X-aws-ec2-metadata-token: ${token}" \
		      --url http://${CLOUD_METADATA_IP}/latest/meta-data/public-ipv4 2> /dev/null)

	    retval=${?}
	    ;;
	azure)
	    ip=$(get-azure-first-standard-ip)
	    retval=${?}

	    if test ${retval} -ne 0 -o -z "${ip}" ; then
		ip=$(get-azure-first-basic-ip)
		retval=${?}
	    fi
		
	    test ${retval} -eq 0 || return 211
	    ;;

	*)
	    return 221
	    ;;
    esac

    echo ${ip}
    return ${retval}

}

get-my-accessible-public-ip()
{
    local ip

    case ${CLOUD_NAME} in
	aws|azure)
	    ip=$(get-cloud-my-first-public-ip)
	    test -n "${ip}" || ip=$(get-my-public-ip)
	    ;;
	*)
	    ip=$(get-my-public-ip)
	    ;;
    esac

    echo ${ip}
}

is-valid-cloud-ip()
{
    local ip=${1}

    local ret

    if test -n "${ip}" ; then
	check-expr-ip ${ip}  || return 11
	ret=0
    else
	ip=$(get-my-public-ip)
	ret=${?}
    fi

    test ${ret} -eq 0 || return ${ret}
    test -n "${ip}" || return 13

    test -f ${CLOUD_NETWORK_IP_FILE} || return 15

    local net brd pfx msk

    while read net brd pfx msk
    do
	test -n "${msk}" || continue

	if test ${pfx} == 32 ; then
	    test ${net} == ${ip} || continue
	    return 0
	fi

	ip-belongs2-network ${ip} ${net} ${msk} || continue
	return 0

    done < ${CLOUD_NETWORK_IP_FILE}

    return 21
}

ip-is-included-in-valid-boradcast()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    test -n "${3}" || return 3
    test -n "${4}" || return 4
    local ip=${1}
    local net=${2}
    local brd=${3}
    local mk=${4}

    test ${net} != 0.0.0.0 -o ${brd} != 255.255.255.255 || return 11
    if ip-is-included ${ip} ${net} ${brd} ${mk} ; then
	echo -n "${net} ${brd}"
    else
	return 13
    fi
}

ip-is-included-in-aux-valid-boradcast()
{
    test -n "${BOND_AUXILIARIES}" || return 11
    ip-is-included-in-valid-boradcast "${@}"
}

encode-string()
{
    local printable=${1}

    echo -n ${printable} | openssl enc -A -base64 2> /dev/null
}

decode-string()
{
    local printable=${1}

    echo -n ${printable} | openssl enc -d -A -base64 2> /dev/null
}

binary-form()
{
    test -n "${1}" || return 1
    
    case "${1}" in
	True)
	    echo -n "1"
	    return 0
	    ;;
	False)
	    echo -n "0"
	    return 0
	    ;;
	*)
	    echo -n "E"
	    return 1
	    ;;
    esac
}

format-number()
{
    test -n "${1}" || return 0
    local nb=${1}

    local len=${#nb}
    local nb1 rang i j

    for ((i=0 ; i<len ; i++))
    do
	j=$[${len} - ${i} - 1]
	rang=$[${i} % 3]
	if test ${rang} -eq 2 ; then
	    nb1=" ${nb:${j}:1}${nb1}"
	else
	    nb1="${nb:${j}:1}${nb1}"
	fi
    done
    echo ${nb1}
}

get-record-field()
{
    test -n "${1}" || return 0
    test -n "${2}" || return 0
    local record=${1}
    local n=${2}

    test ${n} -gt 0 || return 0
    
    local i
    ((n--))

    for ((i=0 ; i<n ; i++))
    do
	record=${record#* }
    done
	
    record=${record/ *}

    echo -n ${record}
    return 0
}

get-modification-time()
{
    if test -z "${1}" ; then
	echo -n 0
	return 1
    else
	local file=${1}
    fi

    local record=$(ls -d --full-time --time=ctime --time-style=+%s ${file} 2> /dev/null)
    local date=$(get-record-field "${record}" 6)

    if test -z "${date}" ; then
	echo -n 0
	return 3
    fi
    
    echo -n ${date}
    return 0
}

file-older-in-second()
{
    test -n "${1}" || return 0
    test -n "${2}" || return 0
    local file1=${1}
    local file2=${2}

    test -f ${file1} || return 1
    test -f ${file2} || return 2

    local date1=$(get-modification-time ${file1})
    local date2=$(get-modification-time ${file2})

    test ${date2} -gt ${date1}
}

wait4-file()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    test -n "${3}" || return 3
    test -n "${4}" || return 4
    local file=${1}
    local ftype=${2}
    local retry=${3}
    local usleep=${4}
    local reverse=${5}

    local i=0

    while true
    do
	if test -z "${reverse}" ; then
	    test ! -${ftype} ${file} || break
	else
	    test -${ftype} ${file} || break
	fi
	test ${i} -lt ${retry} || break
	usleep ${usleep}
	((i++))
    done

    test ${i} -lt ${retry}
}

tee-echo()
{
    test -n "${1}" || return 1
    local tee_file=${1}
    local data=${2}

    test ${tee_file} == /dev/null || echo ${data} >> ${tee_file}
    echo ${data}
}

random-phrase()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2

    declare -a array=("${!1}")
    local len=${2}

    local len_array=${#array[@]}

    local pos phrase i=0
    while test ${i} -lt ${len}
    do
	pos=$[${RANDOM} % ${len_array}]
	phrase="${phrase}${array[${pos}]}"
	((i++))
    done

    echo -n ${phrase}
}

random-phrase-base16()
{
    local len=${1}
    test -n "${len}" || len=32

    local characters=(a b c d e f 0 1 2 3 4 5 6 7 8 9)

    random-phrase characters[@] ${len}
}

random-phrase-base62()
{
    local len=${1}
    test -n "${len}" || len=32

    local characters=(a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9)

    random-phrase characters[@] ${len}
}

random-psk()
{
    local len=40
    local i chunk=8 step=8
    local secret=$(random-phrase-base62 ${len}) psk

    for ((i=0 ; i<len ; i += chunk))
    do
	psk="${psk}-${secret:${i}:${chunk}}"
	((step += chunk))
    done
    psk="${psk:1}"

    echo -n "${psk}"
}

gen-random-nb()
{
    local array=(0 1 2 3 4 5 6 7 8 9)
    local len_array=${#array[*]}

    local i=0 pos len=7 nb
    while test ${i} -lt ${len}
    do
	pos=$[${RANDOM} % ${len_array}]
	nb="${nb}${array[${pos}]}"
	((i++))
    done

    echo ${nb}
}

getpids()
{
    local base=${1##*/}
    pidof -o ${$} -o ${PPID} -x ${base}
}

get-sz-from-http-header()
{
    if test -z "${1}" ; then echo -n 0 ; return 101 ; fi
    local url=${1}
    local connect_timeout=${2}
    local max_time=${3}
    local user_agent=${4}

    test -n "${connect_timeout}" || connect_timeout=3
    test -n "${max_time}" || max_time=5
    test -n "${user_agent}" || user_agent="Mozilla/5.0 (Windows Mobile 10; Android 10.0; Microsoft; Lumia 950XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Mobile Safari/537.36 Edge/40.15254.603"

    local tmp_file=/tmp/clamav.headers.${$}
    local header size ret

    curl --disable --fail -4 --silent --globoff --noproxy --location \
	 --connect-timeout ${connect_timeout} --max-time ${max_time} \
	 --user-agent "${user_agent}" \
	 --insecure --head --url ${url} -o ${tmp_file}
    ret=${?}

    if test ! -f ${tmp_file} -o ${ret} -ne 0 ; then
	echo 0
	return ${ret}
    fi

    while read header size
    do
	test -n "${size}" || continue
	test ${header^^} != "CONTENT-LENGTH:" || break
    done < ${tmp_file}
    rm -f ${tmp_file}

    if test "${header^^}" == "CONTENT-LENGTH:" ; then
	size=${size/%$'\r'/}
	if ! check-digit ${size} ; then
	    echo 0
	    return 103
	else
	    echo ${size}
	    return 0
	fi
    else
	echo 0
	return 105
    fi
}

get-protocol-server-path-from-url()
{
    test -n "${1}" || return 1
    local url=${1}

    local protocol=${url/:\/\/*}
    local len=${#protocol} ; ((len+=3))
    local path=${url:${len}}
    local server=${path/\/*}

    len=${#server}
    local first_path="${path:${len}:1}"
    if test -z "${first_path}" ; then
	path=''
    else
	path=${path#*\/}
    fi

    echo -n "${protocol} ${server} ${path}"
}

get-usage-level-gateway()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local users_nb=${1}
    local rusers_nb=${2}

    local users_level rusers_level

    if test ${users_nb} -le ${LIC_USERS_NB_0} ; then
	users_level=0
    elif test ${users_nb} -le ${LIC_USERS_NB_1} ; then
	users_level=1
    elif test ${users_nb} -le ${LIC_USERS_NB_2} ; then
	users_level=2
    else
	users_level=3
    fi

    if test ${rusers_nb} -le ${LIC_RUSERS_NB_0} ; then
	rusers_level=0
    elif test ${rusers_nb} -le ${LIC_RUSERS_NB_1} ; then
	rusers_level=1
    elif test ${rusers_nb} -le ${LIC_RUSERS_NB_2} ; then
	rusers_level=2
    else
	rusers_level=3
    fi

    local level=$(max-value ${users_level} ${rusers_level})

    echo ${level}
}

get-usage-level-manager()
{
    test -n "${1}" || return 1
    local gateway_nb=${1}

    local level

    if test ${gateway_nb} -le ${LIC_GATEWAYS_NB_0} ; then
	level=0
    elif test ${gateway_nb} -le ${LIC_GATEWAYS_NB_1} ; then
	level=1
    elif test ${gateway_nb} -le ${LIC_GATEWAYS_NB_2} ; then
	level=2
    else
	level=3
    fi

    echo ${level}
}

get-usage-level()
{
    local role=${1}
    test -n "${role}" || role=${APL_ROLE}

    case ${role} in
	gateway)
	    level=$(get-usage-level-gateway ${USERS_NB} ${RUSERS_NB})
	    ;;
	manager)
	    local manager_gateway_nb=${2}
	    test -n "${manager_gateway_nb}" || manager_gateway_nb=${MANAGER_GATEWAY_NB}
	    level=$(get-usage-level-manager ${manager_gateway_nb})
	    ;;
	*)
	    ;;
    esac

    test -n "${level}" || level=0

    echo ${level}
}

get-external-cloud-name()
{
    test -n "${1}" || return 1
    local name=${1}

    case ${name} in
	aws)
	    name=${name^^}
	    ;;
	azure)
	    name=${name^}
	    ;;
	*)
	    ;;
    esac

    echo ${name}
}

is-subscription-required()
{
    case ${CLOUD_NAME} in
	aws|azure)
	    case ${CLOUD_USAGE} in
		free|payg)
		    return 11
		    ;;
		byol)
		    return 0
		    ;;
		*)
		    ;;
	    esac
	    ;;
	*)
	    return 0
	    ;;
    esac
}

is-licensed()
{
    test ${OS_FREE_USAGE} == False || return 21

    case ${CLOUD_NAME} in
	aws|azure)
	    case ${CLOUD_USAGE} in
		free)
		    return 11
		    ;;
		payg)
		    if test "${CLOUD_VALID_IP}" == yes ; then
			return 13
		    else
			return 0
		    fi
		    ;;
		byol)
		    ;;
		*)
		    ;;
	    esac
	    ;;
	*)
	    ;;
    esac

    local level=$(get-usage-level ${@})
    test ${level} -ne 0
}

is-embedded-activable()
{
    test -n "${1}" || return 1
    local embedded=${1}

    case ${CLOUD_NAME} in
	aws|azure)
	    case ${CLOUD_USAGE} in
		payg)
		    if test "${CLOUD_VALID_IP}" == no ; then
			return 11
		    else
			if member "${CLOUD_EMBEDDED_APPLICATIONS}" ${embedded} ; then
			    return 0
			else
			    return 13
			fi
		    fi
		    ;;
		byol)
		    return 0
		    ;;
		*)
		    return 0
		    ;;
	    esac
	    ;;
	*)
	    return 0
	    ;;
    esac
}

is-embedded-license-required()
{
    test -n "${1}" || return 1
    local embedded=${1}

    case ${CLOUD_NAME} in
	aws|azure)
	    case ${CLOUD_USAGE} in
		payg)
		    if test "${CLOUD_VALID_IP}" == no ; then
			return 0
		    else
			if member "${CLOUD_EMBEDDED_APPLICATIONS}" ${embedded} ; then
			    return 11
			else
			    return 0
			fi
		    fi
		    ;;
		byol)
		    return 0
		    ;;
		*)
		    return 0
		    ;;
	    esac
	    ;;
	*)
	    return 0
	    ;;
    esac
}

is-embedded-in-cloud()
{
    test -n "${1}" || return 1
    local embedded=${1}

    case ${CLOUD_NAME} in
	aws|azure)
	    case ${CLOUD_USAGE} in
		payg)
		    if test "${CLOUD_VALID_IP}" == no ; then
			return 11
		    else
			if member "${CLOUD_EMBEDDED_APPLICATIONS}" ${embedded} ; then
			    return 0
			else
			    return 13
			fi
		    fi
		    ;;
		byol)
		    return 15
		    ;;
		*)
		    return 17
		    ;;
	    esac
	    ;;
	*)
	    return 19
	    ;;
    esac
}

gen-testing-sn-user()
{
    local level=$(get-usage-level)
    local date=$(date +%y%m%d)
    local nb=$(gen-random-nb)

    echo "_${SN_PREFIX}-L${level}_${date}T-${nb}"
}

get-date-from-epoch-seconds()
{
    test -n "${1}" || return 11
    local seconds=${1}

    local date ret

    date=$(date --date="1970-01-01 00:00:00 UTC +${seconds} seconds" +"%Y/%m/%d-%T" 2> /dev/null)
    ret=${?}

    echo ${date}
    return ${ret}
}

get-date-from-epoch-days()
{
    test -n "${1}" || return 11
    local days=${1}

    local date ret

    date=$(date --date="1970-01-01 00:00:00 UTC +${days} days" +"%Y/%m/%d" 2> /dev/null)
    ret=${?}

    echo ${date}
    return ${ret}
}

get-disk-sector-sz()
{
    test -n "${1}" || return 1
    local hd_name=${1}

    sector_sz=$(parted /dev/${hd_name} --align optimal --script -- unit B print quit 2> /dev/null | head -3 | tail -1)
    sector_sz=${sector_sz/*:}
    sector_sz=${sector_sz/B\/*}

    echo ${sector_sz}
}

check-tls-cert-validity()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    test -n "${3}" || return 3
    local tls=${1}
    local current_epoch=${2}
    local cert_file=${3}
    test -f ${cert_file} || return 11

    local end_date end_epoch state
    end_date=$(openssl x509 -in ${cert_file} -noout -nameopt multiline -enddate 2> /dev/null)
    end_date=${end_date/notAfter=}
    end_epoch=$(date +"%s" --date="${end_date}" 2> /dev/null)

    if test ${current_epoch} -le ${end_epoch} ; then
	state=OK
    else
	state=K0
    fi
    
    echo ${tls} ${end_epoch} ${state}
}

get-ocsp-process()
{
    local process=$(ps -ef | grep "/usr/bin/openssl ocsp" | grep --invert-match grep 2> /dev/null)
    echo ${process}
}

get-ike-process()
{
    local process=$(ps -ef | grep "${LOCAL_DIR}/libexec/ipsec/charon" | grep --invert-match grep 2> /dev/null)
    echo ${process}
}

get-rlogger-process()
{
    local process=$(ps -ef | grep "${LOCAL_DIR}/bin/apl_rloggerd" | grep --invert-match grep 2> /dev/null)
    echo ${process}
}

get-smanager-process()
{
    local process=$(ps -ef | grep "${LOCAL_DIR}/bin/apl_smanagerd" | grep --invert-match grep 2> /dev/null)
    echo ${process}
}

is-first-login()
{
    test -n "${1}" || return 1
    local user=${1}

    if test ${user} == ${ADMIN_NAME} ; then
	local machine=$(get-machine-type)
	test -f ${BASE_DIR}/${user}/${FIRST_LOGIN}
    else
	test -f ${BASE_DIR}/${user}/${FIRST_LOGIN}
    fi
}

allow-permissive-ssh-login()
{
    test -n "${1}" || return 1
    local conf_file=${1}

    sed -i \
	-e 's/AllowUsers .*/& root/' \
	-e 's/AllowGroups .*/& root/' \
	-e 's/PermitRootLogin .*/PermitRootLogin yes/' \
	-e 's/PermitEmptyPasswords .*/PermitEmptyPasswords yes/' \
	${conf_file}
}

get-memory-sz()
{
    local memory_sz=$(grep MemTotal: /proc/meminfo 2> /dev/null)
    memory_sz=${memory_sz// /}
    memory_sz=${memory_sz/MemTotal:/}
    memory_sz=${memory_sz/kB/}

    test -z "${MAX_MEMORY_SZ}" || test ${memory_sz} -le ${MAX_MEMORY_SZ} || memory_sz=${MAX_MEMORY_SZ}

    echo ${memory_sz}
}

get-cpu-nb()
{
    if test ! -f /proc/cpuinfo ; then
	echo 1
	return 11
    fi

    local cpu rest i=0

    while read cpu rest
    do
	test "${cpu}" != processor || ((i++))
    done < /proc/cpuinfo

    echo ${i}
    return 0
}

get-max-working-cpu()
{
    local cpu_nb=$(get-cpu-nb)

    ((cpu_nb /= 2))
    test ${cpu_nb} -ge 1 || cpu_nb=1

    echo ${cpu_nb}
}

get-variable-value-from-file()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local file=${1}
    local in_variable=${2}

    test -s ${file} || return 11

    local arg1 arg2 assertion
    local variable value len

    while read arg1 arg2
    do
	test -n "${arg1}" || continue
	test ${arg1:0:1} != '#' || continue

	if test ${arg1} == export ; then
	    test -n "${arg2}" || continue
	    assertion=${arg2}
	else
	    assertion=${arg1}
	fi

	variable=${assertion/=*}

	test ${variable} == ${in_variable} || continue

	value=${assertion#*=}

	if test ${value:0:1} == "'" -o ${value:0:1} == '"' ; then
	    value=${value:1}
	    len=${#value}
	    ((len--))
	    value=${value:0:${len}}
	fi

	echo ${value}
	return 0

    done < ${file}

    return 13
}

protect-os()
{
    test "${APL_MODEL}" != 'test' || return 0

    test -n "${1}" || return 1
    test -n "${2}" || return 2
    test -n "${3}" || return 3
    local arch=${1}
    local dir=${2}
    local operation=${3}

    local cur_dir=${PWD} options

    cd ${dir}

    case ${operation} in
	lock)
	    options="+i"
	    ;;
	unlock)
	    options="-i"
	    ;;
	*)
	    return 3
	    ;;
    esac

    chattr -f -R ${options} \
	bin sbin lib boot \
	etc/rc.d/init.d \
	usr/{bin,sbin,lib,share} \
	${LOCAL_DIR:1}/{bin,sbin,lib} \
	${PROXY_DIR:1}/{bin,lib,libexec,sbin,share,usr/lib,usr/local/lib} \
	${WEB_SERVER_DIR:1}/www/{apl.css,favicon.ico} \
	${WEB_SERVER_DIR:1}/{bin,lib,sbin,share,usr/lib,${WAUDIT_NAME},${STANDBY_NAME}} \
	${APPLIANCE_DIR:1} \
	${ADMIN_DIR:1}/{lib,usr} \
	${GUI_DIR:1}/{share,www,etc/html,etc/menu.env,etc/pages-hierarchy}

    chattr -f ${options} \
	   ${WEB_SERVER_DIR:1} \
	   ${WEB_SERVER_DIR:1}/${EMBEDDED_APPLICATIONS_NAME} \
	   ${WEB_SERVER_DIR:1}/${EMBEDDED_APPLICATIONS_NAME}/${EMBEDDED_VPNSUBSCR_NAME} \
	   ${WEB_SERVER_DIR:1}/${EMBEDDED_APPLICATIONS_NAME}/${EMBEDDED_VPNSUBSCR_NAME}/{*.php,*.html} \
	   ${PROXY_DIR:1}

    chattr -f -R ${options} \
	   ${WEB_SERVER_DIR:1}/${EMBEDDED_APPLICATIONS_NAME}/${EMBEDDED_VPNSUBSCR_NAME}{${IMAGE_DIR},${JS_DIR}}

    test ${arch} != 'x86_64' || \
	chattr -f -R ${options} \
	       lib64 usr/lib64 \
	       ${PROXY_DIR:1}/{lib64,usr/lib64} \
	       ${ADMIN_DIR:1}/{lib64,usr/lib64}

    chattr -f -i \
	   ${ADMIN_DIR:1}${LDAP_DIR} \
	   ${ADMIN_DIR:1}${LOCAL_DIR}/var \
	   ${WEB_SERVER_DIR:1}/share/GeoLiteCountry.dat

    cd ${cur_dir}
    return 0
}

lock-os()
{
    protect-os ${@} lock
}

unlock-os()
{
    protect-os ${@} unlock
}

get-service-title()
{
    test -n "${1}" || return 1
    local service=${1}

    local title

    case ${service} in
	slapd)
	    title="LDAP Proxy"
	    ;;
	squid)
	    title="Proxy Server"
	    ;;
	httpd)
	    title="Web Server"
	    ;;
	htcacheclean)
	    title="rCache Cleaner"
	    ;;
	named)
	    title="Name Server"
	    ;;
	dhcpd)
	    title="DHCP Server"
	    ;;
	dyndns)
	    title="Dynamic DNS"
	    ;;	    
	ntpd)
	    title="NTP Server"
	    ;;
	freshclam)
	    title="AV Updater"
	    ;;
	clamd)
	    title="Antivirus"
	    ;;
	c-icap)
	    title="ICAP server"
	    ;;
	snmpd)
            title="SNMP Agent"
            ;;
	keepalived)
	    title="High Availability"
	    ;;
	sshd)
	    title="SSH server"
	    ;;
	${WADMIND_NAME})
	    title="Web GUI server"
	    ;;
	iked)
	    title="IPsec server"
	    ;;
	ocspd)
	    title="OCSP server"
	    ;;
	smanager)
	    title="Manager HA"
	    ;;
	*)
	    ;;
    esac

    echo ${title}
}

get-manager-sync-title()
{
    test -n "${1}" || return 1
    local sync=${1}

    local title

    case ${sync} in
	repository)
	    title="the Repository data"
	    ;;
	logs)
	    title="Manager Operation Reports"
	    ;;
	urllists)
	    title="URL lists Push & Update status"
	    ;;
	avstatus)
	    title="Antivirus extended Push & Update status"
	    ;;
	avsignatures)
	    title="Antivirus extended signatures DB"
	    ;;
	*)
	    ;;
    esac

    echo ${title}
}

get-min-nic()
{
    local min ret

    case ${APL_ROLE} in
	gateway)
	    min=2
	    ret=0
	    ;;
	manager)
	    min=1
	    ret=0
	    ;;
	*)
	    min=1
	    ret=11
	    ;;
    esac

    echo ${min}
    return ${ret}
}

is-efi-system()
{
    test -d /sys/firmware/efi/efivars
}

get-partition-prefix()
{
    test -n "${1}" || return 1
    local hd=${1}
    test ${hd:0:5} == "/dev/" || hd="/dev/${hd}"

    local pp

    case ${hd:5:2} in
	nv)
	    pp=p
	    ;;
	*)
	    ;;
    esac

    echo ${pp}
}

get-other-kernel-release()
{
    test -n "${1}" || return 1
    local the_release=${1}

    cd /boot

    local releases=$(ls -1 kernel-* 2> /dev/null)
    local release other_release

    for release in ${releases}
    do
	release=${release/kernel-}
	test ${release} != ${the_release} || continue
	other_release=${release}
	break
    done

    echo ${other_release}
}

get-inverted-list()
{
    test -n "${1}" || return 0
    local list=${1}

    local inverted_list elt

    for elt in ${list}
    do
	inverted_list="${elt} ${inverted_list}"
    done

    len=${#inverted_list}
    ((len--))
    echo ${inverted_list:0:${len}}
}

get-vpnipsec-profile-extension()
{
    test -n "${1}" || return 0
    local client_type=${1}

    local profile_extension

    case ${client_type} in
	android)
	    profile_extension='sswan'
	    ;;
	apple)
	    profile_extension='mobileconfig'
	    ;;
	linux)
	    profile_extension='bash'
	    ;;
	windows)
	    profile_extension='ps1'
	    ;;
	*)
	    return 11
	    ;;
    esac

    echo ${profile_extension}
}

update-sn-passwd-shadow()
{
    local tmp_passwd=/tmp/passwd.${$}
    local tmp_shadow=/tmp/shadow.${$}

    touch ${tmp_shadow} ${tmp_passwd}
    chmod 400 ${tmp_passwd} ${tmp_shadow}

    grep --invert-match _${SN_PREFIX}- ${ROOT_DIR}/etc/passwd > ${tmp_passwd}
    grep --invert-match _${SN_PREFIX}- ${ROOT_DIR}/etc/shadow > ${tmp_shadow}

    case ${CLOUD_NAME} in
	aws)
	    ;;
	*)
	    local old_sn_user_line=$(cat ${ROOT_DIR}/etc/passwd 2>/dev/null | grep _${SN_PREFIX}- | head -1)
	    local old_sn_pass_line=$(cat ${ROOT_DIR}/etc/shadow 2>/dev/null | grep _${SN_PREFIX}- | head -1)

	    local old_sn_user=${old_sn_user_line/:*}
	    local new_sn_user=$(cat ${APL}${HARD_DIR}/${SN_ID})
	    local len=${#old_sn_user}

	    local new_sn_user_line="${new_sn_user}${old_sn_user_line:${len}}"
	    local new_sn_pass_line="${new_sn_user}${old_sn_pass_line:${len}}"

	    echo ${new_sn_user_line} >> ${tmp_passwd}
	    echo ${new_sn_pass_line} >> ${tmp_shadow}
	    ;;
    esac

    cp -f ${tmp_passwd} ${ROOT_DIR}/etc/passwd
    cp -f ${tmp_shadow} ${ROOT_DIR}/etc/shadow

    rm -f ${tmp_passwd} ${tmp_shadow}
}

gen-sn-password()
{
    local sn_user=$(cat ${APL}${HARD_DIR}/${SN_ID})
    local year=$(date +"%Y" 2> /dev/null)

    echo "${sn_user}:${TECHNICAL_NAME}-${year}-${USERS_NB}"
}

gen-crontab()
{
    local hour

    case ${APL_ROLE} in
	gateway)
	    hour=4
	    ;;
	manager)
	    hour=3
	    ;;
	*)
	    hour=4
	    ;;
    esac

    local minutes=$[${RANDOM} % 121]

    hour=$[${hour} + ${minutes} / 60]

    local mn=$[${minutes} % 60]
    local day=$[(${minutes} % 5) + 6]
 
    echo "SHELL=/bin/bash"
    echo "PATH=/sbin:/bin:/usr/sbin:/usr/bin"
    echo "MAILTO=root"
    echo "HOME=/"
    echo
    echo "# run-parts"
    echo "1 * * * * root run-parts /etc/cron.hourly"
    echo "${mn} ${hour} * * * root run-parts /etc/cron.daily"
    echo "${mn} $[1 + ${hour}] * * 0 root run-parts /etc/cron.weekly"
    echo "${mn} $[2 + ${hour}] ${day} * * root run-parts /etc/cron.monthly"
}

gen-av-extended-cron()
{
    case ${APL_ROLE} in
	gateway)
	    local minutes=$[(RANDOM % 59) + 1]
	    ;;
	manager)
	    local minutes='*/30'
	    ;;
	*)
	    ;;
    esac

    echo "# ${minutes} * * * * root [ -x ${LOCAL_DIR}/bin/apl_av_extended_update ] && ${LOCAL_DIR}/bin/apl_av_extended_update cur update index"
}
