#!/bin/bash

# ogo-create-instance
# Author: Sebastian Ley <ley@debian.org>
# 
# This script creates OpenGroupware.org instances and registers them
# with the Debian init system for OpenGroupware.org.
# This script is free software, you are allowed to do with it whatever
# you want.

# Default settings
DEBCONF=no
USER=ogo
HOMEDIR=/var/lib/opengroupware.org
WEBUI_VERSION=1.0
ZIDESTORE_VERSION=1.4
XMLRPCD_VERSION=1.0
NHSD_VERSION=none
DBSETUP=yes
DB_HOST=localhost
DB_USER=ogo
DB_PASSWD=
DB_PORT=5432
DB_NAME=ogo
SKYFS_PATH_EXT=skyfs
ATTACHMENT_PATH_EXT=documents
NEWS_IMAGES_PATH_EXT=news_images/
NEWS_IMAGES_URL_BASE=NewsImages
WEBUI_PORT=20000
ZIDESTORE_PORT=21000
XMLRPCD_PORT=22000
WEBUI_LOCATION_MATCH=OpenGroupware
ZIDESTORE_LOCATION_MATCH=zidestore
XMLRPCD_LOCATION_MATCH=RPC2

# Subroutines
test_root () {
	if [ ${UID} != "0" ]; then
		return 1
	fi
	return 0
}

get_input () {
	 cat << EOF

The OpenGroupware.org daemons are executed as non-root user. Such a user
will be created during the install process. If you want to create multiple
instances of OpenGroupware.org each instance must be excuted in as a
different user. Please note that the specified user must not already exist
on your system.

EOF
	echo -n "Username (${USER}): "
	read
	if [ "${REPLY}" != "" ]; then
		USER=${REPLY}
	fi

	cat << EOF

You now need to specify the home directory of the user you specified in
the last question. This directory must not already exist.

EOF
	echo -n "Home directory (${HOMEDIR}): "
	read
	if [ "${REPLY}" != "" ]; then
		HOMEDIR=${REPLY}
	fi

	cat << EOF

If you have multiple Versions of OpenGroupware.org installed you can
choose per instance which versions you want to run. If you type "none"
for a daemon's version, that daemon won't be started in this instance.
Please note that only one instance can run the Network Hotsync daemon
on a host because this daemon is tied to a specific port number.

EOF
	echo -n "Web user interface version (${WEBUI_VERSION}): "
	read
	if [ "${REPLY}" != "" ]; then
		WEBUI_VERSION=${REPLY}
	fi
	if [ "${WEBUI_VERSION}" == "none" ]; then
		WEBUI_VERSION=""
	fi

	echo -n "Zidestore version (${ZIDESTORE_VERSION}): "
	read
	if [ "${REPLY}" != "" ]; then
		ZIDESTORE_VERSION=${REPLY}
	fi
	if [ "${ZIDESTORE_VERSION}" == "none" ]; then
		ZIDESTORE_VERSION=""
	fi

	echo -n "XMLRPC daemon version (${XMLRPCD_VERSION}): "
	read
	if [ "${REPLY}" != "" ]; then
		XMLRPCD_VERSION=${REPLY}
	fi
	if [ "${XMLRPCD_VERSION}" == "none" ]; then
		XMLRPCD_VERSION=""
	fi
	
	echo -n "Network Hotsync daemon version (${NHSD_VERSION}): "
	read
	if [ "${REPLY}" != "" ]; then
		NHSD_VERSION=${REPLY}
	fi
	if [ "${NHSD_VERSION}" == "none" ]; then
		NHSD_VERSION=""
	fi
	cat << EOF

OpenGroupware.org keeps all data in a PostgreSQL database. Some parameters
need to be configured in order to enable access to the database. If you
have not yet created a database, you can let the installation script take
care of that. For that to work, you need a locally installed and fully
configured PostgreSQL installation and answer the appropriate question
with "yes". If you want to reuse an existing database or create the 
database manually answer "no". In both cases you need to specify the host
where the database is runing as well as a port, a database user, that
user's password and adatabase name.

EOF
	echo -n "Automatically configure the database? (${DBSETUP}): "
	read
	if [ "${REPLY}" != "" ]; then
		case ${REPLY} in
			yes|Yes|YES) 	DBSETUP=yes;;
			*)		DBSETUP=no;;
		esac
	fi
	
	echo -n "Database host (${DB_HOST}): "
	read
	if [ "${REPLY}" != "" ]; then
		DB_HOST=${REPLY}
	fi

	echo -n "Database port (${DB_PORT}): "
	read
	if [ "${REPLY}" != "" ]; then
		DB_PORT=${REPLY}
	fi

	echo -n "Database user (${DB_USER}): "
	read
	if [ "${REPLY}" != "" ]; then
		DB_USER=${REPLY}
	fi


	echo -n "Database user's password: "
	read -s
	echo
	DB_PASSWD=${REPLY}

	echo -n "Database name (${DB_NAME}): "
	read
	if [ "${REPLY}" != "" ]; then
		DB_NAME=${REPLY}
	fi

	cat << EOF

