#! /bin/bash

# ----------------- ClamAV Milter for Slackware -----------------
# ------ Build script by Martijn Dekker <martijn@inlv.org> ------
# Suggested usage:  $ bash clamav.SlackBuild 2>&1 | tee build.log
#
# Style note: I use long GNU --options where possible, for readability.
#
###### Change log ######
#  4-Nov-2006 - Update creation docdir. Update LogSyslog in `sed' on clamd.conf.
#	- etc.tar.gz: Improved /etc/rc.d/rc.clamav startup script.
# 21-Oct-2006 - Cleanup 'procreate'.
# 11-Aug-2006 - Minor code cleanups.
#  5-Jul-2006 - Install man pages in /usr/man instead of /usr/share/man.
#  1-Jul-2006 - Option not to package virus databases (they're getting big!)
# 14-May-2006 - doinst.sh: If the clamav user already exists, modify it instead
#	  of deleting and re-adding it, changing the UID if > 1000.
#  3-May-2006 - Various improvements to build script.
#	- doinst.sh: find low UID (100-999) for clamav user.
# 13-Apr-2006 - Automatically generate upd_conf() calls for doinst.sh.
#	- upd_conf() deletes empty .new files if corresponding files are already
#	  present. This is used to initialize empty log files if needed.
#	- Check if the packaged virus databases are newer than those already 
#	  present in the system before using them.
#	- Have doinst.sh add us to /etc/rc.d/rc.local.
#	- Check if included virus databases are newer before using them.
#	- Actually compile sendmail-slackware-clamav{,-tls}.cf ourselves!
# 27-Jun-2005 - Set owner & group for /var/log/clamd too.
# 13-May-2005 - doinst.sh: Saner upd_conf() function instead of config(), which
#	  even reports it if new config files differ!
#	- Update settings of pre-existing clamav user.
#  1-May-2005 - doinst.sh: More specific grep for clamav group and user.
#	- doinst.sh: Do not keep redundant clamav-update.new.
#########################
# Copyright 2005-2006 Martijn Dekker, Groningen, Netherlands.
#
# Redistribution and use of this script, with or without modification, is
# permitted provided that the following conditions are met:
#
# 1. Redistributions of this script must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR IMPLIED
#  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
#  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
#  EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
#  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
#  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
#  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
#  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


if test $UID = 0; then
	echo "[1mThere is no need to run this build script as root.[0m You will be"
	echo "asked for your root password at package creation time (unless you"
	echo "have installed fakeroot, in which case even that isn't needed)."
	echo -n "Type \"take risk\" to continue [1;31mat your own risk[0m: "
	read RISK
	test "$RISK" = "take risk" || exit 1
fi


#################################################
#  STAGE 0					#
#  Prepare convenient variables for the three	#
#  following stages.				#
#################################################

# any of these variables can be overridden from outside script
# run e.g.: $ ARCH=i686 BUILD_ID=2 ./clamav.SlackBuild
# or (tip!) `export' some of these in your `.profile' file
VERSION=${VERSION:-0.88.6}
ARCH=${ARCH:-i486}
BUILD_ID=${BUILD_ID:-1}			# the "1" in package-0.0-i486-1xxx.tgz
BUILDER_ID=${BUILDER_ID:-$USER}		# your initials (the "xxx" above)
BUILDER=${BUILDER:-$USER@$HOSTNAME}
PKGROOT=${PKGROOT:-/tmp}
SRCROOT=${SRCROOT:-$(/usr/bin/dirname $0)}
SLACKBUILD_CONFIGCACHE=${SLACKBUILD_CONFIGCACHE:-no}
					# if "yes", enable ./configure cache
CLAM_INC_VIRDB=${CLAM_INC_VIRDB:-no}	# "yes": include virus databases (big!)

