#!/bin/bash

# Copyright (c) 2020 jm1hey
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

# Version
script_version="1.3"

# The purpose of this script is described in the 'printhelp' function defined below.

# define command name -- must be the same as the script file name
cmdname="rm_r_prompt"

# define zenity prompt timeout time
TIMEOUT_TIME=300 # in seconds

# define maximum number of failure notifications
MAX_FAIL_NOTIFICATIONS=3

# define maximum number of authentication tries
MAX_AUTH_TRIES=3

# define usage and help functions
function printusage {
	printf "\nUsage: %s [OPTION(s)] -- FILE(s)\n\n" "$cmdname"
	printf "\t\tNote: arguments after the first '--' are always interpreted as files.\n\n"
	printf "Options:\n"
	printf "\t--help\n"
	printf "\t\tPrint help information and exit\n\n"
	printf "\t--version\n"
	printf "\t\tPrint version information and exit\n\n"
}

function printhelp {
	printf "\n'%s'%s%s%s%s%s%s%s%s%s%s%s%s%s\n" "$cmdname" " is a Bash script that" \
		" utilizes 'zenity' to prompt for the removal of files and directories" \
		" passed as arguments. It is useful for adding a custom menu item to a file" \
		" manager for permanent removal of items while taking a precaution against" \
		" accidental removals. Thus, this script is intended for automation rather" \
		" than for manual usage from a terminal. If the user does not have write" \
		" permission for the selected files, then the user will be prompted for password" \
		" in order to authenticate the deletion(s) using sudo (if applicable)." \
		" Files for which the user does have write permission will be deleted" \
		" before prompting for password. Thus, if the password prompt fails for" \
		" write-protected files, then at least any files that are not" \
		" write-protected will be deleted. Note that directories are removed" \
		" recursively. Naturally, Zenity must be installed."
	printusage
}

function printversion {
	echo "$script_version"
}

