#!/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
. shell-quote
. shell-config

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

# test IP address or hostname: local or remote
is_local(){
	[ "$(traceroute -m2 $1 | wc -l)" -le 2 ]
}


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

### e-mail
# read all available emails
email_list()
{
    if [ -n "$mode" -a "$mode" == "ldap" ];then
	local user="$1";shift
	local email_file="$cache_dir/email-$user"

	if [ -f "$email_file" ];then
	    cat "$email_file"
	else
	    ldap-getent passwd "$user" mail|
	    sed -e 's/,[[:blank:]]*/\n/g'|
	    tee "$email_file"
    	fi
    fi
}

# 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 >/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 >/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"
}

# 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
	sed 's/.*/mail:&/' "$email_file"|ldap-usermod replace "$user" > /dev/null
  else
	printf 'mail:\n'|ldap-usermod replace
  fi
  email_reset "$user"
}

########################
photo_add(){
#    echo $(set|grep "in_") >&2
    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
}

photo_delete(){
    rm -f "$image_dir/last_photo.jpg"
    rm -f "$cache_dir/ldapsearch-jpegPhoto*"
    rm -f "$cache_dir/last_photo.jpg"
    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
}

# list groups for user
group_member(){
    local user="$1";shift
    local group_in_file="$cache_dir/group_in-$user"
    local command="getent"
    case "$mode" in
    local)
    ;;
    ldap)
    command="ldap-getent"
    ;;
    *)
    ;;
    esac
    if [ -f "$group_in_file" ];then
	cat $group_in_file | sort |
	while read name; do
	    local is_def=""
	    echo "$default_groups" | fgrep -wqs "$name" && is_def="(*)"
	    [ "$name" == "$user" ] && is_def="(P)"
	    write_enum_item "$name" "$name $is_def"
	    is_def=""
	done
    else
	$command group | grep $user | sort |
	while IFS=':' read name empty gid members; do
	    echo "$name" >> $group_in_file
	done
        cat $group_in_file |
	while read name; do
	    local is_def=""
	    echo "$default_groups" | fgrep -wqs "$name" && is_def="(*) "
	    [ "$name" == "$user" ] && is_def="(P)"
	    write_enum_item "$name" "$name $is_def"
	    is_def=""
	done
    fi
}

group_not_member(){
    local user="$1";shift
    local group_out_file="$cache_dir/group_out-$user"
    local command="getent"
    case "$mode" in
    local)
    ;;
    ldap)
    command="ldap-getent"
    ;;
    *)
    ;;
    esac

    if [ -f "$group_out_file" ];then
	cat $group_out_file | sort |
	while IFS=':' read name gid; do
	    local is_local=""
	    fgrep -wqs "$name" /etc/group && is_local="(local)"
	    # [ $gid -lt 100 ] || write_enum_item "$name" "$name"
	    write_enum_item "$name" "$name $is_local"
	done
    else
	$command group | grep -v $user | sort |
	while IFS=':' read name empty gid members; do
	    echo "$name:$gid" >> $group_out_file
	done
        cat $group_out_file |
	while IFS=':' read name gid; do
	    local is_local=""
	    fgrep -wqs "$name" /etc/group && is_local="(local)"
	    write_enum_item "$name" "$name $is_local"
	done
    fi
}

membership_add(){
    local user="$1";shift
    local newgrp="$1";shift
    local group_in_file="$cache_dir/group_in-$user"
    local group_out_file="$cache_dir/group_out-$user"
    echo "$newgrp" | tr -s ';,' '\n' |
    	while read ngrp; do
	    quote_sed_regexp_variable grp "$ngrp"
            file_list_add "$group_in_file" "$ngrp"
            sed "/$grp/d" -i "$group_out_file"
	done
}

membership_del(){
    local user="$1";shift
    local delgrp="$1";shift
    local group_in_file="$cache_dir/group_in-$user"
    local group_out_file="$cache_dir/group_out-$user"
    echo "$delgrp" | tr -s ';,' '\n' |
    	while read dgrp; do
	    quote_sed_regexp_variable grp "$dgrp"
            file_list_add "$group_out_file" "$dgrp"
            sed "/$grp/d" -i "$group_in_file"
	done
}

membership_save(){
    local user="$1";shift
    local g_in_f="$cache_dir/group_in-$user"
    local groups=`cat $g_in_f |sed -e '/$user/d'| tr '\n' ','|sed -e 's/,*$//g'`
    case "$mode" in
    local)
	usermod -g "$user" -G "$groups" "$user"
    ;;
    ldap|krb5)
       for i in $groups ;do
    	   [ -n "$(ldap-getent group "$i")" ] &&
    	   printf 'memberUid:%s\n' "$user" | ldap-groupmod add "$i" > /dev/null
       done
    ;;
    *)
    ;;
    esac
#    echo "$savgrp" | tr -s ';,' '\n' |
#    	while read sgrp; do
#	    quote_sed_regexp_variable grp "$sgrp"
#	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 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"
    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
    if [ -n "$mode" -a "$mode" == "ldap" ];then
	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="$(echo ${cn#$sn})"
		patronym="$(echo ${patronym#$givenname})"
		jpegphoto="$(read_ldap_photo $name)"

		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
		)
    else
	getent passwd "$name" |
		(IFS=: read uid userpassword uidNumber gidNumber gecos homedirectory loginshell;
		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
		)
    fi
}

