#!/bin/sh

# Shell script to transfer configuration data for SCC.
# Copyright (C) 2001-2004 Open Challenge B.V.
# Copyright (C) 2004-2005 OpenEyeT Professional Services.
# Copyright (C) 2005-2009 QNH.
#
# This program 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; either version 2, or (at your option)
# any later version.
#
# This program 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING.
# If not, write to the Free Software Foundation,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Contact information: http://scc.qnh-infrastructure.nl

# SCC-release:	1.7.5
# file-version:	$Revision: 1.79 $
 
 

ProgName=${0##*/};			export ProgName

set -u

now=$(date '+%Y-%m-%d %H:%M:%S')

SCC_BIN=/opt/scc-srv/bin
SCC_TOP=/var/opt/scc-srv
export SCC_BIN SCC_TOP

export SCC_DATA=${SCC_TOP}/data
export SCC_TMP=${SCC_TOP}/tmp
export SCC_CONF=${SCC_TOP}/conf

export SCC_LOG=${SCC_DATA}/log
export SCC_SRC=${SCC_DATA}/transfer
export SCC_ERROR=${SCC_DATA}/transfer/error
export SCC_DEST=${SCC_DATA}/www/All

export TMPDIR=${SCC_TMP}
export TMP=${SCC_TMP}

export SHELL=/bin/sh
export LANG=C

PATH=/sbin:/usr/sbin:/usr/bin:/bin:${SCC_BIN};		export PATH

umask 077

export TMP1_FILE=${SCC_TMP}/scc_transfer1_$$
export TMP2_FILE=${SCC_TMP}/scc_transfer2_$$
export TMP3_FILE=${SCC_TMP}/scc_transfer3_$$
export EXIT_FILE=${SCC_TMP}/scc_exit_$$

trap "rm -f ${TMP1_FILE} ${TMP2_FILE} ${TMP3_FILE} ${EXIT_FILE}" 0
trap "exit 2" 1 2 3 15

private_key_file="";		export private_key_file

scc_srv_path="$(sed -n -e 's/^SCC_PATH=//p' ${SCC_CONF}/scc.conf 2>/dev/null)"
if [ "${scc_srv_path}" ]
then
	PATH="${PATH}:${scc_srv_path}"
fi

# Add the (likely) directory of the client to path to use scc-snap2html and scc-log2html.
client_dir="$(echo "${SCC_BIN}" | sed -e 's/-srv//')"
if [ -d "${client_dir}" ]
then
	PATH="${PATH}:${client_dir}"
fi

# Use the following utilities for SunOS.
if [ -x /usr/ccs/bin/what ]
then
	alias what=/usr/ccs/bin/what
fi
if [ -x /usr/xpg4/bin/awk ]
then
	alias awk=/usr/xpg4/bin/awk
fi
if [ -x /usr/xpg4/bin/grep ]
then
	alias grep=/usr/xpg4/bin/grep
fi
if [ -x /usr/xpg4/bin/tail ]
then
	alias tail=/usr/xpg4/bin/tail
fi

# Name of the file from the client part of scc.
TRANSFER=scc-transfer-data

scc_msg()
{
	if [ $# -ne 2 ]
	then
		echo "Syntax error, use ${ProgName}: scc_msg <sev> <msg>" >&2
		exit 2
	fi

	case "${1}" in
	INFO)	if [ -f ${logfile} ]
		then
			echo "INFO	${now}	${2}" >> ${logfile}
		fi
		return 0;;
	WARN)	echo "${2}" >&2
		if [ -f ${logfile} ]
		then
			echo "WARN	${now}	${2}" >> ${logfile}
		fi
		return 0;;
	ERROR)	echo "${2}" >&2
		if [ -f ${logfile} ]
		then
			echo "ERROR	${now}	${2}" >> ${logfile}
		fi
		exit 1;;
	SYNTAX)	echo "${2}" >&2
		exit 2;;
	*)	echo "${ProgName}: scc_msg: unknown severity: ${1}" >&2
		exit 2
	esac

	return 0;
}

