#!/bin/bash

# simple sfs manager for fatdog 
# originally written by 01micko 120618, i18n by L18L 131113
# completely rewritten by SFR 170116
# step 20190214: add support for parsing HTML index file from ibiblio
# requires: gtkdialog, Xdialog, bash, wget, load_sfs.sh

set -a
set -o pipefail

# std localisation stanza
export TEXTDOMAIN=fatdog
. gettext.sh

APPNAME="$(gettext 'SFS Manager')"

[ $(id -u) -ne 0 ] && exec gtksu "$APPNAME" "$0" "$@"

# -----------------------------------------------------------------------------

# Configuration file
CONFFILE='/etc/load_sfs.conf'

# URLs
URL_IBIBLIO="http://distro.ibiblio.org/pub/linux/distributions/fatdog"	# default
URL_UOC="http://ftp.cc.uoc.gr/mirrors/linux/fatdog"
URL_NLUUG="http://ftp.nluug.nl/ibiblio/distributions/fatdog"

# Labels
LABEL_IBIBLIO="Ibiblio ($(gettext "USA"))"
LABEL_UOC="UOC ($(gettext "Greece"))"
LABEL_NLUUG="NLUUG ($(gettext "Netherlands"))"
LABEL_DEFAULT="$LABEL_IBIBLIO"

VERSION="$(< /etc/fatdog-version)"
VERSION=${VERSION%%[!0-9]*} # remove pre-release tags, if any (rc, alpha5, beta33, etc.)
VERSION=${VERSION%?}0 # replace last digit with zero (70x becomes 700, 71x becomes 710, etc)

#SFSDEST="$(readlink /mnt/home)" # DVD saves is okay - it has /mnt/home symlink
SFSDEST="/mnt/home"					# consistent with load_sfs.sh's default path
[ -e "$CONFFILE" ] && . "$CONFFILE"
[ -e "$SFS_DIR" ] && SFSDEST="$SFS_DIR" # override with user's conf
! [ -e "$SFSDEST" ] && SFSDEST="$HOME"  # no savefile, save to $HOME

WORKDIR="/tmp/sfsmanager.${USER}.$$"
mkdir -p "$WORKDIR"
trap 'rm -rf "$WORKDIR"; exit' 0 INT HUP TERM QUIT ABRT

# -----------------------------------------------------------------------------

# 1-type, 2-message
func_msg () {
	case "$1" in
		yesno_error)	Xdialog --stdout --title "$APPNAME" \
						--icon /usr/share/pixmaps/midi-icons/error.xpm \
						--yesno "$2" 0 0
		;;
		yesno_info_chk)	Xdialog --stdout --title "$APPNAME" \
						--icon /usr/share/pixmaps/midi-icons/question.xpm \
						--check "$3" "$4" \
						--yesno "$2" 0 0
		;;
		error)			Xdialog --stdout --title "$APPNAME" \
						--icon /usr/share/pixmaps/midi-icons/error.xpm \
						--msgbox "$2" 0 0
		;;
		splash)			Xdialog --stdout --title "$APPNAME" \
						--no-buttons \
						--infobox "$2" 0 0 999999999 &	# to the background
		;;
		gauge)			Xdialog --stdout --title "$APPNAME" \
						--icon /usr/share/pixmaps/midi-icons/update48.png \
						--gauge "$2" 0 0
		;;
		info|*)			Xdialog --stdout --title "$APPNAME" \
						--icon /usr/share/pixmaps/midi-icons/lamp48.png \
						--msgbox "$2" 0 0
		;;
	esac
}

# -----------------------------------------------------------------------------

# Store save location in config file

func_savesfsdir () {
	local sfsdest="$1" _

	# remove exising variable with location
	if [ -e "$CONFFILE" ]; then
		sed -i "/SFS_DIR=.*/d" "$CONFFILE"
	fi

	# make sure there's a newline
	if [ -s "$CONFFILE" ]; then
		tail -n1 "$CONFFILE" | read -r _ || echo >> "$CONFFILE"
	fi

	sfsdest="${sfsdest//\'/\'\"\'\"\'}"	# escape '
	echo "SFS_DIR='$sfsdest'" >> "$CONFFILE"
}

