#!/bin/sh
# A plain utility for decryption 
#
#       0.0     2006-09-20      NARUMI, Hidetoshi
# $Id$

errmsg()	{ echo "$CMDNAME: $*" >&2; }
verbose()	{ [ $v_flag -eq 0 ] || echo "$CMDNAME: $*" >&2; }

# procedure:	get_path
#	search gpg/pgp command.
# arguments:	none
# variables:
# 	cmd_path	path name of the command
#
get_path()
{
	ifs=$IFS
	IFS=":"
	for d in $PATH; do
		IFS=$ifs
		# where is pgp
		exe="$d/pgp"
		if [ -f "$exe" -a -x "$exe" ]; then
			# found pgp command
			if [ -z "$pgp_path" ]; then
				# path to pgp command
				pgp_path="$exe"
				exe_path="$pgp_path"
			fi
		fi
		# where is gpg
		exe="$d/gpg"
		if [ -f "$exe" -a -x "$exe" ]; then
			# found gpg command
			if [ -z "$gpg_path" ]; then
				# path to gpg command
				gpg_path="$exe"
				exe_path="$gpg_path"
			fi
		fi
		# default path to gpg/pgp command
		if [ -z "$cmd_path" ]; then
			cmd_path="$exe_path"
		fi
	done
}

# procedure:	check_exec
#	check executable file name.
# arguments:
#	1st	-e option on command line
# variables:
# 	cmd_path	path name of the command
# status:
#		If the executable file exists, returns true. Othewise false.
#
check_exec()
{
	err=0
	exe=`expr "$1" : '-e\(.*\)'`
	if [ -f "$exe" -a -x "$exe" ]; then
		cmd_path="$exe"
		case "$exe" in
			*/[Gg][Pp][Gg]|*/[Gg][Pp][Gg].[Ee][Xx][Ee])
				;;
			*/[Pp][Gg][Pp]|*/[Pp][Gg][Pp].[Ee][Xx][Ee])
				;;
			*)
				verbose "$exe: unknown utility."
				;;
		esac
	else
		errmsg "$exe: no such file."
		err=1
	fi
	[ $err -eq 0 ]
}

# procedure:	check_name
# 	check a command name ('gpg' or 'pgp').
# arguments:
#	1st	-s option on command line
# output:
#		If matches, print command name in lowercase.
# status:
#		If matches, returns true. Othewise false.
#
check_name()
{
	typ=`expr "$1" : '-s\(.*\)'`
	echo "$typ" | awk '	BEGIN{s=1} \
			/^[Gg][Pp][Gg]$/{s=0;print tolower($1)} \
			/^[Pp][Gg][Pp]$/{s=0;print tolower($1)} \
			END{exit s}'
	err=$?
	if [ $err -ne 0 ]; then
		errmsg "-s $typ: unknown option. (-s [gpg|pgp])"
	fi
	[ $err -eq 0 ] 
}

# procedure:	check_path
# 	check a command name and the path name.
# arguments:
#	1st	command name ('gpg' or 'pgp').
# variables:
# 	cmd_path	path of the command
# status:
#		If the command exists, returns true. Othewise false.
#
check_path()
{
	err=0
	if [ X$1 = X ]; then
		err=1
	elif [ $1 = gpg ]; then
		if [ -z "$gpg_path" ]; then
			errmsg "\"$1\" not found in \$PATH."
			err=1
		else	# select gpg command
			cmd_path="$gpg_path"
		fi
	elif [ $1 = pgp ]; then
		if [ -z "$pgp_path" ]; then
			errmsg "\"$1\" not found in \$PATH."
			err=1
		else	# select pgp command
			cmd_path="$pgp_path"
		fi
	fi
	[ $err -eq 0 ]
}

# procedure:	check_file
# 	check specified file or directory name(s).
# arguments:
#	1st	file or directory name(s).
# status:
#		If matches, returns true. Othewise false.
#
check_file()
{
	err=0
	if [ -z "$1" ]; then		# null string
		err=1
	elif [ -d "$1" ]; then		# directory
		:
	elif [ -f "$1" ]; then		# regular file
		case "$1" in
			*.[Gg][Pp][Gg])
				;;
			*.[Pp][Gg][Pp])
				;;
			*)
				errmsg "$1: no gpg/pgp file.  Ignored.";
				err=1
				;;
		esac
	else
		errmsg "$1: no such file or directory.  Ignored."
		err=1
	fi
	[ $err -eq 0 ]
}