scc_untar()
{
	# The working directory is the subdirectory mail/cp in transfer.
	if [ $# -ne 2 ]
	then
		scc_msg SYNTAX "Syntax error, use: ${ProgName}: scc_untar <host> <tar>"
	fi

	scc_host="${1}"
	tar_file="${2}"

	contents=$(tar tf "${tar_file}" 2>/dev/null | sed -e 's@\./@@g' | sort | tr "\012" " ")

	expected="scc.${scc_host}.cur scc.${scc_host}.html scc.${scc_host}.log scc.${scc_host}.log.html "

	if [ "${contents}" != "${expected}" ]
	then
		mv "${tar_file}" ${SCC_ERROR}/scc_host.tar
		scc_msg WARN "${ProgName}: unexpected data from host: ${scc_host}:${contents}, expected:${expected}."
	fi

	# When the snapshot exists, use its permissions and user/group. Otherwise, use
	# the permissions and user/group of the target directory.
	if [ -f "${SCC_DEST}"/scc.${scc_host}.cur ]
	then
		example="${SCC_DEST}"/scc.${scc_host}.cur
	else
		example="${SCC_DEST}"
	fi
	# We are only interested in the read-permissions of the directory.
	set dummy $(ls -ld "${example}" )
	case "${2}" in
	?r??r??r??)	new_mode=0444; umask 333;;
	?r??r??-??)	new_mode=0440; umask 337;;
	?r??-??-??)	new_mode=0400; umask 377;;
	?r??-??r??)	new_mode=0404; umask 373;;
	*)		new_mode=0400; umask 077;;
	esac
	new_owner_group="${4}:${5}"

	tar xf "${tar_file}" 2>/dev/null
	rm -f "${tar_file}"

	# Files from a windows system have CR/LF termination of lines.
	# Check the first line of the snapshot to check for CR/LF.
	# The data on this line ends with a digit.
	cr_lf=$(head -n 1 scc.${scc_host}.cur | sed -e 's/.*[0-9]//' | wc -c)
	if [ ${cr_lf} -eq 2 ]
	then
		for f in ${contents}
		do
			tr -d "\015\0" <${f} >${TMP1_FILE}
			mv ${TMP1_FILE} ${f}
		done
	fi

	# The html-production requires a fully functional sort (not the one from busybox).
	# When sort is from busybox, both html-files are empty.
	if [ -s scc.${scc_host}.cur -a ! -s scc.${scc_host}.html ]
	then
		which scc-snap2html >/dev/null 2>/dev/null
		if [ $? -eq 1 ]
		then
			scc_msg WARN "${ProgName}: cannot access scc-snap2html in $PATH"
		else
			scc-snap2html ${scc_host} <scc.${scc_host}.cur >scc.${scc_host}.html
		fi
	fi
	if [ -s scc.${scc_host}.log -a ! -s scc.${scc_host}.log.html ]
	then
		which scc-log2html >/dev/null 2>/dev/null
		if [ $? -eq 1 ]
		then
			scc_msg WARN "${ProgName}: cannot access scc-log2html in $PATH"
		else
			scc-log2html ${scc_host} <scc.${scc_host}.log >scc.${scc_host}.log.html
		fi
	fi

	# Check whether:
	# - scc.<host>.cur and scc.<host>.html are non-empty
	# - scc.<host>.log and scc.<host>.log.html are empty
	# This means that the remote system cannot compare the snapshot for <host>.
	# Typically this happens when a snapshot of a system is split into several parts and
	# the parts possibly migrate to other hosts (for example in a cluster).
	# Another possibility is that a system only contains a limited (BusyBox) 
	# implementation of diff. In that case, no logfile is produced and scc-srv should compare.
	server_compare=""
	if [	-s scc.${scc_host}.cur		-a	\
		-s scc.${scc_host}.html		-a	\
		! -s scc.${scc_host}.log	-a	\
		! -s scc.${scc_host}.log.html			]
	then
		server_compare="yes"

		# compare scc.${scc_host}.cur ${SCC_DEST}/scc.${scc_host}.cur ${SCC_DEST}/scc.${scc_host}.log
		# Remote system cannot compare.
		# Check wether the client indicates a restart.
		restart="$(head scc.${scc_host}.cur | grep "^var:general::restart:true$")"
		remark="$(head scc.${scc_host}.cur | sed -n -e "s/^var:general::remark://p")"
		runtime="$(head scc.${scc_host}.cur | sed -n -e "s/^var:general::runtime://p")"
		timestamp="$(head scc.${scc_host}.cur | awk -F: '/date/ { d=$NF; next }
								/:start time:/	{ print d ":" $NF }' )";
		if [ "${restart}" ]
		then
			# Remove the snapshot to simulate a restart.
			rm -f "${SCC_DEST}/scc.${scc_host}.cur"
		fi

		org_path="${PATH}"
		scc_path="$(echo ${SCC_BIN} | sed -e 's/-srv//')"
		PATH="${PATH}:${scc_path}:/opt/scc/bin"
		which scc-cmp >/dev/null 2>/dev/null
		if [ $? -eq 1 ]
		then
			scc_msg WARN "${ProgName}: cannot access scc-cmp in $PATH"
			rm -f scc.${scc_host}.cur scc.${scc_host}.log scc.${scc_host}.html scc.${scc_host}.log.html
			return 0;	# Do not overwrite existing snapshot.
		fi

		rm -f scc.${scc_host}.log
		touch ${SCC_DEST}/scc.${scc_host}.log
		cp ${SCC_DEST}/scc.${scc_host}.log scc.${scc_host}.log
		scc-cmp "${SCC_DEST}/scc.${scc_host}.cur" scc.${scc_host}.cur "" "" >>scc.${scc_host}.log

		scc-log2html ${scc_host} <scc.${scc_host}.log >scc.${scc_host}.log.html

		PATH=${org_path}
	fi

	for s in html log.html
	do
		sed -e "s@^<LINK HREF=\"style.css@<LINK HREF=\"custom/style.css@" <scc.${scc_host}.${s} >${TMP1_FILE}
		mv ${TMP1_FILE} scc.${scc_host}.${s}
	done

	# Check whether the FQDN is used when sending the files. In that case we
	# have to update the URL's to the logbook/snapshot.
	if [ "${scc_host}" != "${scc_host%%.*}" ]
	then
		for s in html log.html
		do
			sed	-e "s/HREF=scc\.${scc_host%%.*}.html/HREF=scc.${scc_host}.html/g"		\
				-e "s/HREF=\"scc\.${scc_host%%.*}.html/HREF=\"scc.${scc_host}.html/g"		\
				-e "s/HREF=scc\.${scc_host%%.*}.log.html/HREF=scc.${scc_host}.log.html/g"	\
				-e "s/HREF=\"scc\.${scc_host%%.*}.log.html/HREF=\"scc.${scc_host}.log.html/g"	\
					<scc.${scc_host}.${s} >${TMP1_FILE}
			mv ${TMP1_FILE} scc.${scc_host}.${s}
		done
	fi

	# Untarred files get the owner/group/permissions from the source file. Override these settings.
	chmod ${new_mode} ${contents}
	chown "${new_owner_group}" ${contents}

	# The new log file must contain the date-time of the current snapshot on the server. 
	# When this is not the case, the new log file may be an old restore,
	# or the data arrives in the wrong order (when the mail connection was down for some time).
	# Do not check with empty (test)files.
	if [	-s "${SCC_DEST}"/scc.${scc_host}.cur -a	\
		-s scc.${scc_host}.cur -a		\
		-s scc.${scc_host}.log -a		\
		-z "${server_compare}" ]
	then
		# The first lines of a snapshot contain the date and time.
		# Combine the two lines to get the timestamp.
		# :time:start: is before scc.1.4, :start time: from scc.1.4 onwards.
		snap_stamp=$(head -n 10 "${SCC_DEST}"/scc.${scc_host}.cur		|
				sed -n	-e 's/.*::date://p'		\
					-e 's/.*::start time://p'	\
					-e 's/.*::time:start://p'		|
				tr "\012" ":")
		grep -l "^${snap_stamp}" scc.${scc_host}.log >/dev/null 2>/dev/null
		if [ $? -ne 0 ]
		then
			# The run-date of the snapshot on the server does not appear in the
			# data that we are processing. It is possible that the data arrives in
			# the wrong order due to problems in the mail-transfer.
			# Check whether the run-date of the new data appears in the
			# data that is already in the realm.
			marker_new_log="$(tail -n 1 scc.${scc_host}.log)"
			grep -l "^${marker_new_log}$" "${SCC_DEST}/scc.${scc_host}.log" >/dev/null 2>/dev/null
			if [ $? -ne 0 ]
			then
				snap_date=${snap_stamp%:*}
				mv "${SCC_DEST}/scc.${scc_host}.cur" "${SCC_DEST}/scc.${scc_host}.cur.${snap_date}"
				mv "${SCC_DEST}/scc.${scc_host}.log" "${SCC_DEST}/scc.${scc_host}.log.${snap_date}"
				echo "${ProgName}: saved snapshot and log file for ${scc_host} at ${snap_date}"
			fi
		fi
	fi

	if [ ! -s scc.${scc_host}.cur -a ! -s scc.${scc_host}.log ]
	then
		scc_msg WARN "${ProgName}: received empty snapshot and log file from ${scc_host}"
		rm -f scc.${scc_host}.cur scc.${scc_host}.log
		return 0;	# Do not overwrite existing snapshot.
	fi

	( cd "${SCC_DEST}"; rm -f ${contents} )
	mv ${contents} "${SCC_DEST}"

	scc_msg INFO "${ProgName}: received data from ${scc_host}"

	return 0;
}

