#!/bin/sh

network_file=/etc/sysconfig/network
system_file=/etc/sysconfig/system
resolvconf=/sbin/resolvconf
resolvconf_conf=/etc/resolvconf.conf
resolv_conf=/etc/resolv.conf
samba_tool=/usr/bin/samba-tool
max_hostname_length=64
alterator_api_version=1

. alterator-sh-functions
. avahi-sh-functions
. shell-config
. shell-var
. shell-ini-config
. alterator-openldap-functions

test_resolver()
{
	cat /etc/resolv.conf | grep -qs "^nameserver 127.0.0.1"
	if [ $? -eq 1 ]; then
		echo "ERROR: nameserver 127.0.0.1 not present in resolv.conf"
	else
		nameserver=`grep -s ^nameserver /etc/resolv.conf | head -n1`
		if echo $nameserver | grep -qs 127.0.0.1 ; then
			echo "OK"
		else
			echo "ERROR: 127.0.0.1 is not first in resolv.conf"
		fi
	fi
}

test_access()
{
	ping -c 1 $(read_hostname) > /dev/null 2>&1
	if [ $? -eq 1 ]; then
		echo "ERROR: fqdn is unpingable"
	else
		echo "OK"
	fi

}

test_ldap()
{
	fqdn=$(read_hostname)
	domain="${fqdn#*.}"
	dn="$(host_2_dn "$domain")"
	slapd_conf=`ldap-dn find $dn`
	if [ $? -ne 0 -o -z "$slapd_conf" ]; then
		echo "ERROR: no slapd conffile for dn: '$dn'"
		exit
	fi
	slapd_rootpw=`grep ^rootpw $slapd_conf | sed -e 's,^rootpw ,,'`
	if [ -z $slapd_rootpw ] ;then
		echo "ERROR: no rootpw specified"
		exit
	fi
	slapd_rootdn=`grep ^rootdn $slapd_conf | sed -e 's,^rootdn ,,' | sed -e 's,",,g'`
	if [ -z $slapd_rootdn ] ; then
		echo "ERROR: no rootdn specified"
		exit
	fi

	ldapsearch -x -H "ldaps://$fqdn" -b "$dn" -D "$slapd_rootdn" -w"$slapd_rootpw"  > /dev/null 2>&1
	if [ $? -ne 0 ]; then
		echo "Error: connect to ldaps://$fqdn failed"
		exit
	fi

	ldapsearch -x -H "ldaps://$fqdn" -b "$dn" -D "$slapd_rootdn" -w"$slapd_rootpw" ou=People | grep -v "^#" | grep "ou: People" > /dev/null 2>&1
	if [ $? -eq 0 ]; then
		echo "OK"
	else
		echo "Error: failed to ldapsearch ou=People, slapd is broken"
	fi
}

test_kdc()
{
	if ! service krb5kdc status &> /dev/null ; then
		echo "Error: krb5kdc service is stopped"
	fi
	fqdn=$(read_hostname)
	domain="${fqdn#*.}"
	dn="$(host_2_dn "$domain")"
	slapd_conf=`ldap-dn find $dn`
	if [ $? -ne 0 -o -z "$slapd_conf" ]; then
		echo "ERROR: no slapd conffile for dn: '$dn'"
		exit
	fi
	slapd_rootpw=`grep ^rootpw $slapd_conf | sed -e 's,^rootpw ,,'`
	slapd_rootdn=`grep ^rootdn $slapd_conf | sed -e 's,^rootdn ,,' | sed -e 's,",,g'`
	ldapsearch -x -h localhost -b "$dn" -D "$slapd_rootdn"  "ou=kdcroot" -w"$slapd_rootpw" | grep -v "^#" | grep "ou: kdcroot" > /dev/null 2>&1
	if [ $? -ne 0 ]; then
		echo "Error: failed to ldapsearch ou=kdcroot, no KDC base in LDAP"
		exit
	fi

	ldapsearch -x -h localhost -b "$dn" -D "$slapd_rootdn"  "objectClass=krbRealmContainer" -w"$slapd_rootpw" | grep -v "^#" | grep "objectClass: krbRealmContainer" > /dev/null 2>&1
	if [ $? -eq 0 ]; then
		echo "OK"
	else
		echo "Error: failed to ldapsearch objectClass=krbRealmContainer, no realm container in LDAP"
	fi

}


