#!/bin/bash
#
#  Copyright (C) 2011-2016, it-novum GmbH <community@openattic.org>
#
#  openATTIC is free software; you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by
#  the Free Software Foundation; version 2.
#
#  This package is distributed 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 General Public License for more details.


export OACONFIG="True"

for settings in "/etc/default/openattic" "/etc/sysconfig/openattic"
do
	[ -f $settings ] && . $settings
done

if [[ -z $OAUSER ]]; then
	echo "ERROR: OAUSER is not defined. Please check your configuration."
	exit 1
fi

if [[ -z $OADIR ]]; then
	echo "ERROR: OADIR is not defined. Please check your configuration."
	exit 1
fi

if ! [[ -e $OADIR ]]; then
	echo "ERROR: $OADIR does not exist. Please check your installation."
	exit 1
fi

cd $OADIR

if [ "$DJANGO_AUTO_COMPLETE" = "1" ]; then
	MYCOMMANDS="install scan-vgs add-vg add-disk restart reload status dbdump domainjoin version"
	ARRAY_WORDS=( $COMP_WORDS )
	compgen -W "$MYCOMMANDS" "${ARRAY_WORDS[COMP_CWORD]}"
	su "$OAUSER" -c 'python manage.py'
	exit $?
fi

set -e
set -u

usage () {
	echo "Usage: $0 <command> [<args>]"
	echo
	echo "Wrapper around the openATTIC management system."
	echo
	echo "Valid commands are:"
	echo
	echo "    install      Run this command after you installed new openATTIC modules."
	echo "    scan-vgs     Instruct LVM to search for existing VGs."
	echo "    add-vg       Has been removed. Please use 'oaconfig install'."
	echo "    add-disk     Has been removed. Please use the LVM command line tool and 'oaconfig install'."
	echo "    domainjoin   Join a Windows Active Directory Domain."
	echo "    restart      Restart all services that are needed to run openATTIC."
	echo "    reload       Reload all services that are needed to run openATTIC."
	echo "    status       Show the status of all services that are needed to run openATTIC."
	echo "    dbdump       Create a database dump on stdout."
	echo "    version      Returns version information."
	echo
	echo "Any other command will be passed as-is to the openATTIC management system,"
	echo "which supports the commands and options listed below."
	echo
	
	su "$OAUSER" -c 'python manage.py help'
	exit 1
}

if [ "$#" = "0" ]; then
	usage
fi

