-;;; epa-file.el --- the EasyPG Assistant, transparent file encryption
-;; Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc.
+;;; epa-file.el --- the EasyPG Assistant, transparent file encryption -*- lexical-binding: t -*-
+;; Copyright (C) 2006-2013 Free Software Foundation, Inc.
;; Author: Daiki Ueno <ueno@unixuser.org>
;; Keywords: PGP, GnuPG
+;; Package: epa
;; This file is part of GNU Emacs.
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs 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, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
;; GNU Emacs 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., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Code:
(require 'epa)
-
-(defgroup epa-file nil
- "The EasyPG Assistant hooks for transparent file encryption"
- :version "23.1"
- :group 'epa)
-
-(defun epa-file--file-name-regexp-set (variable value)
- (set-default variable value)
- (if (fboundp 'epa-file-name-regexp-update)
- (epa-file-name-regexp-update)))
-
-(defcustom epa-file-name-regexp "\\.gpg\\(~\\|\\.~[0-9]+~\\)?\\'"
- "Regexp which matches filenames to be encrypted with GnuPG.
-
-If you set this outside Custom while epa-file is already enabled, you
-have to call `epa-file-name-regexp-update' after setting it to
-properly update file-name-handler-alist. Setting this through Custom
-does that automatically."
- :type 'regexp
- :group 'epa-file
- :set 'epa-file--file-name-regexp-set)
+(require 'epa-hook)
(defcustom epa-file-cache-passphrase-for-symmetric-encryption nil
- "If non-nil, cache passphrase for symmetric encryption."
- :type 'boolean
- :group 'epa-file)
+ "If non-nil, cache passphrase for symmetric encryption.
-(defcustom epa-file-inhibit-auto-save t
- "If non-nil, disable auto-saving when opening an encrypted file."
+For security reasons, this option is turned off by default and
+not recommended to use. Instead, consider using public-key
+encryption with gpg-agent which does the same job in a safer
+way."
:type 'boolean
:group 'epa-file)
(defcustom epa-file-select-keys nil
- "If non-nil, always asks user to select recipients."
- :type 'boolean
+ "Control whether or not to pop up the key selection dialog.
+
+If t, always asks user to select recipients.
+If nil, query user only when `epa-file-encrypt-to' is not set.
+If neither t nor nil, doesn't ask user. In this case, symmetric
+encryption is used."
+ :type '(choice (const :tag "Ask always" t)
+ (const :tag "Ask when recipients are not set" nil)
+ (const :tag "Don't ask" silent))
:group 'epa-file)
-(defvar epa-file-encrypt-to nil
- "*Recipient(s) used for encrypting files.
-May either be a string or a list of strings.")
-
-;;;###autoload
-(put 'epa-file-encrypt-to 'safe-local-variable
- (lambda (val)
- (or (stringp val)
- (and (listp val)
- (catch 'safe
- (mapc (lambda (elt)
- (unless (stringp elt)
- (throw 'safe nil)))
- val)
- t)))))
-
-;;;###autoload
-(put 'epa-file-encrypt-to 'permanent-local t)
-
-(defvar epa-file-handler
- (cons epa-file-name-regexp 'epa-file-handler))
-
-(defvar epa-file-auto-mode-alist-entry
- (list epa-file-name-regexp nil 'epa-file))
-
(defvar epa-file-passphrase-alist nil)
(eval-and-compile
(defalias 'epa-file--decode-coding-string 'decode-coding-string)
(defalias 'epa-file--decode-coding-string 'identity)))
-(defun epa-file-name-regexp-update ()
- (interactive)
- (unless (equal (car epa-file-handler) epa-file-name-regexp)
- (setcar epa-file-handler epa-file-name-regexp)))
-
(defun epa-file-passphrase-callback-function (context key-id file)
(if (and epa-file-cache-passphrase-for-symmetric-encryption
(eq key-id 'SYM))
(cons entry
epa-file-passphrase-alist)))
(setq passphrase (epa-passphrase-callback-function context
- key-id nil))
+ key-id
+ file))
(setcdr entry (copy-sequence passphrase))
passphrase))))
- (epa-passphrase-callback-function context key-id nil)))
+ (epa-passphrase-callback-function context key-id file)))
+;;;###autoload
(defun epa-file-handler (operation &rest args)
(save-match-data
(let ((op (get operation 'epa-file)))
(if op
- (apply op args)
- (epa-file-run-real-handler operation args)))))
+ (apply op args)
+ (epa-file-run-real-handler operation args)))))
(defun epa-file-run-real-handler (operation args)
(let ((inhibit-file-name-handlers
(if (fboundp 'decode-coding-inserted-region)
(save-restriction
(narrow-to-region (point) (point))
- (let ((multibyte enable-multibyte-characters))
- (set-buffer-multibyte nil)
- (insert string)
- (set-buffer-multibyte multibyte)
+ (insert (if enable-multibyte-characters
+ (string-to-multibyte string)
+ string))
(decode-coding-inserted-region
(point-min) (point-max)
(substring file 0 (string-match epa-file-name-regexp file))
- visit beg end replace)))
+ visit beg end replace))
(insert (epa-file--decode-coding-string string (or coding-system-for-read
'undecided)))))
+(defvar epa-file-error nil)
+(defun epa-file--find-file-not-found-function ()
+ (let ((error epa-file-error))
+ (save-window-excursion
+ (kill-buffer))
+ (signal 'file-error
+ (cons "Opening input file" (cdr error)))))
+
(defvar last-coding-system-used)
(defun epa-file-insert-file-contents (file &optional visit beg end replace)
(barf-if-buffer-read-only)
(error "Attempt to visit less than an entire file"))
(setq file (expand-file-name file))
(let* ((local-copy
- (condition-case inl
+ (condition-case nil
(epa-file-run-real-handler #'file-local-copy (list file))
(error)))
(local-file (or local-copy file))
context
(cons #'epa-file-passphrase-callback-function
local-file))
- (epg-context-set-progress-callback context
- #'epa-progress-callback-function)
+ (epg-context-set-progress-callback
+ context
+ (cons #'epa-progress-callback-function
+ (format "Decrypting %s" file)))
(unwind-protect
(progn
(if replace
(error
(if (setq entry (assoc file epa-file-passphrase-alist))
(setcdr entry nil))
+ ;; Hack to prevent find-file from opening empty buffer
+ ;; when decryption failed (bug#6568). See the place
+ ;; where `find-file-not-found-functions' are called in
+ ;; `find-file-noselect-1'.
+ (when (file-exists-p local-file)
+ (make-local-variable 'epa-file-error)
+ (setq epa-file-error error)
+ (add-hook 'find-file-not-found-functions
+ 'epa-file--find-file-not-found-function
+ nil t))
(signal 'file-error
(cons "Opening input file" (cdr error)))))
(make-local-variable 'epa-file-encrypt-to)
(if (or beg end)
(setq string (substring string (or beg 0) end)))
(save-excursion
- (save-restriction
- (narrow-to-region (point) (point))
- (epa-file-decode-and-insert string file visit beg end replace)
- (setq length (- (point-max) (point-min))))
- (if replace
- (delete-region (point) (point-max)))))
+ ;; If visiting, bind off buffer-file-name so that
+ ;; file-locking will not ask whether we should
+ ;; really edit the buffer.
+ (let ((buffer-file-name
+ (if visit nil buffer-file-name)))
+ (save-restriction
+ (narrow-to-region (point) (point))
+ (epa-file-decode-and-insert string file visit beg end replace)
+ (setq length (- (point-max) (point-min))))
+ (if replace
+ (delete-region (point) (point-max))))
+ (if visit
+ (set-visited-file-modtime))))
(if (and local-copy
(file-exists-p local-copy))
(delete-file local-copy)))
(defun epa-file-write-region (start end file &optional append visit lockname
mustbenew)
(if append
- (error "Can't append to the file."))
+ (error "Can't append to the file"))
(setq file (expand-file-name file))
(let* ((coding-system (or coding-system-for-write
(if (fboundp 'select-safe-coding-system)
context
(cons #'epa-file-passphrase-callback-function
file))
- (epg-context-set-progress-callback context
- #'epa-progress-callback-function)
+ (epg-context-set-progress-callback
+ context
+ (cons #'epa-progress-callback-function
+ (format "Encrypting %s" file)))
(epg-context-set-armor context epa-armor)
(condition-case error
(setq string
context
(if (stringp start)
(epa-file--encode-coding-string start coding-system)
+ (unless start
+ (setq start (point-min)
+ end (point-max)))
(epa-file--encode-coding-string (buffer-substring start end)
coding-system))
- (if (or epa-file-select-keys
- (not (local-variable-p 'epa-file-encrypt-to
- (current-buffer))))
+ (if (or (eq epa-file-select-keys t)
+ (and (null epa-file-select-keys)
+ (not (local-variable-p 'epa-file-encrypt-to
+ (current-buffer)))))
(epa-select-keys
context
- "Select recipents for encryption.
+ "Select recipients for encryption.
If no one is selected, symmetric encryption will be performed. "
recipients)
(if epa-file-encrypt-to
(message "Wrote %s" buffer-file-name))))
(put 'write-region 'epa-file 'epa-file-write-region)
-(defun epa-file-find-file-hook ()
- (if (and buffer-file-name
- (string-match epa-file-name-regexp buffer-file-name)
- epa-file-inhibit-auto-save)
- (auto-save-mode 0))
- (set-buffer-modified-p nil))
-
(defun epa-file-select-keys ()
"Select recipients for encryption."
(interactive)
(make-local-variable 'epa-file-encrypt-to)
(setq epa-file-encrypt-to
+ (mapcar
+ (lambda (key)
+ (epg-sub-key-id (car (epg-key-sub-key-list key))))
(epa-select-keys
(epg-make-context)
- "Select recipents for encryption.
-If no one is selected, symmetric encryption will be performed. ")))
+ "Select recipients for encryption.
+If no one is selected, symmetric encryption will be performed. "))))
;;;###autoload
(defun epa-file-enable ()
(message "`epa-file' already enabled")
(setq file-name-handler-alist
(cons epa-file-handler file-name-handler-alist))
- (add-hook 'find-file-hooks 'epa-file-find-file-hook)
+ (add-hook 'find-file-hook 'epa-file-find-file-hook)
(setq auto-mode-alist (cons epa-file-auto-mode-alist-entry auto-mode-alist))
(message "`epa-file' enabled")))
(progn
(setq file-name-handler-alist
(delq epa-file-handler file-name-handler-alist))
- (remove-hook 'find-file-hooks 'epa-file-find-file-hook)
+ (remove-hook 'find-file-hook 'epa-file-find-file-hook)
(setq auto-mode-alist (delq epa-file-auto-mode-alist-entry
auto-mode-alist))
(message "`epa-file' disabled"))
(message "`epa-file' already disabled")))
-;;;###autoload
-(define-minor-mode epa-file-mode
- "Toggle automatic file encryption and decryption.
-With prefix argument ARG, turn auto encryption on if positive, else off.
-Return the new status of auto encryption (non-nil means on)."
- :global t :init-value nil :group 'epa-file :version "23.1"
- (setq file-name-handler-alist
- (delq epa-file-handler file-name-handler-alist))
- (remove-hook 'find-file-hooks 'epa-file-find-file-hook)
- (setq auto-mode-alist (delq epa-file-auto-mode-alist-entry
- auto-mode-alist))
- (when epa-file-mode
- (setq file-name-handler-alist
- (cons epa-file-handler file-name-handler-alist))
- (add-hook 'find-file-hooks 'epa-file-find-file-hook)
- (setq auto-mode-alist (cons epa-file-auto-mode-alist-entry
- auto-mode-alist))))
-
(provide 'epa-file)
-;; arch-tag: 5715152f-0eb1-4dbc-9008-07098775314d
;;; epa-file.el ends here