test_smb()
{
	if service smb status &> /dev/null ; then
        workgroup="$(ini_config_get /etc/samba/smb.conf global workgroup)"
		echo "OK ($workgroup)"
	else
		echo "Error: smbd service is stopped"
	fi
}

test_dhcpd()
{
	fqdn=$(read_hostname)
	domain="${fqdn#*.}"
	grep -qs "option domain-name \"$domain\";" /etc/dhcp/dhcpd.conf
	if [ $? -ne 0 ]; then
		echo "Error: domain name $domain is not provided to clients"
		exit
	fi
	grep -qs "option domain-name-servers" /etc/dhcp/dhcpd.conf
	if [ $? -ne 0 ]; then
		echo "Error: domain server is not provided to clients"
		exit
	else
		echo "OK"
	fi
}


check_hostname()
{
    local hn="$1"

    # Check valid hostname according RFC 1035
    valid_hostname="$(echo "$hn"|egrep '^([a-zA-Z]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?$')"
    if [ -z "$valid_hostname" ]; then
        write_error "`_ "Domain name is not valid according RFC 1035"`"
        return 1
    fi

    return 0
}

read_hostname()
{
    local value="$(shell_config_get "$network_file" HOSTNAME)"
    [ -n "$value" ] || value="localhost.localdomain"
    echo "$value"
}

read_forwarders()
{
	local forwarders=""
	# Get preinstalled from smb.conf
	forwarders="$(ini_config_get '/etc/samba/smb.conf' global 'dns forwarder')"

	echo "$forwarders"
}

read_domain()
{
    local value="$(read_hostname)"
    local domain="${value#*.}"
    [ "$value" != "$domain" ] || domain="localdomain"

    # Check AD domain
    if [ "$(read_domain_type)" == 'ad' ]; then
        domain="$($samba_tool domain info 127.0.0.1 | sed 's/ *: /:/' | sed -n 's/^Domain://p')"
    fi

    echo "$domain"
}

write_domain()
{
    local old_value="$(read_hostname)"
    local old_name="${old_value%%.*}"
    local new_value="$old_name.$1"

    check_hostname "$new_value" || return 1
    shell_config_set "$network_file" HOSTNAME "$new_value"
    hostname "$new_value"
    if [ -e "$resolvconf_conf" ]; then
	local search_eregexp1="^search_domains=([\"']|.+[[:blank:]])?"
	local search_eregexp2="([\"']|[[:blank:]].+)?$"
	local old_domain="${old_value#*.}"
	if egrep -qs "$search_eregexp1$old_domain$search_eregexp2" "$resolvconf_conf"; then
		sed -i -r "s;$search_eregexp1$old_domain$search_eregexp2;search_domains=\1$1\2;" "$resolvconf_conf"
	elif ! egrep -qs "$search_eregexp1$1$search_eregexp2" "$resolvconf_conf"; then
		local search_domains="$(shell_config_get "$resolvconf_conf" search_domains)"
		shell_var_unquote search_domains "$search_domains"
		search_domains="$1${search_domains:+ }$search_domains"
		shell_config_set "$resolvconf_conf" search_domains "\"$search_domains\""
	fi
	"$resolvconf" -u
    else
	# if resolvconf is not installed then
	# write in the resolv.conf file directly
	shell_config_set "$resolv_conf" domain "$1" ' ' ' '
    fi
    run-parts /etc/hooks/hostname.d "$old_value" "$new_value"

    return 0
}

read_role()
{
    shell_config_get "$system_file" SERVER_ROLE
}

read_domain_type()
{
    local domain_type='dns'

    # Check ALT domain
    if [ "$(read_role)" == 'master' ];then
	domain_type='altdomain'
    fi

    # Check Samba AD DC
    if service samba status &> /dev/null ; then
	domain_type="ad"
    fi

    echo "$domain_type"
}

