zpass: integrate remote

This commit is contained in:
zawz 2020-08-21 11:31:00 +02:00
parent bb003b2200
commit 6551e574df
2 changed files with 90 additions and 242 deletions

View file

@ -1,203 +0,0 @@
#!/bin/sh
fname=$(basename "$0")
usage()
{
echo "$fname <operation>
Call zpass using a remote machine
[Global Operations]:
list-files List eligible files in data path. Shortcut 'lsf'
cache-clear Delete all cached keys. Shortcut 'cc'
help Display help
[File Operations]:
create Create a file or change key
cached Returns wether or not a key is currently cached. Shortcut 'ch'
ls [path] List contents at given path
tree List all contents
get <path> Get the value of target
copy <path> Copy the target value to clipboard. Shortcut 'x'
set <path> <value> Set the value of target
new <path> [length] Generate a random password at target
rm <path> Delete the target
rm-file Remove the current file. Shortcut 'rmf'
[Config]:
*Variable* *Default value* *Description*
----------------------------------------------------------------------------
CONFIGFILE '\$XDG_CONFIG_HOME/zpass/r-defaut.conf' Path to the config file to load
ZPASS_REMOTE_ADDR Key to use for encrypting/decrypting files
ZPASS_REMOTE_PORT '22' Port
ZPASS_REMOTE_COMMAND 'zpass' Command to use on remote host
ZPASS_SSH_ID Private SSH key to use
ZPASS_KEY Key to use for encrypting/decrypting files
ZPASS_CLIPBOARD_TIME '30' Time until clipboard gets cleared after copy, in seconds
ZPASS_UNK_OP_CALL 'copy' Operation to call on unrecognized first argument
All operations can be shortened to their first char unless specified
Unknown first argument will perform the operation described in 'ZPASS_UNK_OP_CALL' on that argument
"
}
# XDG config
datapath="$HOME/.local/share/zpass"
cachepath="$HOME/.cache/zpass"
configpath="$HOME/.config/zpass"
[ -n "$XDG_DATA_HOME" ] && datapath="$XDG_DATA_HOME/zpass"
[ -n "$XDG_CONFIG_HOME" ] && configpath="$XDG_CONFIG_HOME/zpass"
[ -n "$XDG_CACHE_HOME" ] && cachepath="$XDG_CACHE_HOME/zpass"
[ -z "$CONFIGFILE" ] && CONFIGFILE="$configpath/r-default.conf"
# stash env config
ZPASS_CLIPBOARD_TIME_T=$ZPASS_CLIPBOARD_TIME
ZPASS_UNK_OP_CALL_T=$ZPASS_UNK_OP_CALL
ZPASS_REMOTE_ADDR_T=$ZPASS_REMOTE_ADDR
ZPASS_REMOTE_PORT_T=$ZPASS_REMOTE_PORT
ZPASS_REMOTE_COMMAND_T=$ZPASS_REMOTE_COMMAND
ZPASS_SSH_ID_T=$ZPASS_SSH_ID
# load config file
[ -f "$CONFIGFILE" ] && { . "$CONFIGFILE" || exit $? ; }
# insert env config
[ -n "$ZPASS_CLIPBOARD_TIME_T" ] && ZPASS_CLIPBOARD_TIME=$ZPASS_CLIPBOARD_TIME_T
[ -n "$ZPASS_UNK_OP_CALL_T" ] && ZPASS_UNK_OP_CALL=$ZPASS_UNK_OP_CALL_T
[ -n "$ZPASS_REMOTE_ADDR_T" ] && ZPASS_REMOTE_ADDR=$ZPASS_REMOTE_ADDR_T
[ -n "$ZPASS_REMOTE_PORT_T" ] && ZPASS_REMOTE_PORT=$ZPASS_REMOTE_PORT_T
[ -n "$ZPASS_REMOTE_COMMAND_T" ] && ZPASS_REMOTE_COMMAND=$ZPASS_REMOTE_COMMAND_T
[ -n "$ZPASS_SSH_ID_T" ] && ZPASS_SSH_ID=$ZPASS_SSH_ID_T
# default ZPASS config
[ -z "$ZPASS_CLIPBOARD_TIME" ] && ZPASS_CLIPBOARD_TIME=30 # in seconds
[ -z "$ZPASS_UNK_OP_CALL" ] && ZPASS_UNK_OP_CALL=copy
[ -z "$ZPASS_REMOTE_PORT" ] && ZPASS_REMOTE_PORT=22
[ -z "$ZPASS_REMOTE_COMMAND" ] && ZPASS_REMOTE_COMMAND=zpass
file="$datapath/$ZPASS_FILE$ZPASS_EXTENSION"
mkdir -p "$datapath" 2>/dev/null || error 1 "Could not create '$datapath'"
mkdir -p "$cachepath" 2>/dev/null && chmod -R go-rwx "$cachepath" 2>/dev/null
escape_chars() {
echo "$*" | sed 's|\.|\\\.|g;s|/|\\/|g'
}
error(){
ret=$1 && shift 1 && echo $* >&2 && exit $ret
}
# $1 = delay in sec
clipboard_clear() {
if [ -n "$1" ]
then
for I in $(screen -ls | grep "$fname"_clipboard | awk '{print $1}')
do
screen -S "$I" -X stuff "^C"
done
screen -dmS "$fname"_clipboard sh -c "sleep $1 ; printf '' | xclip -selection clipboard ; sleep 1" # put empty string into clipboard
else
xclip -selection clipboard < /dev/null
fi
}
# $1 = prompt message
prompt_password() {
if [ -n "$DISPLAY" ]
then
if which kdialog >/dev/null 2>&2
then kdialog --title "zpass" --password "$1" 2>/dev/null
else zenity --title "zpass" --password 2>/dev/null
fi
else
printf "%s:" "$1" >&2
stty -echo
read -r PASSWORD || return $?
stty echo
printf "\n" >&2
echo $PASSWORD
fi
}
# $1 = message
error_dialog() {
if which kdialog >/dev/null 2>&2
then kdialog --title "zpass" --error "$1" 2>/dev/null
else zenity --title "zpass" --error --text="$1" 2>/dev/null
fi
}
new_key_with_confirm()
{
[ -n "$ZPASS_KEY" ] && echo "$ZPASS_KEY" && return 0
pass1=1
pass2=2
while [ "$pass1" != "$pass2" ]
do
pass1=$(prompt_password "Enter new key") || error 100 "Cancelled"
pass2=$(prompt_password "Confirm key") || error 100 "Cancelled"
[ "$pass1" != "$pass2" ] && error_dialog "Passwords do not match.\nTry again"
done
echo "$pass1"
}
# $1 = prompt message
get_key() {
message="Enter key"
[ -n "$1" ] && message="$1"
key=$(prompt_password "$message") || return $?
echo "$key"
}
copy()
{
{ keyed_remote g "$@" || return $? ; } | tr -d '\n' | xclip -selection clipboard && clipboard_clear "$ZPASS_CLIPBOARD_TIME"
}
[ -z "$1" ] && usage && return 1
keyed_confirm_remote()
{
new_key=$(new_key_with_confirm)
{
if ! remote ch
then
[ -z "$ZPASS_KEY" ] && { remote ch || ZPASS_KEY=$(get_key) ; }
echo "$ZPASS_KEY"
fi
echo "$new_key"
echo "$new_key"
} | remote "$@"
}
keyed_remote()
{
[ -z "$ZPASS_KEY" ] && { remote ch || ZPASS_KEY=$(get_key) ; }
echo "$ZPASS_KEY" | remote "$@"
}
remote()
{
if [ -n "$ZPASS_SSH_ID" ]
then
ssh "$ZPASS_REMOTE_ADDR" -i "$ZPASS_SSH_ID" -p "$ZPASS_REMOTE_PORT" $ZPASS_REMOTE_COMMAND "$@"
else
ssh "$ZPASS_REMOTE_ADDR" -p "$ZPASS_REMOTE_PORT" $ZPASS_REMOTE_COMMAND "$@"
fi
}
case $1 in
lsf|list-files) remote "$@" ;;
rmf|rm-file) remote "$@" ;;
cc|cache-clear) remote "$@" ;;
ch|cached) remote "$@" ;;
c|create) keyed_confirm_remote "$@" ;;
l|ls|list) keyed_remote "$@" ;;
t|tree) keyed_remote "$@" ;;
g|get) keyed_remote "$@" ;;
s|set) keyed_remote "$@" ;;
n|new) keyed_remote "$@" ;;
r|rm) keyed_remote "$@" ;;
x|copy|clipboard) copy "$2" ;;
-h|h|help) usage ;;
*) [ -n "$ZPASS_UNK_OP_CALL" ] && "$0" $ZPASS_UNK_OP_CALL "$@" ;;
esac

