#!/bin/sh

alterator_api_version=1

default_groups="cdwriter cdrom audio video proc radio camera floppy xgrp scanner uucp"
default_groups_file=/usr/share/install3/default-groups

##
# $ALTERATOR_DESTDIR - exported by installer.
#
# XXX: works only for: "add new user"
#

DESTDIR="/"
CHROOT_EXE=""
KIOSK_PROFILES_DIR="/etc/kiosk"

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

#turn off auto expansion
set -f

. alterator-sh-functions
. shell-quote
. autologin-sh-functions
. shell-ini-config

UID_MIN=$(grep '^UID_MIN' /etc/login.defs 2>/dev/null|sed -r 's,UID_MIN[[:space:]]+,,')
[ -z "$UID_MIN" ] && UID_MIN=500

## lowlevel user helpers

local_getent()
{
    local re="${2:-.*}"
    grep "^$re:" "/etc/$1"
}

is_defined()
{
	set |grep -qs "^$1="
}

user_args()
{
	local args=
	is_defined "in_gecos" && args="$args -c \"$(quote_shell "$in_gecos")\""
	[ -n "$in_home" ] && args="$args -d \"$(quote_shell "$in_home")\""
	[ -n "$in_shell" ] && args="$args -s \"$in_shell\""
	echo "$args"
}

user_write_error()
{
    local msg="$(printf "$@")"
    write_error "$msg"
    return 1
}

user_write_retcode()
{
	case "$1" in
		1) write_error "`_ "can't update password file"`" ;;  #'
		2) write_error "`_ "invalid command syntax"`" ;; 
		3) write_error "`_ "invalid argument to option"`" ;; 
		4) write_error "`_ "uid already in use"`" ;; 
		6) write_error "`_ "specified user doesn't exist"`" ;; #'
		8) write_error "`_ "user currently logged in"`" ;;
		9) write_error "`_ "username already in use"`" ;;
		10) write_error "`_  "can't update group file"`" ;; #'
		12) write_error "`_ "can't create or remove home directory"`" ;; #'
		13) write_error "`_ "can't create mail spool"`" ;; #'
		*) write_error "retcode=$1" ;;
	esac
	return "$1"
}

user_chpasswd()
{
    echo "$1:$2"| $CHROOT_EXE /usr/sbin/chpasswd ||
	user_write_error "`_ "cannot change password"`"
}

user_new()
{
    $CHROOT_EXE /usr/sbin/useradd "$@" ||
	user_write_retcode "$?"
}

user_write()
{
    /usr/sbin/usermod "$@" ||
	user_write_retcode "$?"
}

user_passwd()
{
    local password=
    if test_bool "$in_auto" && [ -n "$in_passwd_auto" ]; then
	password="$in_passwd_auto"
    elif ! test_bool "$in_auto" && [ -n "$in_passwd_1" -o -n "$in_passwd_2" ] ; then
	if [ "$in_passwd_1" != "$in_passwd_2" ]; then
	    write_error "`_ "Passwords mismatch"`"
	    return 1
	else
	    password="$in_passwd_1"
	fi
    fi
    echo "$password"
    return 0
}

## lowlevel group helpers

group_write_retcode()
{
	case "$1" in
		2) write_error "`_ "invalid command syntax"`" ;;
		3) write_error "`_ "invalid group name"`" ;;
		4) write_error "`_ "gid not unique"`" ;; 
		6) write_error "`_ "specified group doesn't exist"`" ;;
		8) write_error "`_ "can't remove user's primary group"`" ;; #'
		9) write_error "`_ "group name not unique"`" ;;
		10) write_error "`_ "can't update group file"`" ;; #'
		*) write_error "retcode=$1" ;;
	esac
	return "$1"
}

group_new()
{
    $CHROOT_EXE /usr/sbin/groupadd -f -r "$1" 2>/dev/null ||
	group_write_retcode "$?"
}

group_include()
{
    group_new "$1" || return 1

    $CHROOT_EXE /usr/bin/gpasswd -a "$2" "$1" >/dev/null ||
	user_write_error "`_ "unable to add user %s to group %s"`" "$2" "$1"
}

group_include_default()
{
    [ -s "$DESTDIR/$default_groups_file" ] && default_groups="$(cat "$DESTDIR/$default_groups_file")"
    for i in $default_groups; do
	group_include "$i" "$1" || return 1
    done
}

group_exclude()
{
    $CHROOT_EXE /usr/bin/gpasswd -d "$2" "$1" >/dev/null
}

### high level procedures

list_shell()
{
    while read sh; do
	[ -x "$sh" ] || continue
	write_enum_item "$sh"
    done </etc/shells
    write_enum_item "/sbin/nologin"
}

list_account()
{
    local_getent passwd|
    while IFS=':' read name password uid gid gecos home shell; do
        [ "$uid" -ge "$UID_MIN" ] || continue
	[ "$shell" == "/sbin/nologin" ] || grep -qs "^$shell$" /etc/shells || continue
	[ -x "$shell" ] || continue
	write_enum_item "$name"
    done 2>/dev/null
}