case $1 in
	help)
		usage
		;;

	install)
		if [ "$#" -gt 1 ] && [ "$2" = "--allow-broken-hostname" ]; then
			shift
		else
			if [[ "`hostname --fqdn`" != *.* ]]; then
				echo "'`hostname --fqdn`' is not a fully-qualified domain name."
				echo "You should fix this error before proceeding with the installation."
				echo "To ignore this warning, use oaconfig install --allow-broken-hostname."
				exit 2
			fi
			
			if grep -qP '^\d+$' <<<"$HOSTNAME" ; then
				echo "Your hostname is invalid because it consists of numbers only. (Consider adding a - somewhere.)"
				echo "You should fix this error before proceeding with the installation."
				echo "To ignore this warning, use oaconfig install --allow-broken-hostname."
				exit 2
			fi
			
			if grep -qP '^[\w\d\-]{16,}$' <<<"$HOSTNAME" ; then
				echo "Your hostname is too long for a Windows domain (must be max. 15 characters)."
				echo "You should fix this error before proceeding with the installation."
				echo "To ignore this warning, use oaconfig install --allow-broken-hostname."
				exit 2
			fi
		fi

		if ! service postgresql status >/dev/null; then
			# Start database
			service postgresql start
		fi

		database_ini="/etc/openattic/database.ini"
		host=$(sed -rn 's/^[[:blank:]]*host[[:blank:]]*=[[:blank:]]*(.*)[[:blank:]]*/\1/p' $database_ini)
		if [ "${host}" == 'localhost' ] ; then
			has_local_database=1
		else
			has_local_database=0
		fi

		db_name=$(sed -rn 's/[[:blank:]]*name[[:blank:]]*=[[:blank:]]*(.*)[[:blank:]]*/\1/p' $database_ini)
		if [[ -z $db_name ]]; then
			echo "ERROR: No database name defined in $database_ini"
			exit 1
		fi

		db_user=$(sed -rn 's/[[:blank:]]*user[[:blank:]]*=[[:blank:]]*(.*)[[:blank:]]*/\1/p' $database_ini)
		if [[ -z $db_user ]]; then
			echo "ERROR: No database username defined in $database_ini"
			exit 1
		fi

		if (( has_local_database )) ; then
			# Check if the database role '$db_user' exists and create it if it doesn't exist.
			sql="SELECT 1 FROM pg_roles WHERE rolname='${db_user}'"
			if ! su - postgres -c "psql postgres -tAc \"${sql}\""| grep -q 1 ; then
				echo "Creating database role \"${db_user}\""
				pass=$(openssl rand -hex 10)
				sed -ri --follow-symlinks \
				    "s/(password[[:blank:]]*=[[:blank:]]*)(.*)/\1${pass}/g" $database_ini
				if [ $? -ne 0 ]; then
					echo "Failed to update $database_ini"
					exit 2
				fi

				su - postgres -c "psql -c \"CREATE USER ${db_user} WITH PASSWORD '$pass'\""
				echo "User '${db_user}' created"
			fi

			# Check if database '${db_name}' exists and create it if it doesn't.
			dbu=$(su - postgres -c "psql --list" | awk -F'|' " /${db_name}/ { print \$2 }")
			if [ -n "$dbu" ]; then
				echo "Database ${db_name} exists, owned by $dbu"
			else
				su - postgres -c "psql -c \"CREATE DATABASE ${db_name} WITH OWNER ${db_user}\""

				hba_conf="/var/lib/pgsql/data/pg_hba.conf"
				if [ -e ${hba_conf} ] ; then
					sed -i -e 's/ident$/md5/g' ${hba_conf}
				fi
				service postgresql reload
				service postgresql status
			fi
		fi

		# Check if the connection can be established with the database user and password.
		# Set a new password if the connection cannot be established.
		password=$(sed -rn 's/^[[:blank:]]*password[[:blank:]]*=[[:blank:]]*(.*)[[:blank:]]*/\1/p' $database_ini)
		# We have to have an '$db_name' database already at this point.
		if ! PGPASSWORD="$password" psql ${db_name} ${db_user} -h "${host}" --no-password -c "\q" ; then
			if (( has_local_database )) ; then
				pass=$(openssl rand -hex 10)
				sed -ri --follow-symlinks \
				    "s/(password[[:blank:]]*=[[:blank:]]*)(.*)/\1${pass}/g" $database_ini
				if [ $? -ne 0 ]; then
					echo "Failed to update $database_ini"
					exit 2
				fi

				su - postgres -c "psql -c \"ALTER USER ${db_user} WITH PASSWORD '$pass'\""
				echo "Password of database user ${db_user} and database.ini updated"
			else
				echo "ERROR: Couldn't establish a connection to the remote database" \
					"running on ${host}."
				echo "       Please review the configuration in file ${database_ini}."
				exit 2
			fi
		fi
		
		su "$OAUSER" -c 'python manage.py pre_install'

		if [ "$#" -gt 1 ]; then
			shift
			echo "Please use your operating system's package manager"
			echo "(e.g. apt-get or yum) to install package $@."
		fi
		
		if service openattic-systemd status >/dev/null; then
			# restart the daemons so they don't keep any locks open
			$0 reload
		fi
		
		# Initialize the schema so our daemons can start
		DJANGO_MIGRATE="$(su "$OAUSER" -c 'python - << EOF
from django import VERSION
print "1" if VERSION[:2] > (1, 6) else ""
EOF
'
)"
		DJANGO_FAKE_INITIAL="$(su "$OAUSER" -c 'python - << EOF
