#!/bin/sh

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

cache_dir="/var/cache/alterator/ldap-users"
image_dir="/usr/share/alterator/design/images/ldap-users"

default_groups="cdwriter cdrom audio proc radio camera floppy xgrp scanner uucp users"
#turn off auto expansion
set -f

. alterator-sh-functions
. alterator-openldap-functions
. alterator-service-functions
. alterator-usersource-functions
. shell-quote
. shell-config

# delimeters
rdelim='[[:space:]]\+'
wdelim=' '

# show localized answer from helper
answer_l10n()
{
    local str="$1"; shift
	str="${str#ERROR:}"; str="${str#ERROR(*):}"
	str="${str#Error:}"; str="${str#Error(*):}"
	str="${str#error:}"; str="${str#error(*):}"
    if [ "$str" != "${str# }" ]; then
        "$str"="${str#* }"
    fi
    run_localized gettext "ldap-user-tools" "$(printf "$str" "$@")"
}

### cache
reset_cache()
{
  rm -rf -- "$cache_dir"
  mkdir -p -- "$cache_dir"
}

### e-mail
# read all available emails
email_list() {
	local user="$1"; shift

	local email_file="$cache_dir/email-$user"
	if [ -f "$email_file" ]; then
		cat "$email_file"
		return 0
	fi
	
	case "$mode" in
		ldap|krb5)
			assert_ldap_tools || return 1
			$ldap_getent passwd "$user" 'mail'
			;;
		ad)
			assert_netcmdplus || return 1
			$netcmdplus user getpassword "$user" --attributes=mail | \
				sed -n -e '/^mail:/ { s/^[^:]\+:[[:space:]]*//; p }'
			;;
	esac | \
		sed -e 's/,[[:blank:]]*/\n/g' | tee "$email_file"
}

# add email to list
email_add()
{
  local user="$1";shift
  local email="$1";shift
  local email_file="$cache_dir/email-$user"

  [ -f "$email_file" ] || email_list "$user" >/dev/null
  file_list_add "$email_file" "$email"
}

# remove email from list
email_del()
{
  local user="$1";shift
  local email="$1";shift
  local email_file="$cache_dir/email-$user"

  [ -f "$email_file" ] || email_list "$user" >/dev/null
  file_list_del "$email_file" "$email"
}

# reset email value
email_reset()
{
  local user="$1";shift
  local email_file="$cache_dir/email-$user"
  rm -f -- "$email_file"
}

_read_email_list() {
	cat "$email_file" | (
		addrs=
		while read addr; do
			[ -z "$addrs" ] || addrs="$addrs,"
			addrs="$addrs$addr"
		done
		echo "$addrs"
	)
}

read_ad_cn() {
    assert_netcmdplus || return 1
    $netcmdplus user getpassword "$1" --attributes='cn' | sed -n -e '/^cn:/ { s/^cn:[[:space:]]*//; p; q }'
}

# commit email value
email_commit() {
  local user="$1";shift
  local email_file="$cache_dir/email-$user"

  [ -f "$email_file" ] || return 0

  if [ -s "$email_file" ]; then
	  case "$mode" in
		  ldap|krb5)
			  assert_ldap_tools || return 1
			  sed 's/.*/mail:&/' "$email_file" | \
				  $ldap_usermod replace "$user" > /dev/null
			  ;;
		  ad)
			  assert_netcmdplus || return 1
			  $netcmdplus user update "$(read_ad_cn "$user")" --mail-address="$(_read_email_list)"
			  ;;
	  esac
  else
	  case "$mode" in
		  ldap|krb5)
			  assert_ldap_tools || return 1
			  printf 'mail:\n' | $ldap_usermod replace
			  ;;
		  ad)
			  assert_netcmdplus || return 1
			  $netcmdplus user update "$(read_ad_cn "$user")" --mail-address=""
			  ;;
	  esac
  fi
  
  email_reset "$user"
}

########################
photo_add(){
	#    echo $(set|grep "in_") >&2
	case "$mode" in
		  ldap|krb5)
			  assert_ldap_tools || return 1
			  ldapmodify -a -x -D "$rootdn" $rootpw -H "ldap://${host:-127.0.0.1}" >/dev/null<<EOF