Each daemon has to listen on a different port for connections. Usually
the daemons are not connected directly from application but via apache
and a special module (mod_ngobjweb) which handles the requests.
However you need to specify the ports the applications listen on. Its
also possible to supply listening network address, like so;
localhost:port."

EOF

	echo -n "Web user interface port (${WEBUI_PORT}): "
	read
	if [ "${REPLY}" != "" ]; then
		WEBUI_PORT=${REPLY}
	fi

	echo -n "Zidestore port (${ZIDESTORE_PORT}): "
	read
	if [ "${REPLY}" != "" ]; then
		ZIDESTORE_PORT=${REPLY}
	fi
	
	echo -n "XMLRPC daemon port (${XMLRPCD_PORT}): "
	read
	if [ "${REPLY}" != "" ]; then
		XMLRPCD_PORT=${REPLY}
	fi
	
cat << EOF

The apache module forwards requests to the appropriate daemons based on
the URL which is called. You need to specify which local part of an URL
should be mapped to which daemon. An example: If you specify
"OpenGroupware" as Location Match for the Web UI, OpenGroupware.org's
Web UI will be available under "http://YOURDOMAIN/OpenGroupware".

EOF
	echo -n "Web UI Location Match (${WEBUI_LOCATION_MATCH}): "
	read
	if [ "${REPLY}" != "" ]; then
		WEBUI_LOCATION_MATCH=${REPLY}
	fi
	
	echo -n "Zidestore Location Match (${ZIDESTORE_LOCATION_MATCH}): "
	read
	if [ "${REPLY}" != "" ]; then
		ZIDESTORE_LOCATION_MATCH=${REPLY}
	fi
	
	echo -n "YMLRPC daemon  Location Match (${XMLRPCD_LOCATION_MATCH}): "
	read
	if [ "${REPLY}" != "" ]; then
		XMLRPCD_LOCATION_MATCH=${REPLY}
	fi

	return 0
}

check_user_available () {
	echo -n "Check if user ${USER} is available... "
	if getent group ${USER} >/dev/null 2>&1; then
		echo "failed"
		echo "Group ${USER} already exists."
		return 1
	fi
	if getent passwd ${USER} >/dev/null 2>&1; then
		echo "failed"
		echo "User ${USER} already exists."
		return 1
	fi
	if [ -e ${HOMEDIR} ]; then
		echo "failed"
		echo "Directory ${HOMEDIR} already exists."
		return 1
	fi
	echo "done."
	return 0
}

