#!/bin/bash
#
# Univention Join
#  joins a server to an univention domain
#
# Copyright 2004-2014 Univention GmbH
#
# http://www.univention.de/
#
# All rights reserved.
#
# The source code of this program is made available
# under the terms of the GNU Affero General Public License version 3
# (GNU AGPL V3) as published by the Free Software Foundation.
#
# Binary versions of this program provided by Univention to you as
# well as other copyrighted, protected or trademarked materials like
# Logos, graphics, fonts, specific documentations and configurations,
# cryptographic keys etc. are subject to a license agreement between
# you and Univention and not subject to the GNU AGPL V3.
#
# In the case you use this program under the terms of the GNU AGPL V3,
# the program is provided in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public
# License with the Debian GNU/Linux or Univention distribution in file
# /usr/share/common-licenses/AGPL-3; if not, see
# <http://www.gnu.org/licenses/>.

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

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

if [ -d "$HOME" ]; then
	LOGFILE="$HOME/.univention-server-join.log"
else
	USERTMP="$(mktemp -d)"
	LOGFILE="$USERTMP/.univention-server-join.log"
fi
ADMINOPTIONS=()
ADMINOPTIONS+=(--logfile "$LOGFILE")

display_help() {
	display_header
	cat <<-EOL
	Syntax:
	  univention-server-join -hostname <hostname> -role <role> [options]
	  univention-server-join [--help] [--version]

	Options:
	  -role <role>:            [domaincontroller_master|domaincontroller_backup|
	                            domaincontroller_slave|memberserver]
	  -hostname <hostname>:    hostname of computer system
	  -domainname <domainname>:    domainname of computer system
	  -ip <ip-address>:        IP address of computer system
	  -netmask <netmask>:      Netmask of computer system
	  -mac <mac-address>:      MAC address of computer system

	  -bindaccount             UID for Univention Directory Manager (deprecated)
	  -binddn                  DN for Univention Directory Manager
	  -bindpwfile:             Password for Univention Directory Manager

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

	Description:
	  univention-server-join joins a server to an univention domain,
	  e.g. univention-server-join -role memberserver -hostname membsrv -ip 1.2.3.4

	EOL
}

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

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

log() {
	if [ "$1" = 1 ]; then
		shift
		echo "$@"				>>"$LOGFILE"
		echo "$@"
	else
		shift
		echo "$@"				>>"$LOGFILE"
	fi
}

MAC=""
BINDDN=""
BINDPWFILE=""
DOMAINNAME=""
log 0 "univention-server-join called"
echo "Parameter: $@" >>"$LOGFILE"

while [ $# -gt 0 ]
do
	case "$1" in
		"-role")
			ROLE="${2:?missing role}"
			shift 2 || exit 2
			;;
		"-hostname")
			NEWHOSTNAME="${2:?missing host name}"
			shift 2 || exit 2
			;;
		"-domainname")
			DOMAINNAME="${2:?missing domain name}"
			shift 2 || exit 2
			;;
		"-ip")
			IP="${2:?missing IP address}"
			shift 2 || exit 2
			;;
		"-netmask")
			NETMASK="${2:?missing netmask}"
			shift 2 || exit 2
			;;
		"-certs")
			CERTS="${2:?missing certificate}"
			shift 2 || exit 2
			;;
		"-mac")
			MAC="${2:?missing ethernet MAC address}"
			shift 2 || exit 2
			;;
		"-bindaccount")
			BINDACCOUNT="${2:?missing account name for bind}"
			shift 2 || exit 2
			;;
		"-binddn")
			BINDDN="${2:?missing bind DN}"
			shift 2 || exit 2
			;;
		"-bindpwfile")
			BINDPWFILE="${2:?missing password file for bind}"
			shift 2 || exit 2
			;;
		"-position")
			POSITION="${2:?missing LDAP position}"
			shift 2 || exit 2
			;;
		"--version")
			display_version
			exit 0
			;;
		"--help"|"-h"|"-?")
			display_help
			exit 0
			;;
		*)
			display_help
			exit 1
			;;
	esac
