#!/bin/ash
# Rox desktop drive icon handler
# (C) James Budiono 2012, 2013, 2015, 2016, 2018, 2020, 2023
# License: GNU GPL Version 3 or later
#
# called by Rox, parameter $1 comes either from Args parameter from PinboardAdd,
# or from "option" attribute from AppInfo.xml
# Please see fatdog-drive-icon-udev-handler.sh for details.
#
# Supported parameters:
# - click $DEVNAME		- standard left-click, mount/umount device
# - devmount/labelmount/uuidmount $DEVNAME - mount device by volume, label, or uuid
# - umount $DEVNAME		- umount device
# - pmount				- launch drive mounter
# - explore $DEVNAME	- mount and open Rox folder (optical disc)
# - eject $DEVNAME		- close rox, unmount and eject (optical disc)
# - rip2iso $DEVNAME    - rip optical disk to ISO file (optical disc)
# - safe-remove $DEVNAME - prepare device for safe removal
# - defaultmediaplayer $DEVNAME - start defaultmediaplayer on the device
# - fsck $DEVNAME       - run fsck (or ntfscheck or dosfsck) on the device
# - usage				- launch disk usage utility
# - redraw				- redraw all icons
#
# $DEVNAME is the complete device name including /dev
#
# Note: this is a simple mount/unmount handler, no support for autorun etc.
# FILEMANAGER - if supplied, this will be called for open/close folder instead of rox

### configurations
. $BOOTSTATE_PATH
PINBOARD_FILE=$HOME/.config/rox.sourceforge.net/ROX-Filer/PuppyPin
export PATH=$PATH:/lib64/udev:/lib/udev:/libexec
EVENTMANAGER_CONFIG=/etc/eventmanager
. $EVENTMANAGER_CONFIG # hotspot dimentions, UNPATCHED_ROX
DEFAULT_CLICK_ACTION=${DEFAULT_CLICK_ACTION:-devmount} # devmount, labelmount, uuidmount, etc
LABEL_MOUNT_ROOT=${LABEL_MOUNT_ROOT:-/media}
UUID_MOUNT_ROOT=${LABEL_MOUNT_ROOT:-/media}


################# non-root helpers ###############

# $1-devname
get_mount_point() {
	local dmdev
	dmdev=$(ls -l /dev/mapper/* 2> /dev/null | awk -v dmdev=${1##*-} '$6 == dmdev {print $10; exit; }')
	[ "$dmdev" ] || dmdev=$(ls -l /dev/mapper/* 2> /dev/null | awk -v dmdev="/${1##*/}$" '$NF ~ dmdev { print $(NF-2); exit; }')	
	awk -v dev=$1 -v dmdev=$dmdev '
$1 == dev || $1 == dmdev {
	#print $2; exit;
	mnt[count++]=$2
}
END {
	if (count) {
		# first attempt - find those that starts with /mnt/
		for (i=0;i<count;i++) {
			if (mnt[i] ~ /^\/mnt\//) {
				print mnt[i]; exit;
			}
		}
		# 2nd attempt - find those that starts with /media/
		for (i=0;i<count;i++) {
			if (mnt[i] ~ /^\/media\//) {
				print mnt[i]; exit;
			}
		}
		# 3rd attempt - find those that dont start with /aufs
		for (i=0;i<count;i++) {
			if (mnt[i] ~ /^\/aufs\//) continue;	
			print  mnt[i]; exit;
		}
		# last attempt - find whatever mountpoint there is
		print mnt[0]
	}
}' /proc/mounts	
}

# $1-devname (without /dev prefix)
get_crypt_point() {
	local dmdev
	dmdev=$(find /sys/devices/virtual/block/dm-*/slaves -type l -name "$1" 2>/dev/null | grep -wo 'dm-[0-9]\+' | grep -vw "$1")
	[ "$dmdev" ] && cat /sys/devices/virtual/block/$dmdev/dm/name
}

# $1-mountpoint
open_folder_in_filemanager() {
	if [ -z "$FILEMANAGER" ]; then
		rox -d "$1" -x "$1"
	else
		$FILEMANAGER open "$1"
	fi
}

# $1-mountpoint
close_folder_in_filemanager() {
	if [ -z "$FILEMANAGER" ]; then	
		rox -D "$1"
	else
		$FILEMANAGER close "$1"
	fi
}

### if mounted, check if click happens on the "x" sign, means user wants to unmount
# returns true - "x" clicked
check_for_umount() {
	set --  $ROX_PINBOARD_CLICK # $1-x, $2-y, $3-dx, $-dy
	# click in icon coordinates x,y; icon dimensions w,h (not used)
	local x=$1 y=$2 wi=$3 hi=$4
	local xh yh   wh hh ph   wb hb   xc yc
	
	[ -z $x ] && return 1 # no parameter, not pinboard, leave immediately	
	#Xdialog --msgbox "$x $y $wi $hi" 0 0	
	# pass in click coordinates, hotspot coordinates & hotspot dimensions
	awk -v x=$x -v y=$y \
	    -v xh=${HOTSPOT_X:-0} -v yh=${HOTSPOT_Y:-0} \
		-v wh=${HOTSPOT_W:-12} -v hh=${HOTSPOT_H:-12} -v ph=${HOTSPOT_P:-2} \
	'BEGIN {
		# hotspot semi-dimensions a.k.a. bulls-eye
		wb = (wh + ph) /2; hb = (hh + ph) /2;
		# click in hotspot-center coordinates
		xc = x - xh - wb; yc = y - yh - hb;
		# return 0 if hotspot-centered click is inside bulls-eye
		exit !( (xc < 0 ? -(xc) : xc) <= wb && (yc < 0 ? -(yc) : yc) <= hb )
	}'
	return # exit code from awk above
}