list_kiosk_profile()
{
    lang="${in_language/_*/}"
    write_enum_item "" "`_  "Default desktop"`"
    for file in $(find "$KIOSK_PROFILES_DIR" -name '*.desktop')
    do
        # Test hidden flag
        hidden="$(ini_config_get "$file" 'Desktop Entry' Hidden)"
        [ "$hidden" = "true" ] && continue

        # Ignore missing TryExec
        try_exec="$(ini_config_get "$file" 'Desktop Entry' TryExec)"
        [ -n "$try_exec" -a ! -n "`which "$try_exec" 2>/dev/null`" ] && continue

        # Detect application name (begin from localized GenericName, end with Name)
        name="$(ini_config_get "$file" 'Desktop Entry' "GenericName[$lang]")"
        if [ -z "$name" ]; then
            name="$(ini_config_get "$file" 'Desktop Entry' "Name[$lang]")"
            if [ -z "$name" ]; then
                name="$(ini_config_get "$file" 'Desktop Entry' "GenericName")"
                if [ -z "$name" ]; then
                    name="$(ini_config_get "$file" 'Desktop Entry' "Name")"
                fi
            fi
        fi
        [ -z "$name" ] && continue

        # Add to list
        profile="$(basename $file)"
        write_enum_item "$profile" "$name ($profile)"
    done
}

get_kiosk_profile()
{
    file="/etc/X11/xsession.user.d/$in_name"
    sed -n 's/^PROFILE=//p' "$file" 2>/dev/null
}

set_kiosk_profile()
{
    local profile="$1"
    if [ -n "$profile" ]; then
        echo '#!/bin/sh' > "/etc/X11/xsession.user.d/$in_name"
        echo "PROFILE=$profile" >> "/etc/X11/xsession.user.d/$in_name"
        echo 'e="$(sed -n 's/^Exec[[:space:]]*=[[:space:]]*//p' "/etc/kiosk/$PROFILE")"' >> "/etc/X11/xsession.user.d/$in_name"
        echo 'test -n "$e" && `$e`' >> "/etc/X11/xsession.user.d/$in_name"
        chmod +x "/etc/X11/xsession.user.d/$in_name"
    else
        [ -e "/etc/X11/xsession.user.d/$in_name" ] && /bin/rm -f "/etc/X11/xsession.user.d/$in_name"
    fi
}

create_account()
{
    [ -n "$in_new_name" ] || return

    # prepare password
    local password
    password="$(user_passwd)" || return

    # main settings
    local args="$(user_args)"
    eval user_new $args "$in_new_name" || return

    # wheel
    if test_bool "$in_allow_su";then
	group_include wheel "$in_new_name" || return
    fi

    # default groups
    group_include_default "$in_new_name" || return

    if test_bool "$in_autologin"; then
	al_disable
	al_enable "$in_new_name" || return
    fi

    # change password
    [ -z "$password" ] ||
	user_chpasswd "$in_new_name" "$password" ||
	return
}

destroy_account()
{
    al_disable "$in_new_name"
    [ -z "$in_name" ] ||
	/usr/sbin/userdel "$in_name" ||
	user_write_retcode "$?"
}

read_account()
{
    [ -z "$in_name" ] || {
	local_getent passwd "$in_name"|
	    (IFS=':' read name password uid gid gecos home shell;
		write_string_param gecos "$gecos"
		write_string_param home "$home"
		write_string_param shell "$shell"

		! local_getent group wheel|cut -d: -f4|fgrep -qws "$name"
		write_bool_param allow_su "$?")
	! al_check "$in_name"; write_bool_param autologin "$?"
        write_string_param kiosk_profile "$(get_kiosk_profile)"
    }
}

write_account()
{
    [ -n "$in_name" ] || return

    # main settings
    local args="$(user_args)"
    if [ -n "$args" ];then
	eval user_write $args "$in_name" || return
    fi

    # prepare password
    local password
    password="$(user_passwd)" || return

    # change password
    [ -z "$password" ] ||
	user_chpasswd "$in_name" "$password" ||
	return

    # wheel
    if test_bool "$in_allow_su"; then
	group_include wheel "$in_name" || return
    else
	group_exclude wheel "$in_name"
    fi

    if test_bool "$in_autologin"; then
	if ! al_check "$in_name"; then
	    al_disable	# possibly someone else
	    al_enable "$in_name" || return
        fi
    else
	al_check "$in_name" && al_disable "$in_name" ||:
    fi

    set_kiosk_profile "$in_kiosk_profile"
}

generate_password()
{
    write_string_param passwd_auto "$(pwqgen)"
}

allow_autologin()
{
    local ret=false
    al_possible && ret=true
    write_bool_param allow_autologin "$ret"
}

alterator_export_var \
    name system-account-name \
    new_name system-account-name

alterator_export_proc list_shell
alterator_export_proc list_account
alterator_export_proc list_kiosk_profile
alterator_export_proc create_account
alterator_export_proc destroy_account
alterator_export_proc read_account
alterator_export_proc write_account
alterator_export_proc generate_password
alterator_export_proc allow_autologin

message_loop