add_user () {
	SKYFS_PATH=${HOMEDIR}/${SKYFS_PATH_EXT}
	ATTACHMENT_PATH=${HOMEDIR}/${ATTACHMENT_PATH_EXT}
	NEWS_IMAGES_PATH=${HOMEDIR}/${NEWS_IMAGES_PATH_EXT}
	NEWS_IMAGES_URL=${NEWS_IMAGES_URL_BASE}-${USER}

	echo -n "Adding user and group ${USER}... "
	addgroup --system ${USER} &>/dev/null
	adduser --system --shell /bin/bash --home ${HOMEDIR} \
		--ingroup ${USER} --gecos "OpenGroupware.org" ${USER} &>/dev/null
	mkdir -p ${HOMEDIR}
	mkdir -p ${SKYFS_PATH}
	mkdir -p ${ATTACHMENT_PATH}
	mkdir -p ${NEWS_IMAGES_PATH}
	mkdir -p ${HOMEDIR}/.libFoundation/Defaults
	chown -R ${USER}:${USER} ${HOMEDIR}
	chmod 755 ${HOMEDIR}
	chmod 755 ${NEWS_IMAGES_PATH}
	chmod 700 ${SKYFS_PATH}
	chmod 700 ${ATTACHMENT_PATH}
	chmod 700 ${HOMEDIR}/.libFoundation/Defaults
	echo "done."
	return 0
}

set_defaults () {
	echo -n "Set up application defaults... "
	if [ ! -x /usr/bin/Defaults ]; then
		echo "failed"
		echo "Defaults tool not present. Install the libfoundation-tools package"
		return 1
	fi
	su - -c "Defaults write NSGlobalDomain LSConnectionDictionary '{hostName=\"${DB_HOST}\"; userName=\"${DB_USER}\"; password=\"${DB_PASSWD}\"; port=\"${DB_PORT}\"; databaseName=\"${DB_NAME}\"}'" ${USER}
	su - -c "Defaults write NSGlobalDomain skyrix_id \"`/bin/hostname`-${USER}\"" ${USER}
	su - -c "Defaults write NSGlobalDomain SkyFSPath \"${SKYFS_PATH}\"" ${USER}
	su - -c "Defaults write NSGlobalDomain LSAttachmentPath \"${ATTACHMENT_PATH}\"" ${USER}
	su - -c "Defaults write NSGlobalDomain LSNewsImagesPath \"${NEWS_IMAGES_PATH}\"" ${USER}
	su - -c "Defaults write NSGlobalDomain LSNewsImagesUrl \"/NewsImages-${USER}\"" ${USER}
	su - -c "Defaults write NSGlobalDomain SkyLogoutURL \"/${WEBUI_LOCATION_MATCH}\"" ${USER}
	su - -c "Defaults write NSGlobalDomain TimeZoneName GMT" ${USER}
	# Hack for nhsd not being able to find conduits in FHS layout. Will be fixed for OGo 1.2, see bug #1300.
	su - -c "Defaults write ogo-nhsd-1.0a NGBundlePath /usr/lib/opengroupware.org-1.0a/conduits" ${USER}
	su - -c "Defaults write ogo-nhsd-1.0 NGBundlePath /usr/lib/opengroupware.org-1.0/conduits" ${USER}
	# In case skyaptnotify gets installed, make sure it logs to the right directory
	su - -c "Defaults write skyaptnotify AptNotifySentResourcesFile /var/log/opengroupware.org/${USER}/skyaptnotify_sent-resources.log" ${USER}
	echo "done."
	return 0
}

check_database_available () {
	return 0
}

initialize_database () {
	echo -n "Set up local database... "
	su - -c "createuser -A -D ${DB_USER} &>/dev/null || true" postgres
	su - -c "createdb --encoding LATIN1 -O ${DB_USER} ${DB_NAME} &>/dev/null || true" postgres
	su - -c "psql ${DB_NAME} postgres -c \"ALTER USER ${DB_USER} WITH PASSWORD '${DB_PASSWD}'\" &>/dev/null || true" postgres
        su - -c "export PGPASSWORD=${DB_PASSWD}; psql ${DB_NAME} -U ${DB_USER} -c \"\\dt\" |grep address &>/dev/null || psql ${DB_NAME} -U ${DB_USER} -c \"\\i /usr/lib/opengroupware.org-${WEBUI_VERSION}/commands/OGo.model/Resources/pg-build-schema.psql\" &>/dev/null " ${USER}  || (echo failed; return 1)
	echo "done."
	return 0
}

