#!/bin/bash
#
# File: /usr/local/bin/inkapkgupdate
#
# updates tarball-packages (by automatic executing by cron)
#
#    Copyright (C) 2013  Ingo Kaesmann
#
#    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#
# Script template: 0.3.0
#
# Exit codes:
# 0 = OK
# 1 = Error in parameters
# 2 = Error in configfile, configfile old or not found
# 3 = Library old or not found
# 4 = Package not available
# 5 = Dependency of package missing
# 14= Package not downloaded/untared
# 15= Not from all servers remote list downloadable
# 19= Installation of package failed
##############################################################################
# SOURCE FILES

. /usr/local/etc/main-inka-sh.conf			# main config
. /usr/local/etc/inka-basis.conf			# package config
. $LLIBDIR/lib-inka-std.sh				# functions standard
. $LLIBDIR/lib-inka-basis.sh				# functions basis
##############################################################################
# DECLARATION OF VARIABLES

# STRINGS
pkgname=inka-basis					# package name to work
lib_inka_std_sh_needed=1.3.6				# standard lib version
lib_inka_basis_sh_needed=1.3.3				# basis lib version
conf_file_needed=1.3.0					# conf file version
#----------------------- end of variables to configure -----------------------
version=$(grep "^VERSION" $PROGREG/$pkgname.inf | cut -f 2)	# version
scriptname=$(basename $0)				# name of script
tempfile=$TEMPDIR/$scriptname.$UID.$$.tmp		# temporary file
infolog="logger -p user.info -t $scriptname --"		# log info messages
errlog="logger -p user.err -t $scriptname -- ERROR:"	# log error messages
debuglog="logger -p user.debug -t $scriptname -- DEBUG:" # log debug messages
debug=no						# debug=yes/no
verbose=""						# verbose=""/-q/-v
opt_found=no						# opt_found=yes/no
opt=""							# option
#------------------------- end of standard variables -------------------------
llist=$tempfile.1.tmp					# local (package) list
rlist=$tempfile.2.tmp					# remote (package) list
infodir=/usr/local/share/inka-basis			# dir of info files
aktprog=${infodir}/aktual-inka-basis			# file with OldVers, NewVers
auto="--automatic"					# automatic modus =""/--automatic

# NUMBERS
err=0							# exit code
##############################################################################
# FUNCTIONS