ldap_user_add_default_groups(){
    [ -n "$1" ] || return
    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"
}

ldap_user_del_default_groups(){
    [ -n "$1" ] || return
    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
}

user_chpasswd(){
    local r="$(echo "$2" |ldap-passwd "$1" 2>&1)"
    [ -n "$r" ] && write_error "$r" && return 1
    :
}

user_new(){
    local r="$(ldap-useradd "$1" 2>&1)"
    [ -n "$r" ] && write_error "$r" && return 1
    # trying to init user homedir
    local init_user="$(su - $1 -s /bin/true > /dev/null 2>&1)"
    :
}

user_write(){
	case "$mode" in
	   local)
		local in_cn="$in_sn"
		[ -n "$in_givenname" ] && in_cn="$in_cn $in_givenname"
		[ -n "$in_patronym" ] && in_cn="$in_cn $in_patronym"
		usermod -c "$in_cn" -s "$in_loginshell" -d "$in_homedirectory" "$in_user"
	   ;;
	   ldap|krb5)
		local r="$(user_args |
		ldap-usermod replace "$1" 2>&1)"
		[ -n "$r" ] && write_error "$r" && return 1
		:
	   ;;
	   *);;
	esac

}

user_delete(){
    case "$mode" in
    local)
	userdel -r "$in_user"
	:
    ;;
    ldap|krb5)
	local r="$(ldap-userdel "$1" 2>&1)"
	[ -n "$r" ] && write_error "$r" && return 1
	:
    ;;
    *);;
    esac
}

parse_uploaded(){
 cat $in_passwd_file |
    while read line; do
    echo $line |
	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
    done
}

read_userlist(){
    [ -z "$in_minuid" -o "$in_minuid" == "#f" ] && in_minuid="4999"
    [ -z "$in_maxuid" -o "$in_maxuid" == "#f" ] && in_maxuid="9000"
    [ "$in_maxuid" -gt "$in_minuid" ] || in_minuid="$in_maxuid"

    if [ -n "$mode" -a "$mode" == "ldap" ];then
	local IFS=$'\n'
	for i in $(ldap-getent passwd '*' uid uidNumber|sort) ;do
        echo $i |
	    while IFS=':' read login uid; do
	    if [ "$uid" -ge "$in_minuid" -a "$uid" -le "$in_maxuid" ];then
        	write_enum_item "$login"
            fi
    	    done
	done
    else
	local IFS=$'\n'
	for i in $(getent passwd | sort);do
        echo $i |
	    while IFS=':' read login pass uid gid gecos home shell; do
	    	if [ "$uid" -ge "$in_minuid" -a "$uid" -le "$in_maxuid" ];then
        	    write_enum_item "$login"
        	fi
    	    done
	done
    fi
}

list_bases(){
    ldapsearch -x -H "ldap://${in_rem_host:-127.0.0.1}" -LLL -b "" -s base namingContexts \
	| grep naming | cut -f2 -d ' '| \
      while read base_dn ; do
        write_enum_item "$base_dn" "$base_dn"
      done 2>/dev/null
}

determine_mode(){
    [ -n "$mode" ] || set_dn_conf
    write_string_param mode "$mode"

    if [ "$mode" != "local" ];then
	write_string_param DN_CONF "$DN_CONF"
	write_string_param host "$host"
	write_string_param base "$base"
	write_string_param rootdn "$rootdn"
	write_string_param rootpw "$rootpw"
	method=$(echo "$rootpw"|sed -n -e "s/^.*{\([^}]*\)}.*$/\1/p")
	if [ -n "$method" ]; then
	    write_string_param bind_info "encoded"
	else
	    write_string_param bind_info "present"
	fi
	[ "$mode" == "krb5" ] && write_string_param enable_krb "yes"

    fi
}

check_mode(){
	[ -n "$mode" ] || set_dn_conf
	write_string_param mode "$mode"
	write_string_param host "$host"
	write_string_param base "$base"
	write_string_param rootdn "$rootdn"
	write_string_param rootpw "$rootpw"
	write_string_param bind_info "$bind_info"
}

set_new_source(){
    case "$in_newmode" in
	local)
	    mode="local"
	    host="localhost"
	    base="tcb"
	    rootdn:root
	    rootpw:*********
	    bind_info="present"
	    export mode host base rootdn rootpw bind_info
	;;
	ldap)
	    host="$in_host"
	    base="$in_new_rem_base"
	    DN_CONF="remote"
	    mode="ldap"
	    bind_info="encoded"
	    export DN_CONF mode host base rootdn rootpw bind_info
	;;
	localldap)
	    mode="ldap"
	    host="localhost"
	    base="$in_new_base"
	    DN_CONF=$(ldap-dn find "$base")
	    rootdn=$(egrep "rootdn" "$DN_CONF"|cut -f2 -d' '|tr -d '"')
	    rootpw="-w $(egrep "rootpw" "$DN_CONF"|cut -f2 -d' ')"
	    method=$(echo "$rootpw"|sed -n -e "s/^.*{\([^}]*\)}.*$/\1/p")
	    if [ -n "$method" ]; then
		bind_info="encoded"
	    else
		bind_info="present"
	    fi
	    export DN_CONF mode host base rootdn rootpw bind_info
	;;
	system)
	    set_dn_conf
	;;
	*)
	;;
    esac
}

