#!/bin/sh

po_domain="alterator-openldap"
alterator_api_version=1

set -f

. alterator-sh-functions
. alterator-openldap-functions
. shell-quote
. cert-sh-functions
. shell-config

SLAPD_CONF_DIR="/etc/openldap"
MAIN_SLAPD_CONF="$SLAPD_CONF_DIR/slapd.conf"
LDAP_SYSCONFIG="/etc/sysconfig/ldap"
LDAP_CONF="$SLAPD_CONF_DIR/ldap.conf"
CERT_FILE="/var/lib/ssl/certs/slapd.pem"
KEY_FILE="/var/lib/ssl/private/slapd.key"
SERVICEDIR="/etc/alterator/openldap"
CONTAINERS="$SERVICEDIR/knowncontainers"
DEFAULT_CONTAINERS="People;Group"
cache_dir=/var/cache/alterator/openldap

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

validate_cert()
{
    get_expire_date()
    {
	local cert="$1"; shift
	local d="$(openssl x509 -text -noout -in "$cert" | sed -nr 's/^[[:blank:]]*Not After : (.+)$/\1/p' 2>/dev/null)"
	[ -n "$d" ] && date --date="$d" +%d.%m.%Y%t%H:%M
    }

   if [ -f "$CERT_FILE" ]; then
    expire="$(get_expire_date "$CERT_FILE")"
    [ -n "$expire" ] && str="`_ "Certificate expire"`: $expire"
   else
     str="`_ "Certificate not found"`"
   fi
   printf "$str"
}

write_tls()
{
    quote_sed_regexp_variable certfile "$CERT_FILE"
    quote_sed_regexp_variable keyfile "$KEY_FILE"

    if [ "$in_enable_tls" = "#t" -o "$in_local_and_tls" = "#t" ] ;then
        for i in TLSCACertificateFile TLSCertificateFile ;do
            sed -e "/^#$i[[:space:]]/s/.*/$i $certfile/" -i $MAIN_SLAPD_CONF
        done
	sed -e "/^#TLSCertificateKeyFile[[:space:]]/s/.*/TLSCertificateKeyFile $keyfile/" -i $MAIN_SLAPD_CONF

    else
        for i in TLSCACertificateFile TLSCertificateFile TLSCertificateKeyFile;do
            sed -e "/^$i[[:space:]]/s/.*/#$i /" -i $MAIN_SLAPD_CONF
        done
    fi
}

list_dn()
{
    local name=
    local conf=
    local master_conf=
    local master_dn=
    
    master_dn=$(/usr/sbin/system-auth status|cut -f2 -d' ')
    check_method(){
    	local rootpw=$(egrep "rootpw" "$1"|cut -f2 -d' ')
	method=$(echo "$rootpw"|sed -n -e "s/^.*{\([^}]*\)}.*$/\1/p")
	if [ -n "$method" ]; then
	    echo "`_ "Encoded password"`"
	else
	    echo "`_ "Unsafe password"`"
	fi
    }

    ldap-dn list|
    while read name conf;do
        if [ "$master_dn" != "$name" ];then 
    	    dst=""
    	else
    	   dst="`_ "Used for autentification", `"
    	   
    	fi
	write_table_item  \
        name "$name" \
        conf "$conf" \
        dstat "$dst $(check_method $conf)"
    done
    
}

list_schema(){
    local name=
    local conf=

    grep "/schema" "$MAIN_SLAPD_CONF"|
    while read status conf;do
	name=$(echo "$conf"| sed -e 's/\/etc\/openldap\/schema\///i'|sed -e 's/\.schema//i')
	write_table_item  \
        name "$name" \
        conf "$conf"
    done
}

status_schema(){
    local name= included=

    grep "/schema" "$MAIN_SLAPD_CONF"|
    {
    while read status conf;do
	name=$(echo "$conf"| sed -e 's/\/etc\/openldap\/schema\///i'|sed -e 's/\.schema//i')
	[ "$status" != "#include" ] && included="$name;$included"
	
    done
    write_string_param "avail_schema" "$included"
    }

}

check_samba3(){
    local included=

    grep "samba.schema" "$MAIN_SLAPD_CONF"|
    {
    while read status conf;do
	[ "$status" != "#include" ] && included="Yes" || included="No"
    done
    write_string_param "samba3" "$included"
    }
}