function show_help ()
{
# Show help
# Uses: cat
# Return codes: standard

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG show_help: $# $@" >&2
	#$debuglog show_help: $# $@
fi

cat <<EOT

Aufruf: $scriptname OPTION...
Ueberprueft, ob es von installierten Tarball-Paketen neue Versionen gibt,
laedt diese ggf. herunter und installiert sie.

        -s      nur Anzeige der Pakete, fuer die es neue Versionen gibt
        -w      Anzeige der zu ueberpruefenden Pakete
        -x      Aktualisierungslauf starten
        -n      ohne Automatikmodus
-h, --help      Hilfe anzeigen und beenden
 --version      Versionsinformationen anzeigen und beenden
--show-config      Programmkonfiguration anzeigen und beenden
--shownewfiles  nur Anzeige der Pakete, fuer die es neue Versionen gibt
                  (nur fuer Aufruf durch Programme)
   --debug      Debug-Modus benutzen
        -q      nur mit Fehlermeldungen
        -v      mit vielen Meldungen
EOT

return
}
#-----------------------------------------------------------------------------
function show_config ()
{
# Show configuration of program
# Uses: cat, cut, grep
# Return codes: standard

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG show_config: $# $@" >&2
	#$debuglog show_config: $# $@
fi

cat <<EOT

Installations-Server (URLs):
EOT
for s in $UrlList; do
	echo "$s"					# show all urls
done
cat <<EOT

Lokales Paketverzeichnis von .tgz, .tar.gz, .tar, .tar.bz2:
$PackageDir

Nicht zu ueberpruefende / nicht herunterzuladende Pakete:
EOT
for s in $ExList; do
	echo "$s"					# show excluded packages
done
cat <<EOT

Wenn keine Option gewaehlt wird, wird benutzt: $DefCmd

Programm wird zu folgenden Zeiten aufgerufen:
EOT
grep "&&" /etc/cron.d/inkapkgupdate.cron | cut -f 1

return
}
#-----------------------------------------------------------------------------
function copy_updater ()
{
# Copy inkabasisupdater to /usr/local/bin, if it is new
# Uses: cp, cut, echo
# Return codes: standard

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG copy_updater: $# $@" >&2
	#$debuglog copy_updater: $# $@
fi

local updatervers=""

if [ -e /usr/local/bin/inkabasisupdater ]; then		# updater exist
	updatervers=$(inkabasisupdater --version | cut -d " " -f 3)
	if [ "$updatervers" != "$version" ]; then	# version of updater and program is differ, copy it
		if [ "$verbose" == "-v" ]; then
			echo "Installiere /usr/local/bin/inkabasisupdater."
		fi
		cp /usr/local/share/inka-basis/inkabasisupdater /usr/local/bin/
		cp /usr/local/share/inka-basis/inkabasisupdater.cron /etc/cron.d/
	fi
else							# updater not exist, copy it
	if [ "$verbose" == "-v" ]; then
		echo "Installiere /usr/local/bin/inkabasisupdater."
	fi
	cp /usr/local/share/inka-basis/inkabasisupdater /usr/local/bin/
fi

if ! [ -e /etc/cron.d/inkabasisupdater.cron ]; then	# updater cron not exist
	if [ "$verbose" == "-v" ]; then
		echo "Installiere /usr/local/bin/inkabasisupdater.cron."
	fi
	cp /usr/local/share/inka-basis/inkabasisupdater.cron /etc/cron.d/
fi

return
}
#-----------------------------------------------------------------------------
function compare_packages ()
{
# Get file list from a server, compare it with packages in package dir, if
# package on server is new, make dir, download package in dir and untar it
# Par1: main option of program call
# Par2: URL
# Variables: llist, rlist tempfile
# Uses: cat, cp, echo, mkdir, rm, tar, wc, wget,
# Uses: Pull_Stack, Read_Lines, Erase_Lines, Build_Remote_List, Rebuild_Pkg_Filename Install_Package
# Return codes:
# 0 = ok
# 2 = protocoll not found on server - config wrong
# 5 = dependency of package missing - not installed
# 14 = package not downloaded or untared
# 15 = no remotelist from this url downloadable
# 19 = installation failed

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG compare_packages: $# $@" >&2
	#$debuglog compare_packages: $# $@
fi

local p1 p2 tfile lfile rfile lpkgne rpkgne lpkn rpkn lvers rvers i l m n err ret

p1=$1		# main option of program call
p2=$2		# url
tfile=$tempfile.3.tmp
lfile=''	# local package file
rfile=''	# remote package file
lpkgne=''	# local package without extension
rpkgne=''	# remote package without extension
lpkn=''		# local package name
rpkn=''		# remote paket name
lvers=''	# local version
rvers=''	# remote version

i=0		# local list counter (and line number)
l=0		# no. of lines in local list
m=0		# remote list counter (and line number)
n=0		# no. of lines in remote list
err=0		# errorlevel of function
ret=0		# returncode of command

Build_Remote_List $p2					# build package list from url
ret=$?							# ret: 0, 2, 15
if [ $ret -eq 2 ]; then					# config wrong
	echo "Fehler - Protokoll nicht gefunden - Konfigurationsdatei falsch!"
	return 2
elif [ $ret -eq 15 ]; then				# no rlist downloadable
	echo "Fehler - keine Paketliste vom Server herunterladbar!"
	return 15
fi

l=$(cat $llist | wc -l)					# no. of lines of locallist
n=$(cat $rlist | wc -l)					# no. of lines of remotelist
# work local list
i=0
while [ $i -lt $l ]; do					# make local variables
	let i++
	lfile=$(Read_Lines $llist $i $i)			# local package file
	Rebuild_Pkg_Filename $lfile			# decomposite locale pkg file names
	Pop_Stack lpkn
	Pop_Stack lvers
	Pop_Stack lpkgne
	# work remote list
	m=0
	while [ $m -lt $n ]; do				# make remote variables
		let m++
		rfile=$(Read_Lines $rlist $m $m)		# remote package file
		Rebuild_Pkg_Filename $rfile		# decomposite remote pkg file names
		Pop_Stack rpkn
		Pop_Stack rvers
		Pop_Stack rpkgne
		if [ "$lpkn" == "$rpkn" ]; then         # lpackage name = rpackage name?
			# compare versions, make dir, download rpackage, untar it, install it if possible
			Compare_Version $lvers $rvers
			ret=$?
			if [ $ret -eq 2 ]; then		# rversion ist higher than lversion
				if [ "$p1" == "-x" ]; then	# option -x is given, actualise package
					mkdir -p $PackageDir/$rpkgne			# make new package-dir
					rm -f $PackageDir/$rpkgne/$rfile			# remove rpackage, if exist
					wget -q -P $PackageDir/$rpkgne $p2/$rfile	# download rpackage to package-dir
					tar -C $PackageDir/$rpkgne/ -xf $PackageDir/$rpkgne/$rfile	# extract files
					ret=$?
					if [ "$ret" -eq 0 ]; then			# file is untared
						if [ "$lpkn" == "inka-basis" ]; then	# package is inka-basis
							echo "InstVers=$lpkgne" > $aktprog	# write installed package in file
							echo "NewVers=$rpkgne" >> $aktprog	# write new package in file
						else					# package is not inka-basis
							# before install, old package uninstall, if exist
							if [ -d $PackageDir/$lpkgne ]; then	# old package dir exist?
								cd $PackageDir/$lpkgne
								./install.sh -u --automatic	# small uninstall old package
							fi
							# now install new package
							cd $PackageDir/$rpkgne
							./install.sh -i $auto                   # install new package
							ret=$?					# ret: 0, 1, 3, 5
							if [ $ret -eq 0 ]; then		# installation ok
								if [ "verbose" != "-q" ]; then
									echo "Paket $rpkgne wurde installiert."
								fi
								Clean_Up_Pkgdir $rpkn $rpkgne
							elif [ $ret -eq 5 ]; then	# dependency missing
								echo "Fehler - Voraussetzungen nicht erfuellt - Paket $rpkn nicht installiert!"
								err=5
							else				# installation other error
								echo "Fehler - Installation von Paket $rpkn fehlerhaft!"
								err=19
							fi
						fi
					else		# tar made error
						echo "Fehler - Paket konnte nicht heruntergeladen/entpackt werden!"
						err=14
					fi
				elif [ "$p1" == "-s" ]; then	# option -s is given, only show to actualise packages
					echo "Paket $lpkn nicht mehr aktuell."
					echo "Alte Version: $lvers	Neue Version: $rvers"
					echo ""
				else			# --shownewfiles is given
					echo $rfile
				fi
			fi
			# remove found packages from locale list
			Erase_Lines $llist $i $i > $tfile
			cp $tfile $llist
			let i--
			let l--
			# remove found packages from remote list and stop searching in remote list
			Erase_Lines $rlist $m $m > $tfile
			cp $tfile $rlist
			let n--			# count down no. of lines in remotelist
			m=$n			# stop remote list loop
		fi
	done
done

rm -f $tfile
return $err
}
#-----------------------------------------------------------------------------
function main_proc ()
{
# Copy programupdate-updater to bin dir,
# compare packages from URLs with local packages
# The listing will build up in rlist
# Par1: main option of program call
# Variables: llist, rlist, UrlList
# Uses: cat, echo, rm, wc, Build_Local_List
# Return codes:
# 0 = ok
# 2 = config wrong
# 4 = package not available
# 5 = dependency of package missing
# 14 = package not downloaded/untared
# 15 = no remot list from all servers downloadable
# 19 = installation of package failed

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG main_proc: $# $@" >&2
	#$debuglog main_proc: $# $@
fi

local p1=$1 s='' l=0 ret=0 err=0

copy_updater						# copy updater, if not exist
Build_Local_List --exclude				# build llist
ret=$?							# ret: 0, 2
if [ $ret -eq 8 ]; then
	echo "Fehler - Keine installierten Pakete gefunden - Konfigurationsdatei falsch!"
	return 2
fi

for s in $UrlList; do					# make for all urls
	compare_packages $p1 $s				# ret: 0, 2, 4, 15, 19
	ret=$?
	case "$ret" in
		2) err=2;;
		5) err=5;;
		14) err=14;;
		15) err=15;;
		19) err=19;;
	esac
