#!/bin/sh

alterator_api_version=1
po_domain="alterator-ldap-users"

#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

SLAPD_CONF="/etc/openldap/slapd-generated.conf"

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

user_list()
{
	ldap-getent passwd '*' uid uidNumber loginShell |
		while IFS=: read name uid shell; do
			[ "$uid" -ge "$UID_MIN" ] || continue
			[ "$shell" = "/sbin/nologin" ] || grep -qs "^$shell$" /etc/shells || continue
			[ -x "$shell" ] || continue
			echo "$name"
		done
}

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

user_args()
{
	in_cn="$in_sn"
	[ -n "$in_givenname" ] && in_cn="$in_cn $in_givenname"
	[ -n "$in_patronym" ] && in_cn="$in_cn $in_patronym"
	for attr in givenname sn cn o title telephonenumber mobile homedirectory loginshell; do
		is_defined "in_$attr" && printf '%s:%s\n' "$attr" "$(eval printf %s \"\$in_$attr\")"
	done
	(IFS=",$IFS"
	for mail in $in_mail; do
		printf '%s:%s\n' "mail" "$mail"
	done)
}

user_read()
{
	local name="$1";shift

	ldap-getent passwd "$name" uid userPassword givenName sn cn o title mail telephoneNumber mobile homeDirectory loginShell |
		(IFS=: read name userpassword givenname sn cn o title mail telephonenumber mobile homedirectory loginshell;
		patronym="$(echo ${cn#$sn})"
		patronym="$(echo ${patronym#$givenname})"
		mail="$(printf %s $mail | sed -e 's/,[[:blank:]]*/ /g')"
		for f in userpassword givenname cn sn patronym o title mail telephonenumber mobile homedirectory loginshell; do
			write_string_param "$f" "$(eval printf %s \"\$$f\")"
		done

		[ -n "$userpassword" -a -z "${userpassword##!!*}" ] &&
			write_bool_param is_active false ||
			write_bool_param is_active true
		)
}

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

user_write_retcode()
{
	case "$1" in
		1) write_error "`_ "Operations error"`" ;;
		2) write_error "`_ "Protocol error"`" ;;
		3) write_error "`_ "Time limit exceeded"`" ;;
		4) write_error "`_ "Size limit exceeded"`" ;;
		5) write_error "`_ "Compare False"`" ;;
		6) write_error "`_ "Compare True"`" ;;
		7) write_error "`_ "Authentication method not supported"`" ;;
		8) write_error "`_ "Strong(er) authentication required"`" ;;
		9) write_error "`_ "Partial results and referral received"`" ;;
		10) write_error "`_ "Referral"`" ;;
		11) write_error "`_ "Administrative limit exceeded"`" ;;
		12) write_error "`_ "Critical extension is unavailable"`" ;;
		13) write_error "`_ "Confidentiality required"`" ;;
		14) write_error "`_ "SASL bind in progress"`" ;;
		16) write_error "`_ "No such attribute"`" ;;
		17) write_error "`_ "Undefined attribute type"`" ;;
		18) write_error "`_ "Inappropriate matching"`" ;;
		19) write_error "`_ "Constraint violation"`" ;;
		20) write_error "`_ "Type or value exists"`" ;;
		21) write_error "`_ "Invalid syntax"`" ;;
		32) write_error "`_ "No such object"`" ;;
		33) write_error "`_ "Alias problem"`" ;;
		34) write_error "`_ "Invalid DN syntax"`" ;;
		35) write_error "`_ "Entry is a leaf"`" ;;
		36) write_error "`_ "Alias dereferencing problem"`" ;;
		47) write_error "`_ "Proxy Authorization Failure"`" ;;
		48) write_error "`_ "Inappropriate authentication"`" ;;
		49) write_error "`_ "Invalid credentials"`" ;;
		50) write_error "`_ "Insufficient access"`" ;;
		51) write_error "`_ "Server is busy"`" ;;
		52) write_error "`_ "Server is unavailable"`" ;;
		53) write_error "`_ "Server is unwilling to perform"`" ;;
		54) write_error "`_ "Loop detected"`" ;;
		64) write_error "`_ "Naming violation"`" ;;
		65) write_error "`_ "Object class violation"`" ;;
		66) write_error "`_ "Operation not allowed on non-leaf"`" ;;
		67) write_error "`_ "Operation not allowed on RDN"`" ;;
		68) write_error "`_ "Already exists"`" ;;
		69) write_error "`_ "Cannot modify object class"`" ;;
		70) write_error "`_ "Results too large"`" ;;
		71) write_error "`_ "Operation affects multiple DSAs"`" ;;
		80) write_error "`_ "Internal (implementation specific) error"`" ;;
		113) write_error "`_ "LCUP Resources Exhausted"`" ;;
		114) write_error "`_ "LCUP Security Violation"`" ;;
		115) write_error "`_ "LCUP Invalid Data"`" ;;
		116) write_error "`_ "LCUP Unsupported Scheme"`" ;;
		117) write_error "`_ "LCUP Reload Required"`" ;;
		118) write_error "`_ "Cancelled"`" ;;
		119) write_error "`_ "No Operation to Cancel"`" ;;
		120) write_error "`_ "Too Late to Cancel"`" ;;
		121) write_error "`_ "Cannot Cancel"`" ;;
		122) write_error "`_ "Assertion Failed"`" ;;
		*) write_error "$(printf "`_ "Unknown error (retcode=%d)"`" $1)" ;;
	esac
	return "$1"
}