# -----------------------------------------------------------------------------

# Output SFS list from HTML index

func_getindex () {
	local url=$1 repo=$2
	wget -q -O "${WORKDIR}/sfsindex.tmp" "$url" &&
	html2 < "$WORKDIR/sfsindex.tmp" |
	awk -F= -v REPO="$repo" '
	BEGIN { P = "/html/body/div/table/tbody/" }
	$0 ~ P".*@href.*sfs$" {
		printf substr($2, 1, length($2) - 4)  # name
		GET = NR + 7
	}
	NR == GET { print "|"$2"||"REPO }             # |size|?description|repo
	'
}

# -----------------------------------------------------------------------------

# Output SFS table from sfslist file

func_getlist () {
	local url=$1 repo=$2

	wget -q -O "${WORKDIR}/sfslist.tmp" "$url" &&
	sed -e 's/.sfs|[a-fA-F0-9]\+//g' -e 's/$/|'"$repo"'/g' "${WORKDIR}/sfslist.tmp"
}

# -----------------------------------------------------------------------------

# Build combined SFS table

func_gettable () {
	local url xpid

	# Splash
	func_msg splash "$(gettext 'Please wait, downloading SFS list.')"
	xpid=$!

	# Get SFS tables
	for url in "$URL_IBIBLIO" "$URL_UOC" "$URL_NLUUG"; do

		func_getlist  "${url}/sfs/$VERSION/sfslist" "$(gettext "Official")" > "${WORKDIR}/sfslist-1"
		func_getindex "${url}/sfs/$VERSION/"        "$(gettext "Official")" > "${WORKDIR}/sfsindex-1"
	
		func_getlist  "${url}/contrib/sfs/$VERSION/sfslist" "$(gettext "Contrib")" >> "${WORKDIR}/sfslist-1"
		func_getindex "${url}/contrib/sfs/$VERSION/"        "$(gettext "Contrib")" >> "${WORKDIR}/sfsindex-1"

		[ -s "${WORKDIR}/sfsindex-1" ] || [ -s "${WORKDIR}/sfslist-1" ] && break
	done

	# Kill splash
	kill -0 $xpid 2>/dev/null && kill $xpid

	if ! [ -s "${WORKDIR}/sfsindex-1" ] && ! [ -s "${WORKDIR}/sfslist-1" ]; then
		func_msg error "$(gettext 'Failed to download SFS list.\nCheck your internet connection.')"
		exit 1
	fi

	# Set the default mirror
	case "$url" in
		"$URL_IBIBLIO")	LABEL_DEFAULT="$LABEL_IBIBLIO"	;;
		"$URL_UOC")		LABEL_DEFAULT="$LABEL_UOC"		;;
		"$URL_NLUUG")	LABEL_DEFAULT="$LABEL_NLUUG"	;;
	esac

	# Merge tables without duplicates: sfslist entry wins over matching sfsindex entry.
	> "${WORKDIR}/sfslist" awk -F"|" '
	!($1 in Seen) { Seen[$1] = 1; R[++nR] = $0 }
	END { for(i = 1; i <= nR; i++) { print R[i] } }
	' "${WORKDIR}/sfslist-1" "${WORKDIR}/sfsindex-1"
}

# -----------------------------------------------------------------------------