register_instance () {
	echo -n "Register new instance to the startup system... "
	mkdir -p /etc/opengroupware.org
	mkdir -p /var/log/opengroupware.org/${USER}
	chown ${USER}:${USER} /var/log/opengroupware.org/${USER}
	chmod 755 /var/log/opengroupware.org
	chmod 770 /var/log/opengroupware.org/${USER}
	if [ -e /etc/opengroupware.org/${USER} ]; then
		echo "failed"
		echo "Directory /etc/opengroupware.org/${USER} already exists, cannot register this instance."
		return 1
	fi
	mkdir /etc/opengroupware.org/${USER}

	ln -s ${HOMEDIR}/.libFoundation/Defaults /etc/opengroupware.org/${USER}
	
	echo "USER=${USER}" > /etc/opengroupware.org/${USER}/init.conf
	echo "WEBUI_VERSION=${WEBUI_VERSION}" >> /etc/opengroupware.org/${USER}/init.conf
	echo "WEBUI_PORT=${WEBUI_PORT}" >> /etc/opengroupware.org/${USER}/init.conf
	echo "ZIDESTORE_VERSION=${ZIDESTORE_VERSION}" >> /etc/opengroupware.org/${USER}/init.conf
	echo "ZIDESTORE_PORT=${ZIDESTORE_PORT}" >> /etc/opengroupware.org/${USER}/init.conf
	echo "XMLRPCD_VERSION=${XMLRPCD_VERSION}" >> /etc/opengroupware.org/${USER}/init.conf
	echo "XMLRPCD_PORT=${XMLRPCD_PORT}" >> /etc/opengroupware.org/${USER}/init.conf
	echo "NHSD_VERSION=${NHSD_VERSION}" >> /etc/opengroupware.org/${USER}/init.conf

	echo "<IfModule ngobjweb_module.c>" > /etc/opengroupware.org/${USER}/mod_ngobjweb.conf
  	echo "  <LocationMatch \"^/${WEBUI_LOCATION_MATCH}/*\">" >> /etc/opengroupware.org/${USER}/mod_ngobjweb.conf
	echo "    SetHandler ngobjweb-adaptor" >> /etc/opengroupware.org/${USER}/mod_ngobjweb.conf
	echo "    SetAppPort ${WEBUI_PORT}" >> /etc/opengroupware.org/${USER}/mod_ngobjweb.conf
	echo "  </LocationMatch>" >> /etc/opengroupware.org/${USER}/mod_ngobjweb.conf
	echo >> /etc/opengroupware.org/${USER}/mod_ngobjweb.conf
	echo "  Alias /NewsImages-${USER}/ ${NEWS_IMAGES_PATH}" >> /etc/opengroupware.org/${USER}/mod_ngobjweb.conf
	echo >> /etc/opengroupware.org/${USER}/mod_ngobjweb.conf
	echo "  <LocationMatch \"^/${ZIDESTORE_LOCATION_MATCH}/*\">" >> /etc/opengroupware.org/${USER}/mod_ngobjweb.conf
	echo "    SetHandler ngobjweb-adaptor" >> /etc/opengroupware.org/${USER}/mod_ngobjweb.conf
	echo "    SetAppPort ${ZIDESTORE_PORT}" >> /etc/opengroupware.org/${USER}/mod_ngobjweb.conf
	echo "  </LocationMatch>" >> /etc/opengroupware.org/${USER}/mod_ngobjweb.conf
	echo >> /etc/opengroupware.org/${USER}/mod_ngobjweb.conf
	echo "  <LocationMatch \"^/${XMLRPCD_LOCATION_MATCH}/*\">" >> /etc/opengroupware.org/${USER}/mod_ngobjweb.conf
	echo "    SetHandler ngobjweb-adaptor" >> /etc/opengroupware.org/${USER}/mod_ngobjweb.conf
	echo "    SetAppPort ${XMLRPCD_PORT}" >> /etc/opengroupware.org/${USER}/mod_ngobjweb.conf
	echo "  </LocationMatch>" >> /etc/opengroupware.org/${USER}/mod_ngobjweb.conf
	echo >> /etc/opengroupware.org/${USER}/mod_ngobjweb.conf
	echo "</IfModule>" >> /etc/opengroupware.org/${USER}/mod_ngobjweb.conf
	
	if [ -d /etc/apache/conf.d ]; then
		ln -s /etc/opengroupware.org/${USER}/mod_ngobjweb.conf /etc/apache/conf.d/mod_ngobjweb-${USER}.conf
	fi
	if [ -d /etc/apache2/conf.d ]; then
		ln -s /etc/opengroupware.org/${USER}/mod_ngobjweb.conf /etc/apache2/conf.d/mod_ngobjweb-${USER}.conf
	fi
	cat << EOF > /etc/opengroupware.org/${USER}/README
Configuration directory for an OpenGroupware.org instance

This directory contains information needed to run an OpenGroupware.org
instance. It was created with the 'create-ogo-instance' script and contains
the following files:

Defaults
  A link to the instance user's Defaults dir, which is database holding
  configuration items. They should be modified with the 'Defaults' tool
  from the libfoundation-tools package.

init.conf
  This file will be read by /etc/init.d/opengroupware.org and contains
  variables that determines this instance's behaviour. The USER variable
  must be set to the user that should execute this instance. The VERSION
  variables determine the version of the selected component to start
  (useful if you have multiple versions installed) and the PORT variables
  set the port on which that component's daemon shall listen.

mod_ngobjweb.conf
  mod_ngobjweb is an apache module that handles HTTP requests for the
  specific daemons. This configuration file is linked into 
  /etc/apache/conf.d and/or /etc/apache2/conf.d and contains the
  necesary chunks of apache configuration to make the daemons work
  together with apache. Please note that you need to have mod_include
  and mod_rewrite enabled for having the configuration work out of the
  box.

EOF
	echo "done."
	return 0
}