# variables for convenience
PROG=clamav
PKGNAME=$PROG-${VERSION/-/.}-$ARCH-$BUILD_ID$BUILDER_ID
OPTIMIZE="-O2"
DATE=$(LC_ALL=C /bin/date +%d-%b-%Y)
SRCARCHIVE=$PROG-$VERSION.tar.gz
DL_URL="--timeout=5 http://dl.sf.net/sourceforge/$PROG/$SRCARCHIVE"
PKGTREE=$PKGROOT/$PKGNAME-tree
DOCDIR=$PKGTREE/usr/doc/$PROG-$VERSION
MYDIR=$(/usr/bin/dirname $0)

# make absolute paths
PKGROOT=$(cd $PKGROOT; pwd)
SRCROOT=$(cd $SRCROOT; pwd)
MYDIR=$(cd $MYDIR; pwd)

# set appropriate -march and -mcpu/-mtune flags for i*86
case $ARCH in
( i[345]86 )	# use '-mtune' for GCC >= 3.4, '-mcpu' for GCC < 3.4
	TUNE='tune'
	GCC_VER=$( ${CC:-/usr/bin/gcc} --version | /bin/head --lines=1 |
		/bin/cut --fields=3 --delimiter=' ' )
	case $GCC_VER in ( [12].* | 3.[0123].* ) TUNE='cpu' ;; esac
	OPTIMIZE="$OPTIMIZE -march=$ARCH -m$TUNE=i686"
	;;
( i686 )
	OPTIMIZE="$OPTIMIZE -march=i686"
	;;
esac

set -o errexit -o xtrace	# exit on error, print executed commands
umask 022			# just in case the user changed it, leading to bad permissions


#################################################
#  STAGE 1					#
#  Download, unpack, and compile the source.	#
#################################################

# if source is not present, download in source rootdir if possible, or in /tmp
cd $SRCROOT
if ! test -r $SRCARCHIVE; then
	if ! test -w .; then
		cd /tmp
		SRCROOT=/tmp
	fi
	test -r $SRCARCHIVE || /usr/bin/wget $DL_URL
fi

# unpack in source rootdir, or in /tmp in case of an unwritable source rootdir
# with source tarball already present
test -w . || cd /tmp
/bin/tar --ungzip --extract --file $SRCROOT/$SRCARCHIVE
test -w $SRCROOT || SRCROOT=/tmp

# delete any stale package tree
/bin/rm --recursive --force $PKGTREE

# ---- Compile (the real work starts here) ----
SRCTREE=$SRCROOT/$PROG-$VERSION
cd $SRCTREE
if test "$SLACKBUILD_CONFIGCACHE" = "yes" # Are we using the ./configure cache?
	then CACHE_COMMAND="--cache-file=$SRCROOT/config-$PROG.cache"
	else unset CACHE_COMMAND
fi
./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var/lib	\
	--disable-dependency-tracking --build=$ARCH-slackware-linux-gnu	\
	--mandir=/usr/man --enable-milter --with-dbdir=/var/lib/clamav	\
	$CACHE_COMMAND CFLAGS="$OPTIMIZE"
/usr/bin/make all install-strip DESTDIR=$PKGTREE

# do some tests
set +o errexit	# switch this off as we expect error results
unset FAILED
for F in test/clam.*; do
	clamscan/clamscan --database=./database $F
	if test $? = 1; then
		echo "Test scan of file $F passed."
	else
		echo "Test scan of file $F [1mFAILED[0m."
		FAILED=1
	fi
done
set -o errexit
test -z $FAILED

# make doc dir (skip bloat)
/bin/mkdir --parents $DOCDIR
/bin/cp --archive AUTHORS BUGS ChangeLog COPYING examples FAQ README	\
	TODO UPGRADE NEWS docs/clamav-mirror-howto.pdf			\
	docs/signatures.pdf $DOCDIR/
test -d docs/html && cp --archive docs/html $DOCDIR/
/bin/cp --preserve clamav-milter/INSTALL $DOCDIR/INSTALL.milter