func_getsfs () {
	# Repos
	case "$REPO" in
		"UOC"*)
			URL_OFFICIAL="${URL_UOC}/sfs/${VERSION}/"
			URL_CONTRIB="${URL_UOC}/contrib/sfs/${VERSION}/"
		;;
		"NLUUG"*)
			URL_OFFICIAL="${URL_NLUUG}/sfs/${VERSION}/"
			URL_CONTRIB="${URL_NLUUG}/contrib/sfs/${VERSION}/"
		;;
		"Ibiblio"*|*)
			URL_OFFICIAL="${URL_IBIBLIO}/sfs/${VERSION}/"
			URL_CONTRIB="${URL_IBIBLIO}/contrib/sfs/${VERSION}/"
		;;
    esac

	mkdir -p "$SFSDEST"

	# Download selected SFSes one by one
	for SFS in $SFSCHOICE; do
		# Check if SFS is already in SFSDEST
		if [ -e "${SFSDEST}/${SFS}.sfs" ]; then
			func_msg error "$(eval_gettext 'Aborted, ${SFS}.sfs already exists in ${SFSDEST}.')"
			continue
		fi
		
		# Set repo
		case "$(grep -m 1 "^${SFS}|" "${WORKDIR}/sfslist" | cut -f4 -d '|')" in
			"$(gettext 'Contrib')")	URL="${URL_CONTRIB}"	;;
			*)						URL="${URL_OFFICIAL}"	;;
		esac
		
		# Download checksum
		#CHECKSUM="$(curl -Ls ${URL}/${SFS}.sfs.md5sum.txt | cut -d ' ' -f1)"
		CHECKSUM="$(wget -q -O - ${URL}/${SFS}.sfs.md5sum.txt | cut -d ' ' -f1)"
		if [ $? -ne 0 ]; then
			func_msg error "$(eval_gettext 'Failed to download ${SFS}.sfs checksum.')"
			continue
		fi

		# Download SFS
		# Closing rxvt while downloading is in progress dumps a wget-log
		# file in CWD unless "-o/dev/null --show-progress" is passed to wget.
		# SFR: "-o/dev/stdin" fixes the 'wget-log' problem without reducing the verbosity.
		#rxvt -background grey20 -foreground white -title "$(eval_gettext 'Downloading ${SFS}.sfs')" -geometry 80x8  \
		#	 -e wget -o/dev/stdin --show-progres -t0 --waitretry=5 -4 -P "${SFSDEST}" -c "${URL}/${SFS}.sfs"
		# SFR: problem - after closing rxvt, wget still works in the background
		# SFR: let's do it using Xdialog's gauge instead:
		nohup wget -t0 --waitretry=5 -4 -P "${SFSDEST}" -c "${URL}/${SFS}.sfs" 2>&1 | \
			stdbuf -o0 sed -n 's/.* \([0-9]\+\)%.*/\1/p' 2>/dev/null | \
			func_msg gauge "$(eval_gettext 'Downloading ${SFS}.sfs')"
		
		if [ $? -ne 0 ]; then
			rm -f "${SFSDEST}/${SFS}.sfs"
			func_msg error "$(eval_gettext 'Download aborted!')"
			continue
		fi
		
		# Compare checksums
		if [ -f "${SFSDEST}/${SFS}.sfs" ]; then
			if [ "$(md5sum "${SFSDEST}/${SFS}.sfs" | cut -d ' ' -f1)" != "$CHECKSUM" ];then
				func_msg yesno_error "$(eval_gettext 'MD5 checksum of ${SFS}.sfs is incorrect.\nKeep it anyway? If you click "No" the SFS will be deleted.')"
				[ $? -ne 0 ] && rm -f "${SFSDEST}/${SFS}.sfs" && continue
			fi
		else
			func_msg error "$(eval_gettext 'Failed to download ${SFS}.sfs.')"
			continue
		fi
		
		# Ask to load the SFS
		RET="$(func_msg yesno_info_chk "$(gettext 'File correctly downloaded and MD5 checked.')\n$(eval_gettext 'Do you want to load ${SFS}.sfs now?')" \
								"$(gettext 'Check this box to load this SFS at every boot.')" false)"
		[ $? -ne 0 ] && continue
		
		# Check if SFS is already loaded
		CHECKSFS="$(load_sfs.sh --loaded | grep -m 1 "/${SFS}.sfs")"
		CHECKSFS_FILE="${CHECKSFS##*/}"
		CHECKSFS_PATH="${CHECKSFS%/*}"
		if [ "${CHECKSFS_FILE}" = "${SFS}.sfs" ]; then
			func_msg error "$(eval_gettext 'Another ${SFS}.sfs is already loaded\nfrom directory: ${CHECKSFS_PATH}.')"
			continue
		fi
		
		# Load it
		load_sfs.sh --load "${SFSDEST}/${SFS}.sfs"
		[ "$RET" = "checked" ] && load_sfs.sh --add  "${SFSDEST}/${SFS}.sfs"
	done
}

