#!/bin/bash
#
#  YUP - "Yum Update Proxy" for SLE 10 Maintenance
#  Copyright (C) 2006,2007 SUSE Linux Products GmbH
#
#  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 of the License, 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; if not, write to the Free Software
#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
#
#  This packages allows to configure an update-mirror
#  for Novell maintenance packages for SUSE Linux
#  Enterprise 10.
#
#  Please make sure, that the number of systems to serve with the
#  mirrored packages is in compliance with the number of
#  subscriptions you are allowed to exercise according to your
#  contract with Novell.
#
#  To configure network access rules (proxies, proxy
#  authentication, ...) please use YaST2 -> Network Services ->
#  Proxy.  The resulting configuration can be found in
#  /etc/sysconfig/proxy and /root/.curlrc (authentication data).
#
#  $Id: yup.sh 232 2007-09-11 15:46:13Z mge $
#

# Fail on errors in simple commands.
# set -e

# Keep a log of errors that cause a delayed exit
errorlog=""

# Obtain a temporary file which we will use to capture curl command output
CURLTMPFILE=$(mktemp -t yup.XXXXXXXXXX)
if test "${CURLTMPFILE}." = "." ; then
	echo "Failed to create temp file"
	exit 1
fi

# Obtain another temporary file which we will use for loop data
LOOPTMPFILE=$(mktemp -t yup.XXXXXXXXXX)
if test "${LOOPTMPFILE}." = "." ; then
	echo "Failed to create temp file"
	exit 1
fi

# Clean up after us
trap "rm -f $CURLTMPFILE $LOOPTMPFILE" EXIT 

function fetch_url() {
	# Wrapper function: url [localfile]
	# Fetch a URL to a local file; when the URL cannot be fetched
	# successfully, display details and terminate.

	url=$1
	localfile=$2
	if test "${localfile}." = "."; then
		localfile=$(basename $url)
	fi

	echo -n "Fetching $url... "
	# Fetch the URL. As this command may fail, run it in a subshell so
	# as not to exit immediately.

	( $YUP_CURL $YUP_CURLCRED $YUP_CURLPARAMS --time-cond $localfile $url -o ${localfile}.new > $CURLTMPFILE 2>&1 )
	rc=$?
	if [ $rc -eq 0 ] ; then
		if [ -f "${localfile}.new" ]; then mv ${localfile}.new $localfile ; fi
		echo "done"
	else
		echo "FAILED with exit code $rc"
		echo "Failure details:"
		echo "----------------------------------------------------------------------------"
		cat $CURLTMPFILE
		echo "----------------------------------------------------------------------------"
		return $rc
		# echo "Exiting"
		# exit 1
	fi
	return 0
}

function verify_rpm() {
	url=$1
	if [ -x /bin/rpm ] ; then
		rpm -qKsp --nosignature $url > /dev/null
		rc=$?
		if [ $rc -eq 0 ] ; then
			echo -e -n ""
		else
			if test -f "$uri"; then
				echo " broken file, renaming, ..."
				mv $url $url.broken
			fi
		fi
	fi
}

function fetch_repodata() {
	bn=$1
	# Always download all the necessary metadata
	fetch_url "$bn/repodata/repomd.xml.asc" "repodata/repomd.xml.asc"
	fetch_url "$bn/repodata/repomd.xml.key" "repodata/repomd.xml.key"
	fetch_url "$bn/repodata/repomd.xml" "repodata/repomd.xml"
	grep -E "location[[:space:]]*href" repodata/repomd.xml | grep -v 'other.xml.gz' > $LOOPTMPFILE
	{
		while read line ; do
			uri=`echo "$line"|sed -e 's/^[^"]*"//;s/".*$//;'`
			dn=`dirname $uri`
			test -d $dn || mkdir -p $dn
			fetch_url "$bn/$uri" "$uri" 
		done || exit $?
	} < $LOOPTMPFILE
}