write_role()
{
    local old_server_role="$(read_role)"
    local new_server_role="$1";shift
    if [ "$old_server_role" != "$new_server_role" ];then
	shell_config_set "$system_file" SERVER_ROLE "$new_server_role"
    fi
    export old_server_role
    export new_server_role
    run-parts /usr/lib/alterator/hooks/net-domain.d
}

ad_provision_domain()
{
    local domain_name="$1"
    shift
    local log="/tmp/samba-dc-provision-$(date +%d.%m.%Y-%H:%M:%S)-$domain_name.log"

    # Begin log
    date > "$log"
    echo "Domain: $domain_name" >> "$log"
    echo >> "$log"

    # Cleanup old domain
    rm -f /etc/samba/smb.conf >> "$log"
    rm -rf /var/lib/samba >> "$log"
    mkdir -p /var/lib/samba/sysvol >> "$log"

    # Stop and disable any conflict services
    for service in smb nmb krb5kdc slapd bind
    do
	service $service stop >> "$log" ||:
    	chkconfig $service off >> "$log" ||:
    done

    # Make provision
    read -r -d '' o <<EOF
        $samba_tool domain provision \
	--realm  "$domain_name" \
	--domain "${domain_name/.*/}" \
	--dns-backend=SAMBA_INTERNAL \
	--server-role=dc \
	--use-rfc2307 \
	--use-xattrs=yes
EOF
	echo "$o" >> "$log"
	echo >> "$log"

	eval "$o" 2>&1 | grep -v '^Admin password:' >> "$log.process"
	ret=$?

	# Merge logs
	cat "$log.process" >> "$log"
	rm -f "$log.process"

	if [ $ret -ne 0 ]; then
		write_error "`_ "Error provision Active directory domain. Log in file"` $log"
	else
		service samba restart
		sleep 3
	fi

	# Enable samba service
	chkconfig samba on

	echo "Finished at $(date)" >> "$log"
}

