#!/usr/bin/env zsh
#
# Copyright (c) 2015-2016 parazyd
# gtomb is written and maintained by parazyd <parazyd@dyne.org>
#
# This file is part of gtomb
#
# This source code 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 3 of the License, or
# (at your option) any later version.
#
# This software 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 source code. If not, see <http://www.gnu.org/licenses/>.

# {{{ SETTINGS
ver="0.9.1"
KDFPATH="/usr/libexec/tomb" # Path of your KDF binaries (if you're using them).
SWAPOFF="false" # Set to "true" to swapoff, or "false" to use -f (force) flag.
# The ones below should not need changing
TOMBPATH="$(which tomb)" # Tomb executable's path
HEXENC="$KDFPATH/tomb-kdb-hexencode"
GENSALT="$KDFPATH/tomb-kdb-pbkdf2-gensalt"
GETITER="$KDFPATH/tomb-kdb-pbkdf2-getiter"
PBKDF="$KDFPATH/tomb-kdb-pbkdf2"
_DD="$(which dd)"
# }}}

# {{{ monmort icon
MONMORT="/tmp/monmort.png"
ICONB64="iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAQAAACJ4248AAAAAmJLR0T//xSrMc0AAAAJcEhZcwAA\n
AEgAAABIAEbJaz4AAAAJdnBBZwAAACAAAAAgAIf6nJ0AAADrSURBVFjD5ZZBEsMgCEU/TO/l2XMx\n
04VjQ1K1CDYswkwWJnH+E/EL4RP7jluDCACoim/bvfIpFQiKEJcQHCN9xEtLCgDMQM7f33sZrPNG\n
/05loCXujfAtCAVgNgLwIuycjQAra8G9Fm823ADabPRA1QDelfZAVUZktWrNvL8ew5YTnsStx3Am\n
AyOInJVbYF1prZuU+tsR1g9UMDqGuo5oFWhtSEQNEGmeVrqv73Tj0pIZirANMYqRhyw5Bb9MauSW\n
SwA8l9OzG5LnAsiiDQGQRRvaEwInK54J390hndAIYIeQ4k6AAjE/h06ab0SjP08MA1xDAAAAJXRF\n
WHRkYXRlOmNyZWF0ZQAyMDExLTAxLTEyVDA5OjM0OjI0KzAxOjAwo//d7wAAACV0RVh0ZGF0ZTpt\n
b2RpZnkAMjAxMS0wMS0xMlQwOTozNDoyNCswMTowMNKiZVMAAAAASUVORK5CYII="
echo -e "$ICONB64" | base64 --decode > $MONMORT
# }}}

# {{{ Zenity dialogs
Icon="--window-icon="$MONMORT""
function _zenques {
	zenity \
		$Icon \
		--question \
		--text="$1"
}
function _fsel {
	zenity \
		$Icon \
		--file-selection \
		--title="$1"
}
function _fsave {
	zenity \
		$Icon \
		--file-selection \
		--save \
		--title="$1" \
		--filename="$2"
}
function _zenwarn {
	zenity \
		$Icon \
		--warning \
		--title="Warning" \
		--text="$1"
}
function _info {
	which notify-send > /dev/null
	if [[ $? == "0" ]]; then
		_zenotif $*
	else
		_zeninfo $*
	fi
}
function _zenotif {
	zenity \
		$Icon \
		--notification \
		--title="$1" \
		--text="$2"
}
function _zeninfo {
	zenity \
		$Icon \
		--info \
		--title="$1" \
		--text="$2"
}
function _zenerr {
	zenity \
		$Icon \
		--error \
		--title="$1" \
		--text="$2"
}
function _zenprog {
	zenity \
		$Icon \
		--progress \
		--auto-close \
		--pulsate \
		--title="$1" \
		--text="$2"
}
function _zenprognc {
	zenity \
		$Icon \
		--progress \
		--auto-close \
		--no-cancel \
		--pulsate \
		--title="$1" \
		--text="$2"
}
function _zenentry {
	zenity \
		$Icon \
		--entry \
		--title="$1" \
		--text="$2" \
		--entry-text="$3"
}
# }}}

