#!/bin/bash

# Copyright 2016-2020  Jay Flood, SP, Brasil
# All rights reserved.
#
# 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.
#
# Author: brokenman@porteus.org
#
# This is a script to update Porteus

# Source functions
. /usr/share/porteus/porteus-functions
get_colors

# Source env variables (not passed to this shell on via python/pkexec?)
. /etc/profile.d/porteus.sh

# Set variables
SERVER=`awk -F= '/SERVER=/{print$NF}' /etc/porteus.conf`
PVER=`cat /etc/porteus-version`
CF=/etc/porteus
TMPDIR=/tmp/.updport.$$
BASEFOLDER=$PORTDIR/base

case `uname -m` in
	x86_64) ARCH=x86_64; LIBSUFFIX=64 ;;
	i?86) ARCH=i586; LIBSUFFIX="" ;;
esac

SERVER=$SERVER/$ARCH/$PVER

# Check for debug option
if [ `echo $* | grep -o verbose` ]; then
	bose=0
	log=/var/log/porteus-update.log
fi

sayupdate(){ echo "[${txtbold}${txtcyan}UPDATE$rst] $1"; }
sayok(){ echo "[${txtbold}${txtgreen}OK$rst] $1"; }
saydowngrade(){ echo "[${txtbold}${txtred}DOWNGRADE$rst] $1"; }

cleanup(){
	[ $bose ] && cyan "Cleaning up ..."
	rm -rf $TMPDIR 2>/dev/null
	[ -e /tmp/.list ] && rm /tmp/.list
	[ $bose ] && cyan "Task finished!"
	exit
}
trap cleanup SIGHUP SIGINT SIGTERM

get_target_base(){
echo
echo "The base modules go in your base directory. Unfortunately I am not"
echo "able to write to the destination. You can choose an alternative"
echo "location to download your base module updates to."
echo
echo "Enter a target directory to download to: "
read -ep "> " answ

# Check that this place is a directory and is writable
if [ ! -d $answ ]; then
	red "No directory exists at $answ"
	unset answ
	$FUNCNAME
elif [ ! -w $answ ]; then
	red "$answ is not writable"
	unset answ
	$FUNCNAME
else
	echo
	bold "Modules will be downloaded to $answ"
fi
TARG=$answ && unset answ
}

get_fastest_mirror(){
## get mirror list
MIRRORS=$(curl -s http://porteus.org/porteus-mirrors.txt)
CNT=`echo $MIRRORS | wc -w`

## Number of seconds before the test is considered a failure
TIMEOUT="4"

## Store results in an array
RESULTS=""

echo "Checking fastest mirror ..."
for MIRROR in $MIRRORS ; do
	echo -n "Processing link speed: "
	echo -ne "$CNT"\\r " "
	let CNT=$(( CNT - 1 ))
	URL="${MIRROR}"
	TIME=`curl --max-time $TIMEOUT --silent --output /dev/null --write-out %{time_total} $URL`
	if [ "$TIME" == "0.000" ] ; then
		echo "Fail";
	else
		#echo $TIME
		RESULTS="${RESULTS}${TIME}\t${MIRROR}\n";
	fi
done;

echo -e "\nResults:"
echo -e $RESULTS | sort -n > /tmp/.list
sed -i '/^$/d' /tmp/.list
fm=`head -n1 /tmp/.list | awk '{print$NF}'`
cat /tmp/.list
}

array_menu(){
echo
echo "$1"
echo "$2" && echo
select CHOICE in ${RESULT[@]}; do
    if [ -z "$CHOICE" ]; then
        bold "User aborted." && echo
        cleanup
			else
		bold "$CHOICE locale chosen"
    fi
    break
done
}

menu_multiple_choice() {
	echo
    green "All available updates are selected:"
    echo
    for i in ${!required[@]}; do
        printf "%3d%s) %s\n" $((i+1)) "${choices[i]:- }" "${required[i]}"
    done
    [[ "$msg" ]] && echo "$msg"; :
}

menu_deselect(){
required=( $@ )
clear
choices=( `printf '+ %.0s' {1..100}` )
prompt="`gettext "Deselect an item (again to reselect, ENTER when ready):"`"
while menu_multiple_choice && read -n1 -rp "$prompt" num && [[ "$num" ]]; do
    [[ "$num" != *[![:digit:]]* ]] && (( num > 0 && num <= ${#required[@]} )) || {
        msg="Invalid option: $num"; clear; continue
    }
    ((num--)); msg=""
    [[ "${choices[num]}" ]] && choices[num]="" || choices[num]="+"
clear
done

msg=" nothing"
for i in ${!required[@]}; do
    #[[ "${choices[i]}" = "+" ]] && { FINALPKGS="${FINALPKGS} ${required[i]}"; msg=""; }
    if [ $frompackageGet ]; then
		[[ "${choices[i]}" = "+" ]] && NEWFINAL+=( `echo $FINALPKGS|tr ' ' '\n'|grep ${required[i]}` )
	elif [ $fromsystemDeps=0 ]; then
		[[ "${choices[i]}" = "+" ]] && NEWFINAL+=( ${required[i]} )
	else
		echo "Error at $FUNCNAME $LINENO: please notify brokenman"
	fi
done
FINALPKGS=${NEWFINAL[@]} && unset NEWFINAL
echo
}

# Get local versions and put into array
for a in 001-core 002-xorg 002-xtra 003-xfce 003-cinnamon 003-kde 003-mate 003-lxde 003-lxqt 003-openbox 003-gnome; do
	if [ -e $CF/${a}*.ver ]; then
		[ $bose ] && cyan "$a module detected"
		LVERS+=( "`grep $a $CF/${a}*.ver`" )
	fi
	if [[ `ls /mnt/live/memory/images/ | grep -o $a` ]]; then
		case $a in
			003-xfce)
			desktop=xfce ;;
			003-cinnamon)
			desktop=cinnamon ;;
			003-kde)
			desktop=kde ;;
			003-mate)
			desktop=mate ;;
			003-lxde)
			desktop=lxde ;;
			003-lxqt)
			desktop=lxqt ;;
			003-openbox)
			desktop=openbox ;;
			003-gnome)
			desktop=gnome ;;
		esac
	fi