get_mode(){
	mode="$(/usr/sbin/system-auth status | cut -f1 -d' ')"
	role=$(shell_config_get /etc/sysconfig/system SERVER_ROLE)
	[ "$role" == 'master' ] && mode='krb5'
	echo $mode
}
        

set_dn_conf(){
	local data="$(/usr/sbin/system-auth status)"
    mode="$(get_mode)"

    case "$mode" in
	local)
	    mode="local"
	    host="localhost"
	    base="tcb"
	    rootdn="root"
	    rootpw="*********"
	    bind_info="present"
	    export mode host base rootdn rootpw bind_info
	;;
	ldap|krb5)
	    base="$(echo "$data"|cut -f2 -d' ')"
	    urihost="$(echo "$data"|cut -f3 -d' ')"
	    host=$(echo "$urihost"|sed -e 's/lda.*\/\///i')
	    if is_local "$host" ; then
		    host=127.0.0.1
		    DN_CONF=$(ldap-dn find "$base")
		    rootdn=$(egrep "rootdn" "$DN_CONF"|cut -f2 -d' '|tr -d '"')
		    rootpw="-w $(egrep "rootpw" "$DN_CONF"|cut -f2 -d' ')"
		    export DN_CONF
		    export mode host base rootdn rootpw
		    [ "$mode" == 'krb5' ] && export ENABLE_KRB="yes"
	    fi
	;;
	*)
	;;
    esac
}

set_new_bind(){
    mode="$in_mode"
    base="$in_base"
    host="$in_host"
    rootdn="$in_rootdn"
    rootpw="-w $in_rootpw"
    bind_info="$in_bind_info"
    DN_CONF="remote"

    export DN_CONF mode host base rootdn rootpw bind_info

}

set_dn_conf
reset_cache

on_message(){
	[ "$(get_mode)" == 'krb5' ] && export ENABLE_KRB=yes
	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
			;;
		#object manipulations
		list)
		case "$in__objects" in
			departmentnumber_list)
				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
				;;
			title_list)
				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
			;;
			avail_shell)
			    write_enum_item "/sbin/nologin"
			    while read sh; do
				[ -x "$sh" ] || continue
				write_enum_item "$sh"
			    done </etc/shells
			;;
			email_list)
			    [ -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
			;;
			rem_bases)
			    [ -z "$in_rem_host" ] || list_bases "$in_rem_host"
			;;
			userlist)
			    read_userlist "$in_minuid" "$in_maxuid"
			;;
			avail_users)
			    local IFS=$'\n'
				for i in $(ldap-getent passwd '*' uid|sort) ;do
				write_enum_item "$i"
			    done
			;;
			mode)
			    check_mode
			;;
			*)
			;;
		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
			;;
		    esac
		    ;;
		write)
			case "$in__objects" in
			newgroup)
			  [ -n "$in_user" -a -n "$in_newgrp" ] && membership_add "$in_user" "$in_newgrp"
			;;
			delgroup)
			  [ -n "$in_user" -a -n "$in_delgrp" ] && membership_del "$in_user" "$in_delgrp"
			;;
			*)
			[ -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)
					user_chpasswd "$in_user" "$in_passwd_auto"
				;;
				*);;
			   esac
			elif [ "$in_passwd_1" != "$in_passwd_2" ]; then
			    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)
			    	user_chpasswd "$in_user" "$in_passwd_1"
			    ;;
			    *);;
			    esac
			fi
                    	;;
                        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)
				    user_new "$in_newusername" || return
				    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
				;;
				*);;
			    esac

			    #new
			;;
			source)
			    set_new_source
			;;
			user_photo)
			    [ -z "$in_photo_file" ] || photo_add
			;;
			*)
			;;
		    esac
		;;
		delete)
		    case "$in__objects" in
		      user)
			if [ -n "$in_user" ]; then
			IFS=$'\n'
			   for user in $(echo $in_user|sed -e 's/;/\n/g')
			     do
			       case "$mode" in
			       local)
			       	  userdel "$user" || return
			       ;;
			       ldap)
			        user_delete "$user" || return
			        ldap_user_del_default_groups "$user"
				;;
				*);;
			       esac
			     done
			fi
		      ;;
		      photo)
		     	[ -z "$in_user" ] || photo_delete
		      ;;
		      *);;
		     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
		    ;;
		email_commit)
		    [ -n "$in_user" ] || return
		    case "$mode" in
		    local);;
		    ldap|krb5)
			email_commit "$in_user"
		    ;;
		    *);;
		    esac
		    ;;
		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
		    ;;
	esac
}

message_loop