from django import VERSION
print "1" if VERSION[:2] >= (1, 8) else ""
EOF
'
)"
		if [ "$DJANGO_MIGRATE" ]
		then
		    if [ "$DJANGO_FAKE_INITIAL" ]
		    then
		        su "$OAUSER" -c "python manage.py migrate --fake-initial"
		    else
		        su "$OAUSER" -c "python manage.py migrate"
		    fi
			su "$OAUSER" -c "python manage.py loaddata */fixtures/initial_data.json"
		else
			su "$OAUSER" -c 'python manage.py syncdb --noinput'
			su "$OAUSER" -c 'python manage.py django_16_migrate'
		fi

		
		# Create the status_cache table, ignoring failures (those mean we did this before)
		su "$OAUSER" -c 'python manage.py createcachetable status_cache >/dev/null 2>/dev/null' || /bin/true
		
		su "$OAUSER" -c 'python manage.py add-host'
		
		# (re)start the daemons
		$0 reload
		
		su "$OAUSER" -c 'python manage.py makedefaultadmin'
		
		# Run the hook scripts to initialize modules
		shopt -s nullglob
		for hook in $OADIR/*/bin/oaconfig-install.sh; do
			source $hook
		done
		
		su "$OAUSER" -c 'python manage.py post_install'

		echo "Completed successfully."
		;;
	
	scan-vg|scan-vgs)
		vgscan --mknodes
		vgchange -ay
		;;

	add-vg)
		echo "'add-vg' has been replaced. The input parameter 'vgname' is no longer required. 'oaconfig add-vg' will now search for all existing VG's (without sys tag) and add them to openATTIC."
		su "$OAUSER" -c 'python manage.py post_install'
		;;

	add-disk)
		echo "'add-disk' has been removed from oaconfig. Please use the LVM command line tools instead. Don't forget to run 'oaconfig install' afterwards."
		;;

	reload)
		service openattic-systemd restart
		service "${WEBSERVER_SERVICE}" reload
		;;

	start|stop|restart|force-reload)
		service openattic-systemd $1
		service "${WEBSERVER_SERVICE}" $1
		;;

	status)
		service openattic-systemd status || /bin/true
		service "${WEBSERVER_SERVICE}" status || /bin/true
		;;

	rootshell|shell)
		python manage.py shell
		;;

	domainjoin)
		if grep -qP '^\d+$' <<<"$HOSTNAME" ; then
			echo "Your hostname is invalid because it consists of numbers only. (Consider adding a - somewhere.)"
			exit 2
		fi
		
		if grep -qP '^[\w\d\-]{16,}$' <<<"$HOSTNAME" ; then
			echo "Your hostname is too long (must be max. 15 characters)."
			exit 2
		fi
		
		if [ ! -e "$OADIR"/installed_apps.d/*samba ]; then
			echo "The Samba module must be enabled for this to work."
			exit 2
		fi
		
		if ! which smbd > /dev/null; then
			echo "smbd not found."
			exit 2
		fi
		
		if ! which nmbd > /dev/null; then
			echo "nmbd not found."
			exit 2
		fi
		
		if ! which winbindd > /dev/null; then
			echo "winbindd not found."
			exit 2
		fi
		
		function lower () {
			sed 's/./\l&/g' <<<$1
		}
		function upper () {
			sed 's/./\u&/g' <<<$1
		}
		
		hostname="`lower $HOSTNAME`"
		netbios="`upper $HOSTNAME`"
		machacc="$netbios\$"
		
		if [ "$#" -lt 3 ]; then
			echo "Usage: $0 domainjoin <user> <domain> [<workgroup>]"
			exit 2
		fi
		
		user="$2"
		domain="`lower $3`"
		realm="`upper $3`"
		
		if [ "$#" = 4 ]; then
			workgroup="`upper $4`"
		else
			workgroup="`upper $domain`"
		fi
		
		echo "User:            $user"
		echo "Domain:          $domain"
		echo "Realm:           $realm"
		echo "Workgroup:       $workgroup"
		echo "Machine Account: $machacc"
		
		if [ "`hostname --fqdn`" != "$hostname.$domain" ]; then
			echo "Your FQDN seems wrong. Make sure /etc/hosts and /etc/hostname are correct."
			echo "The command 'hostname --fqdn' must return '$hostname.$domain' to pass this test."
			exit 1
		fi
		
		apt-get install -y openattic-auth-kerberos
		
		echo "Updating krb5.conf..."
		<<EOF cat > /etc/krb5.conf
[libdefaults]
	default_realm = $realm
	default_keytab_name = /etc/krb5.keytab
	default_tkt_enctypes = rc4-hmac des-cbc-crc des-cbc-md5
	default_tgs_enctypes = rc4-hmac des-cbc-crc des-cbc-md5

[domain_realm]
	.$domain = $realm
	$domain = $realm
EOF
		
		echo "Probing Kerberos..."
		kinit "$user"
		
		echo "Configuring Samba..."
		dbus-send --system --print-reply --dest=org.openattic.systemd /samba org.openattic.systemd.writeconf "boolean:false" "int32:0" "string:$realm" "string:$workgroup"

		if [ -e /etc/krb5.keytab ]; then
			echo "Removing old keytab..."
			rm /etc/krb5.keytab
		fi
		echo "Joining Domain..."
		net ads join -k
		net ads keytab -k create
		net ads keytab -k add HTTP
		net ads keytab -k add HTTPS
		
		chmod g+r /etc/krb5.keytab
		chgrp openattic /etc/krb5.keytab
		
		kdestroy
		
		echo "Logging in as $machacc (this may fail a couple of times)..."
		while ! kinit -k "$machacc"; do
			sleep 1
		done
		
		<<EOF cat > /etc/pam.d/openattic
account [default=bad success=ok user_unknown=ignore service_err=ignore system_err=ignore] pam_krb5.so ignore_root

auth            sufficient      pam_krb5.so ccache=/tmp/krb5cc_%u use_first_pass
auth            required        pam_deny.so

session         optional        pam_krb5.so ignore_root
EOF
		
		echo "Configuring openATTIC..."
		<<EOF cat > /etc/openattic/domain.ini
[domain]
realm = $realm
workgroup = $workgroup

[kerberos]
enabled = True

[pam]
enabled = True
EOF
		$0 reload
		
		echo "Configuring libnss..."
		sed -i -e 's/^passwd:.*$/passwd:         compat winbind/' /etc/nsswitch.conf
		sed -i -e 's/^group:.*$/group:          compat winbind/' /etc/nsswitch.conf
		
		echo "Restarting Samba and Winbind..."
		dbus-send --system --print-reply --dest=org.openattic.systemd /samba org.openattic.systemd.writeconf "boolean:false" "int32:0" "string:" "string:"
		for SERVICE in $SAMBA_SERVICES; do
			service $SERVICE restart
		done
		service "$WINBIND_SERVICE" restart
		
		echo "To see if it worked, let's try 'getent passwd \"$user\"':"
		getent passwd "$user"
		;;

	dbshell)
		if which sudo > /dev/null; then
			DBNAME="`grep '^name' /etc/openattic/database.ini  | cut -d= -f2`"
			sudo -u postgres -s `which psql` $DBNAME
		else
			echo "Please install sudo for this command to work."
		fi
		;;

	dbdump|dumpdb)
		if which sudo > /dev/null; then
			DBNAME="`grep name /etc/openattic/database.ini  | cut -d= -f2`"
			sudo -u postgres -s `which pg_dump` $DBNAME
		else
			echo "Please install sudo for this command to work."
		fi
		;;

	version|--version)
 		# Check version.txt for version
		REL_VER_FILE="/usr/share/openattic/version.txt"
		DEV_VER_FILE="/srv/openattic/bin/version.txt"
		if [ -f "$DEV_VER_FILE" ]; then
			echo "openATTIC DEV: $(grep VERSION $DEV_VER_FILE |awk '{print $3}')"
		elif [ -f "$REL_VER_FILE" ]; then
			echo "openATTIC version: $(grep VERSION $REL_VER_FILE |awk '{print $3}')"
		else
			echo "No openATTIC version file found."
		fi
		echo "Django version: $(python manage.py --version)"
		;;

	*)
		# Big thanks to DireFog and pcgod for the following line
		# First pass my "$@" in a whitespace-preserving way to su's subshell,
		# which then passes it on to manage.py.
		su "$OAUSER" -s /bin/sh -- -c 'python manage.py "$@"' dummy "$@"
		;;
esac