# {{{ _clean - Clean function, removes sensitive stuff from memory
function _clean {
	unset $?
	local rr="$RANDOM"
	while [[ ${#rr} -lt 500 ]]; do
		rr+="$RANDOM"
	done

	cmnd="$rr";			unset cmnd
	tombname="$rr";		unset tombname
	tombsize="$rr";		unset tombsize
	keyfile="$rr";		unset keyfile
	tombtmp="/tmp/tombtmp"
	if [ -f $tombtmp ]; then
		dd if=/dev/urandom of=$tombtmp bs=800 count=1
		rm -f $tombtmp
	fi
	tombtmp="$rr";		unset tombtmp
	newkey="$rr";		unset newkey
	jpegfile="$rr";		unset jpegfile
}
# }}}

# {{{ _main - Main window
function _main {
	_clean
	cmnd=`zenity \
		$Icon \
		--title="gtomb" \
		--width=400 \
		--height=445 \
		--list \
		--hide-header \
		--text="gtomb v$ver\nThe GUI wrapper for Tomb, the crypto undertaker." \
		--separator=" & " \
		--column=Function \
		--column=Description \
		"dig" "Dig a new tomb of chosen size" \
		"forge" "Forge a new key used to lock tombs" \
		"lock" "Lock a non-locked tomb using an existing key" \
		"open" "Open an existing tomb" \
		"index" "Index the contents of all tombs." \
		"search" "Search the content of indexed tombs." \
		"list" "List all open tombs and information on them" \
		"close" "Close a specific tomb (or all)" \
		"slam" "Slam a tomb (or all) killing all programs using it" \
		"resize" "Resize a tomb to a new size (can only grow)" \
		"passwd" "Change the passphrase of a key" \
		"setkey" "Change the key of an existing tomb" \
		"engrave" "Generates a QR code of a key to be saved on paper" \
		"bury" "Hide a key inside a JPEG image" \
		"exhume" "Extract a key from a JPEG image"`
	if [[ "$?" = 1 && $SWAPOFF = "true" ]]; then
		zenity --password --title="sudo swapon -a" | sudo swapon -a
		unset $?
	fi
	eval "_$cmnd"
}
# }}}

# {{{ dig - Dig a new tomb
function _dig {
	tombname=`_fsave "Choose where to dig your tomb" "secret.tomb"`
	res=$?

	if [[ -f "$tombname" ]]; then
		_zenerr "Error" "This tomb already exists. I am not digging here."
		exec _main
	elif [[ -z "$tombname" ]]; then
		_info "gtomb" "Cancelled"
		exec _main
	fi

	[[ $res = 0 ]] || exec _main

	tombsize=`_zenentry "Tomb digging" "Enter the size of your tomb in MiB (min. 10 MiB)" "10"`
	res=$?

	re='^[0-9]+$'
	if ! [[ $tombsize =~ $re ]]; then
		_zenerr "Error" "Please choose a valid number."
		exec _main
	elif [[ -z $tombsize ]]; then
		_info "gtomb" "Cancelled"
		exec _main
	fi

	[[ $res = 0 ]] || { _zenwarn "Tomb digging cancelled." ; exec _main }

	"$TOMBPATH" dig -s "$tombsize" "$tombname" | \
		_zenprog "Digging new tomb" "Please wait while your tomb is being dug..." &

	PID_ZEN=$(ps -C zenity h -o pid,command | awk '/Digging new tomb/ {print $1}')
	while [ -n "$PID_ZEN" ]; do
		PID_ZEN=$(ps h -o pid -p ${PID_ZEN})
		PID_DD=$(ps -C $_DD h -o pid,command | awk -v tombtmp="$tombname" '$0~tombtmp{print $1}')
		sleep 1
	done

	[[ -n "$PID_DD" && -z "$PID_ZEN" ]] && {
		kill -9 $PID_DD
		_zenwarn "Tomb digging cancelled."
		rm -f "$tombname"
		exec _main
	}
	
	wait

	_info "Success" "Your tomb has been dug in $tombname"
	exec _main
}
# }}}

# {{{ forge - Forge a new key
function _forge {
	keyfile=`_fsave "Choose where to forge your key" "secret.tomb.key"`
	res=$?

	if [[ -f $keyfile ]]; then
		_zenerr "Error" "This key already exists. I am not overwriting."
		exec _main
	elif [[ -z $keyfile ]]; then
		_info "gtomb" "Cancelled"
		exec _main
	fi

	kdf=""
	kdfiter=""
	if [[ -x $GENSALT ]] && [[ -x $GETITER ]] && [[ -x $PBKDF ]]; then
		_zenques "Do you want to use KDF? (Generates passwords armored against dictionary attacks)"
		if [[ $? == "0" ]]; then
			kdf="--kdf"
			kdfiter=`_zenentry "Iterations" "Enter the delay (itertime) in seconds for each time \n\
this key is used:" "2"`

			re='^[0-9]+$'
			if ! [[ $kdfiter =~ $re ]]; then
				_zenerr "Error" "Please choose a valid number."
				exec _main
			elif [[ -z $kdfiter ]]; then
				_info "gtomb" "Cancelled"
				exec _main
			fi
		fi
	else
		_zenotif "gtomb" "KDF binaries not found."
	fi

	[[ $? = 0 ]] || exec _main

	"$TOMBPATH" forge "$keyfile" "$kdf" "$kdfiter" "$FLAG" | \
		_zenprog "Forging key" "Please wait while your key is being forged...\n\
You can move your mouse around and use your computer to speed up the process." &

	PID_ZEN=$(ps -C zenity h -o pid,command | awk '/Forging key/ {print $1}')
	while [ -n "$PID_ZEN" ]; do
		PID_ZEN=$(ps h -o pid -p ${PID_ZEN})
		PID_DD=$(ps -C $_DD h -o pid,command | awk '/ if/ {print $1}')
		sleep 1
	done
	[[ -n "$PID_DD"  && -z "$PID_ZEN" ]] && {
		kill -9 $PID_DD
		_zenwarn "Forging cancelled."
		rm -f $keyfile
		exec _main
	}
	
	wait

	_info "Success" "Your key is now forged in $keyfile"
	exec _main
}
# }}}

# {{{ lock - Lock a non-locked tomb
function _lock {
	tombname=`_fsel "Select a tomb to lock"`
	[[ -n $tombname ]] || { _zenotif "gtomb" "Cancelled" ; exec _main }
	[[ $? = 0 ]] || exec _main

	keyfile=`_fsel "Choose the key for your tomb"`
	[[ -n $keyfile ]] || { _zenotif "gtomb" "Cancelled" ; exec _main }
	[[ $? = 0 ]] || exec _main

	"$TOMBPATH" lock "$tombname" -k "$keyfile" | \
		_zenprognc "Locking your tomb" "Please wait while your tomb is being locked..."
	
	wait
	
	_info "Success" "Your tomb is now locked."
	exec _main
}
# }}}

# {{{ open - Open an existing tomb
function _open {
	tombname=`_fsel "Choose a tomb to open"`
	[[ $? = 0 ]] || exec _main

	keyfile=`_fsel "Choose the key for your tomb"`
	[[ $? = 0 ]] || exec _main

	"$TOMBPATH" open "$tombname" -k "$keyfile" "$FLAG"
	
	wait
	
	_info "Success" "Your tomb is now open."
	exec _main
}
# }}}

# {{{ list - list all open tombs, along with their mountpoint
function _list {
	tombtmp="/tmp/tombtmp"
	"$TOMBPATH" list --get-mountpoint > $tombtmp
	tombname=`cat $tombtmp | \
		sed 's/.*\/\([^\/]*\)$/\1\n &/' | \
		zenity \
			--title="Currently open tombs" \
			$Icon \
			--width=400 --height=380 --list \
			--separator=" & " \
			--text="Here are your open tombs" \
			--column="Tomb" \
			--column="Path" `

	tombname=`echo "$tombname" | cut -c1-16`

	[[ $? = 0 ]] || exec _main

	listchoice=`zenity \
		--title="Choose action" \
		$Icon \
		--width=400 --height=380 --list \
		--separator=" & " \
		--text="What do you want to do with this tomb?" \
		--column="Command" \
		--column="Description" \
		"disindex" "Disable indexing of this tomb." \
		"enindex" "Enable indexing of this tomb." \
		"close" "Close the selected tomb." \
		"slam" "Slam the selected tomb." \
		"binds" "Edit current bind-hooks." \
		"posts" "Edit current post-hooks."`

	[[ $? = 0 ]] || exec _main

	case $listchoice in
		close)
			"$TOMBPATH" close "$tombname"
			_zeninfo "Success" "Tomb closed successfully!"
			exec _main
			;;
		slam)
			"$TOMBPATH" slam "$tombname"
			_info "Success" "$tombname slammed successfully!"
			exec _main
			;;
		disindex)
			tombloc=`"$TOMBPATH" list "$tombname" --get-mountpoint`
			touch "$tombloc/.noindex"
			_info "Success" "Indexing disabled for this tomb."
			exec _main
			;;
		enindex)
			tombloc=`"$TOMBPATH" list "$tombname" --get-mountpoint`
			rm -f "$tombloc/.noindex"
			_info "Success" "Indexing of this tomb is now enabled."
			exec _main
			;;
		binds)
			tombloc=`"$TOMBPATH" list "$tombname" --get-mountpoint`
			bindhooks=`zenity \
				--text-info \
				--filename="$tombloc/bind-hooks" \
				--editable`
			[[ $? = 0 ]] && {
				echo "$bindhooks" > "$tombloc/bind-hooks"
				_info "gtomb" "bind-hooks saved"
			}
			exec _main
			;;
		posts)
			tombloc=`"$TOMBPATH" list "$tombname" --get-mountpoint`
			posthooks=`zenity \
				--text-info \
				--filename="$tombloc/post-hooks" \
				--editable`
			[[ $? = 0 ]] && {
				echo "$posthooks" > "$tombloc/post-hooks"
				chmod +x "$tombloc/post-hooks"
				_info "gtomb" "post-hooks saved"
			}
			exec _main
			;;
	esac
}
# }}}