done

# extend options for univention-directory-manager
if [ -z "$BINDDN" ]; then
	if [ -n "$BINDACCOUNT" ]; then
		# FIXME: This not longer works with anonymous bind disabled!
		BINDDN="$(ldapsearch -x "(&(uid=$BINDACCOUNT)(objectclass=posixAccount))" dn | ldapsearch-wrapper | sed -ne 's|dn: ||p')"
		log 0 "found BINDDN: $BINDDN" >>"$LOGFILE"
		if [ -z "$BINDDN" ]; then
			log 1 "E: failed to get binddn for $BINDACCOUNT"
			exit 1
		fi
	fi
fi

if [ -n "$BINDDN" ]; then
	ADMINOPTIONS+=(--binddn "$BINDDN")
fi
if [ -n "$BINDPWFILE" ]; then
	ADMINOPTIONS+=(--bindpwd "$(<"$BINDPWFILE")")
fi


eval "$(univention-config-registry shell)"
if [ -z "$ROLE" ]; then
	display_help
	log 1 "E: 	-role is missing"
	exit 1
fi
if [ -z "$NEWHOSTNAME" ]; then
	display_help
	log 1 "E: 	-hostname is missing"
	exit 1
fi

if [ -z "$DOMAINNAME" ]; then
	DOMAINNAME="$domainname"
fi

display_header
create_entry () {
	local desc="${1?:missing description}"
	local module="${2?:missing computer module}"
	local position="${3?:missing LDAP position}"
	local primaryGroup="$4"
	local group="$5"
	log 0 "Join $desc"

	old_dn="$(univention-directory-manager "$module" list --filter name="$NEWHOSTNAME" "${ADMINOPTIONS[@]}" | sed -ne "s|^DN: ||p")"
	if [ $? -gt 0 ]; then
		log 1 "E: failed search $desc [$old_dn]"
		exit 1
	fi

	args=()
	if [ -z "$old_dn" ]; then
		log 0 "	Create new $desc "

		if [ -n "$IP" ]; then
			args+=(--set ip="$IP")
			# DNS
			if [ -n "$forwardZone" ]; then
				args+=(--set dnsEntryZoneForward="$forwardZone")
				if [ -n "$reverseZone" ]; then
					args+=(--set dnsEntryZoneReverse="$reverseZone")
				fi
			fi
		fi
		if [ -n "$MAC" ]; then
			args+=(--set mac="$MAC")
		fi
		# DHCP
		case "$module" in
		computers/managedclient|computers/mobileclient)
			if [ -n "$dhcpEntry" ] && [ -n "$IP" ] && [ -n "$MAC" ]; then
				args+=(--set dhcpEntryZone="$dhcpEntry $IP $MAC")
			fi
			;;
		esac

		cmd=(univention-directory-manager "$module" create \
			--position "$position" \
			--set name="$NEWHOSTNAME" \
			--set domain="$DOMAINNAME" \
			--set password="$computerPassword" \
			--set unixhome=/dev/null \
			--set shell=/bin/sh \
			--set primaryGroup="$primaryGroup" \
			"${args[@]}" "${ADMINOPTIONS[@]}")
		#log 0 "${cmd[@]}"
		if ! rc="$("${cmd[@]}")"
		then
			log 1 "E: failed to create $desc (1) [$rc]"
			exit 1
		fi

		if [ -z "$rc" ]; then
			log 1 "E: failed to create $desc: no result"
			exit 1
		fi

		ldap_dn="$(echo $rc | sed -ne 's|Object created: ||p')"
		if [ -z "$ldap_dn" ]; then
			log 1 "E: failed to create $desc (2) [$rc]"
			exit 1
		fi

		echo "ldap_dn=\"$ldap_dn\""

		if [ -n "$group" ]; then
			rc="$(univention-directory-manager groups/group modify \
				--dn="$group" \
				--append users="$ldap_dn" \
				"${ADMINOPTIONS[@]}")"
			if [ $? -gt 0 ]; then
				log 1 "E: failed to modify groups/group for $desc [$rc]"
				exit 1
			fi
		fi
	else
		log 0 "Modify $desc [$old_dn]"

		if [ -n "$MAC" ]; then
			args+=(--set mac="$MAC")
		fi
		if [ -n "$IP" ]; then
			args+=(--set ip="$IP")
		fi
		rc="$(univention-directory-manager "$module" modify \
			--dn "$old_dn" \
			--set password="$computerPassword" \
			--set domain="$DOMAINNAME" \
			"${args[@]}" "${ADMINOPTIONS[@]}")"
		if [ $? -gt 0 ]; then
			log 1 "E: failed to modify $desc $old_dn [$rc]"
			exit 1
		fi

		echo "ldap_dn=\"$old_dn\" "
	fi

	# Invalidate caches
	if [ "$USER" = "root" ]
	then
		nscd -i passwd || true
		nscd -i group || true
		nscd -i hosts || true
	fi
}

