aboutsummaryrefslogtreecommitdiff
path: root/rofi/bin/rofi_pass.sh
blob: fb31a454a39890a46ef9b0a3d6144828860344af (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# Pick password from local or remote password-store with rofi's dmenu emulation
# mode, and write it to the active X11 window.  Optionally, prefix it with the
# username, being the last part of the slash-delimited password's name, and
# a TAB press to move to the next field in a form.
#
self=rofi_pass

# Abstraction to handle running shell commands (args or stdin) in either
# a remote or local shell.  If the environment variable PASSWORD_STORE_HOST is
# set, it's used as the destination SSH hostname to the password store.
#
pass_shell() {
	[ "$#" -le 1 ] || return
	if [ -n "$PASSWORD_STORE_HOST" ] ; then
		ssh -o StrictHostKeyChecking=yes -T -X -- \
			"$PASSWORD_STORE_HOST" "$@"
	elif [ "$#" -eq 1 ] ; then
		"${SHELL:-/bin/sh}" -c "$1"
	else
		"${SHELL:-/bin/sh}" -s
	fi
}

# Get a list of all the password paths, relative to the password store root,
# including leading dot-slash and trailing .gpg extension.
#
get_paths() {
	pass_shell <<-'EOF'
		dir=${PASSWORD_STORE_DIR:-"$HOME"/.password-store}
		cd -- "$dir" || exit
		find . -name \*.gpg -type f || exit
	EOF
}

# Get a list of all the password names, bytewise-sorted, with leading dot-slash
# and trailing .gpg extension removed.
#
get_names() {
	get_paths |
		sed -e 's_^[.]/__' -e 's_[.]gpg$__' |
		LC_COLLATE=C sort -f
}

# Write a password name to a shell to retrieve it, and read its first line;
# write the name safely to the shell's input rather than as an argument.
#
get_password() {
	name=$1
	[ -n "$name" ] || return
	printf '%s\n' "$name" |
		pass_shell 'IFS= read -r name ; pass show "$name"' |
		head -n 1
}

# Check for --login/-l option to paste a username-password combo, not just the
# password (defaults to the latter).
#
login=0
case $1 in
	--login|-l) login=1 ;;
esac

# Apply rofi -dmenu to pick a password name.  Use case-insensitive matching,
# and don't accept arbitrary input.
#
name=$(get_names | rofi -dmenu -i -no-lazy-grab -only_match -p pass) || exit
[ -n "$name" ] || exit

# Retrieve the username for the chosen password, and then the secret itself;
# check that we actually got more than an empty string back in both cases.
#
username=${name##*/}
[ -n "$username" ] || exit
password=$(get_password "$name") || exit
[ -n "$password" ] || exit

# Have xdotool type either the username-TAB-password, or just the password;
# receiving it on its standard input rather than its arguments, for security.
#
if [ "$login" -eq 1 ] ; then
	printf '%s\t%s' \
		"$username" "$password"
else
	printf '%s' \
		"$password"
fi | xdotool type --clearmodifiers --delay=0 --file - || exit

# Tell the user we wrote the password out, in case they're typing a password
# into a field with echo turned off.
#
notify-send --app-name="$self" --icon=gcr-password -- \
	'Password typed' "$name"