#!/bin/bash
#
# Univention Join
#  joins a system into a UCS domain
#
# SPDX-FileCopyrightText: 2004-2025 Univention GmbH
# SPDX-License-Identifier: AGPL-3.0-only

# execute univention-server-join
LOG='/var/log/univention/join.log'

export PATH="$PATH:/sbin:/usr/sbin:/bin:/usr/bin"

eval "$(univention-config-registry shell)"

. /usr/share/univention-join/joinscripthelper.lib
. /usr/share/univention-lib/all.sh

SIMPLEGUI=""
TYPE=
USERTMP="$(mktemp -d)"
DCPWD="$USERTMP/dcpwd"
VERSION_CHECK=true
CHECK_RUN=false
VERBOSE=false
SKIPIPMAC=false
SKIPADMEMBERMODE=false
CONTAINERADMEMBERMODE=false

trapOnExit() {
	rm -rf "$USERTMP"
	joinscript_remove_credentialfiles
	if [ -n "$VERBOSE" -a "$VERBOSE" = "true" ]; then
		if [ -n "$old_listener_debug_level" ]; then
			ucr set listener/debug/level="$old_listener_debug_level" >>/var/log/univention/join.log 2>&1
		fi
	fi
	echo "$(LC_ALL=C date): finish $0" >>/var/log/univention/join.log 2>&1
}

trap trapOnExit EXIT

display_help() {
	display_header
	cat <<-EOL
	Syntax:
	  univention-join [options]

	Options:
	  -dcname <fqdn>:                Primary Directory Node FQDN
	  -dcaccount <account>:          Name of Primary Directory Node account
	  -dcpwd <password file>:        File with Primary Directory Node password
	  -type <type>:                  Type of computer, e.g. "client"
	  -ldapbase <ldap base>:         LDAP Base DN, e.g. dc=ucs,dc=example
	  -realm <kerberos realm>:       Kerberos realm, e.g. UCS.EXAMPLE
	  -windom <windows domain name>: Name of the windows (Samba) domain, e.g. UCS
	  -disableVersionCheck           Disable version check against _dcname_
	  -checkPrerequisites            Just perform some tests without changing anything
	  -skipIpMac                     Do not register IP and MAC in machine account
	  -skipAdMemberMode              Do not activate AD member mode during join
	  -containerAdMemberMode         Configure ad member in container mode
	  -verbose                       Enable verbose logging (/var/log/univention/join.log)

	  -h | --help | -?:              Print this usage message and exit program
	  --version:                     Print version information and exit program

	Description:
	  univention-join joins a computer to an ucs domain,
	  e.g. univention-join -dcaccount backup_join -dcpwd /tmp/pwd_secret

	EOL
}

display_header() {
	echo "univention-join: joins a computer to an ucs domain"
	echo "copyright (c) 2001-@%@copyright_lastyear@%@ Univention GmbH, Germany"
	echo ""
}

display_version() {
	echo "univention-join @%@package_version@%@"
}

failed_message () {
	{
	echo ""
	echo ""
	echo "**************************************************************************"
	echo "* Join failed!                                                           *"
	echo "* Contact your system administrator                                      *"
	echo "**************************************************************************"
	echo "* Message:  Please visit https://help.univention.com/t/8842 for common problems during the join and how to fix them -- $@"
	echo "**************************************************************************"
	} | tee -a /var/log/univention/join.log
	[ -x /usr/sbin/univention-admin-diary-entry-create ] && [ -n "${context_id:-}" ] &&
	/usr/sbin/univention-admin-diary-entry-create --event JOIN_FINISHED_FAILURE --arg "hostname=$(ucr get hostname)" --context-id "$context_id" > /dev/null
	exit 1
}

bashquote () { # quote arguments for eval
	declare -a escaped
	declare -r quote=\\\'
	local arg
	for arg in "$@"
	do
		escaped+=("'${arg//\'/'$quote'}'")
	done
	echo -n "${escaped[@]}"
}

copy_ca_cert () {
	local ca dst='/etc/univention/ssl/ucsCA'

	{
	rm -rf "$dst"
		install -m 0755 -d "$dst"
		for ca in ucsCA udsCA
		do
			[ -e "$dst/CAcert.pem" ] && break
			univention-scp "$DCPWD" -q "${DCACCOUNT}@${DCNAME}:/etc/univention/ssl/${ca}/CAcert.pem" "$dst/CAcert.pem"
		done
	} >>/var/log/univention/join.log 2>&1
	grep -q '^TLS_CACERT' /etc/ldap/ldap.conf || echo "TLS_CACERT /etc/univention/ssl/ucsCA/CAcert.pem" >>/etc/ldap/ldap.conf
}

setup_ssl () {
	local ca dst='/etc/univention/ssl/ucsCA'

	if [ "$1" = "force" ]
	then
		copy_ca_cert
	fi

	# prevent join from failing if umask is modified (Bug #21587)
	chmod 755 /etc/univention/ssl
	chmod 755 /etc/univention/ssl/ucsCA
	chmod 644 /etc/univention/ssl/ucsCA/CAcert.pem

	ln -snf /etc/univention/ssl/ucsCA/CAcert.pem /usr/local/share/ca-certificates/ucsCA.crt
	update-ca-certificates --fresh >>/var/log/univention/join.log 2>&1

	check_ldap_tls_connection

	download_host_certificate
}