dn: uid=$in_user,ou=People,$base
changetype: modify
replace: jpegPhoto
jpegPhoto:< file://$in_photo_file
EOF
			  ;;
		  ad)
			  write_error "$(_ 'User photo isn'\''t supported with AD yet.')"
              return 1 # Unsupported. Yet?
			  ;;
	esac
}

photo_delete(){
    rm -f "$image_dir/last_photo.jpg"
    rm -f "$cache_dir/ldapsearch-jpegPhoto*"
    rm -f "$cache_dir/last_photo.jpg"
	
	case "$mode" in
		  ldap|krb5)
			  assert_ldap_tools || return 1
			  ldapmodify -x -D "$rootdn" $rootpw -H "ldap://${host:-127.0.0.1}" >/dev/null<<EOF
dn: uid=$in_user,ou=People,$base
changetype: modify
delete: jpegPhoto
EOF
			  ;;
		  ad)
			  write_error "$(_ 'User photo isn'\''t supported with AD yet.')"
			  return 1 # Unsupported. Yet?
			  ;;
	esac
}

# list groups for user
group_member(){
    local user="$1";shift

    # Set group list command
    local command=""
    case "$mode" in
        ldap|krb5)
			assert_ldap_tools || return 1
            command="$ldap_getent"
            ;;
        local)
            command="getent"
            ;;
		ad)
			assert_netcmdplus || return 1
			$netcmdplus user getpassword "$user" --attributes=memberOf | \
				sed -n -e '/^memberOf:/ { s/^[^:]\+:[[:space:]]*//; s/^CN=//; s/,.*$//; p }' | \
				while read name; do
					write_enum_item "$name" "$name"
				done
			return 0 # return early
			;;
    esac

    # Get group list
    $command group | grep ":.*$user" | sort |
        while IFS=':' read name empty gid members; do
            write_enum_item "$name" "$name"
        done
}

group_not_member() {
    local user="$1";shift
    # Set group list command
    local command=""
    case "$mode" in
        ldap|krb5)
			assert_ldap_tools || return 1
            command="$ldap_getent"
            ;;
        local)
            command="getent"
            ;;
		ad)
			assert_netcmdplus || return 1
			$netcmdplus user getpassword "$user" --attributes=memberOf | \
				sed -n -e '/^memberOf:/ { s/^[^:]\+:[[:space:]]*//; s/^CN=//; s/,.*$//; p }' | (
				filter=
				while read name; do
					filter="$filter -e '^$(quote_sed_regexp $name)\$'"
				done
				
				quote_shell_args fargs "$filter"

				$netcmdplus group list | eval "grep -v $fargs" | \
					while read name; do
						write_enum_item "$name" "$name"
					done
			)
			return 0 # return early
			;;
    esac

    # Get group list
    $command group | grep -v ":.*$user" | sort |
        while IFS=':' read name empty gid members; do
            write_enum_item "$name" "$name"
        done
}

membership_add(){
    local user="$1";shift
    local newgrp="$1";shift

    # Add to group 
    case "$mode" in
        ldap|krb5)
			assert_ldap_tools || return 1
            $ldap_groupmod -m "$user" "$newgrp"
            ;;
        local)
            gpasswd -a "$user" "$newgrp"
            ;;
        ad)
			assert_netcmdplus || return 1
			$netcmdplus group addmembers "$newgrp" "$user"
            ;;
    esac
}

membership_del(){
    local user="$1";shift
    local delgrp="$1";shift
    
    # Remove from group 
    case "$mode" in
        ldap|krb5)
			assert_ldap_tools || return 1
            $ldap_groupmod -x "$user" "$delgrp"
            ;;
        local)
            gpasswd -d "$user" "$delgrp"
            ;;
        ad)
			assert_netcmdplus || return 1
			$netcmdplus group removemembers "$delgrp" "$user"
            ;;
    esac
}
###

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

user_args() {
	local 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 ou title telephonenumber mobile homedirectory loginshell departmentnumber postaladdress; do
		is_defined "in_$attr" && printf '%s:%s\n' "$attr" "$(eval printf %s \"\$in_$attr\")"
	done
}