function fetch_binrpms() {
	bn=$1
	# Download all the rpms mentioned in primary.xml.gz for the YUM repo.
	if [ "${YUP_SRCRPM}." = "yes." ] ; then
		zcat repodata/primary.xml.gz | grep -E "location[[:space:]]*href" > $LOOPTMPFILE
	else 
		zcat repodata/primary.xml.gz | grep -E "location[[:space:]]*href" | grep -v 'rpm/src' > $LOOPTMPFILE
	fi
	{
		while read line ; do
			uri=`echo "$line"|sed -e 's/^[^"]*"//;s/".*$//;'`
			dn=`dirname $uri`
			test -d $dn || mkdir -p $dn
			# echo -e -n "$uri"
			verify_rpm "$uri"
			if ! test -f "$uri"; then
				if !  fetch_url "$bn/$uri" "$uri" ; then
					errorlog="$errorlog $bn/$uri"
				fi
				verify_rpm "$uri"
			fi
		done
	} < $LOOPTMPFILE
	return 0
}

function fetch_srcrpms() {
	bn=$1
	# Download all the rpms mentioned in primary.xml.gz for the YUM repo.
	zcat repodata/primary.xml.gz | grep -E "location[[:space:]]*href" | grep 'rpm/src' > $LOOPTMPFILE
	{
		while read line ; do
			uri=`echo "$line"|sed -e 's/^[^"]*"//;s/".*$//;'`
			dn=`dirname $uri`
			test -d $dn || mkdir -p $dn
			# echo -e -n "$uri"
			verify_rpm "$uri"
			if ! test -f "$uri"; then
				if !  fetch_url "$bn/$uri" "$uri" ; then
					errorlog="$errorlog $bn/$uri"
				fi
				verify_rpm "$uri"
			fi
		done
	} < $LOOPTMPFILE
	return 0
}

function fetch_repomd() {
	bn=$1
	# Download all the repo-md patches, cross check with SHA1 sum for changed ones.
	grep -E "(location[[:space:]]*href|checksum[[:space:]]*type..sha)" repodata/patches.xml > $LOOPTMPFILE
	{
		while read line ; do
			echo "$line"|grep "checksum[[:space:]]*type..sha" >/dev/null
			res=$?
			if [ "$res" == "0" ]; then
				sha1=`echo "$line"|sed -e 's/^.*sha">//;s/<.checksum.*$//;'`
			else
				uri=`echo "$line"|sed -e 's/^[^"]*"//;s/".*$//;'`
				dn=`dirname $uri`
				test -d $dn || mkdir -p $dn
				nsum=`sha1sum "$uri"|sed -e 's/ .*//;'`
				if [ "${nsum}." != "${sha1}." ]; then
					echo "$uri is bad ($nsum / $sha1), reloading"
					rm -f "$uri"
				fi
				if !  test -f "$uri" ; then
					if ! fetch_url "$bn/$uri" "$uri" ; then
						errorlog="$errorlog $bn/$uri"
					fi
				fi
				echo $uri
			fi
		done || exit $?
	} < $LOOPTMPFILE
}

function fetch_patchrpms() {
	bn=$1
	# Download all the patch and delta rpms mentioned in the patches.
	grep -E "location[[:space:]]*href" repodata/patch-*.xml > $LOOPTMPFILE
	{
		while read line ; do
			uri=`echo "$line"|sed -e 's/^[^"]*"//;s/".*$//;'`
			dn=`dirname $uri`
			test -d $dn || mkdir -p $dn
			if ! test -f "$uri" ; then
				if ! fetch_url "$bn/$uri" "$uri" ; then
					errorlog="$errorlog $bn/$uri"
				fi
			fi
			echo $uri
		done || exit $?
	} < $LOOPTMPFILE
}