on_message() {
  case "$in_action" in
    type)
	write_type_item domain hostname
	;;
    read)
	write_string_param domain "$(read_domain)"
	local role="$(read_role)"
	# Return current domain type
	write_string_param domain_type "$(read_domain_type)"

	# Status of ALT domain and DNS
	write_string_param resolver "$(test_resolver)"
	write_string_param access "$(test_access)"
	write_string_param ldap "$(test_ldap)"
	write_string_param kdc "$(test_kdc)"
	write_string_param smb "$(test_smb)"
	write_string_param dhcpd "$(test_dhcpd)"
	[ "$role" != "master" ]
	write_bool_param master $?

	# Active Directory DC status
	local ad_service="inactive"
	if service samba status &> /dev/null ; then
		ad_service="OK"
		if service bind status &> /dev/null ; then
			ad_service="`_ "NOT OK (bind is running)"`"
		fi
		# Read configuration
		ad_status_string="$((net ads info 127.0.0.1;$samba_tool domain info 127.0.0.1) | sed 's/ *: /:/')"
		local ad_domain="$(echo "$ad_status_string"|sed -n 's/^Domain://p')"
		local ad_realm="$(echo "$ad_status_string"|sed -n 's/^Realm://p')"
		local ad_dc_name="$(echo "$ad_status_string"|sed -n 's/^DC name://p')"
		local ad_ldap_ip="$(echo "$ad_status_string"|sed -n 's/^LDAP server://p')"
		local ad_ldap_server="$(echo "$ad_status_string"|sed -n 's/^LDAP server name://p')"
		local ad_kdc_server="$(echo "$ad_status_string"|sed -n 's/^KDC server://p')"
	else
		ad_service="`_ "NOT OK (samba service is stopped)"`"
	fi

	local forwarders="$(read_forwarders)"
	if [ "${forwarders:-127.0.0.1}" = "127.0.0.1" ]; then
		# Get initial value from resolvconf
		forwarders="$(/sbin/resolvconf -l|sed -n 's/^nameserver //p'|head -n1)"
	fi

	write_string_param ad_dns "$forwarders"
	write_string_param ad_service "${ad_service:---}"
	write_string_param ad_domain "${ad_domain:---}"
	write_string_param ad_realm "${ad_realm:---}"
	write_string_param ad_dc_name "${ad_dc_name:---}"
	test -n "$ad_ldap_ip" && ad_ldap_server="$ad_ldap_server ($ad_ldap_ip)"
	write_string_param ad_ldap_server "${ad_ldap_server:---}"
	write_string_param ad_kdc_server "${ad_kdc_server:---}"

	# Check availability of domain types
	rpm -q alt-domain-server &>/dev/null
	if [ $? -eq 0 ];then
		write_bool_param altdomain_is_available "on"
	else
		write_bool_param altdomain_is_available "off"
	fi
	rpm -q samba-DC &>/dev/null
	if [ $? -eq 0 ]; then
		write_bool_param ad_is_available "on"
	else
		write_bool_param ad_is_available "off"
	fi
	;;
    write)
	# Variables:
	# $in_domain - domain name
	# $in_domain_type - domain type (altdomain, ad, dns)
	if [ -z "$in_domain" ]; then
	    write_error "`_ "Please define domain name"`"
	    return
	elif echo "$in_domain"|egrep -iwqs "localdomain|localhost|local"; then
	    write_error "`_ "This domain name is registered for internal purposes"`"
	    return
	fi

	# ALT Domain or DNS
	if [ "$in_domain_type" == 'altdomain' -o  "$in_domain_type" == 'dns' ];then
		service samba stop 2>/dev/null
		# Note: write_role should be before write_domain, hooks can use server role value
		role=none
		if [ "$in_domain_type" == 'altdomain' ]; then
			role=master
			chkconfig slapd on
			chkconfig krb5kdc on
		fi
		write_role "$role"
		write_domain "$in_domain" "1" && \
		publish_service alterator-net-domain 'ALT Linux Server (%h)' '_server._tcp' '0' "role=$(read_role)" "domain=$(read_domain)"
		chkconfig bind on
		service bind start
		chkconfig samba off
	fi

	# Active Directory
	if [ "$in_domain_type" == 'ad' ];then
		env > /tmp/net-domain.txt
		ad_current_domain="$($samba_tool domain info 127.0.0.1 | sed 's/ *: /:/' | sed -n 's/^Domain://p')"
		# Check creation of existing domain
		if [ "$ad_current_domain" != "$in_domain" ]; then
			write_domain "$in_domain" "1"
			service bind stop
			ad_provision_domain "$in_domain"
		fi

		# Set dns forwarders
		# Samba 4.5 and later supports a space-sparated list of IPs. Older versions support only one IP.
		if [ -n "$in_ad_dns" -a "$in_ad_dns" != "$(read_forwarders)" ]; then
			ini_config_set '/etc/samba/smb.conf' global 'dns forwarder' "$in_ad_dns"
			service samba reload
		fi

		# Set Administrator password
		if [ -n "$in_ad_password" ]; then
			if [ "$in_ad_password" != "$in_ad_password2" ]; then
				write_error "`_ "Passwords mismatch"`"
			else
				err="$($samba_tool user setpassword Administrator --newpassword="$in_ad_password" 2>&1 >/dev/null)"
				if [ -n "$err" ]; then
					if [[ "$err" == *'the password is too short.'* ]]; then
						err="`_ "the password is too short. It should be equal or longer than 7 characters!"`"
					elif [[ "$err" == *'the password does not meet the complexity criteria'* ]]; then
						err="`_ "the password does not meet the complexity criteria (must contain characters from three of the following five categories: latin letters in upper and lower cases, digits, nonalphanumeric and any other Unicode characters)!"`"
					fi
					write_error "`_ "Error set password for Administrator: "`$err"
				fi
				$samba_tool user setexpiry --noexpiry Administrator
			fi
		fi
	fi
	;;
  esac
}

message_loop