if test $CLAM_INC_VIRDB = "yes"; then
	# Let's include now-current virus databases as a fallback for the user.
	# But let's not download them unnecessarily if we have them locally.
	# The timestamps are lost on `make install' so we re-copy them, either
	# from the source tree or from /var/lib/clamav, whichever is newer.
	CVDs=$(/usr/bin/find database -type f -name '*.cvd' -printf '%P ')
	for CVD in $CVDs; do
		CVD1=/var/lib/clamav/$CVD
		CVD2=$SRCTREE/database/$CVD
		test $CVD1 -nt $CVD2 && FILE=$CVD1 || FILE=$CVD2
		/bin/cp --preserve $FILE $PKGTREE/var/lib/clamav/
	done
	/usr/bin/wget --timestamping				\
		--directory-prefix=$PKGTREE/var/lib/clamav	\
		$(for CVD in $CVDs; do
			echo "http://database.clamav.net/$CVD"
		done) || true  # we don't want to fail the entire build on this
else
	/bin/rm --verbose $PKGTREE/var/lib/clamav/*.cvd
fi

#################################################
#  STAGE 2					#
#  Now, we are leaving the sources behind	#
#  and work on our package tree only.		#
#################################################

cd $PKGTREE
/bin/rm --recursive --force $SRCTREE	# we don't need the source tree anymore

# Initialize directories and logfiles
/bin/mkdir --parents var/run/clamav-milter var/log
/bin/touch var/log/clamd var/log/clamav-update
/bin/chmod o-r var/log/clamd

# copy rc.d, cron.daily, and logrotate.d
/bin/tar --verbose --verbose --ungzip --extract --file $MYDIR/etc.tar.gz

# make some of our own settings for good milter operation
/bin/sed --in-place '
	s,Example config,Config,
	s,^Example$,#Example,
	s,^ *#* *UpdateLogFile .*,UpdateLogFile /var/log/clamav-update,
	s,^ *#* *PidFile /.*,PidFile /var/run/clamav-milter/freshclam.pid,
' etc/freshclam.conf
/bin/sed --in-place '
	s,Example config,Config,
	s,^Example$,#Example,
	s,^ *#* *LogSyslog,LogSyslog,
	s,^ *#* *LogFile /.*,LogFile /var/log/clamd,
	s,^ *#* *PidFile /.*,PidFile /var/run/clamav-milter/clamd.pid,
	s,^ *#* *LocalSocket /.*,LocalSocket /var/run/clamav-milter/clamd.sock,
	s,^ *#* *User .*,User clamav,
' etc/clamd.conf

# Make dummy sendmail-slackware-clamav*.{cf,mc} files. The installation script
# will compile real ones. The dummies are made so that Slackware's package
# system will know about these files and remove them when {remove,upgrade}pkg
# are used.
/bin/mkdir --parents usr/share/sendmail/cf/cf
for FILE in						\
	sendmail-slackware-clamav.cf			\
	sendmail-slackware-tls-clamav.cf		\
	cf/cf/sendmail-slackware-clamav.mc		\
	cf/cf/sendmail-slackware-tls-clamav.mc
do
	echo "# Dummy from Slackware package $PKGNAME.tgz
# If you see this, the package wasn't installed correctly.
"	> usr/share/sendmail/$FILE
done

# compress doc files > 4 kB
/usr/bin/find $DOCDIR -type f -size +4k -maxdepth 1 |
	/usr/bin/xargs --no-run-if-empty /bin/gzip --verbose --best

# compress man and info pages and fix symlinks in them
set +o errexit				# or the loop might exit prematurely
/usr/bin/find $PKGTREE -type d '(' -name man -or -name info ')' | while read DIR
do
	/usr/bin/find $DIR -type l ! -name '*.gz' | while read LINK
	do
		/bin/ln --symbolic $(/usr/bin/readlink $LINK).gz $LINK.gz
		/bin/rm $LINK
	done
	/bin/gzip --recursive --best --verbose $DIR
done
set -o errexit

/bin/mkdir install

# make slack-desc package description file
# note: slack-desc must have exactly 11 lines starting with "$PROG: "
#    |-----handy-ruler------------------------------------------------------|
/bin/cat > install/slack-desc <<END_OF_SLACK_DESC
$PROG: Open Source antivirus scanner - ClamAV $VERSION - see www.clamav.net
$PROG:   Clam AntiVirus is a GPL antivirus toolkit for UNIX. The main purpose
$PROG: is the integration with mailservers (attachment scanning). The package
$PROG: provides a flexible and scalable multi-threaded daemon, a command line
$PROG: scanner, and a tool for automatic updating via Internet.
$PROG:   This package contains a binary distribution of ClamAV, including the
$PROG: Sendmail interface to scan e-mail: clamav-milter. Once installed, it's
$PROG: ready to go and can be started with no modification. If you want Send-
$PROG: mail to use it,  copy /usr/share/sendmail/sendmail-slackware-clamav.cf
$PROG: to /etc/mail/sendmail.cf, then start ClamAV and restart Sendmail.
$PROG:   (Compiled by $BUILDER on $DATE)
END_OF_SLACK_DESC

# in case someone does `finger clamav'
/bin/sed "s/^$PROG: //" install/slack-desc > var/lib/clamav/.plan

# NOTE: Long lines follow. Sorry, 80-column dinosaurs - it wasn't practical.

# Generate doinst.sh package installation script (LONG!)
# Note: in "<<-" here-documents, all intial tab characters are skipped,
# so I'm doing "tab, space, more tabs" indenting and only the tabs after
# the intial space are preserved. Walk over it with the cursor to see.
# Note also the variable and command expansion!
/bin/cat > install/doinst.sh <<-END_OF_DOINST.SH
	#! /bin/bash
	# Generated by $PROG.SlackBuild for $PKGNAME.tgz on $DATE
	# By Martijn Dekker <martijn@inlv.org>
	# Based on install script by Jim Simmons for ClamAV 0.81.

	function upd_conf {
	 	# If there's no config file by that name, mv it over:
	 	if ! test -f \$1; then
	 		mv \$1.new \$1
	 	elif diff --brief \$1 \$1.new; then	# toss the redundant copy
	 		rm \$1.new
	 	elif ! test -s \$1.new; then
	 		rm \$1.new   # do not keep empty initial files (e.g. log)
	 	fi
	 	# Otherwise, we leave the .new copy for the admin to consider...
	}

	function free_user_id {
	 	# Find a free user-ID >= 100 (should be < 1000 so it's not a normal user)
	 	local FREE_USER_ID=100
	 	while grep -q "^.*:.*:\$FREE_USER_ID:.*:.*:.*:" /etc/passwd; do
	 		let FREE_USER_ID++
	 	done
	 	echo \$FREE_USER_ID
	}

	# Update configuration files
END_OF_DOINST.SH
# Find files to rename to $FILE.new, generating corresponding doinst.sh code:
{
	# Rename config files so we don't overwrite user-customized ones:
	echo "{"
	/usr/bin/find etc -type f | while read FILE; do
		echo "	upd_conf $FILE"
		/bin/mv $FILE $FILE.new
	done
	echo "} | sed 's/^/[1mConfig alert: [0m/'"
	echo ""

	# Rename the empty log files too, so upgrading won't delete logs, and
	# empty log files are initialized with correct perms on first install:
	echo "# Initialize log files if not already present"
	/usr/bin/find var/log -type f | while read FILE; do
		echo "upd_conf $FILE 2>&1 > /dev/null"
		/bin/mv $FILE $FILE.new
	done
	echo ""
} >> install/doinst.sh

if test $CLAM_INC_VIRDB = "yes"; then
	# Rename virus databases so doinst.sh can compare them:
	CVDs=$(/usr/bin/find var/lib/clamav -type f -name '*.cvd' -printf '%p ')
	for FILE in $CVDs; do
		/bin/mv $FILE $FILE.new
	done
	/bin/cat >> install/doinst.sh <<-END_OF_APPEND
		# Compare packaged and existing virus databases; keep only the newer
		for FILE in $CVDs; do
		 	test \$FILE.new -nt \$FILE && mv \$FILE.new \$FILE || rm \$FILE.new
		done

END_OF_APPEND
fi

/bin/cat >> install/doinst.sh <<-END_OF_APPEND
	# Set up group
	grep -q '^clamav:' etc/group || groupadd clamav

	# Set up user: add it if it doesn't exist, update it if it already does.
	# In any case, make sure it has or gets a user ID between 100 and 999.
	echo "Setting up user 'clamav' and group 'clamav'."
	if EXISTING_ENTRY=\$(grep '^clamav:' /etc/passwd); then
	 	ACTION=mod				# Modify existing user
	 	USER_ID=\$(echo \$EXISTING_ENTRY | cut --fields=3 --delimiter=':')
	 	if test \$USER_ID -ge 1000; then	# Modify existing uid if >= 1000
	 		USER_ID=\$(free_user_id)
	 	fi
	else
	 	ACTION=add				# Add non-existent user
	 	USER_ID=\$(free_user_id)
	fi
	user\$ACTION -g clamav -d /var/lib/clamav -c "ClamAV Antivirus Daemon"		\\
	 	-u \$USER_ID -s /bin/false clamav

	# Set up permissions
	chown --recursive clamav:clamav var/lib/clamav var/run/clamav-milter		\\
	 	var/log/clamav-update var/log/clamd
	chown root:clamav usr/sbin/clamav-milter usr/sbin/clamd

	# Make us start on boot
	if ! grep -q "rc.clamav start" etc/rc.d/rc.local; then
	 	echo "Adding ClamAV to /etc/rc.d/rc.local so it starts at bootup time."
	 	{	echo ""
	 		echo "# ClamAV, the open source anti-virus scanner"
	 		echo "test -x /etc/rc.d/rc.clamav && . /etc/rc.d/rc.clamav start"
	 	} >> etc/rc.d/rc.local
	else
	 	echo "ClamAV is already in /etc/rc.d/rc.local ."
	fi

	# Generate Sendmail .mc and .cf files with support for the ClamAV milter
	CLMILTER_MC_CMD="dnl# ClamAV milter integration from $PKGNAME.tgz:
	INPUT_MAIL_FILTER(\\\`clamav', \\\`S=local:/var/run/clamav-milter/milter.sock, F=, T=S:4m;R:4m')dnl
	define(\\\`confINPUT_MAIL_FILTERS', \\\`clamav')dnl
	"
	if test -r usr/share/sendmail/cf/cf/sendmail-slackware.mc &&
	 	test -x usr/sbin/sendmail && test -x usr/bin/m4
	then
	 	cd usr/share/sendmail/cf/cf

	 	for SLACK in sendmail-slackware sendmail-slackware-tls; do
	 		SLACK_MC=\$SLACK.mc
	 		CLAM_MC=\$SLACK-clamav.mc
	 		CLAM_CF=\$SLACK-clamav.cf
	 		if test -f \$SLACK_MC; then
	 			echo "Generating /usr/share/sendmail/\$CLAM_CF ..."
	 			sed "/VERSIONID/ s/')dnl/ with ClamAV milter support')dnl/" \$SLACK_MC > \$CLAM_MC
	 			echo "\$CLMILTER_MC_CMD" >> \$CLAM_MC
	 			m4 ../m4/cf.m4 \$CLAM_MC > ../../\$CLAM_CF
	 		else
	 			echo "WARNING: Cannot generate /usr/share/sendmail/\$CLAM_CF because"
	 			echo "	 the file /usr/share/sendmail/cf/cf/\$SLACK_MC doesn't exist."
	 			rm \$CLAM_MC \$CLAM_CF	# remove dummies
	 		fi
	 	done

	 	cd ../../../../..
	 	echo "[1mTo enable Sendmail integration, copy one of these .cf files to[0m"
	 	echo "[1m/etc/mail/sendmail.cf, then start ClamAV and restart Sendmail.[0m"
	else
	 	# if we can't make 'em, remove the dummies
	 	rm usr/share/sendmail/sendmail-slackware-clamav.cf		\\
	 		usr/share/sendmail/sendmail-slackware-tls-clamav.cf	\\
	 		usr/share/sendmail/cf/cf/sendmail-slackware-clamav.mc	\\
	 		usr/share/sendmail/cf/cf/sendmail-slackware-tls-clamav.mc
	 	echo "[1;31mWARNING: [0mThe ClamAV-enabled config files for Sendmail could not be generated."
	 	echo "	 If you want ClamAV to work with Sendmail, first make sure the"
	 	echo "	 [1msendmail[0m, [1msendmail-cf[0m, and [1mm4[0m packages for Slackware are installed,"
	 	echo "	 and then do: [31m# [32mupgradepkg --reinstall $PKGNAME.tgz[0m"
	 	echo "	 If Sendmail support is not desired, ignore this warning."
	fi

END_OF_APPEND

if test $CLAM_INC_VIRDB != "yes"; then
	echo "echo \"This package does [1mnot[0m include a virus database.\"" >> install/doinst.sh
fi

/bin/cat >> install/doinst.sh <<-END_OF_APPEND
	echo "[1;31mYou should update your virus database right away with \\\`freshclam'.[0m"

	# Make symlinks (generated by makepkg)
END_OF_APPEND

# procreate: include this SlackBuild script in the package, updating
# a few defaults if they were overridden from outside the script
/bin/mkdir --parents usr/src/slackbuilds/$PROG
/bin/cp --preserve $MYDIR/etc.tar.gz usr/src/slackbuilds/$PROG/
/bin/sed "	/^VERSION=/		s/:-.*}/:-$VERSION}/
		/^BUILD_ID=/		s/:-.*}/:-$BUILD_ID}/
		/^ARCH=/		s/:-.*}/:-$ARCH}/
		/^CLAM_INC_VIRDB=/	s/:-.*}/:-${CLAM_INC_VIRDB:0:3}}/
	" $MYDIR/$PROG.SlackBuild > usr/src/slackbuilds/$PROG/$PROG.SlackBuild
/bin/chmod +x usr/src/slackbuilds/$PROG/$PROG.SlackBuild


#########################################
#  STAGE 3				#
#  Finally make the package (requires	#
#  root or, preferably, fakeroot).	#
#########################################

set +o xtrace			# no longer print commands upon execution

ROOTCOMMANDS="set -o errexit -o xtrace ; cd $PKGTREE ;
	/bin/chown --recursive root:root .  ;"
# `chgrp bin' is abolished from Slack 11 on
read SLACK SLACKVER < /etc/slackware-version
if test ${SLACKVER%%.*} -lt 11
then ROOTCOMMANDS="$ROOTCOMMANDS
	/usr/bin/find . -type d '(' -name bin -or -name sbin ')'	\
		-exec /bin/chgrp --recursive bin {} ';'  ;"
fi
ROOTCOMMANDS="$ROOTCOMMANDS
	/sbin/makepkg --linkadd y --chown n $PKGROOT/$PKGNAME.tgz  ;
	/bin/rm --recursive --force $PKGTREE"

if test $UID = 0; then
	eval $ROOTCOMMANDS
	set +o xtrace
elif test -x /usr/bin/fakeroot; then
	echo "[1mEntering fakeroot environment.[0m"
	echo $ROOTCOMMANDS | /usr/bin/fakeroot
else
	echo "[1mPlease enter your root password.[0m (Consider installing fakeroot.)"
	/bin/su -c "$ROOTCOMMANDS"
fi


echo "[1;32mYour package is ready in [31m$PKGROOT/$PKGNAME.tgz[32m.
[34mBuild script by Martijn Dekker <martijn@inlv.org>. Have fun. [33m:)[0m"
