#!/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=""

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

#turn off auto expansion
set -f

. alterator-sh-functions
. shell-quote

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
}

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

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

destroy_account()
{
    [ -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 "$?")
}

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
}

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

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 create_account
alterator_export_proc destroy_account
alterator_export_proc read_account
alterator_export_proc write_account
alterator_export_proc generate_password

message_loop
