pw (view raw)
1#!/usr/bin/env bash
2# pw - a mnml password manager
3
4command -v gpg >/dev/null 2>&1 && gpg=gpg
5command -v gpg2 >/dev/null 2>&1 && gpg=gpg2
6
7# check if xclip or pbcopy exist
8# command -v xclip >/dev/null 2>&1 && copy="xclip -rmlastnl -selection clipboard"
9# command -v pbcopy >/dev/null 2>&1 && copy="pbcopy | tr -d '\n'"
10
11# export PW_DIR to your own path
12[[ -z "$PW_DIR" ]] && PW_DIR="$HOME/.pw"
13
14init() {
15 if [[ ! -e "$PW_DIR" ]]; then
16 mkdir -p "$PW_DIR"
17 printf "pw: password directory initialized at %s\n" "$PW_DIR"
18 else
19 printf "PW_DIR is %s\n" "$PW_DIR"
20 die "$PW_DIR exists"
21 fi
22}
23
24add() {
25 # $1: path to file
26 # $2 [optional]: password text
27 [[ -z "$PW_KEY" ]] && die "\$PW_KEY not set"
28
29 if [[ "$#" -eq 2 ]]; then
30 pass="$2"
31 else
32 # uses default length of 25 chars, unless PW_LEN is set
33 pass="$(pwgen "${PW_LEN:-25}" 1 -s)"
34 printf "pw: generated password for %s\n" "$1"
35 fi
36 if [[ ! -f "$PW_DIR/$1.gpg" ]]; then
37 printf "%s" "$pass" | "$gpg" -aer "$PW_KEY" -o "$PW_DIR/$1.gpg"
38 printf "pw: %s/%s.gpg created\n" "$PW_DIR" "$1"
39 else
40 die "the file $PW_DIR/$1.gpg exists"
41 fi
42 (
43 cd $PW_DIR
44 git add .
45 git commit -m "$(date)"
46 remote="$(git remote show)"
47 branch="$(git branch --show-current)"
48 git pull -r "$remote" "$branch"
49 git push "$remote" "$branch"
50 )
51}
52
53list() {
54 for f in "$PW_DIR"/*.gpg; do
55 printf '%s\n' "$(basename "${f%.gpg}")"
56 done
57}
58
59del() {
60 checkf "$PW_DIR/$1.gpg"
61 read -rn 1 -p "pw: are you sure you want to delete $1? [y/n]: "
62 printf "\n"
63 [[ "$REPLY" == [yY] ]] && {
64 rm -f "$PW_DIR/$1.gpg"
65 printf "pw: deleted %s" "$1"
66 }
67}
68
69show() {
70 checkf "$PW_DIR/$1.gpg"
71 "$gpg" --decrypt --quiet --use-agent "$PW_DIR/$1.gpg"
72}
73
74# TODO: rework having to checkf twice
75
76copy() {
77 checkf "$PW_DIR/$1.gpg"
78 if [[ "$OSTYPE" =~ darwin* ]]; then
79 show "$1" | head -1 | pbcopy | tr -d '\n'
80 else
81 show "$1" | head -1 | xclip -rmlastnl -selection clipboard
82 fi
83 printf "pw: copied %s to clipboard\n" "$1"
84}
85
86usage() {
87 usage="
88pw - mnml password manager
89
90usage: pw [options] [NAME]
91All options except -i and -h require a NAME argument.
92
93options:
94 -i Initializes password directory at \$HOME/.pw or at \$PW_DIR, if it exists.
95 -a Add a password.
96 -g Generate a password.
97 -s Print password to STDOUT.
98 -l List out all passwords.
99 -c Copy existing password to clipboard.
100 -d Delete password.
101 -h Display this help message and exit.
102
103Requires PW_KEY to be set. Optionally, set PW_DIR for custom directory location.
104Set PW_LEN to an integer of your choice, to override the default password length of 25.
105"
106
107 printf "%s" "$usage"
108 exit 1
109}
110
111checkf() {
112 [[ ! -f "$1" ]] &&
113 die "$1 does not exist"
114}
115
116die() {
117 printf "error: %s\n" "$1" >&2
118 exit 1
119}
120
121
122main() {
123 [[ -z "$1" ]] && {
124 usage
125 }
126
127 while getopts "ila:g:s:c:d:h" options
128 do
129 # shellcheck disable=SC2221,SC2222
130 case "$options" in
131 i) init ;;
132 l) list ;;
133 g) add "$OPTARG" ;;
134 a)
135 read -rsp "enter password: " pass
136 printf "\n"
137 add "$OPTARG" "$pass"
138 ;;
139 s) show "$OPTARG" ;;
140 c) copy "$OPTARG" ;;
141 d) del "$OPTARG" ;;
142 *|h) usage ;;
143 esac
144 done
145
146 shift $(( OPTIND -1 ))
147}
148
149main "$@"