function yup_check_and_hardlink() {
	FILES_TO_HARDLINK=$( find $YUP_DEST_DIR -xdev -iname "*.rpm" -type f -size $YUP_PATSIZE -mtime $YUP_MTIME )
	for NN in $FILES_TO_HARDLINK ; do
		for MM in $FILES_TO_HARDLINK; do
			if [ $NN != $MM ] && [ $( basename $MM ) == $( basename $NN ) ] ; then
				echo $MM 
				echo $NN 
				if [ $( stat --format="%i" $MM ) == $( stat --format="%i" $NN ) ]; then
					echo "files are hard linked. nothing to do." 
				else
					MD5MM=$( md5sum $MM | cut -d" " -f1 )
					MD5NN=$( md5sum $NN | cut -d" " -f1 )
					echo -e -n "checking md5sum ..." 
					if [ $MD5MM == $MD5NN ] ; then
						echo "OK"
						ln -f $MM $NN
					else
						echo " FALSE." 
					fi
				fi
			fi
		done
	done
}

# import the update parameters
# ( YUP_CURL YUP_CURLPARAMS YUP_DEST_DIR 
#   YUP_ID YUP_PASS YUP_ID_SLED YUP_PASS_SLED 
#   YUP_ID_SLES YUP_PASS_SLES YUP_ARCH YUP_PRODUCTS )
#
if [ -d /etc/sysconfig/yup.d ] ; then
	YUP_CONFIGs=" $( find /etc/sysconfig/yup.d/ -type f ) "
else
	YUP_CONFIGs=" /etc/sysconfig/yup "
fi