SYNTAX="Syntax error, use: ${ProgName} [ -e ] [ -l <log> ] [ -s <dir> ] [ <dir> ]"

logfile=${SCC_LOG}/scc-transfer.log
decrypt_mail=0
while [ $# -gt 0 ]
do
	if [ $# -eq 1 ]
	then
		# All options, except the ones in the following list, should have an argument.
		if [ "x${1}" != "x-e" ]
		then
			echo "${ProgName}: Syntax error, missing argument for option: $1" 
			exit 2
		fi
	fi

	case "${1}" in
	-e)	decrypt_mail=1
		shift 1;;

	-l)	logfile=$2
		shift 2;;

	-s)	SCC_SRC="${2}"
		shift 2;;

	-*)	scc_msg SYNTAX "${SYNTAX}";;

	*)	break;;
	esac
done

if [ ! -d "${SCC_SRC}" ]
then
	scc_msg ERROR "${ProgName}: missing source-directory: ${SCC_SRC}"
fi

if [ $# -gt 1 ]
then
	scc_msg SYNTAX "${SYNTAX}"
fi
if [ $# -eq 1 ]
then
	SCC_DEST="${1}"
fi

if [ ! -d "${SCC_DEST}" ]
then
	scc_msg ERROR "${ProgName}: missing destination-directory: ${SCC_DEST}"
fi

# Successfull, but nothing transferred.
echo "0" >${EXIT_FILE}

which gunzip 2>/dev/null >/dev/null
gunzip_absent=$?
which uncompress 2>/dev/null >/dev/null
uncompress_absent=$?
which openssl 2>/dev/null >/dev/null
openssl_absent=$?
which uudecode 2>/dev/null >/dev/null
uudecode_absent=$?

# Check the directories "cp" and "mail". Check scc (for rcp/scp) and scc-receive-mail (for mail/smtp)
# to find out how the files are placed in these directories.
cd "${SCC_SRC}"
if [ -d "cp" ]
then
	cd cp
	find . -type d			|
	while read dir
	do
		cd ${SCC_SRC}/cp/"${dir}"
		for f in scc.*.signal
		do
			# Check the code in scc.sh
			if [ ! -f ${f} -o ! -s ${f} ]
			then
				continue		# nothing there or transfer in progress
			fi
			rm -f ${f}

			host="${f%.signal}"
			host="${host#scc.}"
			if [ -f scc.${host}.tar.Z ]
			then
				if [ ${uncompress_absent} -eq 1 ]
				then
					if [ ${gunzip_absent} -eq 1 ]
					then
						scc_msg ERROR "${ProgName}: uncompress not found"
					else
						# gunzip can also handle compressed files.
						gunzip scc.${host}.tar.Z
					fi
				else
					uncompress scc.${host}.tar.Z
				fi
			elif [ -f scc.${host}.tar.gz ]
			then
				if [ ${gunzip_absent} -eq 1 ]
				then
					scc_msg ERROR "${ProgName}: gunzip not found"
				else
					gunzip scc.${host}.tar.gz
				fi
			elif [ ! -f scc.${host}.tar ]
			then
				scc_msg WARN "${ProgName}: missing data for host: ${host}"
			fi

			scc_untar "${host}" "scc.${host}.tar"
			echo "3" >${EXIT_FILE}
		done
	done
fi

cd "${SCC_SRC}"
if [ -d "mail" ]
then
	cd "mail"

	# When scc-transfer was not activated for some time, several snapshots from a host
	# can be present. To end up with the most recent version, we start to process the 
	# files with the oldest.
	ls -tr			|
	grep "^done_"		|
	while read f
	do
		if [ ! -f "${f}" -o -s "${f}" ]
		then
			continue		# nothing there or transfer in progress
		fi

		if [ ${uudecode_absent} -eq 1 ]
		then
			scc_msg ERROR "${ProgName}: uudecode not found"
		fi

		id="${f#done_}"
		if [ ! -f "file_${id}" ]
		then
			scc_msg WARN "${ProgName}: missing data for id: ${id}"
			rm -f ${f}
			continue
		fi

		sender="$(awk '/^From/ { print $2 }' "file_${id}" | head -n 1)"

		# Extract and decode the data from the data-file.
		# Make sure we only decode "our" own file.
		sed -n -e '/^begin [0-7][0-7]* '"${TRANSFER}"'/,$p' "file_${id}"	|
		uudecode 2>/dev/null
		rm -f file_${id} done_${id}

		# Data from a pipe gets mode 000 from uuencode on some systems.
		chmod u+r ${TRANSFER}* 2>/dev/null

		# Check whether the data has been encrypted
		if [ -f ${TRANSFER}*.e.tar ]
		then
			if [ ${openssl_absent} -eq 1 ]
			then
				scc_msg ERROR "${ProgName}: openssl not found"
			fi

			if [ -z "${private_key_file}" ]
			then
				private_key_file="$(sed -n 's/^SCC_PRIV_HOST_KEY=//p' ${SCC_CONF}/scc.conf)"
				if [ ! -s "${private_key_file}" ]
				then
					scc_msg ERROR "${ProgName}: key-file ${private_key_file} not found"
				fi
			fi

			transfer=$(basename ${TRANSFER}*.e.tar .e.tar)
			passphrase="passphrase"

			# Check the corresponding code in scc.sh
			# - check the contents of the tar-archive
			# - untar to receive ${passphrase}.e and ${TRANSFER}${suffix}.e
			contents=$(tar tf "${transfer}.e.tar" 2>/dev/null | sed -e 's@\./@@g' | sort | tr "\012" " ")
			case "${contents}" in
			passphrase.e?${TRANSFER}*.e?)
				:		# OK
				;;
			*)
				mv ${transfer}.e.tar ${SCC_ERROR}/scc-transfer.e.tar
				scc_msg WARN "${ProgName}: unexpected data from ${sender}:${contents}, expected:passphrase.e ${TRANSFER}*.e"
				continue
				;;
			esac
			tar xf ${transfer}.e.tar 2>/dev/null
			rm -f ${transfer}.e.tar

			# - decrypt the pass phrase used by the client
			openssl	rsautl				\
				-in ${passphrase}.e		\
				-out ${passphrase}		\
				-inkey "${private_key_file}"	\
				-decrypt
			rm -f ${passphrase}.e

			# Determine the suffix of the SCC-data.
			suffix=$(echo ${TRANSFER}*.e)
			suffix=${suffix##${TRANSFER}}
			suffix=${suffix%.e}

			# - decrypt the scc-data using the decrypted pass phrase
			openssl	des3				\
				-in ${TRANSFER}${suffix}.e	\
				-out ${TRANSFER}${suffix}	\
				-pass file:${passphrase}	\
				-d
			rm -f ${TRANSFER}${suffix}.e ${passphrase}
		else
			if [ ${decrypt_mail} -eq 1 ]
			then
				scc_msg WARN "${ProgName}: non-encrypted mail transfer from ${sender}"
				rm -f ${TRANSFER}* 2>/dev/null
				continue
			fi
		fi

		# Accept only ${TRANSFER}.gz, ${TRANSFER}.Z or ${TRANSFER}
		if [ -f ${TRANSFER}.gz ]
		then
			if [ ${gunzip_absent} -eq 1 ]
			then
				scc_msg ERROR "${ProgName}: gunzip not found"
			else
				gunzip ${TRANSFER}.gz
			fi
		elif [ -f ${TRANSFER}.Z ]
		then
			if [ ${uncompress_absent} -eq 1 ]
			then
				# gunzip can also handle compressed files.
				if [ ${gunzip_absent} -eq 1 ]
				then
					scc_msg ERROR "${ProgName}: uncompress not found"
				else
					gunzip ${TRANSFER}.Z
				fi
			else
				uncompress ${TRANSFER}.Z
			fi
		elif [ ! -f ${TRANSFER} ]
		then
			scc_msg WARN "${ProgName}: missing ${TRANSFER} in email: file_${id} from ${sender}"
		else
			# The client could not find compress or gzip and sent the plain tar-file.
			# Another possibility is a release 1.2.10 scc-transfer on the client,
			# This version sent the compressed tar-file without suffix by email. Try to 
			# uncompress/gunzip the file.
			if [ ${uncompress_absent} -eq 1 ]
			then
				gunzip <${TRANSFER} >${TRANSFER}.tmp 2>/dev/null
			else
				uncompress <${TRANSFER} >${TRANSFER}.tmp 2>/dev/null
			fi
			if [ -s ${TRANSFER}.tmp ]
			then
				# The file contained a compressed archive.
				mv -f ${TRANSFER}.tmp ${TRANSFER}
			else
				rm -f ${TRANSFER}.tmp
			fi
		fi

		if [ -f ${TRANSFER} ]
		then
			host=$(tar tf ${TRANSFER} 2>/dev/null | head -n 1 | sed -e 's/^scc\.//' -e 's/\.cur$//')
			scc_untar "${host}" ${TRANSFER}
			echo "3" >${EXIT_FILE}
		fi
	done
fi

exit $(cat ${EXIT_FILE})
