#!/bin/sh

alterator_api_version=1

##
# $ALTERATOR_DESTDIR - exported by installer.
#

prefer_scrambled='#t'
CA_SRC_DIR="/usr/share/ca-gost-certificates"
cacertdir="/etc/security/pam_pkcs11/cacerts"
CRL_SRC_DIR="/usr/share/ca-gost-certificates"
crldir="/etc/security/pam_pkcs11/crls"

DESTDIR="/"
CHROOT_EXE=
_control=control
if [ -d "${ALTERATOR_DESTDIR:-}" ]; then
    DESTDIR="$ALTERATOR_DESTDIR"
    CHROOT_EXE="chroot $DESTDIR"
    _control="$CHROOT_EXE $_control"
fi

#turn off auto expansion
set -f

. alterator-sh-functions
. alterator-service-functions

read_profile() {
    $_control pam-pkcs11-profile
}

set_profile() {
    if [ -n "$1" ]; then
         if ! $_control pam-pkcs11-profile "$1"; then
             write_error "$(_ "Unable to set PKCS11 profile")"
             return 1
         fi
    fi
}

is_sysauth_compatible() {
    local sysauth="${1:-$($_control system-auth)}"
    case "$sysauth" in
        # List compatible auth. methods below
        pkcs11_strict)
            return 0
            ;;
        *)
            return 1
            ;;
    esac
}

is_eventmgr_enabled() {
    local eventmgr="$($_control pkcs11-events)"
    case "$eventmgr" in
        card_actions)
            if [ -z "$CHROOT_EXE" ] && ! is_chrooted; then
            # If not in the installer mode
                if service_control 'pkcs11-eventmgr' is-active; then
                    return 0
                else
                    return 1
                fi
            else                
                return 0
            fi
            ;;
        *)
            return 1
            ;;
    esac
}

enable_eventmgr() {
    local ret=0
    
    $_control pkcs11-events card_actions || ret=$?
    
    if [ $ret -eq 0 ]; then
        if [ -z "$CHROOT_EXE" ] && ! is_chrooted; then
        # If not in the installer mode
            service_start_wait 'pkcs11-eventmgr' restart || ret=$?
        fi
    fi

    if [ $ret -ne 0 ]; then
        write_error "$(_ "Unable to turn the PKCS11 event manager on")"
    fi

    return $ret
}

disable_eventmgr() {
    local ret=0
    
    $_control pkcs11-events disabled || ret=$?
    
    if [ $ret -eq 0 ]; then
        if [ -z "$CHROOT_EXE" ] && ! is_chrooted; then
        # If not in the installer mode
            service_control 'pkcs11-eventmgr' stop || ret=$?
        fi
    fi

    if [ $ret -ne 0 ]; then
        write_error "$(_ "Unable to turn the PKCS11 event manager off")"
    fi

    return $ret
}

have_gost_ca() {
	$CHROOT_EXE test -d "$CA_SRC_DIR"
}

trust_gost_ca() {
    local ret=0
    find "${DESTDIR%/}$cacertdir" -lname "$CA_SRC_DIR/*" -delete || ret=$?

    if [ $ret -eq 0 -a -e "${DESTDIR%/}$CA_SRC_DIR" ]; then
        $CHROOT_EXE find "$CA_SRC_DIR" -name '[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].[0-9]' \
                    -exec ln -sf '{}' "$cacertdir/" \; || ret=$?
    fi

    if [ $ret -eq 0 ]; then
        find "${DESTDIR%/}$crldir" -lname "$CRL_SRC_DIR/*" -delete || ret=$?
    fi

    if [ $ret -eq 0 -a -e "${DESTDIR%/}$CRL_SRC_DIR" ]; then
        $CHROOT_EXE find "$CRL_SRC_DIR" -name '[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].r[0-9]' \
                         -exec ln -sf '{}' "$crldir/" \; || ret=$?
    fi

    [ $ret -eq 0 ] || \
        write_error "$(_ "Error while installing GOST certificates")"

    return $ret
}

is_pcscd_enabled() {
    service_control 'pcscd' is-enabled
}

is_enabled() {
    local profile="$(read_profile)"
    case "$profile" in
        custom)
            return 1
            ;;
        '')
            return 1
            ;;
    esac

    is_sysauth_compatible && is_pcscd_enabled && is_eventmgr_enabled
}

is_profile_compatible() {
    local profile="${1:-$(read_profile)}"
    case "$profile" in
        custom|'')
            return 1
            ;;
        *)
            return 0
            ;;
    esac
}

default_profile() {
    $_control pam-pkcs11-profile help | (
        IFS="$IFS:"
        while read key value; do
            if is_profile_compatible "$key"; then
                echo "$key"
                break
            fi
        done
    )
}

enable_pcscd() {
    local ret=0
    service_control 'pcscd' enable :prefer-socket: || ret=$?

    if [ $ret -eq 0 ]; then
        if [ -z "$CHROOT_EXE" ] && ! is_chrooted; then
        # If not in the installer mode
            service_control 'pcscd' restart :prefer-socket: || ret=$?
        fi
    fi

    if [ $ret -ne 0 ]; then
        write_error "$(_ "Unable to turn the PC/SC slot daemon on")"
    fi

    return $ret
}

disable_pcscd() {
    local ret=0
    service_control 'pcscd' disable || ret=$?
    
    if [ $ret -eq 0 ]; then
        if [ -z "$CHROOT_EXE" ] && ! is_chrooted; then
        # If not in the installer mode
            service_control 'pcscd' stop || ret=$?
        fi
    fi

    if [ $ret -ne 0 ]; then
        write_error "$(_ "Unable to turn the PC/SC slot daemon off")"
    fi

    return $ret
}

