#!/bin/sh -e

. alterator-openldap-functions

# Read default configuration
set_ldap_config

system_groups_file=/etc/group

[ -n "$DN_CONF" ] || fatal "DN_CONF not set"

if [ $# -ne 0 ]; then
    mod="$1"
    shift
else
    mod="-h"
fi
userlist=
gid=
smb_group=

case $mod in
    -m|-x)
	[ $# -eq 0 ] && fatal "user list is empty"
	userlist="$1";shift
	;;
    -g)
	[ $# -eq 0 ] && fatal "gid number or system user name is missing"
	gid="$1";shift
	;;
    -s)
	if [ $# -gt 1 ]; then
	    smb_group="$1";shift
	fi
	;;
    add|replace|del|-u) ;;
    *)
	echo "Unknown mode '$mod'"
	mod="-h"
	;;
esac

# Parse arguments
case "$mod" in
    --version)
	get_ldap_version
	exit
	;;
    -h|--help)
	cat <<EOF
Usage:

    $0 <mode> <group>
    $0 -m <userlist> <group>
    $0 -x <userlist> <group>
    $0 -g <gid> <group>
    $0 -s [<Samba-group>] <group>
    $0 -u <group>

Arguments:

    mode        'add'. 'replace' or 'del'.
                Pairs of '<name>:<value>' will be read from stdin.
    -m          Add specified users as group members
    -x          Remove specified users from group members
    -g          Set gid number (map LDAP group to system one)
    -s          Map LDAP group to Samba group
    -u          Remove Samba group which LDAP group mapped to
    <userlist>  One or more users separated by comma
    <gid>       The numerical value of the group ID or name of system group
    <Samba-group> Optional name of group in Samba. <group> is used by default.
    <group>     LDAP group name
    -h, --help  show this help
    --version   show version

EOF
	exit
	;;
    -m)
	mod="add"
	;;
    -x)
	mod="del"
	;;
    -g)
	mod="replace"
	;;
esac

[ $# -eq 1 ] || fatal "group name is missing"
group="$1"; shift
[ -z "$(ldap-getent group "$group")" ] &&
    fatal "group \"$group\" does not exist"
memberlist="$(ldap-getent group "$group" memberUid|tr ',' '|')"

#edit ldap
ldap_modify() {
ruby -e '
require "ldap"
require "ldap/ldif"

mod = LDAP::LDAP_MOD_REPLACE
case ARGV[0]
    when "add"
        mod = LDAP::LDAP_MOD_ADD
    when "del"
        mod = LDAP::LDAP_MOD_DELETE
end

ARGV.delete_at(0)

dn = ARGV[0]
attrs = {}
$stdin.each do |l|
  l.force_encoding("UTF-8") if l.respond_to? :force_encoding
  key, val = l.chomp.split(/:/, 2)
  attrs[key] ||= []
  attrs[key] << val unless val.empty?
end
puts LDAP::LDIF.mods_to_ldif(dn, *LDAP.hash2mods(mod, attrs))
' "$mod" "cn=$group,ou=Group,$base" |
	ldapmodify -D "$rootdn" $rootpw -x \
			-H "ldap://${host:-127.0.0.1}" >/dev/null
}

# Get Samba mapped group for specified LDAP group
samba_mapped_group() {
    echo "$(net groupmap list | sed 's/^\(.*\) (S[0-9-]*) -> /\1\t/' | grep -P "\t${1}$" | cut -f1)"
}

# Get Samba mapped group SID for specified LDAP group
samba_mapped_sid() {
    echo "$(net groupmap list | sed 's/^\(.*\) (\(S[0-9-]*\)) -> /\2\t/' | grep -P "\t${1}$" | cut -f1)"
}

# Map to Samba group
samba_map() {
    local unix_group=$1; shift

    if [ $# -eq 0 -o -z "$1" ]; then
	# Generate Samba group name from LDAP group name
	samba_group="$(echo "$unix_group"|sed 's/^./\u&/')"
    else
	samba_group="$1"
    fi
    #echo "$unix_group -> $samba_group"

    [ "$(samba_mapped_group "$unix_group")" = "$samba_group" ] && exit

    # If mapping exists, remove mapping and group
    [ -n "$(samba_mapped_sid "$unix_group")" ] &&
	samba_unmap "$unix_group"

    # Create new group in Samba (needs temporary create fake administrator account)
    admin_name="nt_domain_administrator"
    admin_password="$(pwqgen)"
    [ -z "$(ldap-getent passwd "$admin_name")" ] &&
	ldap-useradd "$admin_name"
    ldap-passwd "$admin_name" "$admin_password"
    echo "uidNumber:0" | ldap-usermod replace "$admin_name"
    net rpc group add "$samba_group" -U$admin_name%"$admin_password" &>/dev/null ||:
    ldap-userdel -r "$admin_name"

    # Map LDAP group to Samba group
    net groupmap add unixgroup="$unix_group" ntgroup="$samba_group" &>/dev/null
}

# Remove mapping to Samba group and remove Samba group
samba_unmap() {
    local unix_group=$1; shift
    local samba_group="$(samba_mapped_group "$unix_group")"
    local samba_group_sid="$(samba_mapped_sid "$unix_group")"

    if [ -n "$samba_group_sid" ]
    then
	# Unmap group
	net groupmap delete sid="$samba_group_sid" &>/dev/null

	# Remove Samba group (needs temporary create fake administrator account)
	admin_name="nt_domain_administrator"
	admin_password="$(pwqgen)"
	[ -z "$(ldap-getent passwd "$admin_name")" ] &&
	    ldap-useradd "$admin_name"
	ldap-passwd "$admin_name" "$admin_password"
	echo "uidNumber:0" | ldap-usermod replace "$admin_name"
	net rpc group delete "$samba_group" -U$admin_name%"$admin_password" &>/dev/null
	ldap-userdel -r "$admin_name"
    fi
}

# Bind and unbind LDAP and Samba groups
case "$mod" in
    -s)
	samba_map "$group" "$smb_group"
	exit
	;;
    -u)
	samba_unmap "$group"
	exit
	;;
esac

# Other operations
if [ -z "$userlist" -a -z "$gid" ]; then
	# Read values from stdin
	ldap_modify
else
	# Set group GID (-g option)
	if [ -n "$gid" ]; then
		if [ -z "$(echo $gid|egrep '^[0-9]+$')" ]; then
			# Possible system user name is defined, lookup in /etc/group
			gid=$(grep "^$gid:" $system_groups_file | cut -f3 -d:)
			test -z "$gid" && fatal "no such system group found"
		fi

		# Check that current gid is equal to the one being set
		[ "$gid" = "$(ldap-getent group "$group" gidNumber)" ] && exit

		# Set new GID for group
		echo "gidNumber:$gid" | ldap_modify
		exit
	fi

	# Read values from user list in -m or -x options
	actual=
	all_users="$(ldap-getent passwd '*' uid|tr '\n' '|')"

	# Check existing user or member
	if [ "$mod" = "add" ]; then
		if [ -n "$memberlist" ]; then
			actual="$(echo "$userlist"|tr , '\n'|egrep -v "^($memberlist)$"|egrep "^($all_users)$")"
		else
			actual="$(echo "$userlist"|tr , '\n'|egrep "^($all_users)$")"
		fi
	fi
	[ "$mod" = "del" ] && actual="$(echo "$userlist"|tr , '\n'|egrep "^($memberlist)$")"

	# Do changes in old style
	[ -n "$actual" ] && echo "$actual" | tr , '\n' | sed -n 's/^./memberUid:&/p' | ldap_modify
fi