read_ldap_photo(){
    mkdir -p "$cache_dir"
    mkdir -p "$image_dir"
    rm -rf "$cache_dir/ldapsearch-jpegPhoto*"
    rm -rf "$image_dir/last_photo.jpg"

	assert_ldap_tools || return 1
	
    ldapsearch -x -H "ldap://${host:-127.0.0.1}" -LLL -b "$base" -t -F "$cache_dir/" -T "$cache_dir/" "(&(objectClass=posixAccount)(uid=$name))" jpegPhoto | \
	while read key value; do
	    if [ "$key" = "jpegPhoto:<" ]; then
	        cp -f "$value" "$image_dir/last_photo.jpg"
		chmod 644 "$image_dir/last_photo.jpg"
	        #ln -s -f "$cache_dir/last_photo.jpg" -t "$image_dir/"
		#cp -f "$cache_dir/last_photo.jpg" "$image_dir/last_photo.jpg"
	        write_string_param "jpegphoto" "ldap-users/last_photo.jpg"
	        return
	    else
		[ "$key" = "" ] && write_string_param "jpegphoto" "ldap-users/notfound.png"
	    fi
    	done
}

user_read() {
    local name="$1";shift
    case "$mode" in
		ldap|krb5)
			assert_ldap_tools || return 1
			$ldap_getent passwd "$name" uid userPassword givenName sn cn o ou title telephoneNumber mobile homeDirectory loginShell uidNumber departmentNumber postalAddress|
				(IFS=: read name userpassword givenname sn cn o ou title telephonenumber mobile homedirectory loginshell uidNumber departmentnumber postaladdress;
				 patronym="${cn#$sn }"
				 patronym="${patronym#$givenname }"
				 jpegphoto="$(read_ldap_photo $name)"

                 [ -n "$loginshell" ] || loginshell='default'

				 for f in userpassword givenname cn sn patronym o ou title telephonenumber mobile homedirectory loginshell uidNumber departmentnumber postaladdress; do
					 write_string_param "$f" "$(eval printf %s \"\$$f\")"
				 done
				 read_ldap_photo $name

				 [ -n "$userpassword" -a -z "${userpassword##!!*}" ] &&
					 write_bool_param is_active false ||
						 write_bool_param is_active true
				)
			;;
		local)
			cat /etc/passwd | grep "^$name:" |
				(IFS=: read uid userpassword uidNumber gidNumber gecos homedirectory loginshell;
                 [ -n "$loginshell" ] || loginshell='default'
				 for f in uid userpassword uidNumber gecos homedirectory loginshell; do
					 write_string_param "$f" "$(eval printf %s \"\$$f\")"
				 done
				 echo "$gecos" | while IFS=' ' read sn givenname patronym; do
					 for i in sn givenname patronym; do
						 write_string_param "$i" "$(eval printf %s \"\$$i\")"
					 done
				 done
				)
			;;
		ad)
			assert_netcmdplus || return 1
			$netcmdplus user getpassword "$name" --attributes=cn,sn,givenName,title,telephoneNumber,mobile,homeDirectory,loginShell,uidNumber,department,physicalDeliveryOfficeName,userAccountControl | (
                loginshell=
				while read key val; do
					case "$key" in
						*::)
							key="${key%:}"
							val="$(echo "$val" | base64 -d)"
							;;
                        loginShell:)
                            loginshell="$val"
                            ;;
					esac
					case "$key" in
						sn:)
							sn="${val%% *}"
                            if [ "$val" != "${val#* }" ]; then
							    patronym="${val#* }"
                            else
                                patronym=
                            fi
							write_string_param 'sn' "$sn"
							[ -z "$patronym" ] || \
								write_string_param 'patronym' "$patronym"
							;;
						uidNumber:)
							write_string_param 'uidNumber' "$val"
							;;
						department:)
							write_string_param 'departmentnumber' "$val"
							;;
						physicalDeliveryOfficeName:)
							write_string_param 'postaladdress' "$val"
							;;
						userAccountControl:)
							# See https://support.microsoft.com/ru-ru/help/305144/how-to-use-the-useraccountcontrol-flags-to-manipulate-user-account-properties
							if [ "$((val & 0x02))" -ne 0 -o \
								 "$((val & 0x10))" -ne 0 ]
							then
								write_bool_param 'is_active' false
							else
								write_bool_param 'is_active' true
							fi
							;;
						passw*:)
							;; # skip password fields
						*:)
							write_string_param "$(echo ${key%:} | tr '[:upper:]' '[:lower:]')" "$val"
							;;
					esac
				done

                if [ -z "$loginshell" ]; then
                    write_string_param 'loginshell' 'default'
                fi
			)
			;;
    esac
}