done
[ $bose ] && cyan "Desktop appears to be: $desktop"

# Create a temp dir
[ ! -d $TMPDIR ] && mkdir -p $TMPDIR || rm -rf $TMPDIR/*

# Download the updates file and doublecheck
download $SERVER/updates/core/updates.txt $TMPDIR
echo
if [ ! -e $TMPDIR/updates.txt ]; then
	sayerror "Could not download updates.txt file"
	sayerror "$SERVER/updates/core/updates.txt"
	cleanup
		else
	mv $TMPDIR/updates.txt $TMPDIR/core-updates.txt
	[ $bose ] && cyan "updates.txt moved to $TMPDIR/core-updates.txt"
fi

# Go through array LVERS and see if any need updating
echo "Checking base modules ..."
for a in ${LVERS[@]}; do
	modname=${a%:*}
	echo
	[[ ! `grep $modname $TMPDIR/core-updates.txt` ]] && continue
	[ $bose ] && cyan "Processing $modname"
	localver=${a##*:}
	[ $bose ] && cyan "localver is:$localver"
	remotever=`awk -F/ '/'${a%:*}'/{print$2}' $TMPDIR/core-updates.txt | sort -V | tail -n1`
	[ $bose ] && cyan "remotever is:$remotever"
	modsize=`awk '/'${modname}'/{print$NF}' $TMPDIR/core-updates.txt`
	[ $bose ] && cyan "modsize is:$modsize"
	if [ "$remotever" ]; then
		# Check if the remote version is newer than the local version
		if [ $remotever -gt $localver ]; then
			UPDATES+=( $modname )
			LINKS+=( $SERVER/updates/core/$remotever/${modname} )
			[ $bose ] && bold "An update is required for $modname"
			[ $bose ] && cyan "Link: $SERVER/updates/core/$remotever/${modname}"
			#elif [ $remotever -eq $localver ]; then
			#	sayok "$modname"
			#	else
			#saydowngrade "$modname"
		fi
	fi
done

## Get patches and put in array
echo "Checking patch availability ..."
PATCHSERVER="$SERVER/updates/patches/"
[ $bose ] && echo "Patch server: $PATCHSERVER"
wget $PATCHSERVER -P $TMPDIR >/dev/null 2>&1

if [ ! -e $TMPDIR/index.html ]; then
	sayerror "Could not download index.html from patchserver"
	cleanup
else
	[ $bose ] && cyan "Downloaded index.html from patchserver"
fi

if [[ `awk -F'"' '/href=/ && /.xzm/{print$8}' $TMPDIR/index.html` ]]; then
	for a in `awk -F'"' '/href=/ && /.xzm/{print$8}' $TMPDIR/index.html`; do
		updateit=`egrep ".*$desktop.*.xzm$|.*alldesktops.*.xzm$" <<<$a`
		[ $updateit ] && remotelist+=( "$a" )
	done
fi
rm $TMPDIR/index.html
localist=( `ls $BASEFOLDER` )

PATCHES=()
for i in "${remotelist[@]}"; do
  skip=
  for j in "${localist[@]}"; do
    [[ $i == $j ]] && { skip=1; break; }
  done
  [[ -n $skip ]] || PATCHES+=("$i")
done

## Get all update links and put into an array
if [[ ${PATCHES[@]} ]]; then
	echo "Creating links ..."
	for a in ${PATCHES[@]}; do
		DLOAD+=( $SERVER/updates/patches/$a )
	done
fi
for a in ${LINKS[@]}; do DLOAD+=( $a ); done

## If no updates then give the all clear
#clear
if [[ -z ${DLOAD[@]} ]]; then
	echo
	green "####################################"
	bold "Porteus update report"
	echo
	cyan "You are up to date."
	echo
	cleanup
else
	for a in ${DLOAD[@]}; do
		b=${a##*/}
		[ `grep "^00[1-3].*\.xzm$" <<<$b | egrep -v "alldesktops|patch|fixes"` ] && COREUPD+=( $b )
		[ `egrep "alldesktops|fixes-[0-9]" <<<$b` ] && PATCHUPD+=( $b )
	done