repair_samba3(){
	sed -e "/^#include.*\/samba\.schema/s/^#//g" -i "$MAIN_SLAPD_CONF"
	/etc/init.d/slapd restart >/dev/null   
	check_samba3
}

write_schemas(){ 
    #Turn OFF all schemas
    sed 's/^\(include[[:space:]][[:space:]]*.*schema\).*$/\#\1/g' -i "$MAIN_SLAPD_CONF"
    
    #Turn ON selected schemas
    local IFS=";"
    for i in $in_schemas; do 
	sed -e "/^#include.*\/$i\.schema/s/^#//g" -i "$MAIN_SLAPD_CONF"
    done

    /etc/init.d/slapd restart >/dev/null   
}

new_schema(){
    cp -f "$in_schema_file" "$cache_dir/$in_schema_name.schema"
    new_name="/etc/openldap/schema/$in_schema_name.schema"
    cp -f "$in_schema_file" "$new_name"
    chown root.root "$new_name"
    chmod 444 "$new_name"
    #determine last line with include
    line="$(grep -n include.*\.schema /etc/openldap/slapd.conf|tail -1|cut -f1 -d:)"
    # Write new include in config after last including
    sed -e "$line a\#include\t\t\/etc\/openldap\/schema\/$in_schema_name\.schema" -i "$MAIN_SLAPD_CONF"
    reset_cache
}

list_actions()
{
    write_enum_item "publish" "`_ "Publish"`"
    write_enum_item "unpublish" "`_ "Unpublish"`"
    write_enum_item "delete" "`_ "Delete"`"
}

present_list() {
	    ldapsearch -x -H ldap://127.0.0.1 -LLL -b "$in_base" -s one | \
	    grep "^dn:" | cut -f2 -d:| sed -e "s/^ //g"|sort| \
	    while read node ; do
		name=$(echo "$node"|sed -e "s/,$in_base//g"|sed -e "s/.*=//g")
		write_enum_item "$name" "$name" 
	    done
}

all_containers(){
    find_known(){
	cat $CONTAINERS | sort |
	while IFS=':' read name description; do
	    echo "$name"
	done 
    }

    container_def(){
	alterator-dump-desktop \
    	    -v lang="$in_language" \
    	    -v out="Name" \
    	    -v def="notfound;" \
    	    $SERVICEDIR/containers/$name.def
    }

    for name in $(find_known); do 
	write_enum_item "$name" "ou=$name" "$(container_def)"
    done 

}

container_list(){
   find_container(){
	    ldapsearch -x -H ldap://127.0.0.1 -LLL -b $in_base  -s one | \
	    grep "^dn:" | cut -f2 -d:| sed -e "s/^ //g"|sort| \
	    while read node ; do
		echo $node|sed -e "s/,$in_base//g"|sed -e "s/.*=//g"
	    done
    }
    find_known(){
	cat $CONTAINERS | sort |
	while IFS=':' read name description; do
	    echo "$name"
	done 
    }

    container_def(){
	alterator-dump-desktop \
    	    -v lang="$in_language" \
    	    -v out="Name" \
    	    -v def="notfound;" \
    	    $SERVICEDIR/containers/$name.def
    }

    for name in $(find_known); do 
	if [ -n "$in_base" ];then
	found=
    	    for ex in $(find_container); do 
    	        [ "$ex" == "$name" ] || continue
    	        found=1
    		break
	    done
	fi
	[ -z "$found" ] && write_enum_item "$name" "ou=$name" "$(container_def)"
    done 
}