129
zpass/zpass Executable file → Normal file
View file

@ -19,6 +19,7 @@ usage()
rm <path> Delete the target rm <path> Delete the target
rm-file Remove the current file. Shortcut 'rmf' rm-file Remove the current file. Shortcut 'rmf'
cached Returns wether or not a key is currently cached. Shortcut 'ch' cached Returns wether or not a key is currently cached. Shortcut 'ch'
rm-cache Delete the cached key for this file. Shortcut 'rmc'
[Config]: [Config]:
*Variable* *Default value* *Description* *Variable* *Default value* *Description*
@ -28,9 +29,12 @@ usage()
ZPASS_CACHE_PATH '\$XDG_CACHE_HOME/zpass' Path used for caching keys ZPASS_CACHE_PATH '\$XDG_CACHE_HOME/zpass' Path used for caching keys
ZPASS_FILE 'default' File to use for operations ZPASS_FILE 'default' File to use for operations
ZPASS_KEY Key to use for encrypting/decrypting files ZPASS_KEY Key to use for encrypting/decrypting files
ZPASS_KEY_CACHE_TIME '1' Time a key stays in cache for decrypting, in minutes ZPASS_KEY_CACHE_TIME '60' Time a key stays in cache for decrypting, in seconds
ZPASS_CLIPBOARD_TIME '30' Time until clipboard gets cleared after copy, in seconds ZPASS_CLIPBOARD_TIME '30' Time until clipboard gets cleared after copy, in seconds
ZPASS_UNK_OP_CALL 'copy' Operation to call on unrecognized first argument ZPASS_UNK_OP_CALL 'copy' Operation to call on unrecognized first argument
ZPASS_REMOTE_ADDR SSH server the file is on
ZPASS_REMOTE_PORT '22' SSH server port
ZPASS_SSH_ID SSH private key to use
All operations can be shortened to their first char unless specified All operations can be shortened to their first char unless specified
Unknown first argument will perform the operation described in 'ZPASS_UNK_OP_CALL' on that argument Unknown first argument will perform the operation described in 'ZPASS_UNK_OP_CALL' on that argument
@ -38,34 +42,30 @@ Unknown first argument will perform the operation described in 'ZPASS_UNK_OP_CAL
} }
# XDG config # XDG config
datapath="$HOME/.local/share/zpass" datapath="~/.local/share/zpass"
cachepath="$HOME/.cache/zpass" cachepath="~/.cache/zpass"
configpath="$HOME/.config/zpass" configpath="~/.config/zpass"
[ -n "$XDG_DATA_HOME" ] && datapath="$XDG_DATA_HOME/zpass" [ -n "$XDG_DATA_HOME" ] && datapath="$XDG_DATA_HOME/zpass"
[ -n "$XDG_CONFIG_HOME" ] && configpath="$XDG_CONFIG_HOME/zpass" [ -n "$XDG_CONFIG_HOME" ] && configpath="$XDG_CONFIG_HOME/zpass"
[ -n "$XDG_CACHE_HOME" ] && cachepath="$XDG_CACHE_HOME/zpass" [ -n "$XDG_CACHE_HOME" ] && cachepath="$XDG_CACHE_HOME/zpass"
[ -z "$CONFIGFILE" ] && CONFIGFILE="$configpath/default.conf" [ -z "$CONFIGFILE" ] && CONFIGFILE="$configpath/default.conf"
randalnum() {
tr -cd '[a-zA-Z]' < /dev/urandom | head -c $1
}
[ -z "$TMPDIR" ] && TMPDIR=/tmp
# stash env config # stash env config
ZPASS_PATH_T=$ZPASS_PATH tmpenv="$TMPDIR/zpassenv_$(randalnum 5)"
ZPASS_CACHE_PATH_T=$ZPASS_CACHE_PATH env | grep '^ZPASS_.*=' | sed "s/'/'\\\''/g;s/=/='/;s/$/'/g" > "$tmpenv"
ZPASS_FILE_T=$ZPASS_FILE
ZPASS_EXTENSION_T=$ZPASS_EXTENSION
ZPASS_KEY_CACHE_TIME_T=$ZPASS_KEY_CACHE_TIME
ZPASS_CLIPBOARD_TIME_T=$ZPASS_CLIPBOARD_TIME
ZPASS_UNK_OP_CALL_T=$ZPASS_UNK_OP_CALL
# load config file # load config file
[ -f "$CONFIGFILE" ] && { . "$CONFIGFILE" || exit $? ; } [ -f "$CONFIGFILE" ] && { . "$CONFIGFILE" || exit $? ; }
# insert env config . "$tmpenv" || exit $?
[ -n "$ZPASS_PATH_T" ] && ZPASS_PATH=$ZPASS_PATH_T rm -f "$tmpenv" 2>/dev/null
[ -n "$ZPASS_CACHE_PATH_T" ] && ZPASS_CACHE_PATH=$ZPASS_CACHE_PATH_T
[ -n "$ZPASS_FILE_T" ] && ZPASS_FILE=$ZPASS_FILE_T
[ -n "$ZPASS_EXTENSION_T" ] && ZPASS_EXTENSION=$ZPASS_EXTENSION_T
[ -n "$ZPASS_KEY_CACHE_TIME_T" ] && ZPASS_KEY_CACHE_TIME=$ZPASS_KEY_CACHE_TIME_T
[ -n "$ZPASS_CLIPBOARD_TIME_T" ] && ZPASS_CLIPBOARD_TIME=$ZPASS_CLIPBOARD_TIME_T
[ -n "$ZPASS_UNK_OP_CALL_T" ] && ZPASS_UNK_OP_CALL=$ZPASS_UNK_OP_CALL_T
# resolve zpass_path # resolve zpass_path
[ -n "$ZPASS_PATH" ] && datapath="$ZPASS_PATH" [ -n "$ZPASS_PATH" ] && datapath="$ZPASS_PATH"
@ -74,15 +74,16 @@ ZPASS_UNK_OP_CALL_T=$ZPASS_UNK_OP_CALL
# default ZPASS config # default ZPASS config
[ -z "$ZPASS_FILE" ] && ZPASS_FILE=default [ -z "$ZPASS_FILE" ] && ZPASS_FILE=default
[ -z "$ZPASS_EXTENSION" ] && ZPASS_EXTENSION=.tar.gpg [ -z "$ZPASS_EXTENSION" ] && ZPASS_EXTENSION=.tar.gpg
[ -z "$ZPASS_KEY_CACHE_TIME" ] && ZPASS_KEY_CACHE_TIME=1 # in minutes [ -z "$ZPASS_KEY_CACHE_TIME" ] && ZPASS_KEY_CACHE_TIME=60 # in seconds
[ -z "$ZPASS_CLIPBOARD_TIME" ] && ZPASS_CLIPBOARD_TIME=30 # in seconds [ -z "$ZPASS_CLIPBOARD_TIME" ] && ZPASS_CLIPBOARD_TIME=30 # in seconds
[ -z "$ZPASS_UNK_OP_CALL" ] && ZPASS_UNK_OP_CALL=copy [ -z "$ZPASS_UNK_OP_CALL" ] && ZPASS_UNK_OP_CALL=copy
[ -z "$TMPDIR" ] && TMPDIR=/tmp
file="$datapath/$ZPASS_FILE$ZPASS_EXTENSION" file="$datapath/$ZPASS_FILE$ZPASS_EXTENSION"
error(){
ret=$1 && shift 1 && echo $* >&2 && exit $ret
}
mkdir -p "$datapath" 2>/dev/null || error 1 "Could not create '$datapath'" mkdir -p "$datapath" 2>/dev/null || error 1 "Could not create '$datapath'"
mkdir -p "$cachepath" 2>/dev/null && chmod -R go-rwx "$cachepath" 2>/dev/null mkdir -p "$cachepath" 2>/dev/null && chmod -R go-rwx "$cachepath" 2>/dev/null
@ -90,22 +91,27 @@ escape_chars() {
echo "$*" | sed 's|\.|\\\.|g;s|/|\\/|g' echo "$*" | sed 's|\.|\\\.|g;s|/|\\/|g'
} }
error(){
ret=$1 && shift 1 && echo $* >&2 && exit $ret
}
randalnum() {
tr -cd '[a-zA-Z]' < /dev/urandom | head -c $1
}
randpass() { randpass() {
len=$1 len=$1
[ -z "$len" ] && len=20 [ -z "$len" ] && len=20
tr -cd 'a-zA-Z0-9!-.' < /dev/urandom | head -c $len tr -cd 'a-zA-Z0-9!-.' < /dev/urandom | head -c $len
} }
# $1 = file
getpath() {
if [ -n "$REMOTE_ADDR" ]
then echo "$REMOTE_PORT:$REMOTE_ADDR:$file"
else readlink -f "$file"
fi
}
# $1 = file
filehash(){
getpath "$file" | md5sum | cut -d' ' -f1
}
keyfile(){ keyfile(){
printf "%s.key" "$(readlink -f "$file" | md5sum | cut -d' ' -f1)" printf "%s.key" "$(filehash)"
} }
# $1 = delay in sec # $1 = delay in sec
@ -116,13 +122,13 @@ clipboard_clear() {
do do
screen -S "$I" -X stuff "^C" screen -S "$I" -X stuff "^C"
done done
screen -dmS "$fname"_clipboard sh -c "sleep $1 ; printf '' | xclip -selection clipboard ; sleep 1" # put empty string into clipboard screen -dmS "$fname"_clipboard sh -c "sleep $1 ; xclip -selection clipboard < /dev/null ; sleep 1" # put empty string into clipboard
else else
xclip -selection clipboard < /dev/null xclip -selection clipboard < /dev/null
fi fi
} }
# $1 = delay in min # $1 = delay in sec
delete_cache() { delete_cache() {
if [ "$1" -gt 0 ] 2>/dev/null if [ "$1" -gt 0 ] 2>/dev/null
then then
@ -130,7 +136,7 @@ delete_cache() {
do do
screen -S "$I" -X stuff "^C" screen -S "$I" -X stuff "^C"
done done
screen -dmS "$fname"_"$(keyfile)" sh -c "sleep $(($1*60)) ; $0" # call zpass for autoclean screen -dmS "$fname"_"$(keyfile)" sh -c "sleep $1 ; $0 rmc" # call zpass with cache delete
else else
rm -f "$cachepath/$(keyfile)" 2>/dev/null rm -f "$cachepath/$(keyfile)" 2>/dev/null
fi fi
@ -138,7 +144,7 @@ delete_cache() {
clean_cache() { clean_cache() {
# key cache # key cache
find "$cachepath" -type f -mmin +"$ZPASS_KEY_CACHE_TIME" -exec rm '{}' ';' find "$cachepath" -type f ! -newermt @$(date -d "-$ZPASS_KEY_CACHE_TIME seconds" +%s) -print0 | xargs -0 rm
# tmp folders older than 1 min # tmp folders older than 1 min
rm -rd $(find "$TMPDIR" -maxdepth 1 -type d -name "$fname"'_*' ! -mmin 1) rm -rd $(find "$TMPDIR" -maxdepth 1 -type d -name "$fname"'_*' ! -mmin 1)
} }
@ -214,9 +220,19 @@ decrypt_with_key()
return $ret return $ret
} }
unset REQ_CLEAN
# $1 = keyfile to write # $1 = keyfile to write
decrypt() decrypt()
{ {
[ -n "$ZPASS_REMOTE_ADDR" ] && {
file="$TMPDIR/zpass_$(filehash)$ZPASS_EXTENSION"
[ -z "$ZPASS_PATH" ] && datapath="~/.local/share/zpass"
if [ -n "$ZPASS_SSH_ID" ]
then scp -i "$ZPASS_SSH_ID" "$ZPASS_REMOTE_ADDR:$datapath/$ZPASS_FILE$ZPASS_EXTENSION" "$file" >/dev/null || return $?
else scp "$ZPASS_REMOTE_ADDR:$datapath/$ZPASS_FILE$ZPASS_EXTENSION" "$file" >/dev/null || return $?
fi
## TODO ##
}
[ ! -f "$file" ] && return 1 # no file [ ! -f "$file" ] && return 1 # no file
if [ -n "$ZPASS_KEY" ] if [ -n "$ZPASS_KEY" ]
@ -240,7 +256,11 @@ decrypt()
done done
fi fi
fi fi
[ $ret -ne 0 ] && { echo "Could not decrypt '$file'" >&2 && return $ret ; }
[ -n "$ZPASS_REMOTE_ADDR" ] && rm -rf "$file" 2>/dev/null
[ $ret -ne 0 ] && { echo "Could not decrypt '$file'" >&2 ; }
return $ret
} }
# $1 = key # $1 = key
@ -274,7 +294,18 @@ pack()
fi fi
tar -cf - * | encrypt "$key" > "$1/$archive" || return $? tar -cf - * | encrypt "$key" > "$1/$archive" || return $?
) || return $? ) || return $?
mv -f "$1/$archive" "$file" if [ -n "$ZPASS_REMOTE_ADDR" ]
then
[ -z "$ZPASS_PATH" ] && datapath="~/.local/share/zpass"
if [ -n "$ZPASS_SSH_ID" ]
then scp -i "$ZPASS_SSH_ID" "$1/$archive" "$ZPASS_REMOTE_ADDR:$datapath/$ZPASS_FILE$ZPASS_EXTENSION" >/dev/null || return $?
else scp "$1/$archive" "$ZPASS_REMOTE_ADDR:$datapath/$ZPASS_FILE$ZPASS_EXTENSION" >/dev/null || return $?
fi
rm -f "$1/$archive" 2>/dev/null
return 0
else
mv -f "$1/$archive" "$file"
fi
} }
# $1 = file , $2 = content # $1 = file , $2 = content
@ -316,14 +347,29 @@ create() {
pack "$tmpdir" || { echo "Encryption error" >&2 && return 1 ; } pack "$tmpdir" || { echo "Encryption error" >&2 && return 1 ; }
rm -rf "$tmpdir" rm -rf "$tmpdir"
else else
# if remote: file tmp
[ -n "$ZPASS_REMOTE_ADDR" ] && {
file="$TMPDIR/zpass_$(filehash)$ZPASS_EXTENSION"
[ -z "$ZPASS_PATH" ] && datapath="~/.local/share/zpass"
}
# get key
[ -z "$ZPASS_KEY" ] && ZPASS_KEY=$(new_key_with_confirm) || { echo "Cancelled" >&2 && return 100 ; } [ -z "$ZPASS_KEY" ] && ZPASS_KEY=$(new_key_with_confirm) || { echo "Cancelled" >&2 && return 100 ; }
# create archive
tar -cf - -T /dev/null | encrypt "$ZPASS_KEY" > "$file" || { tar -cf - -T /dev/null | encrypt "$ZPASS_KEY" > "$file" || {
echo "Encryption error" >&2 echo "Encryption error" >&2
# echo "$tmperr" >&2 # echo "$tmperr" >&2
rm "$file" rm "$file"
return 1 return 1
} }
[ -n "$ZPASS_REMOTE_ADDR" ] && {
if [ -n "$ZPASS_SSH_ID" ]
then scp -i "$ZPASS_SSH_ID" "$file" "$ZPASS_REMOTE_ADDR:$datapath/$ZPASS_FILE$ZPASS_EXTENSION" >/dev/null || return $?
else scp "$file" "$ZPASS_REMOTE_ADDR:$datapath/$ZPASS_FILE$ZPASS_EXTENSION" >/dev/null || return $?
fi
rm -rf "$file" 2>/dev/null
}
fi fi
return 0
} }
list() list()
@ -382,7 +428,12 @@ case $1 in
n|new) setval "$2" "$(randpass $3)" ;; n|new) setval "$2" "$(randpass $3)" ;;
r|rm) remove "$2" "$3" ;; r|rm) remove "$2" "$3" ;;
x|copy|clipboard) copy "$2" ;; x|copy|clipboard) copy "$2" ;;
ch|cached) get_key_cached >/dev/null 2>&1 ; exit $? ;; ch|cached) get_key_cached >/dev/null 2>&1 ;;
rmc|rm-cache) delete_cache 0 >/dev/null 2>&1 ;;
-h|h|help) usage ;; -h|h|help) usage ;;
*) [ -n "$ZPASS_UNK_OP_CALL" ] && "$0" $ZPASS_UNK_OP_CALL "$@" ;; *) [ -n "$ZPASS_UNK_OP_CALL" ] && "$0" $ZPASS_UNK_OP_CALL "$@" ;;
esac esac
# ret=$?
# [ -n "$REQ_CLEAN" ] && rm -rf "$REQ_CLEAN" 2>/dev/null
# return $ret