user_chpasswd()
{
	echo "$2" |ldap-passwd "$1" 1>/dev/null ||
		user_write_error "`_ "cannot change password"`"
}

user_new()
{
	ldap-useradd "$1" 1>/dev/null ||
		user_write "$1" 1>/dev/null ||
		user_write_retcode "$?"
}

user_write()
{
	user_args |
		ldap-usermod "$1" 1>/dev/null ||
		user_write_retcode "$?"
}

user_delete()
{
	ldap-userdel "$1" 1>/dev/null ||
		user_write_retcode "$?"
}

on_message()
{
	case "$in_action" in
		type)
			write_type_item name system-account-name
			;;
		#object manipulations
		list)
			case "$in__objects" in
				avail_shell)
					while read sh; do
						[ -x "$sh" ] || continue
						write_enum_item "$sh"
					done </etc/shells
					write_enum_item "/sbin/nologin"
					;;
				*)
					user_list | write_enum
					;;
			esac
			;;
		read)
			[ -n "$in_name" ] || in_name="$(user_list | head -n1)"

			write_string_param passwd_auto "$(pwqgen)"
			! test_bool "$in_auto"
			write_bool_param auto "$?"

			[ -n "$in_name" ] && user_read "$in_name"
			;;
		write)
			[ -n "$in_name" ] || in_name="$(user_list | head -n1)"

			#main settings
			user_write "$in_name" || return

			#password
			if test_bool "$in_auto" && [ -n "$in_passwd_auto" ]; then
				user_chpasswd "$in_name" "$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
				else
					user_chpasswd "$in_name" "$in_passwd_1"
				fi
			fi

			#disable
			pswd="$(ldap-getent passwd "$in_name" userpassword)"
			if test_bool "$in_is_active"; then
				[ -n "$pswd" -a -z "${pswd##!!*}" ] &&
					printf '%s\n' "${pswd##!!}" | ldap-passwd "$in_name" >/dev/null
			else
				[ -z "$pswd" -o -n "${pswd##!!*}" ] &&
					printf '!!%s\n' "${pswd}" | ldap-passwd "$in_name" >/dev/null
			fi

			write_nop
			;;
		new)
			if [ -z "$in_name" ];then
				write_nop
				return
			fi

			#prepare password
			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
				else
					password="$in_passwd_1"
				fi
			fi

			#new
			user_new "$in_name" || return

			#password
			[ -n "$password" ] && user_chpasswd "$in_name" "$password"

			write_nop
			;;
		delete)
			if [ -n "$in_name" ] ;then
				user_delete "$in_name" || return
			fi
			write_nop
			;;
	esac
}

message_loop