ldap_user_add_default_groups() {
    [ -n "$1" ] || return
	assert_ldap_tools || return 1
    for i in $default_groups ;do
        [ -n "$($ldap_getent group "$i")" ] &&
        printf 'memberUid:%s\n' "$1" | $ldap_groupmod add "$i" > /dev/null
    done
}

user_add_default_groups() {
    [ -n "$1" ] || return
    groups=$(echo "$default_groups"|tr ' ' ',')
    usermod -g "$1" -G "$groups" "$1"
}

ad_assert_default_groups() {
	assert_netcmdplus || return 1
	$netcmdplus group list | (
		_groups="$default_groups"; __groups=
		_groups="$(echo "$_groups" | tr '[:upper:]' '[:lower:]')"
		while read exgr; do
			exgr="$(echo "$exgr" | tr '[:upper:]' '[:lower:]')"
			for g in $_groups; do
				if [ "$g" != "$exgr" ]; then
					__groups="$__groups $g"
				fi
			done
			_groups="${__groups# }"; __groups=
		done

		local ret=0; local __text=
		if [ -n "$_groups" ]; then
			for newg in $_groups; do
				__text="$($netcmdplus group add "$newg" 2>&1)" || ret=$?
				if [ $ret -ne 0 ]; then
					write_error "$(answer_l10n "$__text")"
					break
				fi
			done
		fi
		return $ret
	)
}

ad_user_add_default_groups() {
	[ -n "$1" ] || return 0
	assert_netcmdplus || return 1
	ad_assert_default_groups || return 2	
	for g in $default_groups; do
		$netcmdplus group addmembers "$g" "$1"
	done
}

ldap_user_del_default_groups() {
    [ -n "$1" ] || return
	assert_ldap_tools || return 1
    for i in $($ldap_getent group '*' cn memberUid | egrep "(:$1,|:$1$|,$1,|,$1$)" | cut -f1 -d':')
    do
        printf 'memberUid:%s\n' "$1" | $ldap_groupmod del "$i" >/dev/null
    done
}

ad_user_del_default_groups() {
	return 0 # Not needed. FIXME
}

ldap_user_chpasswd() {
	assert_ldap_tools || return 1
    local r="$(echo "$2" | $ldap_passwd "$1" 2>&1)"
    [ -n "$r" ] && write_error "$(answer_l10n "$r")" && return 1
    return 0
}

ad_user_chpasswd() {
	assert_netcmdplus || return 1
	# FIXME: Password on the command line
	local ret=0; local __text=
	__text="$($netcmdplus user setpassword "$1" --newpassword="$2" 2>&1)" || ret=$?
	if [ $ret -ne 0 ]; then
		write_error "$(answer_l10n "$__text")"
	fi

	return $ret
}

ldap_user_new() {
	assert_ldap_tools || return 1
    local r="$($ldap_useradd "$1" 2>&1)"
    [ -n "$r" ] && write_error "$(answer_l10n "$r")" && return 1
    # trying to init user homedir
    local init_user="$(su - $1 -s /bin/true > /dev/null 2>&1)"
    return 0
}

ad_user_new() {
	assert_netcmdplus || return 1
	local ret=0; local __text=
	__text="$($netcmdplus user create "$1" --random-password 2>&1)" || ret=$?
	if [ $ret -ne 0 ]; then
		write_error "$(answer_l10n "$__text")"
	fi	
	return $ret
}