for CC in $YUP_CONFIGs; do
	if [ -r $CC ]; then
		echo "Using config file $CC"
		. $CC
		# evaluate update parameters
		#
		echo "YUP_CURL       ="${YUP_CURL:="/usr/bin/curl"}
		echo "YUP_SERVER     ="${YUP_SERVER:="nu.novell.com"}
		echo "YOU_SERVER     ="${YOU_SERVER:="you.novell.com"}
		echo "YUP_DEST_DIR   ="${YUP_DEST_DIR:="/var/cache/yup"}
		echo "YUP_PATSIZE    ="${YUP_PATSIZE:="+20000"}
		echo "YUP_MTIME      ="${YUP_MTIME:="-8"}
		echo "YUP_ARCH       ="${YUP_ARCH:="i586"}
		echo "YUP_SUBVERSIONS="${YUP_SUBVERSIONS:="GA"}
		echo "YUP_SP_SUBCHANS="${YUP_SP_SUBCHANS:="-Updates -Online"}
		if [ "${YUP_DEBUGINFO}." = "yes." ] ; then
			YUP_PRODUCTS="${YUP_PRODUCTS:=SLES10} SLE10-Debug"
		else
			YUP_PRODUCTS="${YUP_PRODUCTS:=SLES10}"
		fi
		if [ "${YUP_SDK}." = "yes." ] ; then
			YUP_PRODUCTS="$YUP_PRODUCTS SDK10"
		fi
		echo "YUP_PRODUCTS   =$YUP_PRODUCTS"
		echo "YUP_CURLPARAMS ="${YUP_CURLPARAMS:="--verbose --remote-time --fail"} > /dev/null
			# --verbose: we use a wrapper function and only display curl output in
			# case of errors; in that case, we want details.
			#
			# --fail: we want curl to exit with a non-zero exit status when a URL
			# could not be retrieved, in particular for things like invalid
			# credentials (HTTP 403)
		YUP_CURLPARAMS=$( echo $YUP_CURLPARAMS | sed s/--digest//g );
		YUP_CURLPARAMS=$( echo $YUP_CURLPARAMS | sed s/--basic//g );
		if [ "$YUP_SERVER" = "update.novell.com" ]; then
			YUP_CURLPARAMS="--digest $YUP_CURLPARAMS"
		elif [ "$YUP_SERVER" = "nu.novell.com" ]; then
			YUP_CURLPARAMS="--basic $YUP_CURLPARAMS"
		fi
		echo "YUP_CURLPARAMS ="$YUP_CURLPARAMS
		#
		if [ "${YUP_ID}." = "." ] || [ "${YUP_PASS}." = "." ] ; then
			if [ "${YUP_ID_SLED}." = "." ] || [ "${YUP_PASS_SLED}." = "." ] ; then
				if [ "${YUP_ID_SLES}." = "." ] || [ "${YUP_PASS_SLES}." = "." ] ; then
					echo "UpdateMirror not configured properly - please run: "
					echo "YaST->System->Sysconfig Editor->System->YUP"
					exit 1;
				fi
			fi
		fi

		# import public proxy parameters; proxy-authentication params
		# are in /root/.curlrc and automatically evaluated by curl.
		# ( PROXY_ENABLED HTTPS_PROXY )
		#
		. /etc/sysconfig/proxy

		# evaluate proxy parameters
		#
		if [ "$PROXY_ENABLED" = "yes" ]; then
			if ! [ "$HTTPS_PROXY." = "." ]; then
				YUP_CURLPARAMS="$YUP_CURLPARAMS --proxy $HTTPS_PROXY"
			fi
		fi

		#
		# get via yup-channel
		#
		for useARCH in $YUP_ARCH; do
			for SLE in $YUP_PRODUCTS; do
				useID=$YUP_ID
				usePASS=$YUP_PASS
				useSERVER=$YUP_SERVER
				for SVN in $YUP_SUBVERSIONS; do
					if [ "$SVN" = "GA" ]; then
						SVNSUFFIX=""
						SUBCHANNELS="-Updates"
					elif [ "$SVN" = "SP1" ]; then
						SVNSUFFIX="-SP1"
						SUBCHANNELS=$YUP_SP_SUBCHANS
					elif [ "$SVN" = "SP2" ]; then
						SVNSUFFIX="-SP2"
						SUBCHANNELS=$YUP_SP_SUBCHANS
					fi
					for CHLSUFFIX in $SUBCHANNELS; do
						if [ "${SLE}." = "SLES10." ]; then
							UPD="SLES10$SVNSUFFIX$CHLSUFFIX/sles-10"
							if [ "${YUP_ID_SLES}." != "." ] && [ "${YUP_PASS_SLES}." != "." ] ; then
								useID=$YUP_ID_SLES
								usePASS=$YUP_PASS_SLES
							fi
							if [ "${YUP_SERVER_SLES}." != "." ] ; then
								useSERVER=$YUP_SERVER_SLES
							fi
						elif [ "${SLE}." = "SLE10-Debug." ]; then
							if [ "$CHLSUFFIX" = "-Online" ] ; then
								UPD="."
							else
								UPD="SLE10$SVNSUFFIX-Debuginfo$CHLSUFFIX/sles-10"
								if [ "${YUP_ID_SLES}." != "." ] && [ "${YUP_PASS_SLES}." != "." ] ; then
									useID=$YUP_ID_SLES
									usePASS=$YUP_PASS_SLES
								fi
								if [ "${YUP_SERVER_SLES}." != "." ] ; then
									useSERVER=$YUP_SERVER_SLES
								fi
							fi
						elif [ "${SLE}." = "SLED10." ]; then
							if [ "$useARCH" = "i586" ] || [ "$useARCH" = "x86_64" ] ; then
								UPD="SLED10$SVNSUFFIX$CHLSUFFIX/sled-10"
								if [ "${YUP_ID_SLED}." != "." ] && [ "${YUP_PASS_SLED}." != "." ] ; then
									useID=$YUP_ID_SLED
									usePASS=$YUP_PASS_SLED
								fi
								if [ "${YUP_SERVER_SLED}." != "." ] ; then
									useSERVER=$YUP_SERVER_SLED
								fi
							else 
								UPD="."
							fi
						elif [ "${SLE}." = "SDK10." ]; then
							UPD="SLE10-SDK$SVNSUFFIX$CHLSUFFIX/sles-10"
							if [ "${YUP_ID}." = "." ] || [ "${YUP_PASS}." = "." ] ; then
								if [ "${YUP_ID_SLES}." = "." ] || [ "${YUP_PASS_SLES}." = "." ] ; then
									useID=$YUP_ID_SLED
									usePASS=$YUP_PASS_SLED
								else
									useID=$YUP_ID_SLES
									usePASS=$YUP_PASS_SLES
								fi
							fi
							if [ "${YUP_SERVER}." = "." ] ; then
								if [ "${YUP_SERVER_SLES}." = "." ] ; then 
									useSERVER=$YUP_SERVER_SLED
								else
									useSERVER=$YUP_SERVER_SLES
								fi
							fi
						elif [ "${SLE}." = "OES2." ]; then
							UPD="OES2$SVNSUFFIX$CHLSUFFIX/sles-10"
							if [ "${YUP_ID_SLES}." != "." ] && [ "${YUP_PASS_SLES}." != "." ] ; then
								useID=$YUP_ID_SLES
								usePASS=$YUP_PASS_SLES
							fi
							if [ "${YUP_SERVER_SLES}." != "." ] ; then
								useSERVER=$YUP_SERVER_SLES
							fi
						else
							echo "Warning: SLE $SLE unknown. Proceeding anyways ..."
							# exit 1
						fi
						if [ "${UPD}." != "." ] ; then
							bn="https://${useSERVER}/repo/\$RCE/${UPD}-${useARCH}"
							YUP_CURLCRED=" --user ${useID}:${usePASS} "
							if [ "$YUP_DRYRUN" = "yes" ] ; then
								echo "URL: $bn"
							else
								echo "Using $bn"
								mkdir -p ${YUP_DEST_DIR}/${UPD}-${useARCH}
								cd ${YUP_DEST_DIR}/${UPD}-${useARCH}
								mkdir -p repodata 2>/dev/null
								fetch_repodata 	$bn
								fetch_binrpms	$bn
								fetch_repomd 	$bn
								fetch_patchrpms $bn
							fi
						fi
					done || exit $?
				done || exit $?
			done || exit $?
		done || exit $?

		#
		# Get Source RPMs from you.novell.com
		#
		if [ "${YUP_SRCRPM}." = "you." ] ; then
			YUP_CURLPARAMS=$( echo $YUP_CURLPARAMS | sed s/--digest//g );
			YUP_CURLPARAMS=$( echo $YUP_CURLPARAMS | sed s/--basic//g );
			YUP_CURLPARAMS="$YUP_CURLPARAMS --basic "
			YUP_CURLCRED=" --user $YOU_NCC_ACCOUNT:$YOU_NCC_PASSWORD "
			for useARCH in $YUP_ARCH; do
				useSERVER=$YOU_SERVER
				for SLE in $YUP_PRODUCTS; do
					if [ "${SLE}." = "SLES10." ]; then
						UPD="SUSE-SLES/10"
					elif [ "${SLE}." = "SLED10." ]; then
						UPD="SUSE-SLED/10"
					elif [ "${SLE}." = "SDK10." ]; then
						UPD="SLE-SDK/10"
					else
						echo "Warning: SLE $SLE unknown. Proceeding anyways ..."
						# exit 1
					fi
					YOU_useARCH=$( echo $useARCH | sed s/i586/i386/g );
					bn="https://${useSERVER}/update/$YOU_useARCH/update/${UPD}"
					echo "Using $bn"
					mkdir -p ${YUP_DEST_DIR}/${UPD}-${useARCH}
					cd ${YUP_DEST_DIR}/${UPD}-${useARCH}
					mkdir -p repodata 2>/dev/null
					fetch_repodata	$bn
					fetch_srcrpms	$bn
				done || exit $?
			done || exit $?
		fi
		#
		# Hardlink files - and safe storage space
		#
		if [ "${YUP_HARDLINK}." = "yes." ] ; then
			yup_check_and_hardlink
		fi

		#
		# Error Report
		#
		if test "${errorlog}." != "."; then
			echo "Error exit delayed from previous errors"
			echo "Problematic URIs were: $errorlog"
			exit 1
		fi
	fi
done

#
# Finis Africae
#