# {{{ close - Close open tomb(s)
function _close {
	tombtmp="/tmp/tombtmp"
	"$TOMBPATH" list --get-mountpoint > $tombtmp
	echo "/all" >> $tombtmp
	tombname=`cat $tombtmp | \
		sed 's/.*\/\([^\/]*\)$/\1\n &/' | \
		zenity \
			--title="Choose a tomb to close" \
			$Icon \
			--width=640 --height=380 --list \
			--separator=" & " \
			--column=Tomb \
			--column=Path `

	[[ $? = 0 ]] || exec _main

	tombname=`echo "$tombname" | cut -c1-16`
	"$TOMBPATH" close "$tombname"
	_info "Success" "Closed successfully!"
	exec _main
}
# }}}

# {{{ slam - Slam open tombs
function _slam {
	tombtmp="/tmp/tombtmp"
	"$TOMBPATH" list --get-mountpoint > $tombtmp
	echo "/all" >> $tombtmp
	tombname=`cat $tombtmp | \
		sed 's/.*\/\([^\/]*\)$/\1\n &/' | \
		zenity \
			--title="Choose a tomb to slam" \
			$Icon \
			--width=640 --height=380 --list \
			--separator=" & " \
			--column=Tomb \
			--column=Path `

	[[ $? = 0 ]] || exec _main

	tombname=`echo "$tombname" | cut -c1-16`
	"$TOMBPATH" slam "$tombname"
	_info "Success" "Slammed successfully!"
	exec _main
}
# }}}