user_write() {
	local in_cn="$in_sn"
	[ -n "$in_givenname" ] && in_cn="$in_cn $in_givenname"
	[ -n "$in_patronym" ] && in_cn="$in_cn $in_patronym"

    case "$in_loginshell" in
        default)
            in_loginshell=
            ;;
    esac

	case "$mode" in
	   local)
		   usermod -c "$in_cn" -s "$in_loginshell" -d "$in_homedirectory" "$in_user"
		   ;;
	   ldap|krb5)
		   assert_ldap_tools || return 1
		   local r="$(user_args | $ldap_usermod replace "$1" 2>&1)"
		   [ -n "$r" ] && write_error "$(answer_l10n "$r")" && return 1
		   return 0
		   ;;
	   ad)
		   assert_netcmdplus || return 1
		   local ret=0; local __text=

		   if [ -n "$in_patronym" ]; then
			   in_sn="$in_sn $in_patronym"
		   fi
		   
		   if [ -n "$in_sn" -o -n "$in_givenname" -o -n "$in_title" -o \
				-n "$in_telephonenumber" -o -n "$in_homedirectory" -o \
				-n "$in_loginshell" -o -n "$in_departmentnumber" -o \
				-n "$in_postaladdress" ]
		   then
               local _cn_username=1
               local _cn="$(read_ad_cn "$1")"
               if [ "$_cn" != "$1" ]; then
                   in_givenname=
                   in_sn=
                   _cn_username=
               fi
			   __text="$($netcmdplus user update "$_cn" ${_cn_username:+--use-username-as-cn} ${in_givenname:+--given-name="$in_givenname"} ${in_sn:+--surname="$in_sn"} ${in_title:+--job-title="$in_title"} ${in_telephonenumber:+--telephone-number="$in_telephonenumber"} ${in_homedirectory:+--home-directory="$in_homedirectory"} ${in_loginshell:+--login-shell="$in_loginshell"} ${in_departmentnumber:+--department="$in_departmentnumber"} ${in_postaladdress:+--physical-delivery-office="$in_postaladdress"} 2>&1)" || ret=$?
		   fi
		   if [ $ret -ne 0 ]; then
			   write_error "$(answer_l10n "$__text")"
		   fi
		   return $ret
		   ;;
	esac
}

user_delete() {
    case "$mode" in
		local)
			userdel -r "$in_user"
			return 0
			;;
		ldap|krb5)
			assert_ldap_tools || return 1
			local r="$($ldap_userdel -r "$1" 2>&1)"
			[ -n "$r" ] && write_error "$(answer_l10n "$r")" && return 1
			return 0
			;;
		ad)
			assert_netcmdplus || return 1
			local ret=0; local __text=
			__text="$($netcmdplus user delete "$1" 2>&1)" || ret=$?
			if [ $ret -ne 0 ]; then
				write_error "$(answer_l10n "$__text")"
			fi
			return $ret
			;;
    esac
}


_normalize_uid_limits() {
	local defmin=
	local defmax=
	
	case "$mode" in
		ldap|krb5)
			defmin=4999
			defmax=9000
			;;
		ad)
			defmin=0
			defmax=10000
			;;
		*)
			defmin=500
			defmax=9000
			;;
	esac
			
	[ -z "$in_minuid" -o "$in_minuid" = "#f" ] && in_minuid=$defmin
	[ -z "$in_maxuid" -o "$in_maxuid" = "#f" ] && in_maxuid=$defmax
	[ "$in_maxuid" -gt "$in_minuid" ] || in_minuid="$in_maxuid"
}

parse_uploaded() {
	_normalize_uid_limits
	cat $in_passwd_file |
		while IFS=':' read login password uid gid gecos home shell; do
			if [ "$uid" -ge "$in_minuid" -a "$uid" -le "$in_maxuid" ]
			then
				write_table_item  \
    				name "$login" \
    				password "$password" \
    				uid "$uid" \
    				gid "$gid" \
    				gecos "$gecos" \
    				home "$home" \
    				shell "$shell"
    		fi
	    done
}

read_userlist() {
	_normalize_uid_limits
    case "$mode" in
		ldap|krb5)
			assert_ldap_tools || return 1
			$ldap_getent passwd '*' uid uidNumber | sort | \
				while IFS=':' read login uid; do
					if [ "$uid" -ge "$in_minuid" -a "$uid" -le "$in_maxuid" ]
					then
        				write_enum_item "$login"
					fi
    			done
			;;
		local)
			cat /etc/passwd | sort | \
				while IFS=':' read login pass uid _rest; do
	    			if [ "$uid" -ge "$in_minuid" -a "$uid" -le "$in_maxuid" ]
					then
        				write_enum_item "$login"
        			fi
    			done
			;;
		ad)
			assert_netcmdplus || return 1
			$netcmdplus user list | \
				while read name; do
					write_enum_item "$name"
				done
			;;
    esac
}

