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