#!/bin/bash
#
# File: /usr/local/bin/inkapkginstall
#
# Downloads and installs a tarball-package
#
#    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
# 5 = Precondition missing
# 6 = (De)Installation failed
##############################################################################
# SOURCE FILES

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

# STRINGS
pkgname=inka-basis					# package name
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					# config 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 -------------------------
auto=""						# automatic modus = ""/--automatic
aktprog=/usr/local/share/inka-basis/aktual-inka-basis	# get infos about inst./new inka-basis
llist=$tempfile.1.tmp					# local (package) list
rlist=$tempfile.2.tmp					# remote (package) list

# 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... PAKET
Laed/Installiert PAKET vom Server, bzw. deinstalliert PAKET

        -i      PAKET herunterladen und installieren
        -n      PAKET herunterladen und auspacken
        -u      Deinstallation (programmbezogene Konfigurations- und Benutzer-
                  datendateien bleiben bestehen)
        -U      Deinstallation (vollstaendig)
-h, --help      Hilfe anzeigen und beenden
 --version      Versionsinformationen anzeigen und beenden
--show-config   Programmkonfiguration anzeigen und beenden
--automatic     Automatikmodus aktivieren (nur fuer Aufruf durch Programme)
   --debug      Debug-Modus benutzen
        -q      nur mit Fehlermeldungen
        -v      mit vielen Meldungen

PAKET           Zu installierendes/deinstallierendes Paket
		  Name, Name-Version oder kompletten Dateinamen angeben
EOT

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

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

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

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