set_dn_conf
reset_cache

on_message() {
	if [ "$(get_mode)" = 'krb5' ]; then
		export ENABLE_KRB=yes
	else
		export ENABLE_KRB=
	fi
	
	case "$in_action" in
		type)
			write_type_item newusername ldap-account-name
			write_type_item newemail e-mail
			write_type_item telephonenumber telephone-number
			write_type_item mobile telephone-number
			write_type_item 'netcmdplus_available' 'boolean'
			;;
		#object manipulations
		list)
			case "$in__objects" in
				departmentnumber_list)
					case "$mode" in
						ldap|krb5)
							assert_ldap_tools || return 1
							write_enum_item ""
							deppast=""
							$ldap_getent passwd '*' departmentnumber | sort | \
								while read department; do
									if [ -n "$department" ] && [ "$deppast" != "$department" ]
									then
										write_enum_item "$department"
									fi
									deppast="$department"
								done
							;;
					esac
					;;
				title_list)
					case "$mode" in
						ldap|krb5)
							assert_ldap_tools || return 1
							write_enum_item ""
							titlepast=""
							$ldap_getent passwd '*' title | sort | \
								while read title; do
									if [ -n "$title" ] && [ "$titlepast" != "$title" ]
									then
										write_enum_item "$title"
									fi
									titlepast="$title"
								done
							;;
					esac
					;;
				avail_shell)
					write_enum_item "default" "$(_ 'Default shell')"
					write_enum_item "/sbin/nologin"
					while read sh; do
						[ -x "$sh" ] || continue
						write_enum_item "$sh"
					done </etc/shells
					;;
				email_list)
					# FIXME: Seems this code is not used
					[ -n "$in_user" ] || return
					email_list "$in_user"
					;;
				member_of)
					[ -n "$in_user" ] || return
					group_member "$in_user"
					;;
				member_out)
					[ -n "$in_user" ] || return
					group_not_member "$in_user"
					;;
				passwd)
					in_passwd_file="/etc/passwd"
					parse_uploaded "$in_passwd_file" "$in_minuid" "$in_maxuid"
					;;
				upload)
					parse_uploaded "$in_passwd_file" "$in_minuid" "$in_maxuid"
					;;
				bases)
					list_bases "$in_mode" "$in_rem_host"
					;;
				rem_bases)
					[ -z "$in_rem_host" ] || list_bases "$in_mode" "$in_rem_host"
					;;
				userlist)
					read_userlist "$in_minuid" "$in_maxuid"
					;;
				avail_users)
					case "$mode" in
						ldap|krb5)
							assert_ldap_tools || return 1
							$ldap_getent passwd '*' uid | sort | \
								while read name; do
									write_enum_item "$name"
								done
							;;
						ad)
							read_userlist
							;;
					esac
					;;
				mode)
					check_mode
					;;
				*)
					write_error "Unexpected object: $in__objects"
					;;
			esac
			;;
		read)
		    case "$in__objects" in
				/)
					[ -n "$in_user" ] || return
					write_string_param passwd_auto "$(pwqgen)"
					! test_bool "$in_auto"
					write_bool_param auto "$?"
					[ -n "$in_user" ] && user_read "$in_user"
					;;
				mode)
					determine_mode
					;;
				*)
					write_error "Unexpected object: $in__objects"
					;;
		    esac
		    ;;
		write)
			case "$in__objects" in
				/)
					[ -n "$in_user" ] || return
					user_write "$in_user" || return
					if test_bool "$in_auto" && [ -n "$in_passwd_auto" ]; then
						case "$mode" in
							local)
								echo "$in_user:$in_passwd_auto" | chpasswd
								;;
							ldap|krb5)
								ldap_user_chpasswd "$in_user" "$in_passwd_auto"
								;;
							ad)
								ad_user_chpasswd "$in_user" "$in_passwd_auto"
								;;
						esac
					elif [ "$in_passwd_1" != "$in_passwd_2" ]; then
						# FIXME: Check passwords on the front-end
						write_error "`_ "Passwords mismatch"`"
						return
					elif ! test_bool "$in_auto" && [ -n "$in_passwd_1" -o -n "$in_passwd_2" ] ; then
						case "$mode" in
							local)
			    				echo "$in_user:$in_passwd_1" | chpasswd
								;;
							ldap|krb5)
			    				ldap_user_chpasswd "$in_user" "$in_passwd_1"
								;;
							ad)
								ad_user_chpasswd "$in_user" "$in_passwd_1"
								;;
						esac
					fi
                    ;;
				newgroup)
					if [ -n "$in_user" -a -n "$in_newgrp" ]; then
						membership_add "$in_user" "$in_newgrp"
					fi
					;;
				delgroup)
					if [ -n "$in_user" -a -n "$in_delgrp" ]; then
						membership_del "$in_user" "$in_delgrp"
					fi
					;;
				*)
					write_error "Unexpected object: $in__objects"
					;;
            esac
			;;
		new)
		    case "$in__objects" in
				user)
					[ -n "$in_newusername" ] || return
					case "$mode" in
						local)
							useradd "$in_newusername"
							user_add_default_groups "$in_newusername"
							;;
						ldap|krb5)
							assert_ldap_tools || return 1
							ldap_user_new "$in_newusername" || return 2
							email_add "$in_newusername" "$in_newusername@$(dn_2_host "$base")"
							email_commit "$in_newusername"
							ldap_user_add_default_groups "$in_newusername"
							$ldap_getent passwd '*' o | \
								while read o_main; do
									if [ -n "$o_main" ]; then
										printf '%s:%s\n%s:%s\n' "cn" "$in_newusername" "o" "$o_main" | $ldap_usermod replace "$in_newusername" 2>&1
										break
									fi
								done
							;;
						ad)
							assert_netcmdplus || return 1
							ad_user_new "$in_newusername" || return 2
							email_add "$in_newusername" "$in_newusername@$(dn_2_host "$base")"
							email_commit "$in_newusername"
							ad_user_add_default_groups "$in_newusername"
							;;
					esac
					;;
				source)
					set_new_source
					;;
				user_photo)
					[ -z "$in_photo_file" ] || photo_add
					;;
				*)
					write_error "Unexpected object: $in__objects"
					;;
		    esac
			;;
		delete)
		    case "$in__objects" in
				user)
					if [ -n "$in_user" ]; then
						echo $in_user | sed -e 's/;/\n/g' | \
							while read user; do
								case "$mode" in
									local)
			       						userdel "$user" || return 2
										;;
									ldap|krb5)
										user_delete "$user" || return 2
										ldap_user_del_default_groups "$user"
										;;
									ad)
										user_delete "$user" || return 2
										ad_user_del_default_groups "$user"
										;;
								esac
							done
					fi
					;;
				photo)
		     		[ -z "$in_user" ] || photo_delete
					;;
				*)
					write_error "Unexpected object: $in__objects"
					;;
		    esac
			;;
		email_add)
		    [ -n "$in_user" -a -n "$in_newemail" ] || return
		    email_add "$in_user" "$in_newemail"
		    ;;
		email_del)
		    [ -n "$in_user" -a -n "$in_email" ] || return
		    email_del "$in_user" "$in_email"
		    ;;
		email_list)
		    [ -n "$in_user" ] || return
		    email_list "$in_user" | write_enum_nolabel
		    ;;
		email_commit)
		    [ -n "$in_user" ] || return
			email_commit "$in_user"
		    ;;
		email_reset)
		    [ -n "$in_user" ] || return
		    email_reset "$in_user"
		    ;;
		generate)
		    write_string_param passwd_auto "$(pwqgen)"
		    ;;
		set_bind)
		    [ -n "$in_mode" -a -n "$in_base" -a -n "$in_host"  -a -n "$in_rootdn" -a -n "$in_rootpw" ] && set_new_bind
		    ;;
		*)
			write_error "Unexpected action: $in_action"
			;;
	esac
}

message_loop