fi

if [[ ${COREUPD[@]} ]]; then
	green "#########################"
	bold "Available core updates"
	echo
	printf '%s\n' ${COREUPD[@]}
	echo
fi

if [[ ${PATCHUPD[@]} ]]; then
	green "#########################"
	bold "Available patch updates"
	echo
	printf '%s\n' ${PATCHUPD[@]}
	echo
fi

## Ask user permission to continue
read -p " Would you like to continue? [y/n]" -n 1 -r -s && echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then cleanup; fi
echo

## Present menu for user to deselect options
menu_deselect "${COREUPD[@]} ${PATCHUPD[@]}"

[[ -z $FINALPKGS ]] && cleanup
for a in $FINALPKGS; do
	FINALLINKS+=( `printf '%s\n' ${DLOAD[@]} | grep $a` )
done

if [ $bose ]; then
	cyan "FINAL LINKS:"
	printf '%s\n' ${FINALLINKS[@]}
fi

# Check base folder for writability
if [ -z $1 ]; then
	touch $BASEFOLDER/.test && { rm $BASEFOLDER/.test; WRITE=0; } || WRITE=1
	[ $bose ] && [ $WRITE -eq 0 ] && cyan "$BASEFOLDER is writable"
	[ $bose ] && [ $WRITE -eq 1 ] && cyan "$BASEFOLDER is NOT writable"
else
	WRITE=$1
	[ $bose ] && [ $WRITE -eq 1 ] && cyan "$BASEFOLDER is NOT writable" 
	[ $bose ] && [ $WRITE -eq 0 ] && cyan "$BASEFOLDER is writable"
fi

#touch $BASEFOLDER/.test && { rm $BASEFOLDER/.test; WRITE=0; } || WRITE=1
#[ $bose ] && [ $WRITE -eq 0 ] && cyan "$BASEFOLDER is writable"
#[ $bose ] && [ $WRITE -eq 1 ] && cyan "$BASEFOLDER is NOT writable"

# If base directory is not writable then ask for target
UPDF=$BASEDIR/porteus/updates
if [ "$WRITE" != 0 ]; then
	get_target_base
else
	# Create the updates folder
	echo
	[ $bose ] && cyan "Creating updates folder at: $UPDF"
	mkdir -p $UPDF

	# Check that it is there
	if [ ! -d $UPDFs ]; then
		sayerror "Could not create $UPDF"
		cleanup
	fi
	sayok "Created updates folder"
	TARG=$UPDF
fi

## Download updates
for a in ${FINALLINKS[@]}; do
	[ $bose ] && cyan "Downloading ${a##*/}"
	download $a $TARG
	# Check shasum
	fil=${a##*/}

	if [[ `grep $fil $TMPDIR/core-updates.txt` ]]; then
		remotever=`awk -F/ '/'$fil'/{print$2}' $TMPDIR/core-updates.txt | sort -V | tail -n1`
		rsha=`grep $remotever $TMPDIR/core-updates.txt | awk '/'$fil'/{print$1}'`
		[ $bose ] && cyan "Remote file shasum: $rsha"
		lsha=`sha256sum $TARG/$fil | awk '{print$1}'`
		[ $bose ] && cyan "Local file shasum: $lsha"
		if [ "$rsha" = "$lsha" ]; then
			sayok "shasum for $fil is good"
				else
			sayerror "shasum for $fil is bad"
			echo "Removing file ..."
			[ -e $TARG/$fil ] && rm $TARG/$fil
		fi
	fi
done
echo

green "Updates complete."
if [ "$TARG" = "$UPDF" ]; then
	green "Updates will be activated on next reboot."
else
	cyan "Updates are at $TARG"
	green "Please move them into your base folder."
fi
cleanup