done

l=$(cat $llist | wc -l)
if [ $l -ne 0 ]; then					# is local list empty?
	if [ "$verbose" != "-q" ]; then
		echo "Folgende Pakete wurden auf keinem Server gefunden:"
		cat $llist				# if not, show not found packages
	else
		$infolog packages not found on servers
	fi
	err=4
fi

rm -f $llist $rlist
return $err
}
#-----------------------------------------------------------------------------
function show_watchlist ()
{
# Show packages to watch for update
# Var: llist
# Uses
# Error codes:
# 0 = ok
# 2 = no packages found - config wrong

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG main_proc: $# $@" >&2
	#$debuglog main_proc: $# $@
fi

local ret=0

Build_Local_List --exclude				# build local list
ret=$?							# ret: 0, 2
if [ $ret -eq 2 ]; then					# no installed packages
	echo "Fehler - Keine installierten Pakete gefunden - Konfiguration falsch!"
	rm -f $llist
	return 2
fi
cat $llist						# show package to watch

rm -f llist
return $ret
}
##############################################################################
# PARSE COMMANDLINE

# Read all options with args and all additional args into variables.
# Stop parsing after last parameter.
# Try to do a check on all parameters and version numbers.
# Handle options -h, --show-config and -V directly.
# Exit on error.

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG Parse Commandline: $# $@" >&2
	#$debuglog Parse Commandline: $# $@