download_host_certificate () {
	echo -n "Download host certificate: "
	/usr/sbin/univention-fetch-certificate "$hostname" "$DCNAME" >>/var/log/univention/join.log 2>&1 || failed_message "failed to get host certificate"
	echo -e "\033[60Gdone"
}

check_ldap_tls_connection () {
	echo -n "Check TLS connection: "

	eval "$(ucr shell ldap/master/port)"

	univention-ldapsearch -s base -H "ldap://$DCNAME:$ldap_master_port" -D "$binddn" --bindpwdfile "$DCPWD" dn >/dev/null
	if  [ $? != 0 ]; then
 		failed_message "Establishing a TLS connection with $DCNAME failed. Maybe you didn't specify a FQDN."
	fi

	echo -e "\033[60Gdone"
}

run_join_scripts () {

	LC_COLLATE="C"
	joinscript_check_status_file
	joinscript_create_credentialfiles "$binddn" "$DCPWD" "$DCACCOUNT"

	if test -d "/usr/lib/univention-install/"; then
		for i in /usr/lib/univention-install/*.{inst,uinst}; do
			test -e "$i" || continue
			echo -n "Configure $(basename "$i") "
			[ -n "$SIMPLEGUI" ] && echo
			echo "Configure $(basename "$i") $(LC_ALL=C date)" >>/var/log/univention/join.log
			bashVerbose=""
			if [ -n "$VERBOSE" -a "$VERBOSE" = "true" ]; then
				bashVerbose="bash -x"
			fi

			local args=()
			if joinscript_check_api_nocredentials "$i"; then
				args=()
			else
				args=(--binddn "$binddn" --bindpwdfile "$DCPWD")
			fi
			$bashVerbose "$i" "${args[@]}" >>/var/log/univention/join.log 2>&1

			if [ $? -ne 0 ]; then
				echo -e "\033[60Gfailed"
				failed_message "FAILED: $(basename "$i")"
			else
				echo -e "\033[60Gdone"
				delete_unjoinscript "$(basename "$i")"
			fi
			if [ "$server_role" = "domaincontroller_slave" -o "$server_role" = "domaincontroller_backup" ]; then

				# check failed.ldif
				if [ "$(basename "$i")" = "03univention-directory-listener.inst" ]; then
					if [ -e /var/lib/univention-directory-replication/failed.ldif ]; then
						failed_message "FAILED: failed.ldif exists."
					fi

					# Backup Node
					if [ "$server_role" = "domaincontroller_backup" ]; then
						univention-scp "$DCPWD" -r \
							"${DCACCOUNT}@${DCNAME}:/var/lib/univention-ldap/notify/transaction" \
							"$USERTMP/tlog" >/dev/null 2>&1
						if [ ! -e "$USERTMP/tlog" ]; then
							failed_message " FAILED: failed to copy /var/lib/univention-ldap/notify/transaction from the Primary Directory Node. Please try again."
						fi

						id="$(</var/lib/univention-directory-listener/notifier_id)"
						awk -F ' ' '{ if ( $1 <= '$id') print }' "$USERTMP/tlog" >/var/lib/univention-ldap/notify/transaction
						rm -f /var/lib/univention-ldap/notify/transaction.index

						/etc/init.d/slapd stop >>/var/log/univention/join.log 2>&1
						/usr/share/univention-ldap/setup-translog init
						/etc/init.d/slapd start >>/var/log/univention/join.log 2>&1
						/usr/share/univention-directory-notifier/univention-translog --lenient import --index 2>>"$LOG" &&
							/usr/share/univention-ldap/setup-translog check ||
							failed_message " FAILED: failed to setup $(/usr/share/univention-ldap/setup-translog db). Please try again."
					fi

					# Replica Node
					if [ "$server_role" = "domaincontroller_slave" ]; then
						if [ -n "$listener_supply_notifier" -a "$listener_supply_notifier" = "yes" ]; then
							univention-scp "$DCPWD" -q \
								"${DCACCOUNT}@${DCNAME}:/var/lib/univention-ldap/notify/transaction" \
								"$USERTMP/tlog" >/dev/null 2>&1
							id="$(</var/lib/univention-directory-listener/notifier_id)"
							awk -F ' ' '{ if ( $1 <= '$id') print }' "$USERTMP/tlog" >/var/lib/univention-ldap/notify/transaction
							rm -f /var/lib/univention-ldap/notify/transaction.index
						fi

					fi
				fi
			fi
		done
	fi
	joinscript_remove_credentialfiles
}

check_and_configure_ad_member_mode () {

	if "$SKIPADMEMBERMODE"; then
		echo "AD Member Mode disabled by -skipAdMemberMode"
		return 0
	fi

	# special handling for UCS in container
	local role="$server_role"
	if "$CONTAINERADMEMBERMODE"; then
		role="container"
	fi

	# switch to ad member mode
	if is_domain_in_admember_mode; then
		if ! is_localhost_in_admember_mode; then
			echo -n "Entering AD Member Mode: "
			configure_nonmaster_as_ad_member "$role" >>/var/log/univention/join.log 2>&1 || failed_message "could not set AD Member Mode"
			echo -e "\033[60Gdone"
		fi
	else
		if is_localhost_in_admember_mode; then
			echo -n "Leaving AD Member Mode: "
			revert_nonmaster_ad_member "$role" >>/var/log/univention/join.log 2>&1 || failed_message "could not revert AD Member Mode"
			echo -e "\033[60Gdone"
		fi
	fi
}

reset_status_file () {
	mkdir -p /var/univention-join/
	[ -e "/var/univention-join/status" ] && rm -rf /var/univention-join/status
	[ -e "/usr/lib/univention-install/.index.txt" ] && rm /usr/lib/univention-install/.index.txt
	touch /var/univention-join/status
	ln -sf /var/univention-join/status /usr/lib/univention-install/.index.txt
}

# log univention-join call
echo "$(LC_ALL=C date): starting $0 $*" >>/var/log/univention/join.log 2>&1

umask 0022  # prevent join from failing if umask is modified (Bug #56634)

while [ $# -gt 0 ]
do
	case "$1" in
		"-dcname")
			DCNAME="${2:?missing Primary Directory Node FQDN}"
			shift 2 || exit 2
			;;
		"-dcaccount")
			DCACCOUNT="${2:?missing Primary Directory Node account}"
			shift 2 || exit 2
			;;
		"-dcpwd")
			dcpwd="${2:?missing Primary Directory Node password file}"
			shift 2 || exit 2
			cp "$dcpwd" "$DCPWD" || exit 2
			;;
		"-ldapbase")
			LDAPBASE="${2:?missing LDAP base}"
			shift 2 || exit 2
			;;
		"-realm")
			REALM="${2:?missing kerberos realm}"
			shift 2 || exit 2
			;;
		"-windom")
			WINDOM="${2:?missing windows domain name}"
			shift 2 || exit 2
			;;
		"-type")
			TYPE="${2:?missing computer role}"
			shift 2 || exit 2
			;;
		"-simplegui")
			# output simpler gui for univention-installer to be able to parse output
			shift
			SIMPLEGUI="yes"
			;;
		"-disableVersionCheck")
			shift
			VERSION_CHECK=false
			;;
		"-checkPrerequisites")
			shift
			CHECK_RUN=true
			;;
		"--version")
			display_version
			exit 0
			;;
		"--help"|"-h"|"-?")
			display_help
			exit 0
			;;
		"-verbose")
			VERBOSE="true"
			shift
			;;
		"-skipIpMac")
			SKIPIPMAC=true
			shift
			;;
		"-skipAdMemberMode")
			SKIPADMEMBERMODE=true
			shift
			;;
		"-containerAdMemberMode")
			CONTAINERADMEMBERMODE=true
			shift
			;;
		*)
			display_help
			exit 1
			;;
	esac
done

# verbose logging for univention-join and listener
if [ -n "$VERBOSE" -a "$VERBOSE" = "true" ]; then
	exec 2>>/var/log/univention/join.log
	set -x
	if [ -n "$listener_debug_level" ]; then
		old_listener_debug_level="$listener_debug_level"
	else
		old_listener_debug_level="2"
	fi
	ucr set listener/debug/level=4 >>/var/log/univention/join.log 2>&1
	listener_debug_level=4
fi

display_header

if [ "$server_role" = "domaincontroller_master" ]; then
	echo "Join on Primary Directory Node impossible"
	exit 1
fi

if [ -z "$DCACCOUNT" ]; then
	echo -n "Enter Primary Directory Node Account : "
	read DCACCOUNT
fi
if [ ! -f "$DCPWD" ]; then
	echo -n "Enter Primary Directory Node Password: "
	read -s password
	echo -n "$password" >>"$DCPWD"
	echo ""
	echo ""
fi
chmod 600 "$DCPWD"

if [ -n "$TYPE" ]; then
	server_role="$TYPE"
fi
if [ -z "$server_role" ]; then
	failed_message "No server role defined"
fi


if [ -z "$DCNAME" ]; then
	echo -n "Search Primary Directory Node: "
	DCNAME="$(host -t SRV "_domaincontroller_master._tcp.$domainname" | sed -ne '$s/.* \([^ ]\+\)\.$/\1/p')"
	if [ -n "$DCNAME" ]; then
		echo -e "\033[60Gdone"
	else
		for i in "$nameserver" "$nameserver1" "$nameserver2" "$nameserver3" "$dns_forwarder1" "$dns_forwarder2" "$dns_forwarder3"; do
			if [ -z "$i" ]; then continue; fi
			DCNAME="$(host -t SRV "_domaincontroller_master._tcp.$domainname" "$i" | sed -ne '$s/.* \([^ ]\+\)\.$/\1/p')"
			if [ -n "$DCNAME" ]; then
				echo -e "\033[60Gdone"
				echo "domain $domainname" >/etc/resolv.conf
				echo "nameserver $i" >>/etc/resolv.conf
				test -x /etc/init.d/nscd && /etc/init.d/nscd restart >>/var/log/univention/join.log 2>&1
				break
			fi
		done
	fi
fi
DCNAME="${DCNAME%.}"
if [ -z "$DCNAME" ]; then
	failed_message "missing dns service record for _domaincontroller_master._tcp.$domainname"
fi


echo -n "Check Primary Directory Node: "

nslookup_out="$(nslookup "$DCNAME" 2>&1)"
if [ $? -ne 0 ]; then
	nslookup_out=$(echo "$nslookup_out" | tr '\n' ' ' | tr  '\r' ' ')
	nameservers="$(cat /etc/resolv.conf | sed -ne 's/nameserver //p'| tr -d '\n')"
	failed_message "The UCS Primary Node name \"$DCNAME\" is unknown to the DNS servers (dns servers: $nameservers, nslookup: $nslookup_out)."
fi

ping_out="$(ping -q -c 3 "$DCNAME" 2>&1)"
if [ $? -ne 0 ]; then
	ping6_out="$(ping6 -q -c 3 "$DCNAME" 2>&1)"
	if [ $? -ne 0 ]; then
		ping_out=$(echo "$ping_out" | tr '\n' ' ' | tr  '\r' ' ')
		ping6_out=$(echo "$ping6_out" | tr '\n' ' ' | tr  '\r' ' ')
		failed_message "The UCS Primary Node \"$DCNAME\" is not reachable, ping failed (ping ipv4: $ping_out, ping ipv6: $ping6_out)."
	fi
fi

ssh_out="$(univention-ssh -timeout 10 "$DCPWD" "${DCACCOUNT}@${DCNAME}" ls 2>&1)"
if [ $? -ne 0 ]; then
	ssh_out=$(echo "$ssh_out" | tr '\n' ' ' | tr  '\r' ' ')
	failed_message "The ssh-login to ${DCACCOUNT}@${DCNAME} failed with \"${ssh_out}\". Please make sure the account ${DCACCOUNT} exists and is a member of the Domain Admins group!"
fi

# get Primary Node versions
versions="$(univention-ssh "$DCPWD" "${DCACCOUNT}@${DCNAME}" /usr/sbin/ucr search --brief ^version/)"
OLDIFS=$IFS
IFS=$'\n'
for i in $versions; do
	key=${i%%: *}
	value=${i#*: }
	case "$key" in
		"version/version")
			master_version="$value"
			;;
		"version/patchlevel")
			master_patchlevel="$value"
			;;
	esac
done
IFS=$OLDIFS

# check join constraints
echo "running version check" >>/var/log/univention/join.log

mystatus="no"
if [ -n "$master_version" -a -n "$master_patchlevel" ]; then
	vmaster="$master_version$master_patchlevel"
	vmyself="$version_version$version_patchlevel"
	mystatus="$(echo "$vmaster" "$vmyself" | awk '{if ($1 >= $2) print "yes"; else print "no"}')"
fi

if [ "no" = "$mystatus" ]; then
	vmsg="Warning: UCS version on ${DCNAME} is lower ($vmaster) than local version ($vmyself)."
	vmsg="$vmsg This constellation is not supported."
	if $VERSION_CHECK; then
		failed_message "$vmsg"
	else
		echo "$vmsg Continuing anyway as requested with option (-disableVersionCheck)." >>/var/log/univention/join.log
	fi
else
	echo "OK: UCS version on ${DCNAME} is higher or equal ($vmaster) to the local version ($vmyself)." >>/var/log/univention/join.log
fi

echo -e "\033[60Gdone"

#Check if failed.ldif exists
if [ "$server_role" = "domaincontroller_slave" -o "$server_role" = "domaincontroller_backup" ]; then
	echo "Check if /var/lib/univention-directory-replication/failed.ldif exists" >>/var/log/univention/join.log
	if [ -e /var/lib/univention-directory-replication/failed.ldif ]; then
		rm /var/lib/univention-directory-replication/failed.ldif
		echo "Removed failed.ldif" >>/var/log/univention/join.log
	fi
fi

if [ "$CHECK_RUN" = "false" -a -x /etc/init.d/univention-s4-connector ]; then
	echo -n "Stop S4-Connector: "
	/etc/init.d/univention-s4-connector stop >>/var/log/univention/join.log 2>&1
	echo -e "\033[60Gdone"
fi

if [ "$CHECK_RUN" = "false" -a -x /etc/init.d/slapd ]; then
	echo -n "Stop LDAP Server: "
	/etc/init.d/slapd stop >>/var/log/univention/join.log 2>&1
	echo -e "\033[60Gdone"
fi

if [ "$CHECK_RUN" = "false" -a -x /etc/init.d/samba ]; then
	echo -n "Stop Samba Server: "
	if [ "$dns_backend" = "samba4" ]; then
		ucr set dns/backend=ldap >>/var/log/univention/join.log 2>&1
		/etc/init.d/named restart >>/var/log/univention/join.log 2>&1
	fi
	/etc/init.d/samba stop >>/var/log/univention/join.log 2>&1
	echo -e "\033[60Gdone"
fi

if [ -z "$LDAPBASE" ]; then
	echo -n "Search ldap/base"
	ldap_base="$(univention-ssh "$DCPWD" "${DCACCOUNT}@${DCNAME}" /usr/sbin/ucr search --brief ^ldap/base$ | sed -ne 's|^ldap/base: ||p')"
else
	ldap_base="$LDAPBASE"
fi

if [ -n "$ldap_base" ]; then
	$CHECK_RUN || univention-config-registry set ldap/base="$ldap_base" >/dev/null 2>&1
	echo -e "\033[60Gdone"
else
	failed_message "Failed to determine ldap/base."
fi

if [ "$CHECK_RUN" = "false" -a -x /etc/init.d/slapd ]; then
	echo -n "Start LDAP Server: "
	/etc/init.d/slapd start >>/var/log/univention/join.log 2>&1
	echo -e "\033[60Gdone"
fi

echo -n "Search LDAP binddn "

# First use udm to search the user DN
binddn="$(univention-ssh "$DCPWD" "${DCACCOUNT}@${DCNAME}" \
	/usr/sbin/udm users/user list --filter uid="$DCACCOUNT" --logfile /dev/null  2> >(tee -a /var/log/univention/join.log >&2) | sed -ne 's|^DN: ||p')"

if [ -z "$binddn" ]; then
	echo "binddn search on ${DCNAME} with UDM failed" >>/var/log/univention/join.log
	# Next try ldapsearch with GSSAPI against OpenLDAP
	binddn="$(univention-ssh "$DCPWD" "${DCACCOUNT}@${DCNAME}" \
		kinit --password-file=STDIN "${DCACCOUNT}" ldapsearch -Y GSSAPI -LLL -o ldif-wrap=no "\'(&(uid=$DCACCOUNT)(objectClass=person))\'" dn <"$DCPWD" 2>/dev/null | ldapsearch-decode64 | sed -ne 's|^dn: ||p;s|^DN: ||p')"
fi

if [ -z "$binddn" ]; then
	echo "binddn search on ${DCNAME} with GSSAPI failed" >>/var/log/univention/join.log
	# Next try the local ldapi interface, unlikely to succeed because only accessible for root
	binddn="$(univention-ssh "$DCPWD" "${DCACCOUNT}@${DCNAME}" \
		ldapsearch -x -LLL -o ldif-wrap=no -H ldapi:/// "\'(&(uid=$DCACCOUNT)(objectClass=person))\'" dn | ldapsearch-decode64 | sed -ne 's|^dn: ||p;s|^DN: ||p')"
fi

if [ -z "$binddn" ]; then
	echo "binddn search on ${DCNAME} via LDAPI failed" >>/var/log/univention/join.log
	# Finally try anonymous bind, unlikely to succeed because anonymous bind is disabled by default
	binddn="$(univention-ssh "$DCPWD" "${DCACCOUNT}@${DCNAME}" \
		ldapsearch -x -LLL -o ldif-wrap=no "\'(&(uid=$DCACCOUNT)(objectClass=person))\'" dn | ldapsearch-decode64 | sed -ne 's|^dn: ||p;s|^DN: ||p')"
fi

if [ -z "$binddn" ]; then
	failed_message "binddn for user $DCACCOUNT not found. "
else
	echo -e "\033[60Gdone"
fi

$CHECK_RUN && exit 0

[ -x /usr/sbin/univention-admin-diary-entry-create ] &&
context_id=$(/usr/sbin/univention-admin-diary-entry-create --event JOIN_STARTED --arg "hostname=$(ucr get hostname)")

if [ -x /usr/bin/rdate ]; then
	echo -n "Sync time: "
	if timeout -k 20 15 /usr/bin/rdate -n "$DCNAME" >>/var/log/univention/join.log 2>&1
	then
		echo -e "\033[60Gdone"
	else
		echo -e "\033[60Gfail"
	fi
fi

args=()

if [ -n "$ldap_position" ]; then
	args+=(-position "$ldap_position")
fi


if [ "${master_version:0:1}" -lt 3 ]; then
	# UCS 2.x does not support the -binddn parameter
	args+=(-bindaccount "$DCACCOUNT")
else
	args+=(-binddn "$binddn")
fi

if "$SKIPIPMAC"; then
	echo "Not registering IP and MAC, as requested with -skipIpMac" >>/var/log/univention/join.log
else
	# TODO: Support multiple network interfaces
	# Search for the standard IP:
	IP="$(get_default_ip_address)"
	if [ -n "$IP" ]; then
		args+=(-ip "$IP")
		if [ "${master_version:0:1}" -ge 3 ]; then
			NETMASK="$(get_default_netmask)"
			# Since UCS 3.0 it is possible to append the netmask
			args+=(-netmask "$NETMASK")
		fi
	fi
	mac_addr="$(find /sys/class/net/* -not -lname ../../devices/virtual/\* -type l,d -exec cat {}/address \; | sort -u)"
	if [ -n "$mac_addr" ]; then
		while read line; do
			if [ -n "$line" ]; then
				args+=(-mac "$line")
			fi
		done <<< "$mac_addr"
	fi
fi

# invalidate the nscd hosts cache
#  https://forge.univention.org/bugzilla/show_bug.cgi?id=30886
test -x /usr/sbin/nscd && nscd -i hosts

copy_ca_cert
echo -n "Running pre-join hook(s): "
run_join_hook join/pre-join
echo -e "\033[60Gdone"

echo -n "Join Computer Account: "
args+=(-role "$server_role" -hostname "$hostname" -domainname "$domainname")
# Copy local $DCPWD to remote $DCPWD' and invoke univention-join remotely
univention-ssh --no-split "$DCPWD" "${DCACCOUNT}@${DCNAME}" \
	'DCPWD=$(mktemp) && trap "rm -f \"$DCPWD\"" EXIT && cat >"$DCPWD" && /usr/share/univention-join/univention-server-join -bindpwfile "$DCPWD"' \
	"$(bashquote "${args[@]}")" <"$DCPWD" 2>&1 |
	tee "$USERTMP/secret" |
	grep -v '^KerberosPasswd="' |
	tee "$USERTMP/scrubbed" >>/var/log/univention/join.log

res_message="$(sed -ne 's/^E:\s*//p' "$USERTMP/scrubbed")"
if [ -z "$res_message" ]; then
	echo -e "\033[60Gdone"
else
	failed_message "$res_message"
fi

[ -s "$USERTMP/secret" ] ||
	failed_message "Nothing returned from join process"

eval "$(grep -e '^ldap_dn=' -e '^KerberosPasswd=' "$USERTMP/secret")"
[ -n "$ldap_dn" ] ||
	failed_message "No LDAP Host DN returned"
rdn=${ldap_dn%%,*}
hostname=${rdn#cn=}
[ -n "$hostname" ] ||
	failed_message "Could not get canonical writing of hostname from DN $ldap_dn"

if [ -n "$KerberosPasswd" ]; then
		if [ -e /etc/machine.secret ]; then
			cat /etc/machine.secret >>/etc/machine.secret.SAVE
		fi

		echo -n "$KerberosPasswd" >/etc/machine.secret
		fromdos /etc/machine.secret
		chmod 600 /etc/machine.secret
		if [ -e /etc/machine.secret.SAVE ]; then
			chmod 600 /etc/machine.secret.SAVE
		fi
	else
		if [ -n "$res_message" ]; then
			failed_message "$res_message"
		else
			failed_message "$(<"$USERTMP/scrubbed")"
		fi
	fi

hostname "$hostname" >>/var/log/univention/join.log 2>&1
univention-config-registry set hostname="$hostname" ldap/hostdn="$ldap_dn" >>/var/log/univention/join.log 2>&1

# reset status file for the first time, so all join hooks are able to detect the empty file
reset_status_file

if [ -e "/etc/univention/ssl" ]; then
	mv /etc/univention/ssl "/etc/univention/ssl_$(date +"%y%m%d%H%M")"
	install -m 755 -d /etc/univention/ssl
fi

for service in univention-directory-notifier univention-directory-listener
do
	echo -n "Stopping $service daemon: "
	systemctl stop "$service" >>"$LOG" 2>&1
	echo -e "\033[60Gdone"
done
rm -Rf /var/lib/univention-directory-listener/*

set_kerberos_realm () {
	local DCPWD="$1"
	local DCACCOUNT="$2"
	local DCNAME="$3"
	local realm="$4"
	if [ -z "$realm" ]; then
		realm="$(univention-ssh "$DCPWD" "${DCACCOUNT}@${DCNAME}" '/usr/sbin/univention-config-registry get kerberos/realm')" >>/var/log/univention/join.log 2>&1
		if [ $? != 0 -o -z "$realm" ]; then
			echo "Unable to retrieve the kerberos realm. Try to use option -realm <kerberos/realm>"
			exit 1
		fi
	fi
	univention-config-registry set kerberos/realm="$realm" >>/var/log/univention/join.log 2>&1
}

set_windows_domain () {

	local dcpwd="$1"
	local dcaccount="$2"
	local dcname="$3"
	local windom="$4"

	if [ -z "$windom" ]; then
		windom="$(univention-ssh "$dcpwd" "${dcaccount}@${dcname}" '/usr/sbin/univention-config-registry get windows/domain')" >>/var/log/univention/join.log 2>&1
		if [ $? != 0 -o -z "$windom" ]; then
			echo "Unable to retrieve the windows/domain. Try to use option -windom <windows/domain>"
			exit 1
		fi
	fi
	univention-config-registry set windows/domain="$windom" >>/var/log/univention/join.log 2>&1
}

set_mdb_ldap_listener_size () {

	local dcpwd="$1"
	local dcaccount="$2"
	local dcname="$3"
	local size=""

	# ldap mdb size
	size="$(univention-ssh "$dcpwd" "${dcaccount}@${dcname}" '/usr/sbin/univention-config-registry get ldap/database/mdb/maxsize')" >>/var/log/univention/join.log 2>&1
	if [ $? -eq 0 -a -n "$size" ]; then
		univention-config-registry set ldap/database/mdb/maxsize="$size" >>/var/log/univention/join.log 2>&1
	fi

	# listener cache size
	size="$(univention-ssh "$dcpwd" "${dcaccount}@${dcname}" '/usr/sbin/univention-config-registry get listener/cache/mdb/maxsize')" >>/var/log/univention/join.log 2>&1
	if [ $? -eq 0 -a -n "$size" ]; then
		univention-config-registry set listener/cache/mdb/maxsize="$size" >>/var/log/univention/join.log 2>&1
	fi
}

# get forwarder from Primary
set_dns_forwarder () {

	local dcpwd="$1"
	local dcaccount="$2"
	local dcname="$3"

	forwarder="$(univention-ssh "$dcpwd" "${dcaccount}@${dcname}" /usr/sbin/univention-config-registry search --brief ^dns/forwarder)"
	OLDIFS=$IFS
	IFS=$'\n'
	for i in $forwarder; do
		key=${i%%: *}
		value=${i#*: }
		if [ -n "$value" -a -n "$key" -a ! "$value" = "<empty>" ]; then
			univention-config-registry set "$key"="$value" >>/var/log/univention/join.log 2>&1
		fi
	done
	IFS=$OLDIFS
}

if [ "$server_role" = "domaincontroller_backup" ]; then

	if [ -e "/etc/ldap.secret" ]; then cat /etc/ldap.secret >>/etc/ldap.secret.SAVE; fi
	if [ -e "/etc/ldap-backup.secret" ]; then cat /etc/ldap-backup.secret >>/etc/ldap-backup.secret.SAVE; fi

	echo -n "Sync ldap.secret: "
	univention-scp "$DCPWD" -q "${DCACCOUNT}@${DCNAME}:/etc/ldap.secret" /etc/ldap.secret >>/var/log/univention/join.log 2>&1
	if [ ! -e "/etc/ldap.secret" ]; then
		failed_message "/etc/ldap.secret not found"
	fi
	echo -e "\033[60Gdone"

	echo -n "Sync ldap-backup.secret: "
	univention-scp "$DCPWD" -q "${DCACCOUNT}@${DCNAME}:/etc/ldap-backup.secret" /etc/ldap-backup.secret >>/var/log/univention/join.log 2>&1
	if [ ! -e "/etc/ldap-backup.secret" ]; then
		failed_message "/etc/ldap-backup.secret not found"
	fi
	echo -e "\033[60Gdone"

	univention-config-registry set \
		ldap/server/name="$hostname.$domainname" \
		ldap/server/ip="$IP" \
		ldap/server/port?7389 \
		ldap/master="$DCNAME" \
		ldap/master/port?7389 \
		ldap/server/type=slave \
		>>/var/log/univention/join.log 2>&1

	echo -n "Sync SSL directory: "
	univention-ssh-rsync "$DCPWD" -az "${DCACCOUNT}@${DCNAME}:/etc/univention/ssl/" /etc/univention/ssl/ >>/var/log/univention/join.log 2>&1
	echo -e "\033[60Gdone"

	setup_ssl

	echo -n "Sync SSL settings: "
	eval "$(univention-ssh --no-split "$DCPWD" "${DCACCOUNT}@${DCNAME}" /usr/sbin/univention-config-registry shell ssl/country ssl/state ssl/locality ssl/organization ssl/organizationalunit ssl/common ssl/email)"
	univention-config-registry set \
		ssl/country="$ssl_country" \
		ssl/state="$ssl_state" \
		ssl/locality="$ssl_locality" \
		ssl/organization="$ssl_organization" \
		ssl/organizationalunit="$ssl_organizationalunit" \
		ssl/common="$ssl_common" \
		ssl/email="$ssl_email" \
		>>/var/log/univention/join.log 2>&1
	echo -e "\033[60Gdone"

	echo -n "Purging translog database: "
	/etc/init.d/slapd stop >>/var/log/univention/join.log 2>&1
	rm -rf /var/lib/univention-ldap/translog
	echo -e "\033[60Gdone"

	echo -n "Restart LDAP Server: "
	/etc/init.d/slapd restart >>/var/log/univention/join.log 2>&1
	echo -e "\033[60Gdone"

	#TODO: implement a real sync
	echo -n "Sync Kerberos settings: "
	univention-scp "$DCPWD" -r "${DCACCOUNT}@${DCNAME}:/var/lib/heimdal-kdc/*" /var/lib/heimdal-kdc/ >>/var/log/univention/join.log 2>&1
	echo -e "\033[60Gdone"


	# invalidate the nscd hosts cache
	test -x /usr/sbin/nscd && nscd -i hosts

	univention-config-registry set \
		ldap/server/name?"$DCNAME" \
		ldap/master?"$DCNAME" \
		>>/var/log/univention/join.log 2>&1

	if ! is_domain_in_admember_mode; then
		univention-config-registry set kerberos/adminserver?"$DCNAME"
	fi

	set_kerberos_realm "$DCPWD" "$DCACCOUNT" "$DCNAME" "$REALM"
	set_windows_domain "$DCPWD" "$DCACCOUNT" "$DCNAME" "$WINDOM"
	set_dns_forwarder "$DCPWD" "$DCACCOUNT" "$DCNAME"
	set_mdb_ldap_listener_size "$DCPWD" "$DCACCOUNT" "$DCNAME"
	eval "$(univention-config-registry shell)"

	mkdir -p /var/lib/univention-ldap/notify/

	touch /var/univention-join/joined
	ln -sf /var/univention-join/joined /usr/share/univention-join/.joined

	echo -n "0" >/var/lib/univention-ldap/schema/id/id
	chown listener /var/lib/univention-ldap/schema/id/id

	check_and_configure_ad_member_mode
	echo -n "Running pre-joinscripts hook(s): "
	run_join_hook join/pre-joinscripts
	echo -e "\033[60Gdone"
	# reset status file for the second time to be sure, that the join hooks have not messed around with it
	reset_status_file
	run_join_scripts
	echo -n "Running post-joinscripts hook(s): "
	run_join_hook join/post-joinscripts
	echo -e "\033[60Gdone"

elif [ "$server_role" = "domaincontroller_slave" ]; then

	echo -n "Sync ldap-backup.secret: "

	if [ -e "/etc/ldap-backup.secret" ]; then cat /etc/ldap-backup.secret >>/etc/ldap-backup.secret.SAVE; fi

	univention-scp "$DCPWD" "${DCACCOUNT}@${DCNAME}:/etc/ldap-backup.secret /etc/ldap-backup.secret" >>/var/log/univention/join.log 2>&1

	echo -e "\033[60Gdone"

	univention-config-registry set \
		ldap/server/name="$hostname.$domainname" \
		ldap/server/ip="$IP" \
		ldap/server/port?7389 \
		ldap/master="$DCNAME" \
		ldap/master/port?7389 \
		ldap/server/type=slave \
		>>/var/log/univention/join.log 2>&1

	setup_ssl force

	echo -n "Restart LDAP Server: "
	/etc/init.d/slapd restart >>/var/log/univention/join.log 2>&1
	echo -e "\033[60Gdone"

	echo -n "Sync Kerberos settings: "
	univention-scp "$DCPWD" -q -r "${DCACCOUNT}@${DCNAME}:/var/lib/heimdal-kdc/*" /var/lib/heimdal-kdc/ >>/var/log/univention/join.log 2>&1
	echo -e "\033[60Gdone"

	mkdir -p /var/lib/univention-ldap/notify/

	# invalidate the nscd hosts cache
	test -x /usr/sbin/nscd && nscd -i hosts

	univention-config-registry set \
		ldap/server/name?"$DCNAME" \
		ldap/master?"$DCNAME" \
		>>/var/log/univention/join.log 2>&1

	if ! is_domain_in_admember_mode; then
		univention-config-registry set kerberos/adminserver?"$DCNAME"
	fi

	set_kerberos_realm "$DCPWD" "$DCACCOUNT" "$DCNAME" "$REALM"
	set_windows_domain "$DCPWD" "$DCACCOUNT" "$DCNAME" "$WINDOM"
	set_dns_forwarder "$DCPWD" "$DCACCOUNT" "$DCNAME"
	set_mdb_ldap_listener_size "$DCPWD" "$DCACCOUNT" "$DCNAME"
	eval "$(univention-config-registry shell)"

	echo -n "0" >/var/lib/univention-ldap/schema/id/id
	chown listener /var/lib/univention-ldap/schema/id/id

	touch /var/univention-join/joined
	ln -sf /var/univention-join/joined /usr/share/univention-join/.joined

	check_and_configure_ad_member_mode
	echo -n "Running pre-joinscripts hook(s): "
	run_join_hook join/pre-joinscripts
	echo -e "\033[60Gdone"
	# reset status file for the second time to be sure, that the join hooks have not messed around with it
	reset_status_file
	run_join_scripts
	echo -n "Running post-joinscripts hook(s): "
	run_join_hook join/post-joinscripts
	echo -e "\033[60Gdone"

elif [ "$server_role" = "memberserver" ]; then
	setup_ssl force

	univention-config-registry set \
		ldap/master="$DCNAME" \
		ldap/master/port?7389 \
		>>/var/log/univention/join.log 2>&1
	grep -q '^TLS_CACERT' /etc/ldap/ldap.conf || echo "TLS_CACERT /etc/univention/ssl/ucsCA/CAcert.pem" >>/etc/ldap/ldap.conf

	# invalidate the nscd hosts cache
	test -x /usr/sbin/nscd && nscd -i hosts

	univention-config-registry set \
		ldap/server/name?"$DCNAME" \
		ldap/server/port?7389 \
		ldap/master?"$DCNAME" \
		ldap/master/port?7389 \
		>>/var/log/univention/join.log 2>&1

	if ! is_domain_in_admember_mode; then
		univention-config-registry set kerberos/adminserver?"$DCNAME"
	fi

	set_kerberos_realm "$DCPWD" "$DCACCOUNT" "$DCNAME" "$REALM"
	set_windows_domain "$DCPWD" "$DCACCOUNT" "$DCNAME" "$WINDOM"
	touch /var/univention-join/joined
	ln -sf /var/univention-join/joined /usr/share/univention-join/.joined

	check_and_configure_ad_member_mode
	echo -n "Running pre-joinscripts hook(s): "
	run_join_hook join/pre-joinscripts
	echo -e "\033[60Gdone"
	# reset status file for the second time to be sure, that the join hooks have not messed around with it
	reset_status_file
	run_join_scripts
	echo -n "Running post-joinscripts hook(s): "
	run_join_hook join/post-joinscripts
	echo -e "\033[60Gdone"

else
# Client and Mobile Client
	setup_ssl force

	# invalidate the nscd hosts cache
	test -x /usr/sbin/nscd && nscd -i hosts

	univention-config-registry set \
		ldap/server/name="$DCNAME" \
		ldap/server/port?7389 \
		ldap/master="$DCNAME" \
		ldap/master/port?7389 \
		kerberos/adminserver="$DCNAME" \
		nsswitch/ldap=yes \
		>>/var/log/univention/join.log 2>&1
	set_kerberos_realm "$DCPWD" "$DCACCOUNT" "$DCNAME" "$REALM"
	set_windows_domain "$DCPWD" "$DCACCOUNT" "$DCNAME" "$WINDOM"
	grep -q '^TLS_CACERT' /etc/ldap/ldap.conf || echo "TLS_CACERT /etc/univention/ssl/ucsCA/CAcert.pem" >>/etc/ldap/ldap.conf

	touch /var/univention-join/joined
	ln -sf /var/univention-join/joined /usr/share/univention-join/.joined

	eval "$(univention-config-registry shell)"

	echo -n "Running pre-joinscripts hook(s): "
	run_join_hook join/pre-joinscripts
	echo -e "\033[60Gdone"
	# reset status file for the second time to be sure, that the join hooks have not messed around with it
	reset_status_file
	run_join_scripts
	echo -n "Running post-joinscripts hook(s): "
	run_join_hook join/post-joinscripts
	echo -e "\033[60Gdone"
fi


systemctl restart univention-directory-notifier >>/var/log/univention/join.log 2>&1
sleep 3

systemctl restart univention-directory-listener >>/var/log/univention/join.log 2>&1

if [ "$interfaces_${interfaces_primary:-eth0}_type" != "dhcp" ]; then
	univention-config-registry commit /etc/resolv.conf >>/var/log/univention/join.log 2>&1
fi

[ -x /usr/sbin/univention-admin-diary-entry-create ] && [ -n "${context_id:-}" ] &&
/usr/sbin/univention-admin-diary-entry-create --event JOIN_FINISHED_SUCCESS --arg "hostname=$(ucr get hostname)" --context-id "$context_id" > /dev/null

exit 0
# vim:set sw=4 ts=4 noet:
