#!/bin/bash

###########################################################################
#   Copyright (C) 2008 by Eugene V. Lyubimkin aka JackYF                  #
#                                                                         #
#   This program is free software; you can redistribute it and/or modify  #
#   it under the terms of the GNU General Public License                  #
#   (version 3 or above) as published by the Free Software Foundation.    #
#                                                                         #
#   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 GPLv3                      #
#   along with this program; if not, write to the                         #
#   Free Software Foundation, Inc.,                                       #
#   51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA               #
###########################################################################

# Devoted to Evgeniya V. Katyuk.

# translation support
. gettext.sh
TEXTDOMAIN=daptup
export TEXTDOMAIN
TEXTDOMAINDIR=/usr/share/locale
export TEXTDOMAINDIR

# own eval_gettexte function, as official one doesn't allow to pass -e
eval_gettexte () {
  gettext -e "$1" | (export PATH `envsubst --variables "$1"`; envsubst "$1")
}

# exit if any command returned non-zero status
set -e

SPOOL_DIR=/var/spool/daptup

# spool/cache files
DAPTUP_UPDATES_BEFORE=$SPOOL_DIR/updates-before
DAPTUP_UPDATES_AFTER=$SPOOL_DIR/updates-after
DAPTUP_NEW_BEFORE=$SPOOL_DIR/new-before
DAPTUP_NEW_AFTER=$SPOOL_DIR/new-after
DAPTUP_WATCH_BEFORE=$SPOOL_DIR/watch-before
DAPTUP_WATCH_AFTER=$SPOOL_DIR/watch-after
DAPTUP_OUTDATED=$SPOOL_DIR/outdated
# safe temporary files
DAPTUP_ERRORS_FILE=$(tempfile)
DAPTUP_TMP_FILE=$(tempfile)

function delete_temp_files
{
	rm -f $DAPTUP_TMP_FILE $DAPTUP_ERRORS_FILE
}

trap "do_exit 1024" SIGTERM
trap "do_exit 2048" SIGINT

# $1 - exit code
function do_exit
{
	delete_temp_files
	exit $1
}

DAPTUP_CONFFILE=/etc/daptup.conf
if [ -f $DAPTUP_CONFFILE ]; then
	. $DAPTUP_CONFFILE
else
	eval_gettexte "Cannot read configuration from '\$DAPTUP_CONFFILE'."; echo
	do_exit 1
fi


function check_subcommand
{
	if [ -n "$DAPTUP_SUBCOMMAND" ]; then
		gettext -e "You cannot mix '--pre', '--post' and '--last' options or repeat them."; echo
		do_exit 4
	fi
}

while [ "$#" -gt "0" ]; do
	case $1 in
		--pre)
			check_subcommand
			DAPTUP_SUBCOMMAND=pre
			;;
		--post)
			check_subcommand
			DAPTUP_SUBCOMMAND=post
			;;
		--last)
			check_subcommand
			DAPTUP_SUBCOMMAND=last
			;;
		--nocolor)
			DAPTUP_USE_COLOR=n	
			;;
		-h|--help)
			BASENAME=`basename ${0}`
			eval_gettexte "Usage: \$BASENAME [ --pre | --post | --last ] [ -h | --help | --nocolor ]."; echo
			echo
			gettext -e "daptup runs 'apt-get update' command inside and outputs:"; echo
			gettext -e " - list of packages recently entered to repo;"; echo
			gettext -e " - list of packages which got new updates;"; echo
			gettext -e " - list of changes in 'watched' packages;"; echo
			gettext -e " - list of outdated packages (optionally)."; echo
			echo
			gettext -e "Options:"; echo
			gettext -e " -h, --help: output this help and exit"; echo
			gettext -e " --nocolor: do not use colored output"; echo
			gettext -e " --pre: do only 'pre' stage: collect info that will be used as 'old'"; echo
			gettext -e " --post: do only 'post' stage: collect 'new' info and output changes"; echo
			gettext -e "         if appropriate option is not disabled in config file"; echo
			gettext -e " --last: only output last changes"; echo
			do_exit 0
			;;
		*)
			if [ "$PARAM" != "" ]; then
				eval_gettexte "Unknown param: \$PARAM"; echo
				do_exit 4
			fi
			;;
	esac
	shift
done

# Checking for root privileges
MY_UID=`id -u`
if [ "$MY_UID" != "0" ]; then
	gettext -e "You must run daptup with root privileges."; echo
	do_exit 2
fi

if [ -z "$DAPTUP_SUBCOMMAND" ]; then
	# As we now have apt hook, just do 'apt-get update'.
	# It will call daptup itself.

	set +e
	apt-get update
	RETURN_CODE=$?
	set -e
	do_exit $RETURN_CODE
fi

if [ "$DAPTUP_DISABLE_COLUMNS" == "y" ]; then
	DAPTUP_EXTRA_APTITUDE_ARGUMENTS="$DAPTUP_EXTRA_APTITUDE_ARGUMENTS --disable-columns"
fi


