aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--contrib/dmenu/README.md7
-rwxr-xr-xcontrib/dmenu/passmenu16
-rw-r--r--contrib/emacs/CHANGELOG.md31
-rw-r--r--contrib/emacs/Cask4
-rw-r--r--contrib/emacs/README.md8
-rw-r--r--contrib/emacs/password-store.el164
-rw-r--r--contrib/vim/redact_pass.vim7
-rw-r--r--src/completion/pass.bash-completion2
-rw-r--r--src/completion/pass.fish-completion124
-rw-r--r--src/completion/pass.zsh-completion2
-rwxr-xr-xsrc/password-store.sh27
-rw-r--r--src/platform/darwin.sh6
-rw-r--r--tests/gnupg/gpg.conf6
-rw-r--r--tests/setup.sh12
-rwxr-xr-xtests/t0020-show-tests.sh5
15 files changed, 264 insertions, 157 deletions
diff --git a/contrib/dmenu/README.md b/contrib/dmenu/README.md
index 9d54fb4..8a196cb 100644
--- a/contrib/dmenu/README.md
+++ b/contrib/dmenu/README.md
@@ -4,6 +4,10 @@ clipboard without having to open up a terminal window if you don't already have
one open. If `--type` is specified, the password is typed using [xdotool][]
instead of copied to the clipboard.
+On wayland [dmenu-wl][] is used to replace dmenu and [ydotool][] to replace xdotool.
+Note that the latter requires access to the [uinput][] device, so you'll probably
+need to add an extra udev rule or similar to give certain non-root users permission.
+
# Usage
passmenu [--type] [dmenu arguments...]
@@ -11,3 +15,6 @@ instead of copied to the clipboard.
[dmenu]: http://tools.suckless.org/dmenu/
[xdotool]: http://www.semicomplete.com/projects/xdotool/
[pass]: http://www.zx2c4.com/projects/password-store/
+[dmenu-wl]: https://github.com/nyyManni/dmenu-wayland
+[ydotool]: https://github.com/ReimuNotMoe/ydotool
+[uinput]: https://www.kernel.org/doc/html/v4.12/input/uinput.html
diff --git a/contrib/dmenu/passmenu b/contrib/dmenu/passmenu
index 83268bc..76d92ab 100755
--- a/contrib/dmenu/passmenu
+++ b/contrib/dmenu/passmenu
@@ -8,18 +8,28 @@ if [[ $1 == "--type" ]]; then
shift
fi
+if [[ -n $WAYLAND_DISPLAY ]]; then
+ dmenu=dmenu-wl
+ xdotool="ydotool type --file -"
+elif [[ -n $DISPLAY ]]; then
+ dmenu=dmenu
+ xdotool="xdotool type --clearmodifiers --file -"
+else
+ echo "Error: No Wayland or X11 display detected" >&2
+ exit 1
+fi
+
prefix=${PASSWORD_STORE_DIR-~/.password-store}
password_files=( "$prefix"/**/*.gpg )
password_files=( "${password_files[@]#"$prefix"/}" )
password_files=( "${password_files[@]%.gpg}" )
-password=$(printf '%s\n' "${password_files[@]}" | dmenu "$@")
+password=$(printf '%s\n' "${password_files[@]}" | "$dmenu" "$@")
[[ -n $password ]] || exit
if [[ $typeit -eq 0 ]]; then
pass show -c "$password" 2>/dev/null
else
- pass show "$password" | { IFS= read -r pass; printf %s "$pass"; } |
- xdotool type --clearmodifiers --file -
+ pass show "$password" | { IFS= read -r pass; printf %s "$pass"; } | $xdotool
fi
diff --git a/contrib/emacs/CHANGELOG.md b/contrib/emacs/CHANGELOG.md
index 3217015..e15414f 100644
--- a/contrib/emacs/CHANGELOG.md
+++ b/contrib/emacs/CHANGELOG.md
@@ -1,3 +1,34 @@
+# 2.3.2
+
+* (bugfix) Ensure the system clipboard is cleared after
+ the timeout expired.
+
+# 2.3.1
+
+* (bug) Drop dependency on s library.
+
+# 2.3.0
+
+* (bug) Drop auth-source-pass dependency.
+ Bump Emacs minor version requirement to emacs 26.
+
+# 2.2.0
+
+* (feature) Add command password-store-generate-no-symbols
+
+# 2.1.5
+
+* (bugfix) Fix an infloop on Windows enviroments.
+
+# 2.1.4
+
+* Drop dependency on f library.
+
+# 2.1.3
+
+* Update password-store-clear docstring; clarify that the
+ optional argument is only used in the print out message.
+
# 2.1.2
* Make argument optional in password-store-clear to preserve
diff --git a/contrib/emacs/Cask b/contrib/emacs/Cask
index f46b166..1d8ce9f 100644
--- a/contrib/emacs/Cask
+++ b/contrib/emacs/Cask
@@ -4,9 +4,7 @@
(package-file "password-store.el")
(development
- (depends-on "f")
(depends-on "with-editor")
(depends-on "ecukes")
(depends-on "ert-runner")
- (depends-on "el-mock")
- (depends-on "auth-source-pass"))
+ (depends-on "el-mock"))
diff --git a/contrib/emacs/README.md b/contrib/emacs/README.md
index d3679e9..8269c35 100644
--- a/contrib/emacs/README.md
+++ b/contrib/emacs/README.md
@@ -19,6 +19,14 @@ Interactive:
Password: ........
Confirm password: ........
+ ;; Generate a random password.
+ M-x password-store-generate
+ Password entry: bar-account
+
+ ;; Generate a random password without symbols.
+ M-x password-store-generate-no-symbols
+ Password entry: qux-account
+
M-x password-store-copy
Password entry: foo-account
Copied password for foo-account to the kill ring. Will clear in 45 seconds.
diff --git a/contrib/emacs/password-store.el b/contrib/emacs/password-store.el
index 313f81b..c7cc991 100644
--- a/contrib/emacs/password-store.el
+++ b/contrib/emacs/password-store.el
@@ -4,43 +4,52 @@
;; Author: Svend Sorensen <svend@svends.net>
;; Maintainer: Tino Calancha <tino.calancha@gmail.com>
-;; Version: 2.1.2
+;; Version: 2.3.2
;; URL: https://www.passwordstore.org/
-;; Package-Requires: ((emacs "25") (f "0.11.0") (s "1.9.0") (with-editor "2.5.11") (auth-source-pass "5.0.0"))
-;; Keywords: tools pass password password-store
+;; Package-Requires: ((emacs "26.1") (with-editor "2.5.11"))
+;; SPDX-License-Identifier: GPL-3.0-or-later
+;; Keywords: tools pass password password-store gpg
;; This file is not part of GNU Emacs.
-;; This program is free software: you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
+;; This program is free software: you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation, either version 3 of
+;; the License, or (at your option) any later version.
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
+;; This program is distributed in the hope that it will be
+;; useful, but WITHOUT ANY WARRANTY; without even the implied
+;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+;; PURPOSE. See the GNU General Public License for more details.
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+;; You should have received a copy of the GNU General Public
+;; License along with this program. If not, see
+;; <http://www.gnu.org/licenses/>.
;;; Commentary:
-;; This package provides functions for working with pass ("the
-;; standard Unix password manager").
-;;
-;; http://www.passwordstore.org/
+;; This package provides and Emacs interface for working with
+;; pass ("the standard Unix password manager").
+
+;; https://www.passwordstore.org/
;;; Code:
-(require 'f)
(require 'with-editor)
(require 'auth-source-pass)
(defgroup password-store '()
- "Emacs mode for password-store."
+ "Emacs mode for password-store.
+The standard Unix password manager"
:prefix "password-store-"
- :group 'password-store)
+ :group 'password-store
+ :link '(url-link :tag "Description" "https://www.passwordstore.org/")
+ :link '(url-link :tag "Download" "https://melpa.org/#/password-store")
+ :link `(url-link :tag "Send Bug Report"
+ ,(concat "mailto:" "password-store" "@" "lists.zx2c4" ".com?subject=
+password-store.el bug: \
+&body=Describe bug here, starting with `emacs -q'. \
+Don't forget to mention your Emacs and library versions.")))
(defcustom password-store-password-length 25
"Default password length."
@@ -56,7 +65,7 @@
:type 'number)
(defcustom password-store-url-field "url"
- "Field name used in the files to indicate an url."
+ "Field name used in the files to indicate a URL."
:group 'password-store
:type 'string)
@@ -68,17 +77,20 @@
"Timer for clearing clipboard.")
(defun password-store-timeout ()
- "Number of seconds to wait before clearing the password.
+ "Number of seconds to wait before restoring the clipboard.
-This function just returns `password-store-time-before-clipboard-restore'.
-Kept for backward compatibility with other libraries."
- password-store-time-before-clipboard-restore)
+This function just returns
+`password-store-time-before-clipboard-restore'. Kept for
+backward compatibility with other libraries."
+password-store-time-before-clipboard-restore)
+
+(make-obsolete 'password-store-timeout 'password-store-time-before-clipboard-restore "2.0.4")
(defun password-store--run-1 (callback &rest args)
"Run pass with ARGS.
-Nil arguments are ignored. Calls CALLBACK with the output on success,
-or outputs error message on failure."
+Nil arguments are ignored. Calls CALLBACK with the output on
+success, or outputs error message on failure."
(let ((output ""))
(make-process
:name "password-store-gpg"
@@ -89,9 +101,10 @@ or outputs error message on failure."
(setq output (concat output text)))
:sentinel (lambda (process state)
(cond
- ((string= state "finished\n")
+ ((and (eq (process-status process) 'exit)
+ (zerop (process-exit-status process)))
(funcall callback output))
- ((string= state "open\n") (accept-process-output process))
+ ((eq (process-status process) 'run) (accept-process-output process))
(t (error (concat "password-store: " state))))))))
(defun password-store--run (&rest args)
@@ -118,9 +131,9 @@ Nil arguments are ignored. Output is discarded."
(cons password-store-executable
(delq nil args)) " "))))
-(defun password-store--run-init (gpg-ids &optional folder)
+(defun password-store--run-init (gpg-ids &optional subdir)
(apply 'password-store--run "init"
- (if folder (format "--path=%s" folder))
+ (if subdir (format "--path=%s" subdir))
gpg-ids))
(defun password-store--run-list (&optional subdir)
@@ -187,11 +200,11 @@ Nil arguments are ignored. Output is discarded."
(defun password-store--entry-to-file (entry)
"Return file name corresponding to ENTRY."
- (concat (f-join (password-store-dir) entry) ".gpg"))
+ (concat (expand-file-name entry (password-store-dir)) ".gpg"))
(defun password-store--file-to-entry (file)
"Return entry name corresponding to FILE."
- (f-no-ext (f-relative file (password-store-dir))))
+ (file-name-sans-extension (file-relative-name file (password-store-dir))))
(defun password-store--completing-read (&optional require-match)
"Read a password entry in the minibuffer, with completion.
@@ -214,11 +227,11 @@ ENTRY is the name of a password-store entry."
(defun password-store-list (&optional subdir)
"List password entries under SUBDIR."
(unless subdir (setq subdir ""))
- (let ((dir (f-join (password-store-dir) subdir)))
- (if (f-directory? dir)
+ (let ((dir (expand-file-name subdir (password-store-dir))))
+ (if (file-directory-p dir)
(delete-dups
(mapcar 'password-store--file-to-entry
- (f-files dir (lambda (file) (equal (f-ext file) "gpg")) t))))))
+ (directory-files-recursively dir ".+\\.gpg\\'"))))))
;;;###autoload
(defun password-store-edit (entry)
@@ -230,8 +243,8 @@ ENTRY is the name of a password-store entry."
(defun password-store-get (entry &optional callback)
"Return password for ENTRY.
-Returns the first line of the password data.
-When CALLBACK is non-`NIL', call CALLBACK with the first line instead."
+Returns the first line of the password data. When CALLBACK is
+non-`NIL', call CALLBACK with the first line instead."
(let* ((inhibit-message t)
(secret (auth-source-pass-get 'secret entry)))
(if (not callback) secret
@@ -242,9 +255,10 @@ When CALLBACK is non-`NIL', call CALLBACK with the first line instead."
;;;###autoload
(defun password-store-get-field (entry field &optional callback)
"Return FIELD for ENTRY.
-FIELD is a string, for instance \"url\".
-When CALLBACK is non-`NIL', call it with the line associated to FIELD instead.
-If FIELD equals to symbol secret, then this function reduces to `password-store-get'."
+FIELD is a string, for instance \"url\". When CALLBACK is
+non-`NIL', call it with the line associated to FIELD instead. If
+FIELD equals to symbol secret, then this function reduces to
+`password-store-get'."
(let* ((inhibit-message t)
(secret (auth-source-pass-get field entry)))
(if (not callback) secret
@@ -255,7 +269,14 @@ If FIELD equals to symbol secret, then this function reduces to `password-store-
;;;###autoload
(defun password-store-clear (&optional field)
- "Clear FIELD in kill ring."
+ "Clear secret in the kill ring.
+
+Optional argument FIELD, a symbol or a string, describes the
+stored secret to clear; if nil, then set it to 'secret. Note,
+FIELD does not affect the function logic; it is only used to
+display the message:
+
+\(message \"Field %s cleared from kill ring and system clipboard.\" field)."
(interactive "i")
(unless field (setq field 'secret))
(when password-store-timeout-timer
@@ -263,14 +284,15 @@ If FIELD equals to symbol secret, then this function reduces to `password-store-
(setq password-store-timeout-timer nil))
(when password-store-kill-ring-pointer
(setcar password-store-kill-ring-pointer "")
+ (kill-new "")
(setq password-store-kill-ring-pointer nil)
- (message "Field %s cleared." field)))
+ (message "Field %s cleared from kill ring and system clipboard." field)))
(defun password-store--save-field-in-kill-ring (entry secret field)
(password-store-clear field)
(kill-new secret)
(setq password-store-kill-ring-pointer kill-ring-yank-pointer)
- (message "Copied %s for %s to the kill ring. Will clear in %s seconds."
+ (message "Copied %s for %s to the kill ring and system clipboard. Will clear in %s seconds."
field entry password-store-time-before-clipboard-restore)
(setq password-store-timeout-timer
(run-at-time password-store-time-before-clipboard-restore nil
@@ -280,9 +302,10 @@ If FIELD equals to symbol secret, then this function reduces to `password-store-
(defun password-store-copy (entry)
"Add password for ENTRY into the kill ring.
-Clear previous password from the kill ring. Pointer to the kill ring
-is stored in `password-store-kill-ring-pointer'. Password is cleared
-after `password-store-time-before-clipboard-restore' seconds."
+Clear previous password from the kill ring. Pointer to the kill
+ring is stored in `password-store-kill-ring-pointer'. Password
+is cleared after `password-store-time-before-clipboard-restore'
+seconds."
(interactive (list (password-store--completing-read t)))
(password-store-get
entry
@@ -293,10 +316,12 @@ after `password-store-time-before-clipboard-restore' seconds."
(defun password-store-copy-field (entry field)
"Add FIELD for ENTRY into the kill ring.
-Clear previous secret from the kill ring. Pointer to the kill ring is
-stored in `password-store-kill-ring-pointer'. Secret field is cleared
-after `password-store-timeout' seconds.
-If FIELD equals to symbol secret, then this function reduces to `password-store-copy'."
+Clear previous secret from the kill ring. Pointer to the kill
+ring is stored in `password-store-kill-ring-pointer'. Secret
+field is cleared after
+`password-store-time-before-clipboard-restore' seconds. If FIELD
+equals to symbol secret, then this function reduces to
+`password-store-copy'."
(interactive
(let ((entry (password-store--completing-read)))
(list entry (password-store-read-field entry))))
@@ -335,17 +360,36 @@ Separate multiple IDs with spaces."
Default PASSWORD-LENGTH is `password-store-password-length'."
(interactive (list (password-store--completing-read)
- (when current-prefix-arg
- (abs (prefix-numeric-value current-prefix-arg)))))
- (unless password-length (setq password-length password-store-password-length))
- ;; A message with the output of the command is not printed because
- ;; the output contains the password.
- (password-store--run-generate entry password-length t)
+ (and current-prefix-arg
+ (abs (prefix-numeric-value current-prefix-arg)))))
+ ;; A message with the output of the command is not printed
+ ;; because the output contains the password.
+ (password-store--run-generate
+ entry
+ (or password-length password-store-password-length)
+ 'force)
+ nil)
+
+;;;###autoload
+(defun password-store-generate-no-symbols (entry &optional password-length)
+ "Generate a new password without symbols for ENTRY with PASSWORD-LENGTH.
+
+Default PASSWORD-LENGTH is `password-store-password-length'."
+ (interactive (list (password-store--completing-read)
+ (and current-prefix-arg
+ (abs (prefix-numeric-value current-prefix-arg)))))
+
+ ;; A message with the output of the command is not printed
+ ;; because the output contains the password.
+ (password-store--run-generate
+ entry
+ (or password-length password-store-password-length)
+ 'force 'no-symbols)
nil)
;;;###autoload
(defun password-store-remove (entry)
- "Remove existing password for ENTRY."
+ "Remove ENTRY."
(interactive (list (password-store--completing-read t)))
(message "%s" (password-store--run-remove entry t)))
@@ -358,13 +402,13 @@ Default PASSWORD-LENGTH is `password-store-password-length'."
;;;###autoload
(defun password-store-version ()
- "Show version of pass executable."
+ "Show version of `password-store-executable'."
(interactive)
(message "%s" (password-store--run-version)))
;;;###autoload
(defun password-store-url (entry)
- "Browse URL stored in ENTRY."
+ "Load URL for ENTRY."
(interactive (list (password-store--completing-read t)))
(let ((url (password-store-get-field entry password-store-url-field)))
(if url (browse-url url)
diff --git a/contrib/vim/redact_pass.vim b/contrib/vim/redact_pass.vim
index a3d67e8..2e752fe 100644
--- a/contrib/vim/redact_pass.vim
+++ b/contrib/vim/redact_pass.vim
@@ -35,6 +35,7 @@ function! s:CheckArgsRedact()
" Tell the user what we're doing so they know this worked, via a message and
" a global variable they can check
+ redraw
echomsg 'Editing password file--disabled leaky options!'
let g:redact_pass_redacted = 1
@@ -48,4 +49,10 @@ augroup redact_pass
\,$TMPDIR/pass.?*/?*.txt
\,/tmp/pass.?*/?*.txt
\ call s:CheckArgsRedact()
+ " Work around macOS' dynamic symlink structure for temporary directories
+ if has('mac')
+ autocmd VimEnter
+ \ /private/var/?*/pass.?*/?*.txt
+ \ call s:CheckArgsRedact()
+ endif
augroup END
diff --git a/src/completion/pass.bash-completion b/src/completion/pass.bash-completion
index 95d3e1e..2d23cbf 100644
--- a/src/completion/pass.bash-completion
+++ b/src/completion/pass.bash-completion
@@ -72,7 +72,7 @@ _pass_complete_folders () {
_pass_complete_keys () {
local GPG="gpg"
- which gpg2 &>/dev/null && GPG="gpg2"
+ command -v gpg2 &>/dev/null && GPG="gpg2"
local IFS=$'\n'
# Extract names and email addresses from gpg --list-keys
diff --git a/src/completion/pass.fish-completion b/src/completion/pass.fish-completion
index 8637874..0f57dd2 100644
--- a/src/completion/pass.fish-completion
+++ b/src/completion/pass.fish-completion
@@ -1,28 +1,22 @@
-#!/usr/bin/env fish
-
# Copyright (C) 2012-2014 Dmitry Medvinsky <me@dmedvinsky.name>. All Rights Reserved.
# This file is licensed under the GPLv2+. Please see COPYING for more information.
-set PROG 'pass'
+set -l PROG 'pass'
function __fish_pass_get_prefix
- set -l prefix "$PASSWORD_STORE_DIR"
- if [ -z "$prefix" ]
- set prefix "$HOME/.password-store"
+ if set -q PASSWORD_STORE_DIR
+ realpath -- "$PASSWORD_STORE_DIR"
+ else
+ echo "$HOME/.password-store"
end
- echo "$prefix"
end
function __fish_pass_needs_command
- set -l cmd (commandline -opc)
- if [ (count $cmd) -eq 1 -a $cmd[1] = $PROG ]
- return 0
- end
- return 1
+ [ (count (commandline -opc)) -eq 1 ]
end
function __fish_pass_uses_command
- set cmd (commandline -opc)
+ set -l cmd (commandline -opc)
if [ (count $cmd) -gt 1 ]
if [ $argv[1] = $cmd[2] ]
return 0
@@ -39,7 +33,8 @@ function __fish_pass_print
set -l ext $argv[1]
set -l strip $argv[2]
set -l prefix (__fish_pass_get_prefix)
- printf '%s\n' "$prefix"/**"$ext" | sed "s#$prefix/\(.*\)$strip#\1#"
+ set -l matches $prefix/**$ext
+ printf '%s\n' $matches | sed "s#$prefix/\(.*\)$strip#\1#"
end
function __fish_pass_print_entry_dirs
@@ -55,63 +50,66 @@ function __fish_pass_print_entries_and_dirs
__fish_pass_print_entries
end
+function __fish_pass_git_complete
+ set -l prefix (__fish_pass_get_prefix)
+ set -l git_cmd (commandline -opc) (commandline -ct)
+ set -e git_cmd[1 2] # Drop "pass git".
+ complete -C"git -C $prefix $git_cmd"
+end
-complete -c $PROG -e
-complete -c $PROG -f -A -n '__fish_pass_needs_command' -a help -d 'Command: show usage help'
-complete -c $PROG -f -A -n '__fish_pass_needs_command' -a version -d 'Command: show program version'
+complete -c $PROG -f -n '__fish_pass_needs_command' -a help -d 'Command: show usage help'
+complete -c $PROG -f -n '__fish_pass_needs_command' -a version -d 'Command: show program version'
-complete -c $PROG -f -A -n '__fish_pass_needs_command' -a init -d 'Command: initialize new password storage'
-complete -c $PROG -f -A -n '__fish_pass_uses_command init' -s p -l path -d 'Assign gpg-id for specified sub folder of password store'
+complete -c $PROG -f -n '__fish_pass_needs_command' -a init -d 'Command: initialize new password storage'
+complete -c $PROG -f -n '__fish_pass_uses_command init' -s p -l path -d 'Assign gpg-id for specified sub folder of password store'
-complete -c $PROG -f -A -n '__fish_pass_needs_command' -a ls -d 'Command: list passwords'
-complete -c $PROG -f -A -n '__fish_pass_uses_command ls' -a "(__fish_pass_print_entry_dirs)"
+complete -c $PROG -f -n '__fish_pass_needs_command' -a ls -d 'Command: list passwords'
+complete -c $PROG -f -n '__fish_pass_uses_command ls' -a "(__fish_pass_print_entry_dirs)"
-complete -c $PROG -f -A -n '__fish_pass_needs_command' -a insert -d 'Command: insert new password'
-complete -c $PROG -f -A -n '__fish_pass_uses_command insert' -s e -l echo -d 'Echo the password on console'
-complete -c $PROG -f -A -n '__fish_pass_uses_command insert' -s m -l multiline -d 'Provide multiline password entry'
-complete -c $PROG -f -A -n '__fish_pass_uses_command insert' -s f -l force -d 'Do not prompt before overwritting'
-complete -c $PROG -f -A -n '__fish_pass_uses_command insert' -a "(__fish_pass_print_entry_dirs)"
+complete -c $PROG -f -n '__fish_pass_needs_command' -a insert -d 'Command: insert new password'
+complete -c $PROG -f -n '__fish_pass_uses_command insert' -s e -l echo -d 'Echo the password on console'
+complete -c $PROG -f -n '__fish_pass_uses_command insert' -s m -l multiline -d 'Provide multiline password entry'
+complete -c $PROG -f -n '__fish_pass_uses_command insert' -s f -l force -d 'Do not prompt before overwritting'
+complete -c $PROG -f -n '__fish_pass_uses_command insert' -a "(__fish_pass_print_entry_dirs)"
-complete -c $PROG -f -A -n '__fish_pass_needs_command' -a generate -d 'Command: generate new password'
-complete -c $PROG -f -A -n '__fish_pass_uses_command generate' -s n -l no-symbols -d 'Do not use special symbols'
-complete -c $PROG -f -A -n '__fish_pass_uses_command generate' -s c -l clip -d 'Put the password in clipboard'
-complete -c $PROG -f -A -n '__fish_pass_uses_command generate' -s f -l force -d 'Do not prompt before overwritting'
-complete -c $PROG -f -A -n '__fish_pass_uses_command generate' -s i -l in-place -d 'Replace only the first line with the generated password'
-complete -c $PROG -f -A -n '__fish_pass_uses_command generate' -a "(__fish_pass_print_entry_dirs)"
+complete -c $PROG -f -n '__fish_pass_needs_command' -a generate -d 'Command: generate new password'
+complete -c $PROG -f -n '__fish_pass_uses_command generate' -s n -l no-symbols -d 'Do not use special symbols'
+complete -c $PROG -f -n '__fish_pass_uses_command generate' -s c -l clip -d 'Put the password in clipboard'
+complete -c $PROG -f -n '__fish_pass_uses_command generate' -s f -l force -d 'Do not prompt before overwritting'
+complete -c $PROG -f -n '__fish_pass_uses_command generate' -s i -l in-place -d 'Replace only the first line with the generated password'
+complete -c $PROG -f -n '__fish_pass_uses_command generate' -a "(__fish_pass_print_entry_dirs)"
-complete -c $PROG -f -A -n '__fish_pass_needs_command' -a mv -d 'Command: rename existing password'
-complete -c $PROG -f -A -n '__fish_pass_uses_command mv' -s f -l force -d 'Force rename'
-complete -c $PROG -f -A -n '__fish_pass_uses_command mv' -a "(__fish_pass_print_entries_and_dirs)"
+complete -c $PROG -f -n '__fish_pass_needs_command' -a mv -d 'Command: rename existing password'
+complete -c $PROG -f -n '__fish_pass_uses_command mv' -s f -l force -d 'Force rename'
+complete -c $PROG -f -n '__fish_pass_uses_command mv' -a "(__fish_pass_print_entries_and_dirs)"
-complete -c $PROG -f -A -n '__fish_pass_needs_command' -a cp -d 'Command: copy existing password'
-complete -c $PROG -f -A -n '__fish_pass_uses_command cp' -s f -l force -d 'Force copy'
-complete -c $PROG -f -A -n '__fish_pass_uses_command cp' -a "(__fish_pass_print_entries_and_dirs)"
+complete -c $PROG -f -n '__fish_pass_needs_command' -a cp -d 'Command: copy existing password'
+complete -c $PROG -f -n '__fish_pass_uses_command cp' -s f -l force -d 'Force copy'
+complete -c $PROG -f -n '__fish_pass_uses_command cp' -a "(__fish_pass_print_entries_and_dirs)"
-complete -c $PROG -f -A -n '__fish_pass_needs_command' -a rm -d 'Command: remove existing password'
-complete -c $PROG -f -A -n '__fish_pass_uses_command rm' -s r -l recursive -d 'Remove password groups recursively'
-complete -c $PROG -f -A -n '__fish_pass_uses_command rm' -s f -l force -d 'Force removal'
-complete -c $PROG -f -A -n '__fish_pass_uses_command rm' -a "(__fish_pass_print_entries_and_dirs)"
+complete -c $PROG -f -n '__fish_pass_needs_command' -a rm -d 'Command: remove existing password'
+complete -c $PROG -f -n '__fish_pass_uses_command rm' -s r -l recursive -d 'Remove password groups recursively'
+complete -c $PROG -f -n '__fish_pass_uses_command rm' -s f -l force -d 'Force removal'
+complete -c $PROG -f -n '__fish_pass_uses_command rm' -a "(__fish_pass_print_entries_and_dirs)"
-complete -c $PROG -f -A -n '__fish_pass_needs_command' -a edit -d 'Command: edit password using text editor'
-complete -c $PROG -f -A -n '__fish_pass_uses_command edit' -a "(__fish_pass_print_entries)"
+complete -c $PROG -f -n '__fish_pass_needs_command' -a edit -d 'Command: edit password using text editor'
+complete -c $PROG -f -n '__fish_pass_uses_command edit' -a "(__fish_pass_print_entries)"
-complete -c $PROG -f -A -n '__fish_pass_needs_command' -a show -d 'Command: show existing password'
-complete -c $PROG -f -A -n '__fish_pass_uses_command show' -s c -l clip -d 'Put password in clipboard'
-complete -c $PROG -f -A -n '__fish_pass_uses_command show' -a "(__fish_pass_print_entries)"
+complete -c $PROG -f -n '__fish_pass_needs_command' -a show -d 'Command: show existing password'
+complete -c $PROG -f -n '__fish_pass_uses_command show' -s c -l clip -d 'Put password in clipboard'
+complete -c $PROG -f -n '__fish_pass_uses_command show' -a "(__fish_pass_print_entries)"
# When no command is given, `show` is defaulted.
-complete -c $PROG -f -A -n '__fish_pass_needs_command' -s c -l clip -d 'Put password in clipboard'
-complete -c $PROG -f -A -n '__fish_pass_needs_command' -a "(__fish_pass_print_entries)"
-complete -c $PROG -f -A -n '__fish_pass_uses_command -c' -a "(__fish_pass_print_entries)"
-complete -c $PROG -f -A -n '__fish_pass_uses_command --clip' -a "(__fish_pass_print_entries)"
-
-complete -c $PROG -f -A -n '__fish_pass_needs_command' -a git -d 'Command: execute a git command'
-complete -c $PROG -f -A -n '__fish_pass_uses_command git' -a 'init' -d 'Initialize git repository'
-complete -c $PROG -f -A -n '__fish_pass_uses_command git' -a 'status' -d 'Show status of the repo'
-complete -c $PROG -f -A -n '__fish_pass_uses_command git' -a 'add' -d 'Add changes to the index'
-complete -c $PROG -f -A -n '__fish_pass_uses_command git' -a 'commit' -d 'Commit changes to the repo'
-complete -c $PROG -f -A -n '__fish_pass_uses_command git' -a 'push' -d 'Push changes to remote repo'
-complete -c $PROG -f -A -n '__fish_pass_uses_command git' -a 'pull' -d 'Pull changes from remote repo'
-complete -c $PROG -f -A -n '__fish_pass_uses_command git' -a 'log' -d 'View changelog'
-
-complete -c $PROG -f -A -n '__fish_pass_needs_command' -a find -d 'Command: find a password file or directory matching pattern'
-complete -c $PROG -f -A -n '__fish_pass_needs_command' -a grep -d 'Command: search inside of decrypted password files for matching pattern'
+complete -c $PROG -f -n '__fish_pass_needs_command' -s c -l clip -d 'Put password in clipboard'
+complete -c $PROG -f -n '__fish_pass_needs_command' -a "(__fish_pass_print_entries)"
+complete -c $PROG -f -n '__fish_pass_uses_command -c' -a "(__fish_pass_print_entries)"
+complete -c $PROG -f -n '__fish_pass_uses_command --clip' -a "(__fish_pass_print_entries)"
+
+complete -c $PROG -f -n '__fish_pass_needs_command' -a git -d 'Command: execute a git command'
+complete -c $PROG -f -n '__fish_pass_uses_command git' -a '(__fish_pass_git_complete)'
+complete -c $PROG -f -n '__fish_pass_needs_command' -a find -d 'Command: find a password file or directory matching pattern'
+complete -c $PROG -f -n '__fish_pass_needs_command' -a grep -d 'Command: search inside of decrypted password files for matching pattern'
+complete -c $PROG -f -n '__fish_pass_uses_command grep' -a '(begin
+ set -l cmd (commandline -opc) (commandline -ct)
+ set -e cmd[1 2] # Drop "pass grep".
+ complete -C"grep $cmd"
+end)'
diff --git a/src/completion/pass.zsh-completion b/src/completion/pass.zsh-completion
index 27ce15a..d911e12 100644
--- a/src/completion/pass.zsh-completion
+++ b/src/completion/pass.zsh-completion
@@ -124,7 +124,7 @@ _pass_complete_entries_helper () {
local IFS=$'\n'
local prefix
zstyle -s ":completion:${curcontext}:" prefix prefix || prefix="${PASSWORD_STORE_DIR:-$HOME/.password-store}"
- _values -C 'passwords' ${$(find -L "$prefix" \( -name .git -o -name .gpg-id \) -prune -o $@ -print 2>/dev/null | sed -e "s#${prefix}/\{0,1\}##" -e 's#\.gpg##' -e 's#\\#\\\\#' | sort):-""}
+ _values -C 'passwords' ${$(find -L "$prefix" \( -name .git -o -name .gpg-id \) -prune -o $@ -print 2>/dev/null | sed -e "s#${prefix}/\{0,1\}##" -e 's#\.gpg##' -e 's#\\#\\\\#g' -e 's#:#\\:#g' | sort):-""}
}
_pass_complete_entries_with_subdirs () {
diff --git a/src/password-store.sh b/src/password-store.sh
index 1d119f2..22e818f 100755
--- a/src/password-store.sh
+++ b/src/password-store.sh
@@ -9,7 +9,7 @@ set -o pipefail
GPG_OPTS=( $PASSWORD_STORE_GPG_OPTS "--quiet" "--yes" "--compress-algo=none" "--no-encrypt-to" )
GPG="gpg"
export GPG_TTY="${GPG_TTY:-$(tty 2>/dev/null)}"
-which gpg2 &>/dev/null && GPG="gpg2"
+command -v gpg2 &>/dev/null && GPG="gpg2"
[[ -n $GPG_AGENT_INFO || $GPG == "gpg2" ]] && GPG_OPTS+=( "--batch" "--use-agent" )
PREFIX="${PASSWORD_STORE_DIR:-$HOME/.password-store}"
@@ -20,6 +20,7 @@ GENERATED_LENGTH="${PASSWORD_STORE_GENERATED_LENGTH:-25}"
CHARACTER_SET="${PASSWORD_STORE_CHARACTER_SET:-[:punct:][:alnum:]}"
CHARACTER_SET_NO_SYMBOLS="${PASSWORD_STORE_CHARACTER_SET_NO_SYMBOLS:-[:alnum:]}"
+unset GIT_DIR GIT_WORK_TREE GIT_NAMESPACE GIT_INDEX_FILE GIT_INDEX_VERSION GIT_OBJECT_DIRECTORY GIT_COMMON_DIR
export GIT_CEILING_DIRECTORIES="$PREFIX/.."
#
@@ -69,6 +70,7 @@ verify_file() {
set_gpg_recipients() {
GPG_RECIPIENT_ARGS=( )
GPG_RECIPIENTS=( )
+ local gpg_id
if [[ -n $PASSWORD_STORE_KEY ]]; then
for gpg_id in $PASSWORD_STORE_KEY; do
@@ -97,8 +99,9 @@ set_gpg_recipients() {
verify_file "$current"
- local gpg_id
while read -r gpg_id; do
+ gpg_id="${gpg_id%%#*}" # strip comment
+ [[ -n $gpg_id ]] || continue
GPG_RECIPIENT_ARGS+=( "-r" "$gpg_id" )
GPG_RECIPIENTS+=( "$gpg_id" )
done < "$current"
@@ -126,7 +129,7 @@ reencrypt_path() {
done
gpg_keys="$($GPG $PASSWORD_STORE_GPG_OPTS --list-keys --with-colons "${GPG_RECIPIENTS[@]}" | sed -n 's/^sub:[^idr:]*:[^:]*:[^:]*:\([^:]*\):[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[a-zA-Z]*e[a-zA-Z]*:.*/\1/p' | LC_ALL=C sort -u)"
fi
- current_keys="$(LC_ALL=C $GPG $PASSWORD_STORE_GPG_OPTS -v --no-secmem-warning --no-permission-warning --decrypt --list-only --keyid-format long "$passfile" 2>&1 | sed -n 's/^gpg: public key is \([A-F0-9]\+\)$/\1/p' | LC_ALL=C sort -u)"
+ current_keys="$(LC_ALL=C $GPG $PASSWORD_STORE_GPG_OPTS -v --no-secmem-warning --no-permission-warning --decrypt --list-only --keyid-format long "$passfile" 2>&1 | sed -nE 's/^gpg: public key is ([A-F0-9]+)$/\1/p' | LC_ALL=C sort -u)"
if [[ $gpg_keys != "$current_keys" ]]; then
echo "$passfile_display: reencrypting to ${gpg_keys//$'\n'/ }"
@@ -134,7 +137,7 @@ reencrypt_path() {
mv "$passfile_temp" "$passfile" || rm -f "$passfile_temp"
fi
prev_gpg_recipients="${GPG_RECIPIENTS[*]}"
- done < <(find "$1" -path '*/.git' -prune -o -iname '*.gpg' -print0)
+ done < <(find "$1" -path '*/.git' -prune -o -path '*/.extensions' -prune -o -iname '*.gpg' -print0)
}
check_sneaky_paths() {
local path
@@ -152,7 +155,7 @@ check_sneaky_paths() {
#
clip() {
- if [[ -n $WAYLAND_DISPLAY ]]; then
+ if [[ -n $WAYLAND_DISPLAY ]] && command -v wl-copy &> /dev/null; then
local copy_cmd=( wl-copy )
local paste_cmd=( wl-paste -n )
if [[ $X_SELECTION == primary ]]; then
@@ -160,12 +163,12 @@ clip() {
paste_cmd+=( --primary )
fi
local display_name="$WAYLAND_DISPLAY"
- elif [[ -n $DISPLAY ]]; then
+ elif [[ -n $DISPLAY ]] && command -v xclip &> /dev/null; then
local copy_cmd=( xclip -selection "$X_SELECTION" )
local paste_cmd=( xclip -o -selection "$X_SELECTION" )
local display_name="$DISPLAY"
else
- die "Error: No X11 or Wayland display detected"
+ die "Error: No X11 or Wayland display and clipper detected"
fi
local sleep_argv0="password store sleep on display $display_name"
@@ -259,7 +262,7 @@ cmd_version() {
============================================
= pass: the standard unix password manager =
= =
- = v1.7.3 =
+ = v1.7.4 =
= =
= Jason A. Donenfeld =
= Jason@zx2c4.com =
@@ -352,7 +355,7 @@ cmd_init() {
signing_keys+=( --default-key $key )
done
$GPG "${GPG_OPTS[@]}" "${signing_keys[@]}" --detach-sign "$gpg_id" || die "Could not sign .gpg_id."
- key="$($GPG --verify --status-fd=1 "$gpg_id.sig" "$gpg_id" 2>/dev/null | sed -n 's/^\[GNUPG:\] VALIDSIG [A-F0-9]\{40\} .* \([A-F0-9]\{40\}\)$/\1/p')"
+ key="$($GPG "${GPG_OPTS[@]}" --verify --status-fd=1 "$gpg_id.sig" "$gpg_id" 2>/dev/null | sed -n 's/^\[GNUPG:\] VALIDSIG [A-F0-9]\{40\} .* \([A-F0-9]\{40\}\)$/\1/p')"
[[ -n $key ]] || die "Signing of .gpg_id unsuccessful."
git_add_file "$gpg_id.sig" "Signing new GPG id with ${key//[$IFS]/,}."
fi
@@ -399,7 +402,7 @@ cmd_show() {
else
echo "${path%\/}"
fi
- tree -C -l --noreport "$PREFIX/$path" | tail -n +2 | sed -E 's/\.gpg(\x1B\[[0-9]+m)?( ->|$)/\1\2/g' # remove .gpg at end of line, but keep colors
+ tree -N -C -l --noreport "$PREFIX/$path" 3>&- | tail -n +2 | sed -E 's/\.gpg(\x1B\[[0-9]+m)?( ->|$)/\1\2/g' # remove .gpg at end of line, but keep colors
elif [[ -z $path ]]; then
die "Error: password store is empty. Try \"pass init\"."
else
@@ -411,7 +414,7 @@ cmd_find() {
[[ $# -eq 0 ]] && die "Usage: $PROGRAM $COMMAND pass-names..."
IFS="," eval 'echo "Search Terms: $*"'
local terms="*$(printf '%s*|*' "$@")"
- tree -C -l --noreport -P "${terms%|*}" --prune --matchdirs --ignore-case "$PREFIX" | tail -n +2 | sed -E 's/\.gpg(\x1B\[[0-9]+m)?( ->|$)/\1\2/g'
+ tree -N -C -l --noreport -P "${terms%|*}" --prune --matchdirs --ignore-case "$PREFIX" 3>&- | tail -n +2 | sed -E 's/\.gpg(\x1B\[[0-9]+m)?( ->|$)/\1\2/g'
}
cmd_grep() {
@@ -427,7 +430,7 @@ cmd_grep() {
passfile="${passfile##*/}"
printf "\e[94m%s\e[1m%s\e[0m:\n" "$passfile_dir" "$passfile"
echo "$grepresults"
- done < <(find -L "$PREFIX" -path '*/.git' -prune -o -iname '*.gpg' -print0)
+ done < <(find -L "$PREFIX" -path '*/.git' -prune -o -path '*/.extensions' -prune -o -iname '*.gpg' -print0)
}
cmd_insert() {
diff --git a/src/platform/darwin.sh b/src/platform/darwin.sh
index 342ecce..9a1fda8 100644
--- a/src/platform/darwin.sh
+++ b/src/platform/darwin.sh
@@ -34,15 +34,11 @@ tmpdir() {
qrcode() {
if type imgcat >/dev/null 2>&1; then
echo -n "$1" | qrencode --size 10 -o - | imgcat
- elif type gm >/dev/null 2>&1; then
- echo -n "$1" | qrencode --size 10 -o - | gm display -title "pass: $2" -geometry +200+200 -
- elif type display >/dev/null 2>&1; then
- echo -n "$1" | qrencode --size 10 -o - | display -title "pass: $2" -geometry +200+200 -
else
echo -n "$1" | qrencode -t utf8
fi
}
-GETOPT="$(brew --prefix gnu-getopt 2>/dev/null || { which port &>/dev/null && echo /opt/local; } || echo /usr/local)/bin/getopt"
+GETOPT="$({ test -x /usr/local/opt/gnu-getopt/bin/getopt && echo /usr/local/opt/gnu-getopt; } || brew --prefix gnu-getopt 2>/dev/null || { command -v port &>/dev/null && echo /opt/local; } || echo /usr/local)/bin/getopt"
SHRED="srm -f -z"
BASE64="openssl base64"
diff --git a/tests/gnupg/gpg.conf b/tests/gnupg/gpg.conf
index 60ece49..67daa58 100644
--- a/tests/gnupg/gpg.conf
+++ b/tests/gnupg/gpg.conf
@@ -1,3 +1,3 @@
-group group1 = E4691410 D774A374
-group group2 = E4691410
-group big group = CF90C77B D774A374 EB7D54A8 E4691410 39E5020C
+group group1 = 9378267629F989A0E96677B7976DD3D6E4691410 70BD448330ACF0653645B8F2B4DDBFF0D774A374
+group group2 = 9378267629F989A0E96677B7976DD3D6E4691410
+group big group = D4C78DB7920E1E27F5416B81CC9DB947CF90C77B 70BD448330ACF0653645B8F2B4DDBFF0D774A374 62EBE74BE834C2EC71E6414595C4B715EB7D54A8 9378267629F989A0E96677B7976DD3D6E4691410 4D2AFBDE67C60F5999D143AFA6E073D439E5020C
diff --git a/tests/setup.sh b/tests/setup.sh
index 5d1e794..058ce0a 100644
--- a/tests/setup.sh
+++ b/tests/setup.sh
@@ -52,13 +52,13 @@ fi
export GNUPGHOME="$TEST_HOME/gnupg/"
chmod 700 "$GNUPGHOME"
GPG="gpg"
-which gpg2 &>/dev/null && GPG="gpg2"
+command -v gpg2 &>/dev/null && GPG="gpg2"
# We don't want any currently running agent to conflict.
unset GPG_AGENT_INFO
-KEY1="CF90C77B" # pass test key 1
-KEY2="D774A374" # pass test key 2
-KEY3="EB7D54A8" # pass test key 3
-KEY4="E4691410" # pass test key 4
-KEY5="39E5020C" # pass test key 5
+KEY1="D4C78DB7920E1E27F5416B81CC9DB947CF90C77B" # pass test key 1
+KEY2="70BD448330ACF0653645B8F2B4DDBFF0D774A374" # pass test key 2
+KEY3="62EBE74BE834C2EC71E6414595C4B715EB7D54A8" # pass test key 3
+KEY4="9378267629F989A0E96677B7976DD3D6E4691410" # pass test key 4
+KEY5="4D2AFBDE67C60F5999D143AFA6E073D439E5020C" # pass test key 5
diff --git a/tests/t0020-show-tests.sh b/tests/t0020-show-tests.sh
index a4b782f..81b87b4 100755
--- a/tests/t0020-show-tests.sh
+++ b/tests/t0020-show-tests.sh
@@ -15,6 +15,11 @@ test_expect_success 'Test "show" command with spaces' '
[[ $("$PASS" show "I am a cred with lots of spaces") == "BLAH!!" ]]
'
+test_expect_success 'Test "show" command with unicode' '
+ "$PASS" generate 🏠 &&
+ "$PASS" show | grep -q '🏠'
+'
+
test_expect_success 'Test "show" of nonexistant password' '
test_must_fail "$PASS" show cred2
'