# procedure:	check_flags
# 	check and set flags
# arguments:
#	1st	flags on command line
# status:
#		If any, returns false. Othewise true.
#
check_flags()
{
	err=0
	set -- `echo "$1" | sed 's/^-//;s/\(.\)/-\1 /g'`
	for flag do
		case "$flag" in
			-h)	h_flag=1 ;;	# -h
			-i)	i_flag=0 ;;	# -i
			-u)	u_flag=1 ;;	# -u
			-v)	v_flag=1 ;;	# -v
			*)	errmsg "$flag: unknown option."; err=1 ;;
		esac
	done
	[ $err -eq 0 ]
}

# procedure:	get_opts
# 	get command line options.
# arguments:	arguments on command line
# variables:
# 	stat	1: On error. 
#		0: Othewise.
# 	args	file or directory name(s)
#
get_opts()
{
	for opt in "$@"; do
		if [ $__flag -eq 0 ]; then	# end of options
			: 
		elif check_file "$opt"; then	# file or directory
			args="$args|$opt"
			continue
		else				# ignored
			: #stat=1
			continue
		fi

		if [ $e_flag -eq 1 ]; then	# -e option
			check_exec "-e$opt"
			if [ $? -ne 0 ]; then stat=1; fi
			e_flag=0
			continue
		elif [ $s_flag -eq 1 ]; then	# -s option
			util=`check_name "-s$opt"` && check_path "$util"
			if [ $? -ne 0 ]; then stat=1; fi
			s_flag=0
			continue
		fi

		case "$opt" in
			--)		# end of options
				__flag=1
				;;
			-[hiuv])	# option flag
				check_flags "$opt"
				if [ $? -ne 0 ]; then stat=1; fi
				;;
			-e)		# -e option :-e Path/To/Command
				e_flag=1
				;;
			-e*)		# -e option :-ePath/To/Command
				check_exec "$opt"
				if [ $? -ne 0 ]; then stat=1; fi
				;;
			-s)		# -s option :-s Command
				s_flag=1
				;;
			-s*)		# -s option :-sCommand
				util=`check_name "$opt"` && check_path "$util"
				if [ $? -ne 0 ]; then stat=1; fi
				;;
			-*)		# options
				check_flags "$opt"
				if [ $? -ne 0 ]; then stat=1; fi
				;;
			*)		# file or directory
				if check_file "$opt"; then
					args="$args|$opt"
				else	# ignored
					: #stat=1
				fi
				;;
		esac
	done
	args=`echo "$args" | sed 's/||*/|/g;s/|-/|.\/-/g;s/^|//'`
}