return
}
#-----------------------------------------------------------------------------
function download_install_package ()
{
# Search for package in remotelist, make dir, if found.
# Download, untar and install it
# Par1: -i (install), -n (only download and untar)
# Par2: URL
# Par3: Package name (can be with version or complete file name)
# Variables: rlist, PackageDir
# Uses: echo, mkdir, rm, tar, wget, Rebuild_Pkg_Filename, Pop_Stack, Install_Package
# Returncodes:
# 0 = installed
# 4 = package not found in rlist
# 5 = dependency of package missing
# 6 = package not uninstalled
# 7 = download and untar ok, not installed because opt = -n
# 11 = download, untar ok, but installation failed

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

local name url opt pkgfile fnne pkgn vers
opt=$1		# option of program call
url=$2		# url
name=$3		# search string (package name)
pkgfile=""	# new package file name complete
fnne=""		# new file name without .ext
pkgn=""		# package name
vers=""		# new package version
oldpkgfile=""	# old package file name complete
oldfnne=""	# old file name without .ext
oldpkgn=""	# old package name
oldvers=""	# old package version
ret=0

pkgfile=$(grep -i -m 1 "^$name" $rlist)			# complete filename
ret=$?
if [ "$ret" -eq 0 ]; then				# package found
	Rebuild_Pkg_Filename $pkgfile			# Decomposite file name
	Pop_Stack pkgn
	Pop_Stack vers
	Pop_Stack fnne
	mkdir -p $PackageDir/$fnne			# make directory
	rm -f $PackageDir/$fnne/$pkgfile		# remove package if exist (wget-problem)
	wget -q -P $PackageDir/$fnne $url/$pkgfile	# download package to package-dir
	tar -C $PackageDir/$fnne/ -xf $PackageDir/$fnne/$pkgfile 	# extract files
	ret=$?
	if [ "$ret" -eq 0 ]; then			# file is untared
		if [ "$opt" == "-i" ]; then		# opt is -i
			if [ "$pkgn" == "inka-basis" ]; then    # package is inka-basis
				echo 'InstVers=""' > $aktprog		# set installed package to "" in file
				echo "NewVers=$fnne" >> $aktprog	# write package to install in file
				if [ "$verbose" != "-q" ]; then
					echo "$fnne wurde heruntergeladen."
					echo "Zum Installieren nach Beendigung dieses Programms 'inkabasisupdater -x' aufrufen."
				else
					$infolog package $pkgn installed
				fi

			else
				# before installation old package uninstall, if exist
				oldpkgfile=$(inkapkginform -a | grep -w $pkgn)	# old complete filename
				if [ $? -eq 0 ]; then
					Rebuild_Pkg_Filename $oldpkgfile	# Decomposite old file name
					Pop_Stack oldpkgn
					Pop_Stack oldvers
					Pop_Stack oldfnne		# old file name without .ext is equal to old pkgdir
					cd $PackageDir/$oldfnne
					./install.sh -u --automatic	# small uninstall old package
				fi
				# now install new package
				cd $PackageDir/$fnne
				./install.sh -i $auto		# install package
				ret=$?				# ret = 0, 1, 3, 5
				if [ $ret -eq 0 ]; then		# installation ok
					Clean_Up_Pkgdir $pkgn $fnne
					if [ "$verbose" != "-q" ]; then
						echo "$pkgn wurde installiert."
					else
						$infolog package $pkgn installed
					fi
				else				# installation failed
					echo "Fehler - $pkgn konnte nicht installiert werden!"
					if [ $ret -eq 5 ]; then	# dependency missing
						ret=5
					else
						ret=11
					fi
				fi
			fi
		else					# opt is -n
			if [ "$verbose" != "-q" ]; then
				echo "$pkgn wurde heruntergeladen und entpackt."
			else
				$infolog package $pkgn downloaded and untared
			fi
			ret=7
		fi
	else						# not untared
		echo "Fehler - Paket $pkgfile konnte nicht entpackt werden!" >&2
		ret=11
	fi
else							# package not found
	ret=4
fi

return $ret
}
#-----------------------------------------------------------------------------
function install_proc ()
{
# Build list of packages from URLs, search for package and install it
# The listing will build up in rlist
# Par1: -i, -n (install, only download and untar)
# Par2: Package name (can be with version or complete file name)
# Variables: rlist, UrlList
# Uses: echo, rm, download_install_package, Build_Remote_List
# Return codes:
# 0 = ok
# 1 = par2 wrong
# 2 = config file wrong
# 4 = package not Found in rlist
# 5 = dependency of package missing
# 11 = package found, but download/install. failed
# 15 = wget could not download server information


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

local opt=$1 pn=$2 cntlines="" s="" l=0 ret=0

for s in $UrlList; do				# make for all urls
	Build_Remote_List $s			# build $rlist
	ret=$?					# ret = 0, 2, 15
	if [ $ret -eq 0 ]; then			# $rlist ok
		# test searchstring max. 1 time exist
		cntlines=$(grep -c $pn $rlist)		# nr of lines containig searchstring
		if [ $cntlines -gt 1 ]; then		# contains searchstring more than 1 time
			echo "Fehler - $pn kommt mehrmals vor - Angabe falsch!"
			rm -f $rlist
			return 1
		fi
		download_install_package $opt $s $pn	# search, download, untar, [install] pkg.
		ret=$?				# ret = 0, 4, 5, 7, 11
		if [ $ret -eq 0 ] || [ $ret -eq 7 ]; then	# package installed or downloaded (n-)
			rm -f $rlist
			return 0
		elif [ $ret -eq 4 ]; then	# package not found
			ret=4
		elif [ $ret -eq 5 ]; then	# package found, but dependency missing
			rm -f $rlist
			return 5
		else				# package found,but download/install. failed
			rm -f $rlist
			return 11
		fi
	elif [ $ret -eq 2 ]; then		# unknown protocoll - config wrong
		echo "Fehler - $s hat unbekanntes Server-Protokoll, Konfiguration falsch!" >&2
		rm -f $rlist
		return 2
	elif [ $ret -eq 15 ]; then		# rlist not ok
			echo "Fehler - Von $s keine Paketliste vorhanden!"
		ret=15
	fi
done
if [ $ret -eq 4 ]; then				# package not found on servers
	echo "Fehler - Paket $pn wurde auf keinem Server gefunden."
fi

rm -f $rlist
return $ret
}
#-----------------------------------------------------------------------------
function uninstall_proc ()
{
# Uninstall a package
# Par1: -u, -U
# Par2: Package name (can be with version or complete file name)
# Variables: auto, llist, PackageDir
# Uses: cd, echo, grep, Build_Local_List, Rebuild_Pkg_Filename
# Return codes:
# 0 = ok
# 1 = par2 wrong
# 3 = old library

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


local opt p2 cmd fname pkgns pkgnn verss versn fnne cntlines ret
opt=$1		# -u, -U
pn=$2		# package name (searchstring)
cmd=""		# input from user (o/n)
fname=""	# filename (complete)
pkgnu=""	# package name to uninstall
pkgnn=""	# package name that need pkgnu
versu=""	# version of package to uninstall
versn=""	# version of package that need pkgnu
fnneu=""	# filename without .ext of package to uninstall
cntlines=0
ret=0

Build_Local_List $llist			# make local list
# test searchstring only 1 time exist
cntlines=$(grep -c $pn $llist)		# nr of lines containing searchstring
if [ $cntlines -eq 0 ]; then		# searchstring not found - wrong
	echo "Fehler - $pn nicht gefunden - Angabe falsch!"
	rm -f $llist
	return 1
elif [ $cntlines -gt 1 ]; then		# contains searchstring more than 1 time
	echo "Fehler - $pn kommt mehrmals vor - Angabe falsch!"
	rm -f $llist
	return 1
else					# searchstring ok
	fname=$(grep $pn $llist)	# package file name
fi

Rebuild_Pkg_Filename $fname		# decomposite filename
Pop_Stack pkgnu
Pop_Stack versu
Pop_Stack fnneu
for f in $PROGREG/*.inf; do	# search for pkgns is required from other packages
	grep "^PKGDEPS" $f | grep -q $pkgnu
	if [ $? -eq 0 ]; then					# requirement exist
		pkgnn=$(grep "^PKGNAME" $f | cut -f 2)		# package name of depending package
		versn=$(grep "^VERSION" $f | cut -f 2)		# version of this package
		if [ -z "$auto" ]; then				# not --automatic
			echo "$pkgnu wird von $pkgnn benoetigt."
			echo "Soll $pkgnn mit $opt deinstalliert werden?"	# ask for uninstall depending package
			echo "(o)k / (n)ein"
			read cmd
			if [ "$cmd" == "o" ]; then			# ok - uninstall
				inkapkginstall $opt $auto $pkgnn-$versn	# uninstall depending package
				if [ $? -ne 0 ]; then			# uninstallation of depending package failed - stop work
					echo "$pkgnu-$versu konnte nicht deinstalliert werden."
					rm -f $llist
					return 6
				fi
			else						# no - not uninstall
				echo "$pkgnu-$versu wurde nicht deinstalliert."
				rm -f $llist
				return 6
			fi
		else						# --automatic - stop work
			echo "Fehler - $pkgnu wird von $pkgnn benoetigt!"
			echo "$pkgnu-$versu wurde nicht deinstalliert."
			rm -f $llist
			return 6
		fi
	fi
done

cd $PackageDir/$fnneu			# change to package dir
./install.sh $opt $auto			# uninstall package
ret=$?					# ret = 0, 1, 3, 5
if [ $ret -eq 0 ]; then
	Clean_Up_Pkgdir $pkgnu		# remove all package dirs they have same name
	if [ "$verbose" != "-q" ]; then
		echo "Paket $pkgnu wurde deinstalliert."
	else
		$infolog package $pn uninstalled
	fi
else
	ret=6
fi

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 --------------------------
	  -i|-n|-u|-U)	opt="$1";;			# option -i, -u, -U
	  --automatic)	auto="$1";;			# option --automatic
	  *)	if [ "$verbose" != "-q" ]; then
			echo "Fehler - Option $1 falsch!" >&2	# error
		else
			$errlog unknown option $1
		fi
		exit 1;;
	esac
	shift
	opt_found=yes
done

# handle standard options -h, -V, -C
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
  -i|-n|-u|-U)	if [ "$#" -eq 1 ]; then		# parameter count ok
			arg1="$1"
			shift
		else				# error in param count
			echo "Fehler - Parameteranzahl falsch!" >&2
			exit 1
		fi;;
  *)	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
  -i|-n)						# opt -i, -n
	install_proc $opt $arg1
	err=$?;;
  -u|-U)						# opt -u, -U
	uninstall_proc $opt $arg1
	err=$?;;
  "")							# no opt given
	echo "Fehler - Option fehlt!" >&2
	err=1;;
esac
exit $err