# -----------------------------------------------------------------------------

func_gui () {
	export choice='<window title="'${APPNAME}'" window-position="1" image-name="/usr/share/pixmaps/midi-icons/squashfs-image48.png">
	<vbox>
		<hbox>
		
		<hbox space-fill="false" space-expand="false">
			<text><label>'$(gettext "Choose your download site:")' </label></text>
			<comboboxtext>
				<variable>REPO</variable>
				<default>'${LABEL_DEFAULT}'</default>
				<item>'${LABEL_IBIBLIO}'</item>
				<item>'${LABEL_UOC}'</item>
				<item>'${LABEL_NLUUG}'</item>
			</comboboxtext>
		</hbox>
	
		<text space-fill="true" space-expand="true"><label>""</label></text>
		<vseparator></vseparator>
		<text space-fill="true" space-expand="true"><label>""</label></text>
		
		<text><label>'$(gettext "Save SFS files to:")' </label></text>
		<hbox>
			<entry fs-action="folder" fs-folder="'${SFSDEST}'" fs-title="'$(gettext "Select an existing folder")'">
				<variable>SFSDEST</variable>
				<input>echo "${SFSDEST}"</input>
			</entry>
			<button>
				<input file stock="gtk-directory"></input>
				<action>fileselect:SFSDEST</action>
			</button>
			<button tooltip-text="'$(gettext "Set as default SFS directory")'">
				<input file stock="gtk-save"></input>
				<action>func_savesfsdir "${SFSDEST}"</action>
			</button>
		</hbox>
	
		</hbox>
	
		<hseparator></hseparator>
	
		<text><label>'"$(gettext "These are the SFS files available for download"):"'</label></text>
		<tree auto-sort="true" column-type="string|int64" selection-mode="3" column-sizing="0|0|300|0" rules-hint="true">
			<label>'$(gettext "File name")'|'$(gettext "Size (MiB)")'|'$(gettext "Description")'|'$(gettext "Repository")'</label>
			<variable>SFSCHOICE</variable>
			<input file>'"${WORKDIR}/sfslist"'</input>
			<action>func_getsfs</action>
			<action signal="button-release-event">enable:DL_BUTTON</action>
			<action signal="key-release-event">enable:DL_BUTTON</action>
			<action signal="button-release-event" condition="command_is_true( [ \"${SFSCHOICE}\" = \"\" ] && echo true)">disable:DL_BUTTON</action>
			<action signal="key-release-event" condition="command_is_true( [ \"${SFSCHOICE}\" = \"\" ] && echo true)">disable:DL_BUTTON</action>
		</tree>
		
		<hbox>
		<button space-fill="false" space-expand="false">
		  <label>'$(gettext "System SFS Loader")'</label>
		  <input file>/usr/share/midi-icons/squashfs-image48.png</input>
		  <height>24</height>
		  <action>load_sfs.sh &</action>
		</button>
		
		<text space-fill="true" space-expand="true"><label>""</label></text>
		
		<button sensitive="false">
			<variable>DL_BUTTON</variable>
			<label>'$(gettext "Download selected")'</label>
			<input file stock="gtk-goto-bottom"></input>
			<action>func_getsfs</action>
		</button>
		<button>
			<label>'$(gettext "Quit")'</label>
			<input file stock="gtk-quit"></input>
			<action>exit:exit</action>
		</button>
		</hbox>
		
	</vbox>
	</window>'

	gtkdialog -p choice -G 750x380
}

# =============================================================================

### START ###

func_gettable
func_gui

#END