# $1 - output
# $2 - errors
function get_avail_pkg_list()
{
	if [ -f /usr/bin/aptitude ]; then
		aptitude search "~n(.*)" \
			--display-format "$DAPTUP_NEW_DISPLAY_FORMAT" \
			--width $DAPTUP_NEW_DISPLAY_WIDTH \
			$DAPTUP_EXTRA_APTITUDE_ARGUMENTS > $1 2> $2
	else
		apt-cache search ".*" | sort > $1 2> $2
	fi
}

# $1 - output
function get_updates()
{
	apt-show-versions -u | sort > $1
}

# $1 - output
function get_watched()
{
	local GREP_EXPRESSION
	for package in $DAPTUP_PACKAGES_WATCH_FOR; do
		if [ -n "$GREP_EXPRESSION" ]; then
			GREP_EXPRESSION="${GREP_EXPRESSION}|"
		fi
		GREP_EXPRESSION="${GREP_EXPRESSION}^${package}$"
	done
	if [ -z "$GREP_EXPRESSION" ]; then # no packages to watch
		touch $1
	elif [ -f /usr/bin/aptitude ]; then
		aptitude search "~n($GREP_EXPRESSION)" \
			--display-format "$DAPTUP_WATCH_DISPLAY_FORMAT" \
			--width $DAPTUP_WATCH_DISPLAY_WIDTH \
			$DAPTUP_EXTRA_APTITUDE_ARGUMENTS > $1
	else
		local WATCHED_PACKAGES=`apt-cache search --names-only "$GREP_EXPRESSION" | sed 's/ .*//'`
		local VERSION

		rm -f $1
		touch $1
	
		for package in $WATCHED_PACKAGES; do
			VERSION=`apt-cache policy $package | fgrep "Candidate" | sed 's/.*Candidate: //'`
			echo "$package $VERSION" >> $1
		done

	fi
}

# $1 - result file
function find_old_packages
{
	local DOC_DIR=/usr/share/doc
	local SECONDS_IN_DAY=`expr 60 \* 60 \* 24`

	rm -f $1
	touch $1
	
	cat $DAPTUP_UPDATES_AFTER | while read line; do
		package=`echo $line | grep -Eo "^.*/" | sed 's|/||'`
		local CHANGELOG_FILE
		if [ -f $DOC_DIR/$package/changelog.Debian.gz ]; then
			CHANGELOG_FILE=$DOC_DIR/$package/changelog.Debian.gz
		elif [ -f $DOC_DIR/$package/changelog.gz ]; then
			CHANGELOG_FILE=$DOC_DIR/$package/changelog.gz
		else
			eval_gettexte "warning: cannot find any changelog for package '\$package'"; echo
		fi

		if [ -n "$CHANGELOG_FILE" ]; then # if we've found a changelog
			local LAST_MODIFIED_LINE
			LAST_MODIFIED_LINE=`zgrep -E "^ -- " $CHANGELOG_FILE | head -1`;
			if [ -n "$LAST_MODIFIED_LINE" ]; then # changelog is correct

				# extracting date
				# example: " -- James Troup <james@nocrew.org>  Mon, 24 Apr 2006 04:24:07 +0100"

				DATE=`echo $LAST_MODIFIED_LINE | grep -Eo "[0-9 ][0-9] [A-Z][a-z][a-z] [0-9]{4}"`
				if [ -n "$DATE" ]; then # date is extracted correctly
					CURRENT_TIMESTAMP=`date +%s`
					PACKAGE_TIMESTAMP=`date -d "$DATE" +%s`
					DAYS=`expr \( $CURRENT_TIMESTAMP - $PACKAGE_TIMESTAMP \) / $SECONDS_IN_DAY`
					if [ $DAYS -gt $DAPTUP_MINIMAL_DAY_COUNT_TREATING_OUTDATED ]; then # package is old
						echo -n $line >> $1
						echo -n ", $DAYS " >> $1
						ngettext -e "day" "days" $DAYS >> $1
						echo >> $1
					fi
				else # date is extracted badly
					eval_gettexte "error: cannot extract last modification date for package '\$package'"; echo
				fi
			else # changelog is not correct
				eval_gettexte "error: cannot fetch last entry from changelog for package '\$package'"; echo
			fi
		fi
	done
}

function print_done
{
	gettext -e "[done]"; echo
}

# $1 - before
# $2 - after
# $3 - where result will be stored
function diff_cmd()
{
	diff --minimal $1 $2 | grep -E "^[<>]" | sort > $3
}

function reset_color()
{
	# resetting to default color if needed...
	if [ "$DAPTUP_USE_COLOR" == "y" ]; then
		tput sgr0
	fi
}

function do_pre
{
	gettext -e "Building old list of packages... "
	get_avail_pkg_list $DAPTUP_NEW_BEFORE $DAPTUP_ERRORS_FILE

	RETURN_RESULT=`wc -l < $DAPTUP_ERRORS_FILE`
	if [ "$RETURN_RESULT" != "0" ]; then
		gettext -e "errors present. Is apt/dpkg running?"; echo
		do_exit 8
	fi
	print_done

	gettext -e "Building old list of available updates... "
	get_updates $DAPTUP_UPDATES_BEFORE
	print_done

	gettext -e "Building old list of watched packages... "
	get_watched $DAPTUP_WATCH_BEFORE
	print_done
}