usage()
{
	cat >&2 <<!
Usage:
    $CMDNAME [-h] [-i] [-u] [-v] [-e execpath] [-s [gpg|pgp]] [--] NAMEs

-h		Print this message.
-i		For every file, enter passphrase interactively.
		(default: enter passphrase only once and re-use it.)
-u		Search/select all encrypted files.  (default: search/select
		un-decrypted files or newer than decrypted file.)
-v		Verbose.
-e execpath	Specify path name of decryption command.
-s [gpg|pgp]	Switch decryption command.
NAMEs		Specify encrypted files(*.gpg/*.pgp) or directory names.
		If directory names are specified, search encrypted files
		(*.gpg/*.pgp) recursively.
!
}

print_var()
{
	cat >&2 <<!
Status:
	'gpg' command	$gpg_path
	'pgp' command	$pgp_path
	search/select	$u_msg
	your choice	$cmd_path
	NAMEs		$names
!
}

# procedure:	print_prompt
# 	print "Enter passphrase: "
# arguments:
#	1st	file no. of stdin
#	2nd	file no. of stdout
#	3rd	file no. of stderr
#
print_prompt()
{
	i=$1
	shift
	if [ -t $i ] ;then
		for o do
			[ ! -t $o ] && continue
			printf "\aEnter passphrase: " >&$o
			break
		done
		stty -echo <&$i
	fi
}

# procedure:	erase_prompt
# 	erase "Enter passphrase: "
# arguments:
#	1st	file no. of stdin
#	2nd	file no. of stdout
#	3rd	file no. of stderr
#
erase_prompt()
{
	i=$1
	shift
	if [ -t $i ] ;then
		stty echo <&$i
		for o do
			[ ! -t $o ] && continue
			printf "\r                 \r" >&$o
			break
		done
	fi
}

# MAIN
CMDNAME=`basename $0`

#
cygwin=0
case `uname` in
	CYGWIN*)	# using CYGWIN
		cygwin=1
		;;
	*)		# using UNIX like, maybe
		;;
esac

stat=0
__flag=0	# -- end of options
h_flag=0	# -h option (flag)
i_flag=1	# -i option (flag)
e_flag=0	# -e option
s_flag=0	# -s option
u_flag=0	# -u option (flag)
v_flag=0	# -v option (flag)

get_path

get_opts "$@"

exe=`basename "$cmd_path"`
case "$exe" in
	[Gg][Pp][Gg]|[Gg][Pp][Gg].[Ee][Xx][Ee])
		util="gpg"
		cmd_opts="--batch --no-tty --passphrase-fd 0 --yes"

		;;
	[Pp][Gg][Pp]|[Pp][Gg][Pp].[Ee][Xx][Ee])
		util="pgp"
		cmd_opts="+batchmode +force"
		export PGPPASS
		;;
	.)
		errmsg "gpg/pgp command not found in \$PATH."
		stat=1
		;;
	*)
		util="$exe"
		errmsg "$util: unknown decryption utility."
		stat=1
	;;
esac

if [ $h_flag -eq 1 -o $stat -eq 1 ]; then
	names=`echo "$args" | sed 's/|/" "/g;s/^/"/;s/$/"/;s/""//g'`
	if [ $u_flag -eq 0 ]; then
		u_msg="un-decrypted files only" 
	else
		u_msg="all encrypted files" 
	fi
	usage
	print_var
	exit $stat
fi

exec 3<&0
LST=/var/tmp/$CMDNAME.lst.$$
TMP=/var/tmp/$CMDNAME.tmp.$$
trap "[ ! -t 3 ] || stty echo <&3 ; rm -f $LST $TMP" 0
trap "exit 1" 1 2 3

ifs=$IFS
IFS="|";
for name in $args; do
	IFS=$ifs
	if [ -d "$name" ]; then
		# in case of directory, search *.gpg/*.pgp files recursively
		find "$name" -type f \( -name '*.[Gg][Pp][Gg]' -o -name '*.[Pp][Gg][Pp]' \) -print | sort
	elif [ -f "$name" ]; then
		# in case of file
		echo "$name"
	fi
done >$LST

while read enc; do
	# remove extension
	case "$enc" in
		*.[Gg][Pp][Gg])
			dec=`echo "$enc" | sed 's/\.[Gg][Pp][Gg]$//'`
			;;
		*.[Pp][Gg][Pp])
			dec=`echo "$enc" | sed 's/\.[Pp][Gg][Pp]$//'`
			;;
		*)
			continue
			;;
	esac

	if [ $cygwin -ne 0 ]; then		# in case of CYGWIN
		ENC=`cygpath -w "$enc"`
		DEC=`cygpath -w "$dec"`
	else
		ENC="$enc"
		DEC="$dec"
	fi

	# check output file
	if [ -f "$dec" ]; then			# regular file
		if [ $u_flag -ne 0 ]; then
			:
		elif /usr/bin/test "$dec" -nt "$enc"; then
			verbose "\"$enc\" has been decrypted already.  Skipped."
			continue
		fi
		verbose "\"$enc\" has been decrypted already."
		touch -r "$dec" $TMP
	elif /usr/bin/test -e "$dec"; then	# maybe directory
		errmsg "$enc: \"$dec\" exists already.  Skipped."
		ls -ld "$dec" | sed 's/^/	/' >&2
		continue
	else
		touch -r "$enc" $TMP
	fi

	if [ $i_flag -eq 1 ]; then	# get passphrase (batch mode)
		print_prompt 3 1 2
		PHRASE=`/usr/bin/awk '{print; exit}' <&3`
		erase_prompt 3 1 2
		i_flag=2
	fi

	# decrypting
	echo "$ENC"
	if [ $i_flag -eq 0 ]; then	# get passphrase (interactive mode)
		print_prompt 3 1 2
		PHRASE=`/usr/bin/awk '{print; exit}' <&3`
		erase_prompt 3 1 2
	fi

	if [ "$util" = "gpg" ]; then	# use gpg
		"$cmd_path" $cmd_opts -o "$DEC" "$ENC" <<!
$PHRASE
!
		stat=$?
	elif [ "$util" = "pgp" ]; then	# use pgp
		PGPPASS="$PHRASE"
		"$cmd_path" $cmd_opts -o "$DEC" "$ENC"
		stat=$?
		if [ $stat -eq 1 ]; then
			stat=0		# no signatuer on the file
		fi
		PGPPASS=
	else
		errmsg "$util: unknown decryption utility."
		exit 1
	fi

	if [ $stat -ne 0 ]; then
		echo "$util: failed in decryption."
	elif [ ! -f "$dec" ]; then
		echo "$util: failed in decryption."
	elif /usr/bin/test "$dec" -nt $TMP; then
		echo "$util: processed successfully."
	else
		echo "$util: failed in decryption."
	fi
	rm -f $TMP
	echo
done <$LST

rm -f $LST

exit 0