new_base_from_ldif(){
    newdn="$(head -n 1 $cache_dir/new_base.ldif|cut -f2 -d ' ')"
    local domain=$(dn_2_host "$newdn")
    local basedir="$SLAPD_CONF_DIR"
    local template="$basedir/slapd-template.conf"
    local new_dn_conf="$basedir/slapd-$domain.conf"
    local binddn="cn=ldaproot,$newdn"
    local bindpw="$in_bindpw"

    #copy template into slapd-domain.conf
    cp "$template" "$new_dn_conf"
    chmod 640 "$new_dn_conf"
    chown root:ldap "$new_dn_conf"

    [ "$in_encript_pw" == "on" ] && bindpw=`slappasswd -h {SSHA} -s $bindpw`
    #fix dc=. base, password
    sed -i -e "s/dc=template/$newdn/g" $new_dn_conf
    sed -i -e "s/template/$domain/g" $new_dn_conf
    sed -i -e "s/^rootdn.*/rootdn \"$binddn\"/g"  $new_dn_conf
    sed -i -e "s/secret/$(quote_sed_regexp "$bindpw")/g" $new_dn_conf
    sed -i -e "s/REALM/$(to_realm "$domain")/g" $new_dn_conf
    echo "include $new_dn_conf" >>  "$MAIN_SLAPD_CONF"
    export DN_CONF="$new_dn_conf"

    db_dir="$(read_config "$DN_CONF" directory)"
    mkdir -p "$db_dir"
    chmod 700 "$db_dir" 
    # copy default berkeley db config into $db_dir
    # before initial slapadd
    cp "/var/lib/ldap/bases/DB_CONFIG" "$db_dir"
    chown root:ldap "$db_dir/DB_CONFIG"
    chmod 640 "$db_dir/DB_CONFIG"
    # initial slapadd
    slapadd -b "$newdn" -l "$cache_dir/new_base.ldif"
    chown -R ldap:ldap "$db_dir"
    /etc/init.d/slapd restart >/dev/null
    reset_cache
}

read_ldif(){
    newdn="$(head -n 1 $cache_dir/new_base.ldif|cut -f2 -d ' ')"
    prepare_new_base "$newdn"
}

prepare_new_base(){
    local newdnname
    local newfqdnname
    echo "$1" | grep -qs "^dc="
    if [ $? -eq 0 ];then
        newdnname="$1"
        newfqdnname=$(dn_2_host "$1")
    else
        newdnname=$(host_2_dn "$1")
        newfqdnname="$1"
    fi
    grep -qs "$SLAPD_CONF_DIR/slapd-$newfqdnname.conf$" "$MAIN_SLAPD_CONF"
    if [ $? -eq 0 ];then
	write_error "`_ "Base DN"` $1 `_ "already exists in the system"`"
        write_string_param "newfqdnname" ""
	write_string_param "basedn" ""
	write_string_param "binddn" ""
	write_string_param "bindpw" ""
    else
        write_string_param "newfqdnname" "$newfqdnname"
	write_string_param "basedn" "$newdnname"
	write_string_param "binddn" "cn=ldaproot,$newdnname"
	write_string_param "bindpw" `pwqgen`
    fi
}

read_main_config(){
  local url
  url="$(shell_config_get "$LDAP_SYSCONFIG" SLAPDURLLIST)"
  [ "${url/\/\/127\.0\.0\.1\/}" != "$url" ] && write_bool_param 'local' 'yes'
  [ "${url/localhost/}" != "$url" ] && write_bool_param 'local' 'yes'
  [ "${url/ldaps:\/\//}" != "$url" ] && write_bool_param 'enable_tls'  'yes'
  if [ "${url/ldap:\/\/localhost\/[[:space:]]ldaps:\/\/\//}" != "$url" ];then
    write_bool_param 'local_and_tls' 'yes'
    write_bool_param 'local' 'no'
    write_bool_param 'enable_tls' 'no'
  fi
  write_string_param key_state "$(validate_cert)"
}

delete_selected_dn(){
    master_dn=$(/usr/sbin/system-auth status|cut -f2 -d' ')
    local IFS=";"
    for i in $in_avail_dn; do 
    if [ "$master_dn" != "$i" ];then 
    	    ldap-dn delete "$i" ;
    	else
    	   write_error  "`_ "Base DN"` $i `_ " used for system autentification."`"
    	fi
    done
}

check_and_create() {
    local newdnname
    echo "$1" | grep -qs "^dc="
    if [ $? -eq 0 ];then
        newdnname="$1"
    else
        newdnname=$(host_2_dn "$1")
    fi
    grep -qs "$SLAPD_CONF_DIR/slapd-$(dn_2_host "$newdnname").conf$" "$MAIN_SLAPD_CONF"
    if [ $? -eq 0 ];then
        write_error "`_ "Base DN"` $newdnname `_ "already exists in the system"`"
    else
    ldap-dn create "$newdnname"
    fi
}

to_realm()
{
    echo "$1"|tr '[[:lower:]]' '[[:upper:]]'
}

