]> git.gir.st - sendHID.git/blob - pass-modified
added modified version of pass, the license for it, and a small demo for sending...
[sendHID.git] / pass-modified
1 #!/usr/bin/env bash
2
3 # Copyright (C) 2012 - 2014 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
4 # This file is licensed under the GPLv2+. Please see COPYING for more information.
5
6 # modified by tobias girstmair for the hardpass project at github.com/girst/hardpass
7 # pass show displays a 'flat' list of all passwords, for easier scripting and uses a
8 # new variable $PASS_GPG_PHRASE which can handle the master password instead of gpg-
9 # agent, which is hard to script. Usage: PASS_GPG_PHRASE='Pa$$w0rd' pass show github/girst
10
11 umask "${PASSWORD_STORE_UMASK:-077}"
12 set -o pipefail
13
14 GPG_OPTS=( "--quiet" "--yes" "--compress-algo=none" "--no-encrypt-to" "--passphrase" $PASS_GPG_PHRASE)
15 GPG="gpg"
16 export GPG_TTY="${GPG_TTY:-$(tty 2>/dev/null)}"
17 which gpg2 &>/dev/null && GPG="gpg2"
18 [[ -n $GPG_AGENT_INFO || $GPG == "gpg2" ]] && GPG_OPTS+=( "--batch" "--use-agent" )
19
20 PREFIX="${PASSWORD_STORE_DIR:-$HOME/.password-store}"
21 X_SELECTION="${PASSWORD_STORE_X_SELECTION:-clipboard}"
22 CLIP_TIME="${PASSWORD_STORE_CLIP_TIME:-45}"
23
24 export GIT_DIR="${PASSWORD_STORE_GIT:-$PREFIX}/.git"
25 export GIT_WORK_TREE="${PASSWORD_STORE_GIT:-$PREFIX}"
26
27 #
28 # BEGIN helper functions
29 #
30
31 git_add_file() {
32 [[ -d $GIT_DIR ]] || return
33 git add "$1" || return
34 [[ -n $(git status --porcelain "$1") ]] || return
35 git_commit "$2"
36 }
37 git_commit() {
38 local sign=""
39 [[ -d $GIT_DIR ]] || return
40 [[ $(git config --bool --get pass.signcommits) == "true" ]] && sign="-S"
41 git commit $sign -m "$1"
42 }
43 yesno() {
44 [[ -t 0 ]] || return 0
45 local response
46 read -r -p "$1 [y/N] " response
47 [[ $response == [yY] ]] || exit 1
48 }
49 die() {
50 echo "$@" >&2
51 exit 1
52 }
53 set_gpg_recipients() {
54 GPG_RECIPIENT_ARGS=( )
55 GPG_RECIPIENTS=( )
56
57 if [[ -n $PASSWORD_STORE_KEY ]]; then
58 for gpg_id in $PASSWORD_STORE_KEY; do
59 GPG_RECIPIENT_ARGS+=( "-r" "$gpg_id" )
60 GPG_RECIPIENTS+=( "$gpg_id" )
61 done
62 return
63 fi
64
65 local current="$PREFIX/$1"
66 while [[ $current != "$PREFIX" && ! -f $current/.gpg-id ]]; do
67 current="${current%/*}"
68 done
69 current="$current/.gpg-id"
70
71 if [[ ! -f $current ]]; then
72 cat >&2 <<-_EOF
73 Error: You must run:
74 $PROGRAM init your-gpg-id
75 before you may use the password store.
76
77 _EOF
78 cmd_usage
79 exit 1
80 fi
81
82 local gpg_id
83 while read -r gpg_id; do
84 GPG_RECIPIENT_ARGS+=( "-r" "$gpg_id" )
85 GPG_RECIPIENTS+=( "$gpg_id" )
86 done < "$current"
87 }
88
89 reencrypt_path() {
90 local prev_gpg_recipients="" gpg_keys="" current_keys="" index passfile
91 local groups="$($GPG --list-config --with-colons | grep "^cfg:group:.*")"
92 while read -r -d "" passfile; do
93 local passfile_dir="${passfile%/*}"
94 passfile_dir="${passfile_dir#$PREFIX}"
95 passfile_dir="${passfile_dir#/}"
96 local passfile_display="${passfile#$PREFIX/}"
97 passfile_display="${passfile_display%.gpg}"
98 local passfile_temp="${passfile}.tmp.${RANDOM}.${RANDOM}.${RANDOM}.${RANDOM}.--"
99
100 set_gpg_recipients "$passfile_dir"
101 if [[ $prev_gpg_recipients != "${GPG_RECIPIENTS[*]}" ]]; then
102 for index in "${!GPG_RECIPIENTS[@]}"; do
103 local group="$(sed -n "s/^cfg:group:$(sed 's/[\/&]/\\&/g' <<<"${GPG_RECIPIENTS[$index]}"):\\(.*\\)\$/\\1/p" <<<"$groups" | head -n 1)"
104 [[ -z $group ]] && continue
105 IFS=";" eval 'GPG_RECIPIENTS+=( $group )' # http://unix.stackexchange.com/a/92190
106 unset GPG_RECIPIENTS[$index]
107 done
108 gpg_keys="$($GPG --list-keys --with-colons "${GPG_RECIPIENTS[@]}" | sed -n 's/sub:[^:]*:[^:]*:[^:]*:\([^:]*\):[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[a-zA-Z]*e[a-zA-Z]*:.*/\1/p' | LC_ALL=C sort -u)"
109 fi
110 current_keys="$($GPG -v --no-secmem-warning --no-permission-warning --list-only --keyid-format long "$passfile" 2>&1 | cut -d ' ' -f 5 | LC_ALL=C sort -u)"
111
112 if [[ $gpg_keys != "$current_keys" ]]; then
113 echo "$passfile_display: reencrypting to ${gpg_keys//$'\n'/ }"
114 $GPG -d "${GPG_OPTS[@]}" "$passfile" | $GPG -e "${GPG_RECIPIENT_ARGS[@]}" -o "$passfile_temp" "${GPG_OPTS[@]}" &&
115 mv "$passfile_temp" "$passfile" || rm -f "$passfile_temp"
116 fi
117 prev_gpg_recipients="${GPG_RECIPIENTS[*]}"
118 done < <(find "$1" -iname '*.gpg' -print0)
119 }
120 check_sneaky_paths() {
121 local path
122 for path in "$@"; do
123 [[ $path =~ /\.\.$ || $path =~ ^\.\./ || $path =~ /\.\./ || $path =~ ^\.\.$ ]] && die "Error: You've attempted to pass a sneaky path to pass. Go home."
124 done
125 }
126
127 #
128 # END helper functions
129 #
130
131 #
132 # BEGIN platform definable
133 #
134
135 clip() {
136 # This base64 business is because bash cannot store binary data in a shell
137 # variable. Specifically, it cannot store nulls nor (non-trivally) store
138 # trailing new lines.
139 local sleep_argv0="password store sleep on display $DISPLAY"
140 pkill -f "^$sleep_argv0" 2>/dev/null && sleep 0.5
141 local before="$(xclip -o -selection "$X_SELECTION" 2>/dev/null | base64)"
142 echo -n "$1" | xclip -selection "$X_SELECTION" || die "Error: Could not copy data to the clipboard"
143 (
144 ( exec -a "$sleep_argv0" sleep "$CLIP_TIME" )
145 local now="$(xclip -o -selection "$X_SELECTION" | base64)"
146 [[ $now != $(echo -n "$1" | base64) ]] && before="$now"
147
148 # It might be nice to programatically check to see if klipper exists,
149 # as well as checking for other common clipboard managers. But for now,
150 # this works fine -- if qdbus isn't there or if klipper isn't running,
151 # this essentially becomes a no-op.
152 #
153 # Clipboard managers frequently write their history out in plaintext,
154 # so we axe it here:
155 qdbus org.kde.klipper /klipper org.kde.klipper.klipper.clearClipboardHistory &>/dev/null
156
157 echo "$before" | base64 -d | xclip -selection "$X_SELECTION"
158 ) 2>/dev/null & disown
159 echo "Copied $2 to clipboard. Will clear in $CLIP_TIME seconds."
160 }
161 tmpdir() {
162 [[ -n $SECURE_TMPDIR ]] && return
163 local warn=1
164 [[ $1 == "nowarn" ]] && warn=0
165 local template="$PROGRAM.XXXXXXXXXXXXX"
166 if [[ -d /dev/shm && -w /dev/shm && -x /dev/shm ]]; then
167 SECURE_TMPDIR="$(mktemp -d "/dev/shm/$template")"
168 remove_tmpfile() {
169 rm -rf "$SECURE_TMPDIR"
170 }
171 trap remove_tmpfile INT TERM EXIT
172 else
173 [[ $warn -eq 1 ]] && yesno "$(cat <<-_EOF
174 Your system does not have /dev/shm, which means that it may
175 be difficult to entirely erase the temporary non-encrypted
176 password file after editing.
177
178 Are you sure you would like to continue?
179 _EOF
180 )"
181 SECURE_TMPDIR="$(mktemp -d "${TMPDIR:-/tmp}/$template")"
182 shred_tmpfile() {
183 find "$SECURE_TMPDIR" -type f -exec $SHRED {} +
184 rm -rf "$SECURE_TMPDIR"
185 }
186 trap shred_tmpfile INT TERM EXIT
187 fi
188
189 }
190 GETOPT="getopt"
191 SHRED="shred -f -z"
192
193
194 #
195 # END platform definable
196 #
197
198
199 #
200 # BEGIN subcommand functions
201 #
202
203 cmd_version() {
204 cat <<-_EOF
205 ============================================
206 = pass: the standard unix password manager =
207 = =
208 = v1.6.5 =
209 = =
210 = Jason A. Donenfeld =
211 = Jason@zx2c4.com =
212 = =
213 = http://www.passwordstore.org/ =
214 ============================================
215 _EOF
216 }
217
218 cmd_usage() {
219 cmd_version
220 echo
221 cat <<-_EOF
222 Usage:
223 $PROGRAM init [--path=subfolder,-p subfolder] gpg-id...
224 Initialize new password storage and use gpg-id for encryption.
225 Selectively reencrypt existing passwords using new gpg-id.
226 $PROGRAM [ls] [subfolder]
227 List passwords.
228 $PROGRAM find pass-names...
229 List passwords that match pass-names.
230 $PROGRAM [show] [--clip,-c] pass-name
231 Show existing password and optionally put it on the clipboard.
232 If put on the clipboard, it will be cleared in $CLIP_TIME seconds.
233 $PROGRAM grep search-string
234 Search for password files containing search-string when decrypted.
235 $PROGRAM insert [--echo,-e | --multiline,-m] [--force,-f] pass-name
236 Insert new password. Optionally, echo the password back to the console
237 during entry. Or, optionally, the entry may be multiline. Prompt before
238 overwriting existing password unless forced.
239 $PROGRAM edit pass-name
240 Insert a new password or edit an existing password using ${EDITOR:-vi}.
241 $PROGRAM generate [--no-symbols,-n] [--clip,-c] [--in-place,-i | --force,-f] pass-name pass-length
242 Generate a new password of pass-length with optionally no symbols.
243 Optionally put it on the clipboard and clear board after $CLIP_TIME seconds.
244 Prompt before overwriting existing password unless forced.
245 Optionally replace only the first line of an existing file with a new password.
246 $PROGRAM rm [--recursive,-r] [--force,-f] pass-name
247 Remove existing password or directory, optionally forcefully.
248 $PROGRAM mv [--force,-f] old-path new-path
249 Renames or moves old-path to new-path, optionally forcefully, selectively reencrypting.
250 $PROGRAM cp [--force,-f] old-path new-path
251 Copies old-path to new-path, optionally forcefully, selectively reencrypting.
252 $PROGRAM git git-command-args...
253 If the password store is a git repository, execute a git command
254 specified by git-command-args.
255 $PROGRAM help
256 Show this text.
257 $PROGRAM version
258 Show version information.
259
260 More information may be found in the pass(1) man page.
261 _EOF
262 }
263
264 cmd_init() {
265 local opts id_path=""
266 opts="$($GETOPT -o p: -l path: -n "$PROGRAM" -- "$@")"
267 local err=$?
268 eval set -- "$opts"
269 while true; do case $1 in
270 -p|--path) id_path="$2"; shift 2 ;;
271 --) shift; break ;;
272 esac done
273
274 [[ $err -ne 0 || $# -lt 1 ]] && die "Usage: $PROGRAM $COMMAND [--path=subfolder,-p subfolder] gpg-id..."
275 [[ -n $id_path ]] && check_sneaky_paths "$id_path"
276 [[ -n $id_path && ! -d $PREFIX/$id_path && -e $PREFIX/$id_path ]] && die "Error: $PREFIX/$id_path exists but is not a directory."
277
278 local gpg_id="$PREFIX/$id_path/.gpg-id"
279
280 if [[ $# -eq 1 && -z $1 ]]; then
281 [[ ! -f "$gpg_id" ]] && die "Error: $gpg_id does not exist and so cannot be removed."
282 rm -v -f "$gpg_id" || exit 1
283 if [[ -d $GIT_DIR ]]; then
284 git rm -qr "$gpg_id"
285 git_commit "Deinitialize ${gpg_id}."
286 fi
287 rmdir -p "${gpg_id%/*}" 2>/dev/null
288 else
289 mkdir -v -p "$PREFIX/$id_path"
290 printf "%s\n" "$@" > "$gpg_id"
291 local id_print="$(printf "%s, " "$@")"
292 echo "Password store initialized for ${id_print%, }"
293 git_add_file "$gpg_id" "Set GPG id to ${id_print%, }."
294 fi
295
296 reencrypt_path "$PREFIX/$id_path"
297 git_add_file "$PREFIX/$id_path" "Reencrypt password store using new GPG id ${id_print%, }."
298 }
299
300 cmd_show() {
301 local opts clip=0
302 opts="$($GETOPT -o c -l clip -n "$PROGRAM" -- "$@")"
303 local err=$?
304 eval set -- "$opts"
305 while true; do case $1 in
306 -c|--clip) clip=1; shift ;;
307 --) shift; break ;;
308 esac done
309
310 [[ $err -ne 0 ]] && die "Usage: $PROGRAM $COMMAND [--clip,-c] [pass-name]"
311
312 local path="$1"
313 local passfile="$PREFIX/$path.gpg"
314 check_sneaky_paths "$path"
315 if [[ -f $passfile ]]; then
316 if [[ $clip -eq 0 ]]; then
317 $GPG -d "${GPG_OPTS[@]}" "$passfile" || exit $?
318 else
319 local pass="$($GPG -d "${GPG_OPTS[@]}" "$passfile" | head -n 1)"
320 [[ -n $pass ]] || exit 1
321 clip "$pass" "$path"
322 fi
323 elif [[ -d $PREFIX/$path ]]; then
324 if [[ -z $path ]]; then
325 echo "Password Store">/dev/null
326 else
327 echo "${path%\/}"
328 fi
329 #tree -C -l --noreport "$PREFIX/$path" | tail -n +2 | sed 's/\.gpg\(\x1B\[[0-9]\+m\)\{0,1\}\( ->\|$\)/\1\2/g' # remove .gpg at end of line, but keep colors
330
331 tree -f -i -l --noreport "$PREFIX/$path" | tail -n +2 |grep "\.gpg"| sed 's/\.gpg\(\x1B\[[0-9]\+m\)\{0,1\}\( ->\|$\)/\1\2/g' | sed "s|$PREFIX/||g"
332
333 #find "$PREFIX/$path" -type f \( ! -iname ".*" \) not -iwholename '*.git*' | tail -n +2 | sed 's/\.gpg\(\x1B\[[0-9]\+m\)\{0,1\}\( ->\|$\)/\1\2/g' | sed 's|'$PREFIX'/||' # remove .gpg at end of line, but keep colors
334 elif [[ -z $path ]]; then
335 die "Error: password store is empty. Try \"pass init\"."
336 else
337 die "Error: $path is not in the password store."
338 fi
339 }
340
341 cmd_find() {
342 [[ -z "$@" ]] && die "Usage: $PROGRAM $COMMAND pass-names..."
343 IFS="," eval 'echo "Search Terms: $*"'
344 local terms="*$(printf '%s*|*' "$@")"
345 tree -n -f -i -l --noreport -P "${terms%|*}" --prune --matchdirs --ignore-case "$PREFIX" | tail -n +2 | sed 's/\.gpg\(\x1B\[[0-9]\+m\)\{0,1\}\( ->\|$\)/\1\2/g'
346 }
347
348 cmd_grep() {
349 [[ $# -ne 1 ]] && die "Usage: $PROGRAM $COMMAND search-string"
350 local search="$1" passfile grepresults
351 while read -r -d "" passfile; do
352 grepresults="$($GPG -d "${GPG_OPTS[@]}" "$passfile" | grep --color=always "$search")"
353 [ $? -ne 0 ] && continue
354 passfile="${passfile%.gpg}"
355 passfile="${passfile#$PREFIX/}"
356 local passfile_dir="${passfile%/*}/"
357 [[ $passfile_dir == "${passfile}/" ]] && passfile_dir=""
358 passfile="${passfile##*/}"
359 printf "\e[94m%s\e[1m%s\e[0m:\n" "$passfile_dir" "$passfile"
360 echo "$grepresults"
361 done < <(find -L "$PREFIX" -iname '*.gpg' -print0)
362 }
363
364 cmd_insert() {
365 local opts multiline=0 noecho=1 force=0
366 opts="$($GETOPT -o mef -l multiline,echo,force -n "$PROGRAM" -- "$@")"
367 local err=$?
368 eval set -- "$opts"
369 while true; do case $1 in
370 -m|--multiline) multiline=1; shift ;;
371 -e|--echo) noecho=0; shift ;;
372 -f|--force) force=1; shift ;;
373 --) shift; break ;;
374 esac done
375
376 [[ $err -ne 0 || ( $multiline -eq 1 && $noecho -eq 0 ) || $# -ne 1 ]] && die "Usage: $PROGRAM $COMMAND [--echo,-e | --multiline,-m] [--force,-f] pass-name"
377 local path="$1"
378 local passfile="$PREFIX/$path.gpg"
379 check_sneaky_paths "$path"
380
381 [[ $force -eq 0 && -e $passfile ]] && yesno "An entry already exists for $path. Overwrite it?"
382
383 mkdir -p -v "$PREFIX/$(dirname "$path")"
384 set_gpg_recipients "$(dirname "$path")"
385
386 if [[ $multiline -eq 1 ]]; then
387 echo "Enter contents of $path and press Ctrl+D when finished:"
388 echo
389 $GPG -e "${GPG_RECIPIENT_ARGS[@]}" -o "$passfile" "${GPG_OPTS[@]}"
390 elif [[ $noecho -eq 1 ]]; then
391 local password password_again
392 while true; do
393 read -r -p "Enter password for $path: " -s password || exit 1
394 echo
395 read -r -p "Retype password for $path: " -s password_again || exit 1
396 echo
397 if [[ $password == "$password_again" ]]; then
398 $GPG -e "${GPG_RECIPIENT_ARGS[@]}" -o "$passfile" "${GPG_OPTS[@]}" <<<"$password"
399 break
400 else
401 echo "Error: the entered passwords do not match."
402 fi
403 done
404 else
405 local password
406 read -r -p "Enter password for $path: " -e password
407 $GPG -e "${GPG_RECIPIENT_ARGS[@]}" -o "$passfile" "${GPG_OPTS[@]}" <<<"$password"
408 fi
409 git_add_file "$passfile" "Add given password for $path to store."
410 }
411
412 cmd_edit() {
413 [[ $# -ne 1 ]] && die "Usage: $PROGRAM $COMMAND pass-name"
414
415 local path="$1"
416 check_sneaky_paths "$path"
417 mkdir -p -v "$PREFIX/$(dirname "$path")"
418 set_gpg_recipients "$(dirname "$path")"
419 local passfile="$PREFIX/$path.gpg"
420
421 tmpdir #Defines $SECURE_TMPDIR
422 local tmp_file="$(mktemp -u "$SECURE_TMPDIR/XXXXX")-${path//\//-}.txt"
423
424
425 local action="Add"
426 if [[ -f $passfile ]]; then
427 $GPG -d -o "$tmp_file" "${GPG_OPTS[@]}" "$passfile" || exit 1
428 action="Edit"
429 fi
430 ${EDITOR:-vi} "$tmp_file"
431 [[ -f $tmp_file ]] || die "New password not saved."
432 $GPG -d -o - "${GPG_OPTS[@]}" "$passfile" | diff - "$tmp_file" &>/dev/null && die "Password unchanged."
433 while ! $GPG -e "${GPG_RECIPIENT_ARGS[@]}" -o "$passfile" "${GPG_OPTS[@]}" "$tmp_file"; do
434 yesno "GPG encryption failed. Would you like to try again?"
435 done
436 git_add_file "$passfile" "$action password for $path using ${EDITOR:-vi}."
437 }
438
439 cmd_generate() {
440 local opts clip=0 force=0 symbols="-y" inplace=0
441 opts="$($GETOPT -o ncif -l no-symbols,clip,in-place,force -n "$PROGRAM" -- "$@")"
442 local err=$?
443 eval set -- "$opts"
444 while true; do case $1 in
445 -n|--no-symbols) symbols=""; shift ;;
446 -c|--clip) clip=1; shift ;;
447 -f|--force) force=1; shift ;;
448 -i|--in-place) inplace=1; shift ;;
449 --) shift; break ;;
450 esac done
451
452 [[ $err -ne 0 || $# -ne 2 || ( $force -eq 1 && $inplace -eq 1 ) ]] && die "Usage: $PROGRAM $COMMAND [--no-symbols,-n] [--clip,-c] [--in-place,-i | --force,-f] pass-name pass-length"
453 local path="$1"
454 local length="$2"
455 check_sneaky_paths "$path"
456 [[ ! $length =~ ^[0-9]+$ ]] && die "Error: pass-length \"$length\" must be a number."
457 mkdir -p -v "$PREFIX/$(dirname "$path")"
458 set_gpg_recipients "$(dirname "$path")"
459 local passfile="$PREFIX/$path.gpg"
460
461 [[ $inplace -eq 0 && $force -eq 0 && -e $passfile ]] && yesno "An entry already exists for $path. Overwrite it?"
462
463 local pass="$(pwgen -s $symbols $length 1)"
464 [[ -n $pass ]] || exit 1
465 if [[ $inplace -eq 0 ]]; then
466 $GPG -e "${GPG_RECIPIENT_ARGS[@]}" -o "$passfile" "${GPG_OPTS[@]}" <<<"$pass"
467 else
468 local passfile_temp="${passfile}.tmp.${RANDOM}.${RANDOM}.${RANDOM}.${RANDOM}.--"
469 if $GPG -d "${GPG_OPTS[@]}" "$passfile" | sed $'1c \\\n'"$(sed 's/[\/&]/\\&/g' <<<"$pass")"$'\n' | $GPG -e "${GPG_RECIPIENT_ARGS[@]}" -o "$passfile_temp" "${GPG_OPTS[@]}"; then
470 mv "$passfile_temp" "$passfile"
471 else
472 rm -f "$passfile_temp"
473 die "Could not reencrypt new password."
474 fi
475 fi
476 local verb="Add"
477 [[ $inplace -eq 1 ]] && verb="Replace"
478 git_add_file "$passfile" "$verb generated password for ${path}."
479
480 if [[ $clip -eq 0 ]]; then
481 printf "\e[1m\e[37mThe generated password for \e[4m%s\e[24m is:\e[0m\n\e[1m\e[93m%s\e[0m\n" "$path" "$pass"
482 else
483 clip "$pass" "$path"
484 fi
485 }
486
487 cmd_delete() {
488 local opts recursive="" force=0
489 opts="$($GETOPT -o rf -l recursive,force -n "$PROGRAM" -- "$@")"
490 local err=$?
491 eval set -- "$opts"
492 while true; do case $1 in
493 -r|--recursive) recursive="-r"; shift ;;
494 -f|--force) force=1; shift ;;
495 --) shift; break ;;
496 esac done
497 [[ $# -ne 1 ]] && die "Usage: $PROGRAM $COMMAND [--recursive,-r] [--force,-f] pass-name"
498 local path="$1"
499 check_sneaky_paths "$path"
500
501 local passfile="$PREFIX/${path%/}"
502 if [[ ! -d $passfile ]]; then
503 passfile="$PREFIX/$path.gpg"
504 [[ ! -f $passfile ]] && die "Error: $path is not in the password store."
505 fi
506
507 [[ $force -eq 1 ]] || yesno "Are you sure you would like to delete $path?"
508
509 rm $recursive -f -v "$passfile"
510 if [[ -d $GIT_DIR && ! -e $passfile ]]; then
511 git rm -qr "$passfile"
512 git_commit "Remove $path from store."
513 fi
514 rmdir -p "${passfile%/*}" 2>/dev/null
515 }
516
517 cmd_copy_move() {
518 local opts move=1 force=0
519 [[ $1 == "copy" ]] && move=0
520 shift
521 opts="$($GETOPT -o f -l force -n "$PROGRAM" -- "$@")"
522 local err=$?
523 eval set -- "$opts"
524 while true; do case $1 in
525 -f|--force) force=1; shift ;;
526 --) shift; break ;;
527 esac done
528 [[ $# -ne 2 ]] && die "Usage: $PROGRAM $COMMAND [--force,-f] old-path new-path"
529 check_sneaky_paths "$@"
530 local old_path="$PREFIX/${1%/}"
531 local new_path="$PREFIX/$2"
532 local old_dir="$old_path"
533
534 if [[ ! -d $old_path ]]; then
535 old_dir="${old_path%/*}"
536 old_path="${old_path}.gpg"
537 [[ ! -f $old_path ]] && die "Error: $1 is not in the password store."
538 fi
539
540 mkdir -p -v "${new_path%/*}"
541 [[ -d $old_path || -d $new_path || $new_path =~ /$ ]] || new_path="${new_path}.gpg"
542
543 local interactive="-i"
544 [[ ! -t 0 || $force -eq 1 ]] && interactive="-f"
545
546 if [[ $move -eq 1 ]]; then
547 mv $interactive -v "$old_path" "$new_path" || exit 1
548 [[ -e "$new_path" ]] && reencrypt_path "$new_path"
549
550 if [[ -d $GIT_DIR && ! -e $old_path ]]; then
551 git rm -qr "$old_path"
552 git_add_file "$new_path" "Rename ${1} to ${2}."
553 fi
554 rmdir -p "$old_dir" 2>/dev/null
555 else
556 cp $interactive -r -v "$old_path" "$new_path" || exit 1
557 [[ -e "$new_path" ]] && reencrypt_path "$new_path"
558 git_add_file "$new_path" "Copy ${1} to ${2}."
559 fi
560 }
561
562 cmd_git() {
563 if [[ $1 == "init" ]]; then
564 git "$@" || exit 1
565 git_add_file "$PREFIX" "Add current contents of password store."
566
567 echo '*.gpg diff=gpg' > "$PREFIX/.gitattributes"
568 git_add_file .gitattributes "Configure git repository for gpg file diff."
569 git config --local diff.gpg.binary true
570 git config --local diff.gpg.textconv "$GPG -d ${GPG_OPTS[*]}"
571 elif [[ -d $GIT_DIR ]]; then
572 tmpdir nowarn #Defines $SECURE_TMPDIR. We don't warn, because at most, this only copies encrypted files.
573 export TMPDIR="$SECURE_TMPDIR"
574 git "$@"
575 else
576 die "Error: the password store is not a git repository. Try \"$PROGRAM git init\"."
577 fi
578 }
579
580 #
581 # END subcommand functions
582 #
583
584 PROGRAM="${0##*/}"
585 COMMAND="$1"
586
587 case "$1" in
588 init) shift; cmd_init "$@" ;;
589 help|--help) shift; cmd_usage "$@" ;;
590 version|--version) shift; cmd_version "$@" ;;
591 show|ls|list) shift; cmd_show "$@" ;;
592 find|search) shift; cmd_find "$@" ;;
593 grep) shift; cmd_grep "$@" ;;
594 insert|add) shift; cmd_insert "$@" ;;
595 edit) shift; cmd_edit "$@" ;;
596 generate) shift; cmd_generate "$@" ;;
597 delete|rm|remove) shift; cmd_delete "$@" ;;
598 rename|mv) shift; cmd_copy_move "move" "$@" ;;
599 copy|cp) shift; cmd_copy_move "copy" "$@" ;;
600 git) shift; cmd_git "$@" ;;
601 *) COMMAND="show"; cmd_show "$@" ;;
602 esac
603 exit 0
Imprint / Impressum