summaryrefslogtreecommitdiff
path: root/contrib/emacs/password-store.el
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/emacs/password-store.el')
-rw-r--r--contrib/emacs/password-store.el173
1 files changed, 173 insertions, 0 deletions
diff --git a/contrib/emacs/password-store.el b/contrib/emacs/password-store.el
new file mode 100644
index 0000000..f95c9c7
--- /dev/null
+++ b/contrib/emacs/password-store.el
@@ -0,0 +1,173 @@
+;;; password-store.el --- Password store (pass) support
+
+;; Copyright (C) 2014 Svend Sorensen <svend@ciffer.net>
+
+;; Author: Svend Sorensen <svend@ciffer.net>
+;; Version: 0.1
+;; Package-Requires: ((f "0.11.0") (s "1.9.0"))
+;; Keywords: pass
+
+;; 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 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/>.
+
+;;; Commentary:
+
+;; This package provides functions for working with pass ("the
+;; standard Unix password manager").
+;;
+;; http://www.zx2c4.com/projects/password-store/
+
+;;; Code:
+
+(require 'f)
+(require 's)
+
+(defvar pass-executable
+ (executable-find "pass")
+ "Pass executable.")
+
+(defconst password-store-password-length 8
+ "Default password length.")
+
+(defconst password-store-timeout 45
+ "Number of seconds to wait before clearing the password.")
+
+(defun password-store--run (&rest args)
+ "Run pass with ARGS.
+
+Returns the output on success, or outputs error message on
+failure."
+ (with-temp-buffer
+ (let ((exit-code
+ (apply 'call-process
+ (append
+ (list pass-executable nil (current-buffer) nil)
+ args))))
+ (if (zerop exit-code)
+ (buffer-string)
+ (error (s-chomp (buffer-string)))))))
+
+(defvar password-store-kill-ring-pointer nil
+ "The tail of of the kill ring ring whose car is the password.")
+
+(defun password-store-dir ()
+ "Return password store directory."
+ (or (getenv "PASSWORD_STORE_DIR")
+ "~/.password-store"))
+
+(defun password-store--entry-to-file (entry)
+ "Return file name corresponding to ENTRY."
+ (concat (f-join (password-store-dir) entry) ".gpg"))
+
+(defun password-store--file-to-entry (file)
+ "Return entry name corresponding to FILE."
+ (f-no-ext (f-relative file (password-store-dir))))
+
+(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)
+ (mapcar 'password-store--file-to-entry
+ (f-files dir (lambda (file) (equal (f-ext file) "gpg")) t)))))
+
+;;;###autoload
+(defun password-store-edit (entry)
+ "Edit password for ENTRY.
+
+This edits the password file directly in Emacs, so changes will
+need to be commited manually if git is being used."
+ (interactive (list (completing-read "Password entry: " (password-store-list))))
+ (find-file (password-store--entry-to-file entry)))
+
+;;;###autoload
+(defun password-store-get (entry)
+ "Return password for ENTRY.
+
+Returns the first line of the password data."
+ (car (s-lines (password-store--run "show" entry))))
+
+;;;###autoload
+(defun password-store-clear ()
+ "Clear password in kill ring."
+ (interactive)
+ (if password-store-kill-ring-pointer
+ (progn
+ (setcar password-store-kill-ring-pointer "")
+ (setq password-store-kill-ring-pointer nil)
+ (message "Password cleared."))))
+
+;;;###autoload
+(defun password-store-copy (entry)
+ "Add password for ENTRY to kill ring.
+
+Clear previous password from kill ring. Pointer to kill ring is
+stored in `password-store-kill-ring-pointer'. Password is cleared
+after `password-store-timeout' seconds."
+ (interactive (list (completing-read "Password entry: " (password-store-list))))
+ (let ((password (password-store-get entry)))
+ (password-store-clear)
+ (kill-new password)
+ (setq password-store-kill-ring-pointer kill-ring-yank-pointer)
+ (message "Copied %s to the kill ring. Will clear in %s seconds." entry password-store-timeout)
+ (run-at-time password-store-timeout nil 'password-store-clear)))
+
+;;;###autoload
+(defun password-store-generate (entry &optional password-length)
+ "Generate a new password for ENTRY with PASSWORD-LENGTH.
+
+Default PASSWORD-LENGTH is `password-store-password-length'."
+ (interactive (list (read-string "Password entry: ")
+ (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" "-f" entry (number-to-string password-length))
+ nil)
+
+;;;###autoload
+(defun password-store-insert (entry password)
+ "Insert a new ENTRY containing PASSWORD."
+ (interactive (list (read-string "Password entry: ")
+ (read-passwd "Password: " t)))
+ (message (s-chomp (shell-command-to-string (format "echo %s | %s insert -m -f %s" password pass-executable entry)))))
+
+;;;###autoload
+(defun password-store-remove (entry)
+ "Remove existing password for ENTRY."
+ (interactive (list (completing-read "Password entry: " (password-store-list))))
+ (message (s-chomp (password-store--run "rm" "-f" entry))))
+
+;;;###autoload
+(defun password-store-url (entry)
+ "Browse URL stored in ENTRY.
+
+This will only browse URLs that start with http:// or http:// to
+avoid sending a password to the browser."
+ (interactive (list (completing-read "Password entry: " (password-store-list))))
+ (let ((url (password-store-get entry)))
+ (if (or (string-prefix-p "http://" url)
+ (string-prefix-p "https://" url))
+ (browse-url url)
+ (error "%s" "String does not look like a URL"))))
+
+;;;###autoload
+(defun password-store-version ()
+ "Show version of pass executable."
+ (interactive)
+ (message (s-chomp (password-store--run "version"))))
+
+;;; password-store.el ends here
t(1) on partial writes. 2013-03-20ui-shared: squelch compiler warning.Jason A. Donenfeld1-0/+1 Since tail is initialized to 0, we will never get a warning on the last if statement, but recent gcc complains anyway. So, we initialize len as well. Future gcc versions should be able to optimize this out anyway. 2013-03-20cgit.mk: Use SHELL_PATH_SQ to run gen-version.shJohn Keeping1-1/+1 On some platforms (notably Solaris) /bin/sh doesn't support enough of POSIX for gen-version.sh to run. Git's Makefile provides SHELL_PATH_SQ to address this issue so we just have to use it. Signed-off-by: John Keeping <john@keeping.me.uk> 2013-03-20cgit.mk: don't rebuild everything if CGIT_VERSION changesJohn Keeping1-1/+8 If CGIT_VERSION is in CGIT_CFLAGS then a change in version (for example because you have committed your changes) causes all of the CGit objects to be rebuilt. Avoid this by using EXTRA_CPPFLAGS to add the version for only those files that are affected and make them depend on VERSION. Signed-off-by: John Keeping <john@keeping.me.uk> 2013-03-20ui-patch: use cgit_version not CGIT_VERSIONJohn Keeping1-1/+1 We already have a global cgit_version which is set from the #define'd CGIT_VERSION in cgit.c. Change ui-patch.c to use this so that we only need to rebuild cgit.o when the version changes. Signed-off-by: John Keeping <john@keeping.me.uk> 2013-03-20Makefile: re-use Git's Makefile where possibleJohn Keeping3-119/+80 Git does quite a lot of platform-specific detection in its Makefile, which can result in it defining preprocessor variables that are used in its header files. If CGit does not define the same variables it can result in different sizes of some structures in different places in the same application. For example, on Solaris Git uses it's "compat" regex library which has a different sized regex_t structure than that available in the platform regex.h. This has a knock-on effect on the size of "struct rev_info" and leads to hard to diagnose runtime issues. In order to avoid all of this, introduce a "cgit.mk" file that includes Git's Makefile and make all of the existing logic apply to CGit's objects as well. This is slightly complicated because Git's Makefile must run in Git's directory, so all references to CGit files need to be prefixed with "../". In addition, OBJECTS is a simply expanded variable in Git's Makefile so we cannot just add our objects to it. Instead we must copy the two applicable rules into "cgit.mk". This has the advantage that we can split CGit-specific CFLAGS from Git's CFLAGS and hence avoid rebuilding all of Git whenever a CGit-specific value changes. Signed-off-by: John Keeping <john@keeping.me.uk> Acked-by: Jamie Couture <jamie.couture@gmail.com>