create_expert_dn(){
    local dn="$1"; shift
    local binddn="$1";shift
    local bindpw="$1";shift 
    local encript_pw="$1";shift
    local containers="$1";shift
    local plus_containers="$1";shift

    local domain=$(dn_2_host "$dn")
    local basedir="$SLAPD_CONF_DIR"
    local template="$basedir/slapd-template.conf"
    local new_dn_conf="$basedir/slapd-$domain.conf"

    #copy template into slapd-domain.conf
    cp "$template" "$new_dn_conf"
    chmod 640 "$new_dn_conf"
    chown root:ldap "$new_dn_conf"

    [ "$encript_pw" == "on" ] && bindpw=`slappasswd -h {SSHA} -s $bindpw`
    #fix dc=. base, password
    sed -i -e "s/dc=template/$dn/g" $new_dn_conf
    sed -i -e "s/template/$domain/g" $new_dn_conf
    sed -i -e "s/^rootdn.*/rootdn \"$binddn\"/g"  $new_dn_conf
    sed -i -e "s/secret/$(quote_sed_regexp "$bindpw")/g" $new_dn_conf
    sed -i -e "s/REALM/$(to_realm "$domain")/g" $new_dn_conf
    echo "include $new_dn_conf" >>  "$MAIN_SLAPD_CONF"

    export DN_CONF="$new_dn_conf"
#    base_rootdn_rootpw
#    ldap-init 
     init_ldap_base "$dn" "$binddn" "$containers;$(echo "$plus_containers"|tr ', ' ';')"
    /etc/init.d/slapd restart >/dev/null
}

init_ldap_base(){
    local base="$1";shift
    local rootdn="$1";shift
    local containers="$1";shift

    TMPFILE="$(mktemp -t "ldap-db-init.XXXXXXXXXX")" || fatal "can't create tempfile"
    set_cleanup_handler cleanup_function

    cleanup_function(){
	[ -z "$TMPFILE" ] || rm -rf -- "$TMPFILE"
    }
                        
    db_dir="$(read_config "$DN_CONF" directory)"
# Default entries
cat > "$TMPFILE" <<EOF
dn: $base
objectclass: organization
objectclass: dcObject
$(printf %s\\n "$base" | sed -r 's/^dc=([^[:blank:],]*).*$/dc: \1/')
$(printf %s\\n "$base" | sed -r 's/^.*dc=([^[:blank:],]*)/o: \1/')

dn: $rootdn
objectclass: organizationalRole
$(printf %s\\n "$rootdn" | sed -r 's/^cn=([^[:blank:],]*).*$/cn: \1/')

dn: ou=kdcroot,$base
objectClass: organizationalUnit
ou: kdcroot
EOF

# Creating checked containers
    local IFS=";"
    for container in $containers; do 
cat >> "$TMPFILE" <<EOF

dn: ou=$container,$base
objectClass: organizationalUnit
ou: $container
EOF

    done

mkdir -p "$db_dir"
chmod 700 "$db_dir"

# copy default berkeley db config into $db_dir
# before initial slapadd
cp "/var/lib/ldap/bases/DB_CONFIG" "$db_dir"
chown root:ldap "$db_dir/DB_CONFIG"
chmod 640 "$db_dir/DB_CONFIG"

# initial slapadd
slapadd -b "$base" -l "$TMPFILE"
chown -R ldap:ldap "$db_dir"

}

upgrade_base(){
#    local base="$in_base"
#    local rootdn="$in_binddn"
#    local rootpw="$in_bindpw"
     local containers="$in_containers;$(echo "$in_plus_containers"|tr ', ' ';')"

    TMPFILE="$(mktemp -t "ldap-db-upgr.XXXXXXXXXX")" || fatal "can't create tempfile"
    set_cleanup_handler cleanup_function

    cleanup_function(){
	[ -z "$TMPFILE" ] || rm -rf -- "$TMPFILE"
    }
# Creating checked containers
    local IFS=";"
    for container in $containers; do 
cat >> "$TMPFILE" <<EOF

dn: ou=$container,$in_base
objectClass: organizationalUnit
ou: $container
EOF
    done
    
    ldapadd -a -c -x -D "$in_binddn" -w "$in_bindpw" -h "localhost" -f $TMPFILE
}

determine_mode(){
	DN_CONF=$(ldap-dn find "$in_name")
	rootdn=$(egrep "rootdn" "$DN_CONF"|cut -f2 -d' '|tr -d '"')
	rootpw=$(egrep "rootpw" "$DN_CONF"|cut -f2 -d' ')

	write_string_param DN_CONF "$DN_CONF"
	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
}