if [ -n "$IP" ]; then
	if [ -n "$NETMASK" ]; then
		subnet="$(univention-ipcalc6 --ip "$IP" --netmask "$NETMASK" --output reverse --calcdns)"
	else
		# Fallback
		subnet="$(univention-ipcalc6 --ip "$IP" --netmask "$interfaces_${interfaces_primary:-eth0}_netmask" --output reverse --calcdns)"
	fi
	log 0 "	Calculated subnet = $subnet"

	forwardZone="$(univention-directory-manager dns/forward_zone list \
		--filter zone="$DOMAINNAME" \
		"${ADMINOPTIONS[@]}" | sed -ne 's/^DN: //p')"
	reverseZone="$(univention-directory-manager dns/reverse_zone list \
		--filter subnet="$subnet" \
		"${ADMINOPTIONS[@]}" | sed -ne 's/^DN: //p')"
	dhcpEntry="$(univention-directory-manager dhcp/service list \
		--filter name="$DOMAINNAME" \
		"${ADMINOPTIONS[@]}" | sed -ne 's/^DN: //p')"

	log 0 "	forwardZone $forwardZone"
	log 0 "	reverseZone $reverseZone"
	log 0 "	dhcpEntry $dhcpEntry"
fi

computerPassword="$(create_machine_password)"

case "$ROLE" in
domaincontroller_master)
	create_entry "DC Master" "computers/domaincontroller_master" \
		"${POSITION:-cn=dc,cn=computers,$ldap_base}" \
		"cn=DC Backup Hosts,cn=groups,$ldap_base"
	;;
domaincontroller_backup)
	create_entry "DC Backup" "computers/domaincontroller_backup" \
		"${POSITION:-cn=dc,cn=computers,$ldap_base}" \
		"cn=DC Backup Hosts,cn=groups,$ldap_base" \
		"cn=DC Slave Hosts,cn=groups,$ldap_base"
	;;
domaincontroller_slave)
	create_entry "DC Slave" "computers/domaincontroller_slave" \
		"${POSITION:-cn=dc,cn=computers,$ldap_base}" \
		"cn=DC Slave Hosts,cn=groups,$ldap_base"
	;;
memberserver)
	create_entry "Member Server" "computers/memberserver" \
		"${POSITION:-cn=memberserver,cn=computers,$ldap_base}" \
		"cn=Computers,cn=groups,$ldap_base"
	;;
client)
	create_entry "Client" "computers/managedclient" \
		"${POSITION:-cn=computers,$ldap_base}" \
		"cn=Computers,cn=groups,$ldap_base"
	;;
mobileclient)
	create_entry "Client" "computers/mobileclient" \
		"${POSITION:-cn=computers,$ldap_base}" \
		"cn=Computers,cn=groups,$ldap_base"
	;;
ucc)
	create_entry "Client" "computers/ucc" \
		"${POSITION:-cn=computers,$ldap_base}" \
		"cn=Computers,cn=groups,$ldap_base"
	;;
*)
	log 1 "E: 	-role $ROLE is unknown"
	exit 1
esac

echo "KerberosPasswd=\"$computerPassword\" "
# vim:set sw=4 ts=4 noet:
