~palb91/pass-wl

ref: b163ca723d23a873a83689a2d10ec9d6c92fd86c pass-wl/pass-wl -rwxr-xr-x 4.2 KiB
b163ca72 — Pierre-Albéric TROUPLIN fix: script 5 months ago
                                                                                
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#!/bin/bash
#
# © 2021, Pierre-Albéric TROUPLIN (palb91)

set -e


# Environments
declare -A content

cmd=wtype
asset=pass
url=
selection=


# Utils
help() {
    cat <<HELP
Type a password from password-store in a Wayland environment.

Usage:
    ${0##*/} [-h]
    ${0##*/} [-u|-b|-o] [URL]
    ${0##*/} -c [-u|-o] [URL]

Options:
    -u, --user  Type the username instead of the password.
    -b, --both  Type the username and the password separated by a <Tab>.
    -o, --otp   Type the otp code if exists.
    -c, --copy  Copy instead of type.
    -h, --help  Print this help.

Argument:
    URL         If any, must be the last parameter. ${0##*/} will try to
                find only matching values from \`pass\`.
HELP

    exit "${1:-0}"
}

die()       { printf '%s\n'   "${*}" >&2; exit 1;                           }
die_help()  { printf '%s\n\n' "${*}" >&2; help 1;                           }
check_dep() { [[ -n "$(command -v "${1}")" ]] || die "${1} not installed";  }
menu()      { check_dep bemenu; bemenu --prompt "${1:-${0##*/}}" | head -1; }


# Parse arguments, check dependancies
parse_args() {
    while [[ "${#}" -gt 0 ]]; do
        case "${1}" in
            -h|--help) help                          ;;
            -c|--copy) cmd=wl-copy                   ;;
            -u|--user) asset=user                    ;;
            -b|--both) asset=both                    ;;
            -o|--otp)  asset=otp                     ;;
            -cu)       cmd=wl-copy asset=user        ;;
            -co)       cmd=wl-copy asset=otp         ;;
            -*)        die_help "Wrong option: ${1}" ;;
            *)         url="${1}"; break             ;;
        esac

        shift
    done

    [[ "${#}" -gt 1 ]] \
        && die "Wrong number of arguments."

    [[ "${cmd}" = 'wl-copy' ]] && [[ "${asset}" = 'both' ]] \
        && die 'Cannot copy both username and password.'

    check_dep "${cmd}"
}


# Set $pass_list with the password-store values
get_pass_list() {
    shopt -s globstar

    local _store="${PASSWORD_STORE_DIR:-${HOME}/.password-store}"
    pass_list=( "${_store}"/**/*.gpg         )
    pass_list=( "${pass_list[@]%.gpg}"       )
    pass_list=( "${pass_list[@]#${_store}/}" )
}


# Select a single value (use a menu if a choice has to be made).
filter_list() {
    local _url _name _tmp
    declare -a _match

    if [[ -n "${url}" ]]; then
        _url="${url#*://}"
        _url="${_url%%/*}"
        _url="${_url%%:*}"

        # Get matching pass values, check (sub)domains from the right.
        for _name in "${pass_list[@]}"; do
            _tmp="${_url%${_name##*/}}"
            [[ "${_tmp}" != "${_url}" ]] || continue
            # Has more subdomains than the pass entry (separated with a dot):
            [[ -n "${_tmp}" ]] && [[ "${_tmp: -1}" != '.' ]] && continue
            _match+=( "${_name}" )
        done
    fi

    case "${#_match[@]}" in
        0) _match=( "${pass_list[@]}" )     ;;
        1) selection="${_match[0]}"; return ;;
    esac

    selection="$(printf '%s\n' "${_match[@]}" | tac | menu 'select:')"

    [[ -n "${selection}" ]] || die 'No selection... abort.'
}


# Get the content of the pass entry selected
get_pass_content() {
    shopt -s nocasematch

    while read -r line; do
        [[ -z "${content[pass]}" ]]                       \
            && content[pass]="${line}"                    \
            && continue

        [[ -z "${content[user]}" ]]                       \
            && [[ "${line}" =~ ^user(name)?: ]]           \
            && content[user]="${line#*: }"                \
            && continue

        [[ -z "${content[otp]}" ]]                        \
            && [[ "${line}" =~ ^otpauth:// ]]             \
            && content[otp]="$(pass otp "${selection}")"  \
            && continue
    done < <(pass "${selection}")
    true
}


# Run wtype or wl-copy with asked content
type_or_copy() {
    set -- "${content[${asset}]}"

    if [[ "${cmd}" = 'wtype' ]]; then
        [[ "${asset}" = 'both' ]] \
            && set -- "${content[user]}" -k Tab "${content[pass]}"

        # Add a short delay before typing
        set -- -s 150 "${@}"
    fi

    exec "${cmd}" "${@}"
}


main() {
    parse_args "${@}"
    get_pass_list
    filter_list
    get_pass_content
    type_or_copy
}

main "${@}"