### override these functions with unpatched-ROX version when asked
if [ "$UNPATCHED_ROX" ]; then
### if mounted, check if click happens on the "x" sign, means user wants to unmount
# true - "x" clicked
check_for_umount() {
	local xy x y 
	
	# find the location of the click, relative to our "x" sign
	xy=$(grep $(dirname $0) $PINBOARD_FILE | xml2 2> /dev/null | sed -n '/@x/ {s/.*=//; s/$/-/; p}; /@y/ {s/.*=//; p}')
	set -- $(getcurpos)
	x=$(($1 - ${xy%%-*} + 24)) y=$(($2 - ${xy##*-} + 30)) # empirical based on 48x48 drive icons
	
	# if within our "x" range, unmount (checks based 12x12 "close" grid - see fatdog-mount-wrapper.sh)
	[ $x -le 14 -a $y -le 14 ] && return 0
	return 1
}
fi

################# root helpers ###############

### close Rox and un-mount
# $1-devname, $2-mount point
do_umount() {
	local dmdev
	
	# do not unmount devices/images mounted in /aufs (devsave, devbase, pup_save)
	dmdev="$(cat /sys/devices/virtual/block/${1##*/}/dm/name 2>/dev/null)"
	if grep -qE "^${1} ${AUFS_ROOT}|^/dev/mapper/${dmdev} ${AUFS_ROOT}" /proc/mounts ||
		[ "$2" != "${2#${AUFS_ROOT}*}" ]
	then
		Xdialog --title "Error" --infobox "${1##*/} is used by system, it cannot be un-mounted." 0 0 10000 
		return
	fi
	
	# umount - escalate to root if we are not root
	if [ $(id -u) -ne 0 ]; then	
		gtksu "Unmounting ${1##*/}" umount "$2"
	else 
		umount "$2"
	fi
	
	# prompt user if fails
	if [ $? -ne 0 ]; then
		if Xdialog --title "Error" --yesno "Unable to unmount ${1##*/}, it is currently used by the following process:
$(fuser -m $2 | xargs ps -o cmd -p | sed 's/CMD//')\n\n
Do you want me to stop them and try again?" 0 0; then
			fuser -m -k $2
			do_umount $1 "$2"
		fi
	else
		# close all rox folders, but only if unmounting succeeded
		close_folder_in_filemanager "$2"
		# dm-* drive icons don't seem to refresh for non-root users
		[ $(id -u) -ne 0 ] && fatdog-drive-icon-refresh-icon.sh $1
	fi
}

# $1-devname $2-cryptpoint
do_close_luks() {
	local dmname dmname_msg

	if [ $(id -u) -ne 0 ]; then	
		gtksu "Closing ${1##*/}" cryptsetup close "$2"
	else
		cryptsetup close "$2"
	fi

	if [ $? -ne 0 ]; then
		
		if dmname=$(find /sys/devices/virtual/block/dm-*/slaves -type l -name "${1##*/}" 2>/dev/null | grep -wo 'dm-[0-9]\+' | grep -vw "${1##*/}"); then
			[ "$(get_mount_point "/dev/${dmname}")" ] && dmname_msg="\nThe corresponding $dmname device is still mounted."
		fi
		Xdialog --title "Error" --infobox "Failed to close ${1##*/}.${dmname_msg}" 0 0 10000
	else
		fatdog-drive-icon-refresh-icon.sh $1
	fi
}

### mount (if not already mounted), and open Rox - given device and mountpoint
# $1-devname, $2-mount point
do_mount() {
	if /usr/sbin/fatdog-drive-icon-mount-helper.sh "$1" "$2"; then open_folder_in_filemanager "$2"
	else Xdialog --title "Error" --infobox "Cannot mount ${1##*/}." 0 0 10000
	fi
}

# $1-devname, $2-crypt point
do_open_luks() {
	local result
	if ! result="$(/usr/sbin/fatdog-drive-icon-luks-helper.sh "$1" "$2")"; then
		Xdialog --title "Error" --infobox "Cannot open ${1##*/}.\n$result" 0 0 10000
	fi
}

### mount (if not already mounted), and open Rox - by devname, label, uuid, etc
# $1-devname
do_mount_by_devname() {
	do_mount $1 /mnt/${1##*/} 
}
# $1-devname $2-bypath $3-mountroot
do_mount_by_path() {
	# find the volume label
	local p pp done=
	if [ -d $2 ]; then
		for p in $2/*; do
			pp=$(readlink -f "$p")
			if [ $pp = $1 ]; then
				# label is always hex-encoded, see udev test-builtin blkid ID_FS_LABEL_ENC
				# others are plain ascii, so they are safe
				#Xdialog --msgbox "$p" 0 0
				p="$3/$(echo "${p##${2}/}" | sed 's|\\x[0-9a-f][0-9a-f]||g')"
				if ! mountpoint -q "$p"; then
					do_mount $1 "$p"
					done=ok
				else
					Xdialog --title "Error" --infobox "Mountpoint $p is already used." 0 0 10000
				fi
			fi
		done
	fi
	if [ -z $done ]; then
		Xdialog --title "Error" --infobox "${1##*/} does not have ${2##*-}." 0 0 10000
	fi
}

### play media, if possible. Does not return unless it can't play the media.
# $1-devname $2-mounted (blank if not mounted)
play_media() {
	local mountpoint=${2:-/mnt/${1##*/}}
	echo $mountpoint
	case $(disktype $1) in 
		*"Audio track"*) 
			if [ -x /usr/local/bin/defaultcdplayer ];then 
				exec defaultcdplayer $1
			else
				exec defaultmediaplayer cdda://$1
			fi
			;;
		
		*"CDI_VCD"*) 
			defaultmediaplayer vcd://$1
			;;
		
		*)  # could be dvd, bluray, or data - mount to check
			if [ "$2" ] || /usr/sbin/fatdog-drive-icon-mount-helper.sh $1 $mountpoint; then
				if [ -d $mountpoint/BDMV -o -d $mountpoint/bdmv ]; then				# bluray
					grep -q 'vlc' /etc/defaultprograms && exec defaultmediaplayer --fullscreen --disc-caching=1000 bluray:///$mountpoint
					grep -q 'xine' /etc/defaultprograms && exec defaultmediaplayer bluray:///$mountpoint
					
				elif [ -d $mountpoint/VIDEO_TS -o -d $mountpoint/AUDIO_TS ] ||
				     [ -d $mountpoint/video_ts -o -d $mountpoint/audio_ts ]; then	# video dvd
					[ -x /usr/local/bin/defaultdvdplayer ] && exec defaultdvdplayer $1
					grep -q 'mplayer' /etc/defaultprograms && exec defaultmediaplayer dvdnav://$1
					grep -q 'vlc' /etc/defaultprograms && exec defaultmediaplayer --disc-caching=1000 dvd://$1 
					grep -q 'xine' /etc/defaultprograms && exec defaultmediaplayer dvd://$1 
				else
					open_folder_in_filemanager $mountpoint		# everything else - data
				fi
			fi
	esac
}


################# main ##################
#Xdialog --infobox "$# $@" 0 0 10000
[ $# -eq 1 ] && set -- $1	# rox combines all params into one string, split it
if [ $# -eq 0 ]; then		# no param, assumed clicked outside pinboard
	p=${0%/AppRun}
	set -- click /dev/${p##*/}
fi

# $1 - action, $2-devname
if [ $2 ]; then
	mountpoint=$(get_mount_point $2)
	#fstype=$(guess_fstype $2)	# doesn't work for non-root users!!!
	fstype=$(lsblk -n -f -r -d -o FSTYPE $2 | head -n 1)

	if [ "$fstype" = 'crypto_LUKS' ]; then
		cryptpoint="$(get_crypt_point "${2##*/}")"
	fi
fi

case $1 in
	click|devmount|labelmount|uuidmount)
		if [ "$cryptpoint" ]; then
			# already unlocked (LUKS)
			if check_for_umount; then
				do_close_luks "$2" "$cryptpoint"
			else
				Xdialog --title "Info" --infobox "${2##*/} is already open." 0 0 10000
			fi
		elif [ "$mountpoint" ]; then
			# already mounted
			if check_for_umount; then do_umount $2 "$mountpoint"
			else
				case $2 in
					/dev/sr[0-9]*) play_media $2 "$mountpoint" ;;
					*) open_folder_in_filemanager "$mountpoint" ;;
				esac
			fi
		else
			# unmounted, so let's mount
			case $2 in
				/dev/sr[0-9]*) play_media $2 ;;
				*)	[ $1 = click ] && action=$DEFAULT_CLICK_ACTION || action=$1
					[ "$fstype" = 'crypto_LUKS' ] && action='luksopen'
					case "$action" in
						""|devmount)   do_mount_by_devname  $2 ;;
						labelmount) do_mount_by_path $2 /dev/disk/by-label "$LABEL_MOUNT_ROOT" ;;
						uuidmount)  do_mount_by_path $2 /dev/disk/by-uuid "$UUID_MOUNT_ROOT"   ;;
						luksopen)	do_open_luks $2 "dmcrypt_${2##*/}" ;;
					esac ;;
			esac
		fi
		;;
		
	umount) 
		if [ "$cryptpoint" ]; then
			do_close_luks $2 "$cryptpoint"
		elif [ "$mountpoint" ]; then
			do_umount $2 "$mountpoint"
		fi
		;;

	pmount)
		exec /usr/sbin/fatdog-drive-mounter.sh
		;;
	
	### extra options for optical disk
	explore) 
		if [ "$mountpoint" ]; then open_folder_in_filemanager "$mountpoint"
		else do_mount_by_devname $2 
		fi
		;;
		
	eject)
		[ "$mountpoint" ] && do_umount $2 "$mountpoint"
		[ $(id -u) -ne 0 ] && exec gtksu "Ejecting ${2##*/}" /lib64/udev/cdrom_id --eject-media $2
		cdrom_id --eject-media $2 > /dev/null
		;;

	rip2iso) [ $(id -u) -ne 0 ] && exec gtksu "Ripping ${2##*/}" rip2iso $2
		rip2iso $2
		;;
	
	defaultmediaplayer)
		play_media $2
		Xdialog --title "Error" --infobox "Not a playable media." 0 0 10000
		;;
		
	safe-remove)
		if [ "$cryptpoint" ]; then
			do_close_luks $2 "$cryptpoint"
		elif [ "$mountpoint" ]; then
			do_umount $2 "$mountpoint"
		fi

		if case ${2##*/} in
			*mmc*) mmc-safe-remove ${2##*/} ;;
			*) usb-safe-remove ${2##*/} ;;
		esac; then 
			Xdialog --title "Success!" --infobox "${2##*/} is now safe for removal." 0 0 10000
		else
			Xdialog --title "Problem"  --infobox "${2##*/} cannot be removed because it is still in use." 0 0 10000
		fi
		;;
		
	fsck)
		if [ "$mountpoint" ]; then
			Xdialog --title "Problem" --infobox "Cannot check integrity when ${2##*/} is mounted. Please un-mount it first." 0 0 10000
		else
			[ $(id -u) -ne 0 ] && exec gtksu "Check ${2##*/} integrity" "$0" $1 $2
			tmpfifo=$(mktemp -p /tmp fatdog-drive-icon-fsck.XXXXXXXX)
			#mkfifo $tmpfifo/fifo # doesn't work, will buffer
							
			case "$fstype" in
				ext*) fsck -y $2 1>$tmpfifo 2>&1 & ;;
				vfat*) dosfsck -a $2 1>$tmpfifo 2>&1 & ;;
				ntfs) ntfsfix $2 1>$tmpfifo 2>&1 & ;;
				f2fs) fsck.f2fs -a $2 1>$tmpfifo 2>&1 & ;;
				exfat) fsck.exfat $2 1>$tmpfifo 2>&1 & ;;
				btrfs) btrfs check --repair $2 1>$tmpfifo 2>&1 & ;;
				crypto_LUKS) cryptsetup -v repair $2 1>$tmpfifo 2>&1 & ;;
				*) echo "I don't know how to handle filesystem $fstype on ${2##*/}, sorry." > $tmpfifo & ;;
			esac
			XPID=$!
			Xdialog --title "Checking ${2##*/}" --backtitle "Click OK when finished." --no-cancel --tailbox $tmpfifo 20 80
			kill $XPID
			rm -rf $tmpfifo
		fi
		;;
		
	usage)
		exec /usr/sbin/fatdog-disk-usage.sh
		;;
		
	redraw)
		exec /usr/sbin/fatdog-drive-icon-redraw-icons.sh
		;;
esac
