#!/bin/bash /etc/ikcommon

Include lock.sh, check.sh, interface.sh, iproute.sh errcode.sh
Include convert_ipmask.sh

OPENCONNECT_STAT_DIR="/tmp/iktmp/openconnect/server"
OPENCONNECT_CONF="/var/etc/ocserv.conf"
OPENCONNECT_PID="$OPENCONNECT_STAT_DIR/ocserv.pid"
OPENCONNECT_LOG="$OPENCONNECT_STAT_DIR/ocserv.log"
OPENCONNECT_VIRTUAL_IFACE="socvpn"
OPENCONNECT_PARAMETERS="enabled tcp_port udp_port dtls subnet mask dns max_clients max_same_clients cisco_compat auth ca cert key extra_config"

sql_quote()
{
    printf '%s' "$1" | sed "s/'/''/g"
}

register_module_urls()
{
    echo "url=vpn/openconnect/services name=openconnect-server get=data,status post= put=edit delete= post_require_body=1"
}

boot()
{
    mkdir -p "$OPENCONNECT_STAT_DIR"
    init
}

init()
{
    local res=$(sql_config_get_list "$IK_DB_CONFIG" "select * from openconnect_server limit 1")
    if [ -z "$res" ]; then
        __reset_config
        return
    fi
    local $res
    if [ "$enabled" = "yes" ]; then
        __start
    fi
}

__reset_config()
{
    sqlite3 "$IK_DB_CONFIG" "delete from openconnect_server"
    local enabled=no tcp_port=443 udp_port=443 dtls=no subnet=192.168.90.0
    local mask=255.255.255.0 dns="" max_clients=100 max_same_clients=2
    local cisco_compat=yes auth=plain ca="" cert="" key="" extra_config=""
    local names="" values="" var value escaped
    for var in $OPENCONNECT_PARAMETERS; do
        value="${!var}"
        escaped=$(sql_quote "$value")
        names="$names$var,"
        values="$values'$escaped',"
    done
    names=${names%,}
    values=${values%,}
    sqlite3 "$IK_DB_CONFIG" "insert into openconnect_server ($names) values ($values)"
}

__check_param()
{
    check_varl \
        'enabled == "yes" or == "no"' \
        'tcp_port > 0 and <= 65535' \
        'udp_port > 0 and <= 65535' \
        'dtls == "yes" or == "no"' \
        'subnet ip' \
        'mask ip' \
        'max_clients >= 1 and <= 10000' \
        'max_same_clients >= 1 and <= 100' \
        'cisco_compat == "yes" or == "no"' \
        'auth == "plain"'
}

__check_port()
{
    local proto="$1" port="$2" p
    if [ "$proto" = tcp ]; then
        p=t
    else
        p=u
    fi
    for p in $(netstat -ln${p} 2>/dev/null | awk -F: 'NR > 1 {print $NF}' | awk '{print $1}'); do
        [ "$p" = "$port" ] && return 0
    done
    return 1
}

__write_config()
{
    mkdir -p "$OPENCONNECT_STAT_DIR"
    local udp_prefix="#"
    [ "$dtls" = yes ] && udp_prefix=""
    sed \
        -e "s#|AUTH|#plain#" \
        -e "s#|DYNDNS|#false#" \
        -e "s#|MAX_CLIENTS|#$max_clients#" \
        -e "s#|MAX_SAME|#$max_same_clients#" \
        -e "s#|PORT|#$tcp_port#" \
        -e "s#|UDP|#$udp_prefix#" \
        -e "s#|UDP_PORT|#$udp_port#" \
        -e "s#|DPD|#180#" \
        -e "s#|PREDICTABLE_IPS|#true#" \
        -e "s#|IPV4ADDR|#$subnet#" \
        -e "s#|NETMASK|#$mask#" \
        -e "s#|PING_LEASES|#yes#" \
        -e "s#|CISCO_COMPAT|#$([ "$cisco_compat" = yes ] && printf true || printf false)#" \
        /etc/ocserv/ocserv.conf.template > "$OPENCONNECT_CONF"
    sed -i '/^auth =/a password-file = /var/etc/ocserv.passwd' "$OPENCONNECT_CONF"
    for dns_ip in ${dns//,/ }; do
        [ -n "$dns_ip" ] && echo "dns = $dns_ip" >> "$OPENCONNECT_CONF"
    done
    [ -n "$extra_config" ] && printf '%s\n' "$extra_config" >> "$OPENCONNECT_CONF"
}

__stop()
{
    local pid=""
    [ -f "$OPENCONNECT_PID" ] && pid=$(cat "$OPENCONNECT_PID")
    if [ -n "$pid" ] && [ -d "/proc/$pid" ]; then
        kill "$pid" 2>/dev/null || true
        sleep 1
        kill -9 "$pid" 2>/dev/null || true
    fi
    killall ocserv 2>/dev/null || true
    rm -f "$OPENCONNECT_PID" "$OPENCONNECT_CONF" "$OPENCONNECT_LOG"
}

__start()
{
    __stop
    __write_config
    [ -r /etc/ocserv/server-cert.pem ] || { echo "OpenConnect server certificate is missing"; return 1; }
    [ -r /etc/ocserv/server-key.pem ] || { echo "OpenConnect server key is missing"; return 1; }
    [ -r /var/etc/ocserv.passwd ] || { echo "OpenConnect password file is missing"; return 1; }
    /usr/sbin/ocserv -f -c "$OPENCONNECT_CONF" 2>"$OPENCONNECT_LOG" &
    echo $! > "$OPENCONNECT_PID"
    return 0
}

edit()
{
    __check_param || { outputErrorLog "errcode_param_error" "$errmsg"; return 1; }
    local res=$(sql_config_get_list "$IK_DB_CONFIG" "select * from openconnect_server limit 1")
    local $res
    if __check_port tcp "$tcp_port"; then
        outputErrorLog "errcode_param_error" "TCP port is already in use"
        return 1
    fi
    if [ "$dtls" = yes ] && __check_port udp "$udp_port"; then
        outputErrorLog "errcode_param_error" "UDP port is already in use"
        return 1
    fi
    local set_clause="" var value escaped
    for var in $OPENCONNECT_PARAMETERS; do
        value=$(sql_quote "${!var}")
        set_clause="$set_clause$var='$value',"
    done
    set_clause=${set_clause%,}
    sqlite3 "$IK_DB_CONFIG" "update openconnect_server set $set_clause"
    if [ "$enabled" = yes ]; then __start; else __stop; fi
}

show()
{
    local res=$(sql_config_get_list "$IK_DB_CONFIG" "select * from openconnect_server limit 1")
    [ -n "$res" ] || __reset_config
    Show __json_result__
}

__show_data()
{
    local data=$(sql_config_get_json "$IK_DB_CONFIG" "select * from openconnect_server")
    json_append __json_result__ data:json
}

__show_status()
{
    local status=0
    [ -f "$OPENCONNECT_PID" ] && kill -0 "$(cat "$OPENCONNECT_PID")" 2>/dev/null && status=1
    json_append __json_result__ status:int
}

reset_default()
{
    __stop
    __reset_config
}