[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]


From: Stefan Reichör
Subject: pwsafe.el
Date: Thu, 13 Apr 2006 22:04:04 +0200
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/22.0.50 (gnu/linux)

;;; pwsafe.el --- emacs interface to pwsafe

;; Copyright (C) 2006 by Stefan Reichoer

;; Author: Stefan Reichoer, <address@hidden>

;; pwsafe.el 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 2, or (at your option)
;; any later version.

;; pwsafe.el is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;;; Commentary:

;; pwsafe.el provides an Emacs interface for pwsafe:

;; The latest version of pwsafe.el can be found at:

;; Usage:
;; put the following in your .emacs:
;; (require 'pwsafe)
;; Then run M-x pwsafe
;; - if the password database file does not yet exist it will be created now
;; - otherwise a list of nicknames for your stored passwords is shown

;; I am very interested in feedback about the usability and about
;; security concerns for this package

;; Before using pwsafe.el, I used a simple unencrypted text file
;; for my passwords. So this package enhances the security for my use case.

;;; History:

;;; Code:

(defvar pwsafe-database "~/.pwsafe.dat")

(defvar pwsafe-use-long-listing nil "Display comments for the pwsafe entries.
This means that pwsafe -l is used to get the database entries.")

;; internal variables
(defvar pwsafe-password-prompt-regexp
  "[Pp]ass\\(word\\|phrase\\).*:\\s *\\'"
  "*Regexp matching prompts for passwords for pwsafe.")

;; taken from DVC.el
(defsubst pwsafe-face-add (str face &optional keymap menu help)
  "Add to string STR the face FACE.
Optionally, also add the text properties KEYMAP, MENU and HELP.

If KEYMAP is a symbol, (symbol-value KEYMAP) is used
as a keymap; and `substitute-command-keys' result
against (format \"\\{%s}\" (symbol-name keymap)) is appended to HELP.

If HELP is nil and if MENU is non nil, the MENU title is used as HELP."
  (let* ((strcpy (copy-sequence str))
         (key-help (when (symbolp keymap)
                     (substitute-command-keys (format "\\{%s}" (symbol-name 
         (prefix-help (if help help (when (and menu (stringp (cadr menu))) 
(cadr menu))))
         (long-help (if key-help
                        (if prefix-help (concat prefix-help "\n"
                                                "================" "\n"
                                                key-help) key-help)
         (keymap (if (symbolp keymap) (symbol-value keymap) keymap)))
    (add-text-properties 0 (length strcpy)
                         `(face ,face
                                font-lock-face ,face
                                ,@(when keymap
                                    `(mouse-face highlight
                                                 keymap ,keymap
                                                 help-echo ,long-help))
                                ,@(when menu
                                    `(,dar-cmenu ,menu))

(defun pwsafe-process-filter (proc string)
  (with-current-buffer (process-buffer proc)
    (if (string-match pwsafe-password-prompt-regexp string)
        (progn (string-match "^\\([^\n]+\\)\n*\\'" string)
               (let ((passwd (read-passwd (match-string 1 string))))
                 (process-send-string proc (concat passwd "\n"))))
      (insert string))
    (cond ((member pwsafe-running-command '(copy-passwd copy-user-and-passwd))
           (dolist (line (split-string string "\n"))
             (when (string-match "\\(You are ready to paste\\|Sending 
\\(username\\|password\\) for\\)" line)
               (message (format "pwsafe: %s" line)))))
          ((eq pwsafe-running-command 'add)
           ;(message "  pwsafe-process-filter: %s" string)
           (dolist (line (split-string string "\n"))
             (when (string-match "^\\(group \\[<none>\\]\\|username\\|notes\\): 
" line)
               (let ((answer (read-string (concat "pwsafe add " line))))
                 (process-send-string proc (concat answer "\n"))))
             (when (string-match "Generate random password\\? \\[y\\]" line)
               (process-send-string proc "y"))
             (when (string-match "^type .+ length .+ bits of entropy" line)
               (let ((answer (read-char-exclusive (concat "pwsafe password: " 
                 (process-send-string proc (char-to-string answer)))))))))

(defun pwsafe-run (cmd &rest args)
  (with-current-buffer (get-buffer-create "*pwsafe*")
    (delete-region (point-min) (point-max)))
  (let ((process (apply 'start-process "pwsafe" "*pwsafe*" "pwsafe" args)))
    (setq pwsafe-running-command cmd)
    (set-process-filter process 'pwsafe-process-filter)
    (set-process-sentinel process 'pwsafe-process-sentinel)))

(defun pwsafe-process-sentinel (process event)
    (set-buffer (process-buffer process))
    (cond ((string= event "finished\n")
           (cond ((eq pwsafe-running-command 'createdb)
                  (message "Created pwsafe database %s" (expand-file-name 
                 ((eq pwsafe-running-command 'list)
                 ((eq pwsafe-running-command 'copy-passwd)
                  );; do nothing here
                 ((eq pwsafe-running-command 'copy-user-and-passwd)
                  );; do nothing here
                 ((eq pwsafe-running-command 'delete)
                  (message "pwsafe delete finished"))
                  (message "pwsafe process finished")))
           (setq dar-running-command nil))
          ((string= event "killed\n")
           (message "pwsafe process killed")
           (setq dar-running-command nil))
          ((string-match "exited abnormally" event)
           (while (accept-process-output process 0 100))
           ;; find last error message and show it.
           (goto-char (point-max))
           (message "pwsafe failed: %s" event)
           (setq pwsafe-running-command nil))
           (message "pwsafe process had unknown event: %s" event)))))

(defun pwsafe-createdb ()
  "Run pwsafe --createdb"
  (let ((database-file-name (expand-file-name pwsafe-database)))
    (if (file-exists-p database-file-name)
        (message "The pwsafe database %s does already exist." 
      (pwsafe-run 'createdb "--createdb" "-f" database-file-name)
      (message "Created pwsafe database %s" database-file-name))))

(defun pwsafe ()
  "Major mode to interact with the command line password safe pwsafe.
The following keys are defined:
  (let ((database-file-name (expand-file-name pwsafe-database)))
    (if (file-exists-p database-file-name)
        (pwsafe-run 'list (if pwsafe-use-long-listing "-l" "") "-f" 
      (when (yes-or-no-p (format "pwsafe database %s does not exist - create 
it? " database-file-name))
        (with-current-buffer (get-buffer "*pwsafe*")
          (delete-region (point-min) (point-max)))

(defun pwsafe-list-passwords ()
  (let ((pwlist)
    (with-current-buffer (get-buffer "*pwsafe*")
      (goto-char (- (point-max) 1))
      (re-search-backward "^$")
      (forward-line 1)
      (while (< (point) (- (point-max) 2))
        (if pwsafe-use-long-listing
              (when (looking-at "^\\(.+\\)  -  \\(.+\\)")
                (add-to-list 'pwlist (list (match-string 1) (match-string 2))))
              (when (looking-at "^> \\(.+\\)")
                (setcar pwlist (append (car pwlist) (list (match-string 1))))))
          (add-to-list 'pwlist (list (buffer-substring-no-properties 
(line-beginning-position) (line-end-position)) nil)))
        (forward-line 1)))
    (pop-to-buffer "*pwsafe-list*")
    (let ((buffer-read-only nil))
      (delete-region (point-min) (point-max))
      (unless pwlist
        (insert "Empty pwsafe database."))
      (dolist (e (nreverse pwlist))
        (setq start-pos (point))
        (insert (dar-face-add (format "%s" (car e)) 
        (when (nth 2 e)
          (insert (format "  %s\n" (nth 2 e))))
        (setq overlay (make-overlay start-pos (point)))
        (overlay-put overlay 'pwsafe e))
      (goto-char (point-min)))))

(defvar pwsafe-list-mode-map () "Keymap used in `pwsafe-list-mode' buffers.")
(when (not pwsafe-list-mode-map)
  (setq pwsafe-list-mode-map (make-sparse-keymap))
  (define-key pwsafe-list-mode-map [?q] 'bury-buffer)
  (define-key pwsafe-list-mode-map [?n] 'pwsafe-next)
  (define-key pwsafe-list-mode-map [?p] 'pwsafe-previous)
  (define-key pwsafe-list-mode-map [?g] 'pwsafe)
  (define-key pwsafe-list-mode-map [?a] 'pwsafe-add-entry)
  (define-key pwsafe-list-mode-map [(control ?d)] 'pwsafe-delete-entry)
  (define-key pwsafe-list-mode-map [?U] 'pwsafe-copy-user-name)
  (define-key pwsafe-list-mode-map [?P] 'pwsafe-copy-password)
  (define-key pwsafe-list-mode-map [?B] 'pwsafe-copy-user-name-and-password))

(easy-menu-define pwsafe-mode-menu pwsafe-list-mode-map
"`pwsafe-list-mode' menu"
                    ["Copy user name, then password" 
pwsafe-copy-user-name-and-password t]
                    ["Copy user name" pwsafe-copy-user-name t]
                    ["Copy password" pwsafe-copy-password t]
                    ["Add new entry" pwsafe-add-entry t]
                    ["Delete entry" pwsafe-delete-entry t]

(defun pwsafe-list-mode ()
  "Major Mode to entries from pwsafe."
  (toggle-read-only 1)
  (use-local-map pwsafe-list-mode-map)
  (setq major-mode 'pwsafe-list-mode)
  (setq mode-name "pwsafe-list"))

(defun pwsafe-list-line-info ()
  (let ((pwsafe-info nil))
    (dolist (overlay (overlays-at (point)))
      (setq pwsafe-info (or pwsafe-info
                            (overlay-get overlay 'pwsafe))))

(defun pwsafe-next ()
  (let ((next (next-overlay-change (point))))
    (when (< next (point-max))
      (goto-char next))))

(defun pwsafe-previous ()
  (goto-char (previous-overlay-change (point))))

(defun pwsafe-copy-user-name ()
  (let ((info (pwsafe-list-line-info)))
    (pwsafe-run 'copy-passwd "-u" "-f" (expand-file-name pwsafe-database) (car 

(defun pwsafe-copy-password ()
  (let ((info (pwsafe-list-line-info)))
    (pwsafe-run 'copy-passwd "-p" "-f" (expand-file-name pwsafe-database) (car 

(defun pwsafe-copy-user-name-and-password ()
  (let ((info (pwsafe-list-line-info)))
    (pwsafe-run 'copy-user-and-passwd "-u" "-p" "-f" (expand-file-name 
pwsafe-database) (car info))))

(defun pwsafe-add-entry (name)
  (interactive "spwsafe add bookmark name: ")
  (pwsafe-run 'add "-f" (expand-file-name pwsafe-database) "--add" name))

(defun pwsafe-delete-entry ()
  (let ((info (pwsafe-list-line-info)))
    (when (yes-or-no-p (format "Delete pwsafe entry for %s? " (car info)))
      (pwsafe-run 'delete "-f" (expand-file-name pwsafe-database) "--delete" 
(car info)))))

(provide 'pwsafe)

;;; arch-tag: 900b1054-5492-4b81-ae06-54a5d48e6aca
;;; pwsafe.el ends here

reply via email to

[Prev in Thread] Current Thread [Next in Thread]