# {{{ resize - Resize an existing *closed* tomb
function _resize {
	tombname=`_fsel "Choose a tomb to resize"`
	res=$?
	_zenques "Is your tomb closed?"

	[[ $? = 0 ]] || { _zenwarn "Please close the tomb before resizing." ; exec _main }

	[[ $res = 0 ]] || exec _main

	tombsize=`_zenentry "New tomb size" "Enter the new size of your tomb in MiB. Must be higher than the current size." "100"`
	[[ -n $tombsize ]] || _zenotif "gtomb" "Cancelled"

	re='^[0-9]+$'
	if ! [[ $tombsize =~ $re ]]; then
		_zenerr "Error" "Please choose a valid number."
		exec _main
	elif [[ -z $tombsize ]]; then
		_info "gtomb" "Cancelled"
		exec _main
	fi

	[[ $? = 0 ]] || exec _main

	keyfile=`_fsel "Choose according keyfile"`
	[[ $? = 0 ]] || exec _main

	"$TOMBPATH" resize "$tombname" -s "$tombsize" -k "$keyfile" | \
		_zenprognc "Resizing tomb." "Please wait while your tomb is being resized..."
	_info "Success" "Tomb resized successfully!"
	exec _main
}
# }}}

# {{{ passwd - Change existing key's passphrase
function _passwd {
	keyfile=`_fsel "Choose a keyfile"`
	[[ $? = 0 ]] || exec _main

	"$TOMBPATH" passwd -k "$keyfile" "$FLAG" | \
		_zenprognc "Changing passphrase" "Please wait while your key's passphrase is being changed..."

	_info "Success" "$keyfile passphrase changed successfully!"
	exec _main
}
# }}}