function do_post
{
	if [ "$DAPTUP_SUBCOMMAND" != "last" ]; then

		gettext -e "Building new list of packages... "
		get_avail_pkg_list $DAPTUP_NEW_AFTER /dev/null
		print_done

		gettext -e "Building new list of available updates... "
		get_updates $DAPTUP_UPDATES_AFTER
		print_done

		gettext -e "Building new list of watched packages... "
		get_watched $DAPTUP_WATCH_AFTER
		print_done
	
	fi

	if [ "$DAPTUP_SUBCOMMAND" == "last" ] || [ "$DAPTUP_SHOW_CHANGES_IN_POST" == "y" ]; then

		# show changes
			
		OUTPUT_OUTDATED=n
		if [ "$DAPTUP_CHECK_FOR_OUTDATED_PACKAGES" == "y" ]; then
			# if DAPTUP_MINIMAL_DAY_COUNT_TREATING_OUTDATED variable contains non-numeric
			# data, print an error and don't try to check for outdated packages
			set +e
			expr "$DAPTUP_MINIMAL_DAY_COUNT_TREATING_OUTDATED" + 0 >/dev/null 2>/dev/null
			exit_code=$?
			set -e
			if [ "$exit_code" == "0" ]; then
				gettext -e "Building list of outdated packages... "
				find_old_packages $DAPTUP_OUTDATED
				OUTPUT_OUTDATED=y
				print_done
			else
				gettext -e "error: DAPTUP_MINIMAL_DAY_COUNT_TREATING_OUTDATED contains non-numeric data"; echo
			fi
		else
			gettext -e "Skipping check for outdated packages."; echo
		fi

		if [ "$DAPTUP_USE_COLOR" == "y" ]; then
			tput init
		fi
		# by default daptup tries to use colors now, it can be disabled by specifying '--nocolor' option

		echo

		diff_cmd $DAPTUP_UPDATES_BEFORE $DAPTUP_UPDATES_AFTER $DAPTUP_TMP_FILE
		if [ "`wc -l < $DAPTUP_TMP_FILE`" == "0" ]; then # no updates
			gettext -e "No new updates."; echo
		else
			gettext -e "New updates:"; echo
			cat $DAPTUP_TMP_FILE | sed 's/upgradeable from/-/g' | sed 's/ to / -> /g' | while read line; do
				if [ "$DAPTUP_USE_COLOR" == "y" ]; then
					if [ "`expr "$line" : '<'`" == "0" ]; then
						tput setf 2; # green
					else
						tput setf 4; # red
					fi;
				fi;
				echo $line;
				done
		fi

		reset_color

		diff_cmd $DAPTUP_NEW_BEFORE $DAPTUP_NEW_AFTER $DAPTUP_TMP_FILE
		if [ "`wc -l < $DAPTUP_TMP_FILE`" == "0" ]; then # no new
			gettext -e "No new or removed packages."; echo
		else
			gettext -e "New and removed packages:"; echo
			cat $DAPTUP_TMP_FILE | while read line; do
				if [ "$DAPTUP_USE_COLOR" == "y" ]; then
					if [ "`expr "$line" : '<'`" == "0" ]; then
						tput setf 3; # blue
					else
						tput setf 4; # red
					fi;
				fi;
				echo $line;
				done
		fi

		reset_color

		diff_cmd $DAPTUP_WATCH_BEFORE $DAPTUP_WATCH_AFTER $DAPTUP_TMP_FILE
		if [ "`wc -l < $DAPTUP_TMP_FILE`" == "0" ]; then # no changes in watched
			gettext -e "No news in watched packages."; echo
		else
			gettext -e "Changes in watched packages:"; echo
			cat $DAPTUP_TMP_FILE | while read line; do
				if [ "$DAPTUP_USE_COLOR" == "y" ]; then
					if [ "`expr "$line" : '<'`" == "0" ]; then
						tput setf 2; # green
					else
						tput setf 4; # red
					fi;
				fi;
				echo $line;
				done
		fi

		reset_color

		if [ "$OUTPUT_OUTDATED" == "y" ]; then
			if [ "`wc -l < $DAPTUP_OUTDATED`" == "0" ]; then # no data in outdated
				gettext -e "No outdated packages."; echo
			else
				gettext -e "Outdated packages:"; echo
				if [ "$DAPTUP_USE_COLOR" == "y" ]; then
					tput setf 5 # magenta
				fi
				cat $DAPTUP_OUTDATED | sed 's/upgradeable from/-/g' | sed 's/ to / -> /g' | sed 's/^/  /'
			fi
		fi

		reset_color

	fi
}

# start of program

if [ "$DAPTUP_SUBCOMMAND" == "pre" ]; then
	do_pre
else # post or last
	do_post
fi

delete_temp_files

do_exit 0

