Compare commits

..

No commits in common. "6dc0cd6d16f9c6723b3ee02562dec9f2b6595e67" and "846c6793538514a2ea8bbbddad5ff82fddf21a33" have entirely different histories.

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,212 +195,210 @@ _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##*/}"
else fi
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