generate_password () {
	MATRIX="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	LENGTH="8"
	while [ "${n:=1}" -le "$LENGTH" ]; do
		DB_PASSWD="$DB_PASSWD${MATRIX:$(($RANDOM%${#MATRIX})):1}"
		let n+=1
	done
	return 0
}

reload_apache () {
	echo -n "Reload apache... "
	if [ -x /usr/sbin/invoke-rc.d ]; then
		invoke-rc.d apache reload &> /dev/null
		invoke-rc.d apache-ssl reload &> /dev/null
		invoke-rc.d apache-perl reload &> /dev/null
		invoke-rc.d apache2 reload &> /dev/null
	else
		/etc/init.d/apache reload &> /dev/null
		/etc/init.d/apache-ssl reload &> /dev/null
		/etc/init.d/apache-perl reload &> /dev/null
		/etc/init.d/apache2 reload &> /dev/null
	fi
	echo "done."
	return 0
}

### Execution starts here ###

while getopts "d" option ; do
	case "$option" in
		d) DEBCONF=yes; generate_password;;
	esac
done

if ! test_root; then
	echo "This script must be run as root. Exiting..."
	exit 1
fi

if [ "$DEBCONF" = "no" ]; then
	if ! get_input; then
		echo "Could not gather user input. Exiting..."
		exit 1
	fi
fi

#if ! check_user_available; then
#	exit 1
#fi

if ! add_user; then
	echo "Adding user ${USER} failed. Exiting..."
	exit 1
fi

if ! set_defaults; then
	echo "Could not set application defaults. Exiting..."
	exit 1
fi

if [ ${DBSETUP} == "yes" ]; then
	if ! check_database_available; then
		echo "Could not access local database. Skipping automatic setup of database."
	else
		if ! initialize_database; then
			echo "Could not set up the database. Skipping automatic setup."
		fi
	fi
else
	echo "Skipping automatic setup of database"
fi

if ! register_instance; then
	echo "Could not register the new OpenGroupware.org instance to the startup system."
fi

reload_apache

cat << EOF
Initialization of OpenGroupware.org instance finished.
Now make sure OpenGroupware.org can access the specified database. Check
/etc/postgres/postgresql.conf if TCP/IP connections are allowed.
Also verify that a suitable accessmethod is enabled in
/etc/postgresql/pg_hba.conf.

Please note that you need to have mod_include and mod_rewrite enabled in
apache for this configuration working out of the box. They are
enabled by default in apache 1.3, but are not in apache 2.

You need to (re)start opengroupware.org for the changes to take effect.
EOF