# check for sufficient number of arguments
if [ $# -lt 1 ]
then
	printusage
	exit 1
fi

# check for arguments and find '--'
file_arg_mark="NONE"
num_i=1
for i in "$@"
do
	# check for '--'
	if [ "$i" == "--" ]
	then
		file_arg_mark="$num_i"
		# make sure there is at least one more argument
		if [ ! $# -gt "$num_i" ]
		then
			printusage
			exit 1
		fi
		break
	fi
	# check for "--help" argument
	if [ "$i" == "--help" ]
	then
		# print help text
		printhelp
		exit 0
	fi
	# check for "--version" argument
	if [ "$i" == "--version" ]
	then
		printversion
		exit 0
	fi
	num_i=$((num_i+1))
done

# if '--' was not found, then exit
if [ "$file_arg_mark" == "NONE" ]
then
	printusage
	exit 1
fi

# check if 'zenity' command is available
which zenity > /dev/null
if [ $? != 0 ]
then
	printf "\nError: zenity not found\n\n"
	exit 1
fi

# make sure that all of the file arguments are valid
# also check for files for which user does not have write permission
num_i=1
num_files=0
needWPerm=0
for arg in "$@"
do
	# skip to file arguments
	if [ "$num_i" -le "$file_arg_mark" ]
	then
		num_i=$((num_i+1))
		continue
	fi
	# handle file names beginning with "-"
	if [ "${arg:0:1}" == "-" ]
	then
		arg_fix="./""$arg"
	else
		arg_fix="$arg"
	fi
	if [ ! -e "$arg_fix" ]
	then
		printf "Error: \"%s\" not found\n\n" "$arg"
		exit 1
	elif [ $needWPerm != 1 ]
	then
		if [ ! -w "$arg_fix" ]
		then
			if [ -d "$arg_fix" ]
			then
				if [ "$(ls -Aq "$arg_fix" | wc -l)" != 0 ]
				then
					needWPerm=1
				fi
			else
				needWPerm=1
			fi
		fi
	fi
	num_files=$((num_files+1))
	num_i=$((num_i+1))
done

if [ $needWPerm == 1 ]
then
	# check if user is a sudoer
	which sudo > /dev/null
	if [ $? == 0 ]
	then
		isSudoer=0
		for grp in $(groups)
		do
			if [ "$grp" == "sudo" ]
			then
				isSudoer=1
				break
			fi
		done
	else
		isSudoer=0
	fi
fi

# define function for wiping user password variable
function wipe_pw {
	userpw="111111111111111111111111111111111"
	userpw="000000000000000000000000000000000"
	userpw=""
}

# confirm and perform deletion(s)
if [ $num_files == 1 ]
then
	# only one item is selected for deletion, so prompt using its name
	num_i=1
	for i in "$@"
	do
		if [ "$num_i" -gt "$file_arg_mark" ]
		then
			fileToDelete="$i"
		fi
		num_i=$((num_i+1))
	done
	# handle file names beginning with "-"
	if [ "${fileToDelete:0:1}" == "-" ]
	then
		fileToDelete_fix="./""$fileToDelete"
	else
		fileToDelete_fix="$fileToDelete"
	fi
	if [ -d "$fileToDelete_fix" ]
	then
		file_type="directory"
	else
		file_type="file"
	fi
	zenity --question \
		--text="Permanently remove ""$file_type"" \"""${fileToDelete##*/}""\"?" \
		--ok-label="OK" --cancel-label="Cancel" --no-wrap \
		--title="Confirm Deletion" --timeout="$TIMEOUT_TIME"
	confirm_choice=$?
else
	# multiple items are selected for deletion, so prompt for all at once
	zenity --question --text="Permanently remove the selected items?" \
		--ok-label="OK" --cancel-label="Cancel" --no-wrap \
		--title="Confirm Deletions" --timeout="$TIMEOUT_TIME"
	confirm_choice=$?
fi
if [ $confirm_choice == 0 ] # zenity returns 0 for 'OK'
then
	# delete files
	curr_fail_notifications=0
	max_reached_notification_printed=0
	sudo_authed=0
	sudo_prompted=0
	num_i=1
	# auth_cycle:
	#	0: user has write permission
	#	1: no write permission--authentication necessary
	for auth_cycle in $(eval echo "{0..$needWPerm}")
	do
		if [ $auth_cycle == 1 ] && [ $isSudoer == 1 ] && [ $sudo_prompted == 0 ]
		then
			# prompt for sudo authentication
			#if [ $num_files == 1 ]
			#then
			#	zenity --notification \
			#		--text="Authentication required for removing ""$file_type"" \"""${fileToDelete##*/}""\""
			#else
			#	zenity --notification --text="Authentication required for removing some items"
			#fi
			for i in $(eval echo "{1..$MAX_AUTH_TRIES}")
			do
				userpw="$(zenity --password --title="Sudo Authentication for Deletion(s)" --timeout="$TIMEOUT_TIME")"
				pw_prompt_result=$?
				if [ $pw_prompt_result != 0 ] # zenity returns 0 for 'OK'
				then
					wipe_pw
					if [ $pw_prompt_result == 5 ]
					then
						# prompt timed out, so simply exit now
						exit $confirm_choice
					fi
					break
				fi
				# try the provided password
				printf "$userpw" | sudo -S -k -v
				if [ $? == 0 ]
				then
					# password correct
					sudo_authed=1
					break
				else
					# password incorrect
					wipe_pw
					if [ $i -ge $MAX_AUTH_TRIES ]
					then
						zenity --notification --text="Authentication failed -- maximum tries reached"
					fi
				fi
			done
			sudo_prompted=1
		fi
		for i in "$@"
		do
			# skip to file arguments
			if [ "$num_i" -le "$file_arg_mark" ]
			then
				num_i=$((num_i+1))
				continue
			else
				fileToDelete="$i"
			fi
			# handle file names beginning with "-"
			if [ "${fileToDelete:0:1}" == "-" ]
			then
				fileToDelete_fix="./""$fileToDelete"
			else
				fileToDelete_fix="$fileToDelete"
			fi
			# depending on auth_cycle, delete files for which user does or does not have write permissions
			if [ $auth_cycle == 1 ]
			then
				if [ ! -e "$fileToDelete_fix" ]
				then
					continue
				elif [ -w "$fileToDelete_fix" ] ||
					[ -d "$fileToDelete_fix" ] && [ "$(ls -Aq "$fileToDelete_fix" | wc -l)" == 0 ]
				then
					continue
				elif [ $sudo_authed == 1 ]
				then
					printf "$userpw" | sudo -S -k -- rm -rf "$fileToDelete_fix"
				else
					rm -rf "$fileToDelete_fix"
				fi
			elif [ ! -w "$fileToDelete_fix" ]
			then
				if [ ! -d "$fileToDelete_fix" ]
				then
					continue
				elif [ "$(ls -Aq "$fileToDelete_fix" | wc -l)" != 0 ]
				then
					continue
				else
					rm -rf "$fileToDelete_fix"
				fi
			else
				rm -rf "$fileToDelete_fix"
			fi
			if [ $? != 0 ]
			then
				if [ $curr_fail_notifications -lt $MAX_FAIL_NOTIFICATIONS ]
				then
					if [ -d "$fileToDelete_fix" ]
					then
						file_type="directory"
					else
						file_type="file"
					fi
					zenity --notification \
						--text="Failed to remove ""$file_type"" \"""$fileToDelete""\""
					curr_fail_notifications=$((curr_fail_notifications+1))
				elif [ $max_reached_notification_printed == 0 ]
				then
					zenity --notification \
						--text="Additional files could not be removed"
					max_reached_notification_printed=1
				fi
			fi
		done
	done
fi
wipe_pw
exit $confirm_choice