enable() {
    local profile="$(read_profile)"
    
    if ! is_profile_compatible "$profile"; then
        profile="$(default_profile)"
        if ! is_profile_compatible "$profile"; then
            write_error "$(_ "No compatible profile available")"
            return 1
        fi
        set_profile "$profile" || return 1
    fi

    if ! is_sysauth_compatible; then
        DEFAULT_SYSAUTH="$($_control system-auth)"
        if ! $_control system-auth pkcs11_strict; then
            write_error "$(_ "Unable to switch the system authentication mechanism to PKCS11")"
            return 1
        fi
    fi

    enable_pcscd && enable_eventmgr
}

default_sysauth() {
    echo "${DEFAULT_SYSAUTH:-local}"
}

disable() {
    if ! $_control system-auth "$(default_sysauth)"; then
        write_error "$(_ "Unable to switch the system authentication mechanism to default")"
        return 1
    fi
    
    disable_eventmgr && disable_pcscd
}

list_profiles() {
    $_control pam-pkcs11-profile help | (
        IFS="$IFS:"
        while read key value; do
            # Try to translate the profile description
            write_enum_item "$key" "$(_ "$value")"
        done
    )
}

read_mapping() {
    $_control pam-pkcs11-mapping
}

is_mapping_compatible() {
    local mapping="${1:-$(read_mapping)}"
    case "$mapping" in
        custom|'')
            return 1
            ;;
        # List compatible mapping profiles below
        snils*)
            if test_bool "$prefer_scrambled"; then
                case "$mapping" in
                    *_scrambled)
                        return 0
                        ;;
                    *)
                        return 1
                        ;;
                esac
            else
                return 0
            fi
            ;;
    esac

    return 1
}

default_mapping() {
    $_control pam-pkcs11-mapping help | (
        IFS="$IFS:"
        while read key value; do
            if is_mapping_compatible "$key"; then
                echo "$key"
                break
            fi
        done
    )
}

is_mkuser_enabled() {
    local mkuser="$($_control pam_mkuser)"
    case "$mkuser" in
        enabled)
            return 0
            ;;
        *)
            return 1
            ;;
    esac
}

enable_mkuser() {
    $_control pam_mkuser enabled || \
        write_error "$(_ "Unable to turn auto user add on")"
}

disable_mkuser() {
    $_control pam_mkuser disabled || \
        write_error "$(_ "Unable to turn auto user add off")"
}

facility_exists() {
    [ -e "/etc/control.d/facilities/$1" ]
}
is_desktop() {
    for dtop in "$@"; do
        if [ "$(cat /etc/sysconfig/desktop 2>/dev/null)" = "$dtop" ]; then
            return 0
        fi
    done
    return 1
}

control_login_unknown() {
    if facility_exists 'sddm-login-unknown' && is_desktop 'KDE' ''; then
        $_control sddm-login-unknown "$@"
    elif facility_exists 'lightdm-login-unknown'; then
        $_control lightdm-login-unknown "$@"
    else
        return 1
    fi
}

is_autoadd_enabled() {
    local loginunk="$(control_login_unknown)"
    case "$loginunk" in
        enabled)
            is_mapping_compatible && is_mkuser_enabled
            return $?
            ;;
    esac

    return 1
}

set_mapping() {
    [ -n "$1" ] && \
    $_control pam-pkcs11-mapping "$1" || \
        write_error "$(_ "Unable to switch the PKCS11 mapping profile")"
}

enable_autoadd() {
    local mapping="$(read_mapping)"

    if ! is_mapping_compatible "$mapping"; then
        mapping="$(default_mapping)"
        if ! is_mapping_compatible "$mapping"; then
            write_error "$(_ "No compatible mapping profile available")"
            return 1
        fi
        set_mapping "$mapping"
    fi

    control_login_unknown 'enabled' || \
        write_error "$(_ "Unable to turn on the unknown user login mode")"
    
    enable_mkuser
}

disable_autoadd() {
    control_login_unknown 'disabled' || \
        write_error "$(_ "Unable to turn off the unknown user login mode")"
    disable_mkuser
}

on_message() {
    case "$in_action" in
        type)
            write_type_item 'enabled' 'boolean'
            write_type_item 'profile' 'keyword'
            write_type_item 'autoadd' 'boolean'
            write_type_item 'gost'    'boolean'
            ;;
        list)
            case "$in__objects" in
                profiles)
                    list_profiles
                    ;;
				*)
					write_error "$(printf "$(_ "Unexpected object: %s")" "$in__objects")"
					;;
            esac
            ;;
        read)
            if is_enabled; then
                write_bool_param   'enabled' 'on'
            else
                write_bool_param   'enabled' 'off'
            fi
            write_string_param 'profile' "$(read_profile)"
            if is_autoadd_enabled; then
                write_bool_param   'autoadd' 'on'
            else
                write_bool_param   'autoadd' 'off'
            fi
            write_bool_param   'gost' 'off'
			if have_gost_ca; then
				write_bool_param   'have_gost' 'on'
			else
				write_bool_param   'have_gost' 'off'
			fi
            ;;
        write)
            if [ -n "$in_enabled" ]; then
                if test_bool "$in_enabled"; then
                    enable
                else
                    disable
                fi
            fi
            if [ -n "$in_profile" ]; then
                set_profile "$in_profile"
            fi
            if [ -n "$in_autoadd" ]; then
                if test_bool "$in_autoadd"; then
                    enable_autoadd
                else
                    disable_autoadd
                fi
            fi
            if [ -n "$in_gost" ]; then
                if test_bool "$in_gost"; then
                    trust_gost_ca
                fi
            fi
            ;;
		*)
			write_error "$(printf "$(_ "Unexpected action: %s")" "$in_action")"
			;;
    esac
}

message_loop
