Compare commits

...

2 commits

Author SHA1 Message Date
6dc0cd6d16
Fix regression for username fallback
While usernames were now picked up correctly as a fallback from the
filename if none were present in the fields themselves, if there were
any in the fields the program would return an empty string.

This fixes it for both cases, the username existing in the entry fields
or only in the filename. Entry fields will still take precedence.
2022-01-11 17:50:24 +01:00
2236d7fec8
Reformat file 2022-01-11 17:47:30 +01:00

478
pass-pick
View file

@ -57,137 +57,137 @@
# Passes along any options given to main script.
rofi_opts=("$@")
_picker() {
if [ -n "$PICKER_BACKEND" ]; then
"${PICKER_BACKEND[@]}"
elif command -v rofi 1>/dev/null 2>/dev/null; then
rofi -dmenu -no-auto-select -i "${rofi_opts[@]}" "$@" -p "entry"
elif command -v bemenu 1>/dev/null 2>/dev/null; then
bemenu -l 20 -i -p "entry >"
elif command -v dmenu 1>/dev/null 2>/dev/null; then
dmenu -i -p "entry >"
else
printf "%s: 📦 %s must be installed for %s function.\n" "critical" "rofi/bemenu/dmenu" "this" >&2
notify-send "📦 rofi/bemenu/dmenu" --urgency="critical" "must be installed for this function."
exit 1
fi
if [ -n "$PICKER_BACKEND" ]; then
"${PICKER_BACKEND[@]}"
elif command -v rofi 1>/dev/null 2>/dev/null; then
rofi -dmenu -no-auto-select -i "${rofi_opts[@]}" "$@" -p "entry"
elif command -v bemenu 1>/dev/null 2>/dev/null; then
bemenu -l 20 -i -p "entry >"
elif command -v dmenu 1>/dev/null 2>/dev/null; then
dmenu -i -p "entry >"
else
printf "%s: 📦 %s must be installed for %s function.\n" "critical" "rofi/bemenu/dmenu" "this" >&2
notify-send "📦 rofi/bemenu/dmenu" --urgency="critical" "must be installed for this function."
exit 1
fi
}
# copies to clipboard, removes any trailing newlines,
# and only keeps it in for 1 paste (1 loop to read in script, 1 to output)
_clipper() {
if command -v wl-copy 1>/dev/null 2>/dev/null; then
wl-copy -o -n
elif command -v xclip 1>/dev/null 2>/dev/null; then
xclip -i -selection 'clipboard' -loops 2 -rmlastnl
elif command -v xsel 1>/dev/null 2>/dev/null; then
xsel -b
else
notify-send "No clipboard utility" "Install wl-copy, xclip or xsel."
fi
if command -v wl-copy 1>/dev/null 2>/dev/null; then
wl-copy -o -n
elif command -v xclip 1>/dev/null 2>/dev/null; then
xclip -i -selection 'clipboard' -loops 2 -rmlastnl
elif command -v xsel 1>/dev/null 2>/dev/null; then
xsel -b
else
notify-send "No clipboard utility" "Install wl-copy, xclip or xsel."
fi
}
# parse, see https://unix.stackexchange.com/a/331965/8541
_parse_config() {
(grep -e "^$2=" -m 1 "$1" 2>/dev/null || printf "var=__UNDEFINED__\n") | head -n1 | cut -d '=' -f 2-
(grep -e "^$2=" -m 1 "$1" 2>/dev/null || printf "var=__UNDEFINED__\n") | head -n1 | cut -d '=' -f 2-
}
# read config file
get_config() {
local locations=(
"$PP_CONFIGURATION_FILE"
"${xdg_config_home:-$HOME/.config}/pass-picker/pass-picker.conf"
"$HOME/.pass-picker.conf"
"/etc/pass-picker.conf"
)
local locations=(
"$PP_CONFIGURATION_FILE"
"${xdg_config_home:-$HOME/.config}/pass-picker/pass-picker.conf"
"$HOME/.pass-picker.conf"
"/etc/pass-picker.conf"
)
# return the first config file with a valid path
for config in "${locations[@]}"; do
if [[ -n "$config" && -f "$config" ]]; then
# see if the config has been given a value
local val
val="$(_parse_config "$config" "$1")"
break
fi
done
# return the first config file with a valid path
for config in "${locations[@]}"; do
if [[ -n $config && -f $config ]]; then
# see if the config has been given a value
local val
val="$(_parse_config "$config" "$1")"
break
fi
done
# if there was a config file but no value
# or there was no config file at all
if [ "$val" = "__UNDEFINED__" ] || [ -z "$val" ]; then
val="$2"
fi
printf -- "%s" "$val"
# if there was a config file but no value
# or there was no config file at all
if [ "$val" = "__UNDEFINED__" ] || [ -z "$val" ]; then
val="$2"
fi
printf -- "%s" "$val"
}
set_defaults() {
# The location of the pass-picker config file
# PP_CONFIGURATION_FILE="~/.config/pass-picker/pass-picker.conf"
# set options, leaving already set environment variables intact
# try to read any settings from config files
PICKER_BACKEND="${PP_PICKER_BACKEND:-$(get_config PICKER_BACKEND)}"
# The location of the pass-picker config file
# PP_CONFIGURATION_FILE="~/.config/pass-picker/pass-picker.conf"
# set options, leaving already set environment variables intact
# try to read any settings from config files
PICKER_BACKEND="${PP_PICKER_BACKEND:-$(get_config PICKER_BACKEND)}"
KEY_AUTOFILL="${PP_KEY_AUTOFILL:-$(get_config KEY_AUTOFILL Return)}"
KEY_ENTRY_OPEN="${PP_KEY_ENTRY_OPEN:-$(get_config KEY_ENTRY_OPEN Alt+Return)}"
KEY_FILL_USER="${PP_KEY_FILL_USER:-$(get_config KEY_FILL_USER Alt+u)}"
KEY_CLIP_USER="${PP_KEY_CLIP_USER:-$(get_config KEY_CLIP_USER Ctrl+Alt+u)}"
KEY_FILL_PASS="${PP_KEY_FILL_PASS:-$(get_config KEY_FILL_PASS Alt+p)}"
KEY_CLIP_PASS="${PP_KEY_CLIP_PASS:-$(get_config KEY_CLIP_PASS Ctrl+Alt+p)}"
KEY_ENTRYMENU_FILL="${PP_KEY_ENTRYMENU_FILL:-$(get_config KEY_ENTRYMENU_FILL Return)}"
KEY_ENTRYMENU_CLIP="${PP_KEY_ENTRYMENU_CLIP:-$(get_config KEY_ENTRYMENU_CLIP Alt+Return)}"
KEY_ENTRYMENU_SHOWFIELD="${KEY_ENTRYMENU_SHOWFIELD:-$(get_config KEY_ENTRYMENU_SHOWFIELD Alt+s)}"
KEY_ENTRYMENU_QUIT="${PP_KEY_ENTRYMENU_QUIT:-$(get_config KEY_ENTRYMENU_QUIT Alt+BackSpace)}"
KEY_AUTOFILL="${PP_KEY_AUTOFILL:-$(get_config KEY_AUTOFILL Return)}"
KEY_ENTRY_OPEN="${PP_KEY_ENTRY_OPEN:-$(get_config KEY_ENTRY_OPEN Alt+Return)}"
KEY_FILL_USER="${PP_KEY_FILL_USER:-$(get_config KEY_FILL_USER Alt+u)}"
KEY_CLIP_USER="${PP_KEY_CLIP_USER:-$(get_config KEY_CLIP_USER Ctrl+Alt+u)}"
KEY_FILL_PASS="${PP_KEY_FILL_PASS:-$(get_config KEY_FILL_PASS Alt+p)}"
KEY_CLIP_PASS="${PP_KEY_CLIP_PASS:-$(get_config KEY_CLIP_PASS Ctrl+Alt+p)}"
KEY_ENTRYMENU_FILL="${PP_KEY_ENTRYMENU_FILL:-$(get_config KEY_ENTRYMENU_FILL Return)}"
KEY_ENTRYMENU_CLIP="${PP_KEY_ENTRYMENU_CLIP:-$(get_config KEY_ENTRYMENU_CLIP Alt+Return)}"
KEY_ENTRYMENU_SHOWFIELD="${KEY_ENTRYMENU_SHOWFIELD:-$(get_config KEY_ENTRYMENU_SHOWFIELD Alt+s)}"
KEY_ENTRYMENU_QUIT="${PP_KEY_ENTRYMENU_QUIT:-$(get_config KEY_ENTRYMENU_QUIT Alt+BackSpace)}"
AUTOFILL_BACKEND="${PP_AUTOFILL_BACKEND:-$(get_config AUTOFILL_BACKEND wtype)}"
AUTOFILL_CHAIN="${PP_AUTOENTRY_CHAIN:-$(get_config AUTOFILL_CHAIN 'username :tab password')}"
AUTOFILL_DELAY="${PP_AUTOENTRY_DELAY:-$(get_config AUTOFILL_DELAY 30)}"
AUTOFILL_BACKEND="${PP_AUTOFILL_BACKEND:-$(get_config AUTOFILL_BACKEND wtype)}"
AUTOFILL_CHAIN="${PP_AUTOENTRY_CHAIN:-$(get_config AUTOFILL_CHAIN 'username :tab password')}"
AUTOFILL_DELAY="${PP_AUTOENTRY_DELAY:-$(get_config AUTOFILL_DELAY 30)}"
PASS_USERNAME_FIELD="${PP_PASS_USERNAME_FIELD:-$(get_config PASS_USERNAME_FIELD 'username user login')}"
PASS_COFFIN_OPEN_TIME="${PP_PASS_COFFIN_OPEN_TIME:-$(get_config PASS_COFFIN_OPEN_TIME 0)}"
PASS_COFFIN_LOCATION="${PP_PASS_COFFIN_LOCATION:-$(get_config PASS_COFFIN_LOCATION)}"
PASS_USERNAME_FIELD="${PP_PASS_USERNAME_FIELD:-$(get_config PASS_USERNAME_FIELD 'username user login')}"
PASS_COFFIN_OPEN_TIME="${PP_PASS_COFFIN_OPEN_TIME:-$(get_config PASS_COFFIN_OPEN_TIME 0)}"
PASS_COFFIN_LOCATION="${PP_PASS_COFFIN_LOCATION:-$(get_config PASS_COFFIN_LOCATION)}"
}
# exit on escape pressed
exit_check() {
[ "$1" -eq 1 ] && exit
[ "$1" -eq 1 ] && exit
}
# simply return a list of all passwords in pass store
# TODO only show website names (+ folder names), and account names for multiple accounts on one site
list_passwords() {
shopt -s nullglob globstar
prefix=${PASSWORD_STORE_DIR:-~/.password-store}
password_files=("$prefix"/**/*.gpg)
password_files=("${password_files[@]#"$prefix"/}")
password_files=("${password_files[@]%.gpg}")
shopt -s nullglob globstar
prefix=${PASSWORD_STORE_DIR:-~/.password-store}
password_files=("$prefix"/**/*.gpg)
password_files=("${password_files[@]#"$prefix"/}")
password_files=("${password_files[@]%.gpg}")
printf '%s\n' "${password_files[@]}"
printf '%s\n' "${password_files[@]}"
}
# return password for argument passed
show_password() {
pass show "$1" | head -n1
pass show "$1" | head -n1
}
# send password to clipboard
clip_password() {
pass show -c "$1"
pass show -c "$1"
}
# attempt to return the field specified
# attempts all (space separated) fields until the
# first one successfully returned
_p_get_field() {
local gp_entry="$1"
local gp_field="$2"
local gp_entry="$1"
local gp_field="$2"
# return on first successfully returned key
for key in $gp_field; do
local value
value=$(_p_get_key_value "$gp_entry" "$key")
# return on first successfully returned key
for key in $gp_field; do
local value
value=$(_p_get_key_value "$gp_entry" "$key")
# found entry
if [ -n "$value" ]; then
echo "$value" && break
fi
done
# found entry
if [ -n "$value" ]; then
echo "$value" && break
fi
done
}
# returns the corresponding value for the key passed in
@ -195,210 +195,212 @@ _p_get_field() {
# $1: pass (file) entry to search through
# $2: string name of the containting key
_p_get_key_value() {
local value
value=$(list_fields "$1" | grep "$2")
local value
value=$(list_fields "$1" | grep "$2")
# get everything after first colon, remove whitespace
echo "$value" | cut -d':' -f2- | tr -d '[:blank:]'
# get everything after first colon, remove whitespace
echo "$value" | cut -d':' -f2- | tr -d '[:blank:]'
}
# return username for argument passed
# Prefers in-metadata username, falls back to filename
show_username() {
result=$(_p_get_field "$1" "${PASS_USERNAME_FIELD}")
if [ -z "$result" ]; then
echo "${1##*/}"
fi
result=$(_p_get_field "$1" "${PASS_USERNAME_FIELD}")
if [ -z "$result" ]; then
echo "${1##*/}"
else
echo "$result"
fi
}
clip_username() {
show_username "$1" "${PASS_USERNAME_FIELD}" | _clipper
show_username "$1" "${PASS_USERNAME_FIELD}" | _clipper
}
show_field() {
_p_get_field "$1" "$2"
_p_get_field "$1" "$2"
}
clip_field() {
show_field "$1" "$2" | _clipper
show_field "$1" "$2" | _clipper
}
list_fields() {
pass show "$1" | tail -n+2
pass show "$1" | tail -n+2
}
# invoke the dotool to type inputs
_type() {
local tool="${AUTOFILL_BACKEND}"
local toolmode="$1"
local key="$2"
local tool="${AUTOFILL_BACKEND}"
local toolmode="$1"
local key="$2"
if [ "$tool" = "wtype" ]; then
if [ "$toolmode" = "type" ]; then
"$tool" -s "${AUTOFILL_DELAY}" -- "$key"
elif [ "$toolmode" = "key" ]; then
"$tool" -s "${AUTOFILL_DELAY}" -k "$key"
fi
elif [ "$tool" = "xdotool" ]; then
"$tool" "$toolmode" --delay "${AUTOFILL_DELAY}" "$key"
elif [ "$tool" = "ydotool" ]; then
"$tool" "$toolmode" --key-delay "${AUTOFILL_DELAY}" "$key"
else
"$tool" "$toolmode" "$key"
fi
if [ "$tool" = "wtype" ]; then
if [ "$toolmode" = "type" ]; then
"$tool" -s "${AUTOFILL_DELAY}" -- "$key"
elif [ "$toolmode" = "key" ]; then
"$tool" -s "${AUTOFILL_DELAY}" -k "$key"
fi
elif [ "$tool" = "xdotool" ]; then
"$tool" "$toolmode" --delay "${AUTOFILL_DELAY}" "$key"
elif [ "$tool" = "ydotool" ]; then
"$tool" "$toolmode" --key-delay "${AUTOFILL_DELAY}" "$key"
else
"$tool" "$toolmode" "$key"
fi
}
# automatically fill out fields
# transform special chain entries into valid dotool commands
autofill() {
local selected="${1}"
local autoentry_chain="${2}"
local selected="${1}"
local autoentry_chain="${2}"
for part in $autoentry_chain; do
case "$part" in
":tab") _type key Tab ;;
":return") _type key Return ;;
":space") _type key space ;;
"username") _type type "$(show_username "$selected")" ;;
"password") _type type "$(show_password "$selected")" ;;
":direct") _type type "$selected" ;;
*) printf '%s' "$selected" ;;
esac
done
for part in $autoentry_chain; do
case "$part" in
":tab") _type key Tab ;;
":return") _type key Return ;;
":space") _type key space ;;
"username") _type type "$(show_username "$selected")" ;;
"password") _type type "$(show_password "$selected")" ;;
":direct") _type type "$selected" ;;
*) printf '%s' "$selected" ;;
esac
done
}
# opens a menu for the specified pass entry, containing its individual fields
entrymenu() {
local entry="$1"
local deobfuscate="$2"
local k_entrymenu_fill="${KEY_ENTRYMENU_FILL}"
local k_entrymenu_clip="${KEY_ENTRYMENU_CLIP}"
local k_entrymenu_showfield="${KEY_ENTRYMENU_SHOWFIELD}"
local k_entrymenu_quit="${KEY_ENTRYMENU_QUIT}"
local entry="$1"
local deobfuscate="$2"
local k_entrymenu_fill="${KEY_ENTRYMENU_FILL}"
local k_entrymenu_clip="${KEY_ENTRYMENU_CLIP}"
local k_entrymenu_showfield="${KEY_ENTRYMENU_SHOWFIELD}"
local k_entrymenu_quit="${KEY_ENTRYMENU_QUIT}"
local pass
if [ "$deobfuscate" = "true" ]; then
pass="$(show_password "$entry")"
else
pass="(hidden)"
fi
local pass
if [ "$deobfuscate" = "true" ]; then
pass="$(show_password "$entry")"
else
pass="(hidden)"
fi
local field
field=$(
printf "password: %s\n%s" "$pass" "$(list_fields "$entry")" |
_picker \
-kb-accept-entry "" \
-kb-custom-1 "$k_entrymenu_fill" \
-kb-custom-2 "$k_entrymenu_clip" \
-kb-custom-3 "$k_entrymenu_quit" \
-kb-custom-4 "$k_entrymenu_showfield" \
-mesg " ᐊ $k_entrymenu_quit ᐊ | $k_entrymenu_fill: fill selection | $k_entrymenu_clip: clip selection | $k_entrymenu_showfield: reveal password"
)
exit_value=$?
exit_check "$exit_value"
local field
field=$(
printf "password: %s\n%s" "$pass" "$(list_fields "$entry")" \
| _picker \
-kb-accept-entry "" \
-kb-custom-1 "$k_entrymenu_fill" \
-kb-custom-2 "$k_entrymenu_clip" \
-kb-custom-3 "$k_entrymenu_quit" \
-kb-custom-4 "$k_entrymenu_showfield" \
-mesg " ᐊ $k_entrymenu_quit ᐊ | $k_entrymenu_fill: fill selection | $k_entrymenu_clip: clip selection | $k_entrymenu_showfield: reveal password"
)
exit_value=$?
exit_check "$exit_value"
# get field name
field=${field%%:*}
case "$exit_value" in
"0" | "10")
if [ "$field" = "password" ]; then
autofill "$entry" "password"
else
autofill "$(show_field "$entry" "$field")" ":direct"
fi
exit 0
;;
"11")
if [ "$field" = "password" ]; then
clip_password "$entry"
else
clip_field "$entry" "$field"
fi
exit 0
;;
"12")
main
;;
"13")
local toggle
if [ "$deobfuscate" = "true" ]; then
toggle=false
else
toggle=true
fi
entrymenu "$entry" "$toggle"
;;
esac
# get field name
field=${field%%:*}
case "$exit_value" in
"0" | "10")
if [ "$field" = "password" ]; then
autofill "$entry" "password"
else
autofill "$(show_field "$entry" "$field")" ":direct"
fi
exit 0
;;
"11")
if [ "$field" = "password" ]; then
clip_password "$entry"
else
clip_field "$entry" "$field"
fi
exit 0
;;
"12")
main
;;
"13")
local toggle
if [ "$deobfuscate" = "true" ]; then
toggle=false
else
toggle=true
fi
entrymenu "$entry" "$toggle"
;;
esac
}
open_coffin() {
## there's a closed coffin in our directory
if [ -f "${PASS_COFFIN_LOCATION:-${PASSWORD_STORE_DIR:-~/.password-store}/.coffin/coffin.tar.gpg}" ]; then
if [ "$PASS_COFFIN_OPEN_TIME" -eq 0 ]; then
coffin_should_close_instantly=true
pass open -t 1h # we still set a maximum time limit just to hedge against failures
else
pass open -t "${PASS_COFFIN_OPEN_TIME:-3h}"
fi
fi
## there's a closed coffin in our directory
if [ -f "${PASS_COFFIN_LOCATION:-${PASSWORD_STORE_DIR:-~/.password-store}/.coffin/coffin.tar.gpg}" ]; then
if [ "$PASS_COFFIN_OPEN_TIME" -eq 0 ]; then
coffin_should_close_instantly=true
pass open -t 1h # we still set a maximum time limit just to hedge against failures
else
pass open -t "${PASS_COFFIN_OPEN_TIME:-3h}"
fi
fi
}
# make sure we remember to close the coffin if the program terminates
close_coffin() {
if [ "$coffin_should_close_instantly" = true ]; then
pass close
fi
if [ "$coffin_should_close_instantly" = true ]; then
pass close
fi
}
trap close_coffin SIGINT SIGTERM ERR EXIT
main() {
local autoentry_chain="${AUTOFILL_CHAIN}"
local k_autofill="${KEY_AUTOFILL}"
local k_fill_user="${KEY_FILL_USER}"
local k_clip_user="${KEY_CLIP_USER}"
local k_fill_pass="${KEY_FILL_PASS}"
local k_clip_pass="${KEY_CLIP_PASS}"
local k_submenu="${KEY_ENTRY_OPEN}"
local autoentry_chain="${AUTOFILL_CHAIN}"
local k_autofill="${KEY_AUTOFILL}"
local k_fill_user="${KEY_FILL_USER}"
local k_clip_user="${KEY_CLIP_USER}"
local k_fill_pass="${KEY_FILL_PASS}"
local k_clip_pass="${KEY_CLIP_PASS}"
local k_submenu="${KEY_ENTRY_OPEN}"
open_coffin
open_coffin
entry="$(
list_passwords |
_picker -kb-accept-entry "" \
-kb-custom-1 "$k_autofill" \
-kb-custom-2 "$k_clip_user" \
-kb-custom-3 "$k_clip_pass" \
-kb-custom-4 "$k_fill_user" \
-kb-custom-5 "$k_fill_pass" \
-kb-custom-6 "$k_submenu" \
-mesg "| $k_autofill: fill credentials | $k_submenu: open entry | $k_fill_user: fill username | $k_fill_pass: fill password | $k_clip_user: clip username | $k_clip_pass: clip password |"
)"
exit_value=$?
entry="$(
list_passwords \
| _picker -kb-accept-entry "" \
-kb-custom-1 "$k_autofill" \
-kb-custom-2 "$k_clip_user" \
-kb-custom-3 "$k_clip_pass" \
-kb-custom-4 "$k_fill_user" \
-kb-custom-5 "$k_fill_pass" \
-kb-custom-6 "$k_submenu" \
-mesg "| $k_autofill: fill credentials | $k_submenu: open entry | $k_fill_user: fill username | $k_fill_pass: fill password | $k_clip_user: clip username | $k_clip_pass: clip password |"
)"
exit_value=$?
echo "$entry"
exit_check "$exit_value"
case "$exit_value" in
"0" | "10")
autofill "$entry" "$autoentry_chain"
;;
"11")
clip_username "$entry"
;;
"12")
clip_password "$entry"
;;
"13")
autofill "$entry" "username"
;;
"14")
autofill "$entry" "password"
;;
"15")
entrymenu "$entry"
;;
esac
echo "$entry"
exit_check "$exit_value"
case "$exit_value" in
"0" | "10")
autofill "$entry" "$autoentry_chain"
;;
"11")
clip_username "$entry"
;;
"12")
clip_password "$entry"
;;
"13")
autofill "$entry" "username"
;;
"14")
autofill "$entry" "password"
;;
"15")
entrymenu "$entry"
;;
esac
exit 0
exit 0
}
set_defaults