fi

# get options and options with arguments
while [ "${1:0:1}" == "-" ]; do				# get all options
	case "$1" in
		-h|--help)	opt=-h;;			# option -h
		--version)	opt=--v;; 			# option --version
		--show-config)	opt=--s;;			# option --show-config
		--debug)	debug=yes;;			# option --debug
		-q|--quiet)	verbose=-q;;			# option -q
		-v|--verbose)	verbose=-v;;			# option -v
#-------------------------- end of standard options --------------------------
	  -s|-w|-x|--shownewfiles)	opt="$1";;	# option -a|-s|-w|-x|--shownewfiles
	  -n)	auto="";;			# option -n
	  *)
		echo "Fehler - Option $1 falsch!" >&2	# error
		exit 1;;
	esac
	shift
	opt_found=yes
done

# handle standard options -h, --v, --s
case "$opt" in
  -h)	show_help; exit 0;;					# show help
  --v)	echo "$scriptname (${pkgname}) $version"; exit 0;;	# show version
  --s)	show_config; exit 0;;					# show config
esac

# check standard library
Compare_Version $lib_inka_std_sh_needed $Lib_Inka_Std_Sh_Version
ret=$?						# ret: 0,1,2,9,127
if [ $ret -eq 1 ]; then		# needed lib-version higher than installed
	if [ "$verbose" != "-q" ]; then
		echo "Fehler - Programm benoetigt neuere Version von lib-inka-std.sh!" >&2
	else
		$errlog newer version of lib-inka-std.sh needed
	fi
	exit 3
elif [ $ret -eq 9 ] || [ $ret -eq 127 ]; then	# other error (version or lib missing)
	if [ "$verbose" != "-q" ]; then
		echo "Fehler - Versionspruefung von lib-inka-std.sh nicht moeglich!"
	else
		$errlog check of version of lib-inka-std.sh impossible
	fi
	exit 3
else						# check ok, reset $ret
	ret=0
fi

# check basis library
Compare_Version $lib_inka_basis_sh_needed $Lib_Inka_Basis_Sh_Version
ret=$?						# ret: 0,1,2,9
if [ $ret -eq 1 ]; then		# needed lib-version higher than installed
	if [ "$verbose" != "-q" ]; then
		echo "Fehler - Programm benoetigt neuere Version von lib-inka-basis.sh!" >&2
	else
		$errlog newer version of lib-inka-basis.sh needed
	fi
	exit 3
elif [ $ret -eq 9 ]; then		# other error (version or lib missing)
	if [ "$verbose" != "-q" ]; then
		echo "Fehler - Versionspruefung von lib-inka-basis.sh nicht moeglich!"
	else
		$errlog check of version of lib-inka-basis.sh impossible
	fi
	exit 3
else					# check ok, reset $ret
	ret=0
fi

# check version of config files				# set config var!
Compare_Version $conf_file_needed $Inka_Basis_Conf
#Compare_Version $conf_file_needed $Program_Rc
ret=$?						# ret: 0,1,2,9
if [ $ret -eq 1 ]; then		# needed config-version higher than installed
	if [ "$verbose" != "-q" ]; then
		echo "Fehler - $scriptname benoetigt neuere Version der Konfig-datei!" >&2
	else
		$errlog newer version of conf file needed
	fi
	exit 2
elif [ $ret -eq 9 ]; then		# other error (version or conffile missing)
	if [ "$verbose" != "-q" ]; then
		echo "Fehler - Versionspruefung der Konfig-datei von $scriptname nicht moeglich!"
	else
		$errlog check of version of conf file impossible
	fi
	exit 2
else					# check ok, reset $ret
	ret=0
fi

# check arguments after options
case "$opt" in
  ""|*)	if [ "$#" -ne 0 ]; then				# error in param count
		echo "Fehler - Parameteranzahl falsch!" >&2
		exit 1
	fi;;
esac
##############################################################################
# MAINPROGRAM

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG Main: $# $@" >&2
	#$debuglog Main: $# $@
fi

# Check what to do (initial options and args are removed from cmdline)
case "$opt" in
	-w)						# opt -w
		show_watchlist
		err=$?;;
	-s|-x|--shownewfiles)				# opt -s|-x|--shownewfiles
		main_proc $opt
		err=$?;;
	"")
		if [ "$DefCmd" == "-s" ] || [ "$DefCmd" == "-x" ]; then	# no opt given
	  		main_proc $DefCmd
	  		err=$?
	  	else
			echo "Fehler - Konfigurationsdatei fehlerhaft!" >&2
			err=2
		fi;;
esac
exit $err