make_ldif(){
    file="$cache_dir/$(dn_2_host $in_base)_$(date +%Y-%m-%d).ldif"
    /usr/sbin/slapcat -b "$in_base" > $file
    write_blob_param "ldif_file" "$file"
}

reset_cache

on_message()
{
    case "$in_action" in
	check)
	    check_samba3
	;;
	repair)
	    repair_samba3
	;;
        list)
        case "$in__objects" in
            avail_dn)
        	list_dn
            ;;
            avail_actions)
        	list_actions
            ;;
            available_containers)
        	if [ -n "$in_base" ]; then
            	    container_list "$in_base"
                else
	    	    all_containers
		fi
	    ;;
	    present_containers)
	    [ -n "$in_base" ] && present_list "$in_base"
	    ;;
	    avail_schema)
		list_schema
	    ;;
	    *)
	    ;;
        esac
        ;;
        read)
        case "$in__objects" in
            config)
        	! slapd_daemon_status
		write_bool_param daemon "$?"
		write_string_param daemon_state "$(service slapd status)"
		read_main_config
            ;;
            selected_dn)
                write_string_param  bdn "$in_name"
                determine_mode
            ;;
            available_containers)
		write_string_param "available_containers" "$DEFAULT_CONTAINERS"
            ;;
            base)
              [ -n "$in_newdnname"  ] || return 0
              prepare_new_base "$in_newdnname"
            ;;
            fromldif)
        	   [ -f "$cache_dir/new_base.ldif" ] && read_ldif "$cache_dir/new_base.ldif"
            ;;
            avail_schema)
        	status_schema
            ;;
            *)
            ;;
        esac
        ;;
        save)
            local host=""
            local url="ldap://localhost/"

            if [ "$in_enable_tls" = "#t" ]; then
                url="ldaps:///"
            fi
            
            if [ "$in_enable_tls" = "#t" -a "$in_local" = "#t" ]; then
                url="ldaps://localhost/"
            fi

	    if [ "$in_local_and_tls" = "#t" ]; then
                url="ldap://localhost/ ldaps:///"
            fi
            write_tls
	    shell_config_set "$LDAP_SYSCONFIG" SLAPDURLLIST "\"'$url'\""
         ;;
        write)
        case "$in__objects" in
        avail_schema)
            [ -n "$in_schemas" ] && write_schemas "$in_schemas"
        ;;
        daemon)
            if test_bool "$in_status"; then
		slapd_daemon_on
	    else
		slapd_daemon_off
	    fi
	;;
	newldif)
	    [ -z "$in_ldif_file" ] || cp -f "$in_ldif_file" "$cache_dir/new_base.ldif" 
	;;
        *)
        ;;
        esac
        ;;
        new)
           case "$in__objects" in
            base)
              [ -n "$in_newdnname"  ] || fatal "DN not set"
              prepare_new_base "$in_newdnname"
            ;;
            dn)
        	# create new dn
        	#echo "$(set|grep -a "in_")" >&2
        	[ -n "$in_newdnname"  ] || fatal "DN not set"
        	check_and_create "$in_newdnname" 
            ;;
            schema)
        	[ -z "$in_schema_file" ] || new_schema
            ;;
            fromldif)
               [ -n "$in_bindpw" -a -n "$in_encript_pw" ] || \
               write_error "`_ "Not all data present."`"
               [ -f "$cache_dir/new_base.ldif" ] && new_base_from_ldif
            ;;
            exp_dn)
               #echo "$(set|grep -a "in_")" >&2
               [ -n "$in_basedn" -a -n "$in_binddn" -a -n "$in_bindpw" -a -n "$in_encript_pw" ] || \
               write_error "`_ "Not all data present."`"
               create_expert_dn "$in_basedn" "$in_binddn" "$in_bindpw" "$in_encript_pw" "$in_containers" "$in_plus_containers"
    	    esac
        ;;
        delete)
           # echo "$(set|grep -a "in_")" >&2
            [ -z "$in_avail_dn"  ] || delete_selected_dn "$in_avail_dn"
        ;;
	generate)
        	write_string_param "bindpw" $(pwqgen)
        ;;
        upgrade)
    	    case "$in__objects" in
    	    exp_dn)
    	      [ -z "$in_containers" ] || upgrade_base
    	    ;;
    	    esac
        ;;
        download)
    	    [ -n "$in_base" ] && make_ldif $in_base
        ;;
    esac
}

message_loop