# {{{ setkey - Change a tomb's keyfile
function _setkey {
	tombname=`_fsel "Choose a tomb to change its keyfile"`
	[[ $? = 0 ]] || exec _main

	keyfile=`_fsel "Chosse your tomb's old keyfile"`
	 [[ $? = 0 ]] || exec _main

	newkey=`_fsel "Choose your tomb's new keyfile"`
	[[ $? = 0 ]] || exec _main

	"$TOMBPATH" setkey -k "$newkey" "$keyfile" "$tombname" "$FLAG" | \
		_zenprognc "Changing key" "Please wait while your tomb's key is being changed..."

	_info "Success" "$tombname keyfile successfully changed! Now using $newkey"
	exec _main
}
# }}}

# {{{ engrave - generate QR code of a key
function _engrave {
	which qrencode || _zenwarn "qrencode is not installed. Install it and try again."
	keyfile=`_fsel "Choose a keyfile to engrave"`
	[[ $? = 0 ]] || exec _main

	jpegfile=`_fsave "Choose where to save your keyfile (PNG format)"`
	[[ $? = 0 ]] || exec _main

	awk '/^-----/ {next}; /^Version/ {next}; {print $0}' $keyfile \
		| qrencode --size 4 --level H --casesensitive -o $jpegfile

	_info "Success" "QR code generated in $jpegfile"
	exec _main
}
# }}}

# {{{ bury - hide a keyfile in a JPEG image
function _bury {
	which steghide || _zenwarn "steghide is not installed. Install it and try again."
	keyfile=`_fsel "Choose keyfile"`
	[[ $? = 0 ]] || exec _main

	jpegfile=`_fsel "Choose JPEG file"`
	[[ $? = 0 ]] || exec _main

	"$TOMBPATH" bury -k "$keyfile" "$jpegfile"
	_info "Success" "Your key is now hidden in $jpegfile"
	exec _main
}
# }}}

# {{{ exhume - extract keyfile from JPEG
function _exhume {
	which steghide || _zenwarn "steghide is not installed. Install it and try again."
	jpegfile=`_fsel "Choose JPEG file"`
	[[ $? = 0 ]] || exec _main

	keyfile=`_fsave "Choose where to extract your key"`
	[[ $? = 0 ]] || exec _main

	"$TOMBPATH" exhume -k "$keyfile" "$jpegfile"
	_info "Success" "Your keyfile is extracted to $keyfile"
	exec _main
}
# }}}

# {{{ index - index the contents of open tombs
function _index {
	which locate || _zenwarn "mlocate is not installed. Install it and try again."
	"$TOMBPATH" index | _zenprognc "Indexing" "Please wait while the open tombs are being indexed..."
	_info "Success" "Tombs indexed!"
	exec _main
}
# }}}

# {{{ search - searches the contents of indexed tombs
function _search {
	strings=""
	_searchstring
	exec _main
}

function _searchstring {
	srchstring=`_zenentry "Search" "Enter an argument to search. Cancel to finish."`
	res=$?
	strings="$strings $srchstring"

	if [[ $res = 1 ]]; then
		tombtmp="/tmp/tombtmp"
		_info "Success" "Search complete!"
		"$TOMBPATH" search "$strings" > $tombtmp
			zenity \
				--text-info \
				--width=800 --height=640 \
				--title="Search results" \
				--filename=$tombtmp
	else
		_searchstring
	fi
}


# }}}

function _ { _clean } # I like cleaning :)

[[ -x $TOMBPATH ]] || {
	_zenwarn "Tomb binary is not executable or doesn't exist in the current path. Install it or edit the script to point to the correct path."
	exit 1 }

if [[ $SWAPOFF = "true" ]]; then
	FLAG=""
	zenity --password --title="sudo swapoff -a" | sudo swapoff -a
	unset $?
else
	FLAG="-f"
fi

_main

