]> code.delx.au - gnu-emacs/blob - lisp/epa-file.el
Merge from origin/emacs-25
[gnu-emacs] / lisp / epa-file.el
1 ;;; epa-file.el --- the EasyPG Assistant, transparent file encryption -*- lexical-binding: t -*-
2 ;; Copyright (C) 2006-2016 Free Software Foundation, Inc.
3
4 ;; Author: Daiki Ueno <ueno@unixuser.org>
5 ;; Keywords: PGP, GnuPG
6 ;; Package: epa
7
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software: you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
14
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
22
23 ;;; Code:
24
25 (require 'epa)
26 (require 'epa-hook)
27
28 (defcustom epa-file-cache-passphrase-for-symmetric-encryption nil
29 "If non-nil, cache passphrase for symmetric encryption.
30
31 For security reasons, this option is turned off by default and
32 not recommended to use. Instead, consider using gpg-agent which
33 does the same job in a safer way. See Info node `(epa) Caching
34 Passphrases' for more information.
35
36 Note that this option has no effect if you use GnuPG 2.0."
37 :type 'boolean
38 :group 'epa-file)
39
40 (defcustom epa-file-select-keys nil
41 "Control whether or not to pop up the key selection dialog.
42
43 If t, always asks user to select recipients.
44 If nil, query user only when `epa-file-encrypt-to' is not set.
45 If neither t nor nil, doesn't ask user. In this case, symmetric
46 encryption is used."
47 :type '(choice (const :tag "Ask always" t)
48 (const :tag "Ask when recipients are not set" nil)
49 (const :tag "Don't ask" silent))
50 :group 'epa-file)
51
52 (defvar epa-file-passphrase-alist nil)
53
54 (eval-and-compile
55 (if (fboundp 'encode-coding-string)
56 (defalias 'epa-file--encode-coding-string 'encode-coding-string)
57 (defalias 'epa-file--encode-coding-string 'identity)))
58
59 (eval-and-compile
60 (if (fboundp 'decode-coding-string)
61 (defalias 'epa-file--decode-coding-string 'decode-coding-string)
62 (defalias 'epa-file--decode-coding-string 'identity)))
63
64 (defun epa-file-passphrase-callback-function (context key-id file)
65 (if (and epa-file-cache-passphrase-for-symmetric-encryption
66 (eq key-id 'SYM))
67 (progn
68 (setq file (file-truename file))
69 (let ((entry (assoc file epa-file-passphrase-alist))
70 passphrase)
71 (or (copy-sequence (cdr entry))
72 (progn
73 (unless entry
74 (setq entry (list file)
75 epa-file-passphrase-alist
76 (cons entry
77 epa-file-passphrase-alist)))
78 (setq passphrase (epa-passphrase-callback-function context
79 key-id
80 file))
81 (setcdr entry (copy-sequence passphrase))
82 passphrase))))
83 (epa-passphrase-callback-function context key-id file)))
84
85 (defvar epa-inhibit nil
86 "Non-nil means don't try to decrypt .gpg files when operating on them.")
87
88 ;;;###autoload
89 (defun epa-file-handler (operation &rest args)
90 (save-match-data
91 (let ((op (get operation 'epa-file)))
92 (if (and op (not epa-inhibit))
93 (apply op args)
94 (epa-file-run-real-handler operation args)))))
95
96 (defun epa-file-run-real-handler (operation args)
97 (let ((inhibit-file-name-handlers
98 (cons 'epa-file-handler
99 (and (eq inhibit-file-name-operation operation)
100 inhibit-file-name-handlers)))
101 (inhibit-file-name-operation operation))
102 (apply operation args)))
103
104 (defun epa-file-decode-and-insert (string file visit beg end replace)
105 (if (fboundp 'decode-coding-inserted-region)
106 (save-restriction
107 (narrow-to-region (point) (point))
108 (insert (if enable-multibyte-characters
109 (string-to-multibyte string)
110 string))
111 (decode-coding-inserted-region
112 (point-min) (point-max)
113 (substring file 0 (string-match epa-file-name-regexp file))
114 visit beg end replace))
115 (insert (epa-file--decode-coding-string string (or coding-system-for-read
116 'undecided)))))
117
118 (defvar epa-file-error nil)
119 (defun epa-file--find-file-not-found-function ()
120 (let ((error epa-file-error))
121 (save-window-excursion
122 (kill-buffer))
123 (signal 'file-error
124 (cons "Opening input file" (cdr error)))))
125
126 (defvar last-coding-system-used)
127 (defun epa-file-insert-file-contents (file &optional visit beg end replace)
128 (barf-if-buffer-read-only)
129 (if (and visit (or beg end))
130 (error "Attempt to visit less than an entire file"))
131 (setq file (expand-file-name file))
132 (let* ((local-copy
133 (condition-case nil
134 (epa-file-run-real-handler #'file-local-copy (list file))
135 (error)))
136 (local-file (or local-copy file))
137 (context (epg-make-context))
138 (buf (current-buffer))
139 string length entry)
140 (if visit
141 (setq buffer-file-name file))
142 (epg-context-set-passphrase-callback
143 context
144 (cons #'epa-file-passphrase-callback-function
145 local-file))
146 (epg-context-set-progress-callback
147 context
148 (cons #'epa-progress-callback-function
149 (format "Decrypting %s" file)))
150 (setf (epg-context-pinentry-mode context) epa-pinentry-mode)
151 (unwind-protect
152 (progn
153 (if replace
154 (goto-char (point-min)))
155 (condition-case error
156 (setq string (epg-decrypt-file context local-file nil))
157 (error
158 (if (setq entry (assoc file epa-file-passphrase-alist))
159 (setcdr entry nil))
160 ;; If the decryption program can't be found,
161 ;; signal that as a non-file error
162 ;; so that find-file-noselect-1 won't handle it.
163 ;; Borrowed from jka-compr.el.
164 (if (and (eq (car error) 'file-error)
165 (equal (cadr error) "Searching for program"))
166 (error "Decryption program `%s' not found"
167 (nth 3 error)))
168 (when (file-exists-p local-file)
169 ;; Hack to prevent find-file from opening empty buffer
170 ;; when decryption failed (bug#6568). See the place
171 ;; where `find-file-not-found-functions' are called in
172 ;; `find-file-noselect-1'.
173 (setq-local epa-file-error error)
174 (add-hook 'find-file-not-found-functions
175 'epa-file--find-file-not-found-function
176 nil t)
177 (epa-display-error context))
178 (signal 'file-error
179 (cons "Opening input file" (cdr error)))))
180 (set-buffer buf) ;In case timer/filter changed/killed it (bug#16029)!
181 (setq-local epa-file-encrypt-to
182 (mapcar #'car (epg-context-result-for
183 context 'encrypted-to)))
184 (if (or beg end)
185 (setq string (substring string (or beg 0) end)))
186 (save-excursion
187 ;; If visiting, bind off buffer-file-name so that
188 ;; file-locking will not ask whether we should
189 ;; really edit the buffer.
190 (let ((buffer-file-name
191 (if visit nil buffer-file-name)))
192 (save-restriction
193 (narrow-to-region (point) (point))
194 (epa-file-decode-and-insert string file visit beg end replace)
195 (setq length (- (point-max) (point-min))))
196 (if replace
197 (delete-region (point) (point-max))))
198 (if visit
199 (set-visited-file-modtime))))
200 (if (and local-copy
201 (file-exists-p local-copy))
202 (delete-file local-copy)))
203 (list file length)))
204 (put 'insert-file-contents 'epa-file 'epa-file-insert-file-contents)
205
206 (defun epa-file-write-region (start end file &optional append visit lockname
207 mustbenew)
208 (if append
209 (error "Can't append to the file"))
210 (setq file (expand-file-name file))
211 (let* ((coding-system (or coding-system-for-write
212 (if (fboundp 'select-safe-coding-system)
213 ;; This is needed since Emacs 22 has
214 ;; no-conversion setting for *.gpg in
215 ;; `auto-coding-alist'.
216 (let ((buffer-file-name
217 (file-name-sans-extension file)))
218 (select-safe-coding-system
219 (point-min) (point-max)))
220 buffer-file-coding-system)))
221 (context (epg-make-context))
222 (coding-system-for-write 'binary)
223 string entry
224 (recipients
225 (cond
226 ((listp epa-file-encrypt-to) epa-file-encrypt-to)
227 ((stringp epa-file-encrypt-to) (list epa-file-encrypt-to))))
228 buffer)
229 (epg-context-set-passphrase-callback
230 context
231 (cons #'epa-file-passphrase-callback-function
232 file))
233 (epg-context-set-progress-callback
234 context
235 (cons #'epa-progress-callback-function
236 (format "Encrypting %s" file)))
237 (setf (epg-context-armor context) epa-armor)
238 (setf (epg-context-pinentry-mode context) epa-pinentry-mode)
239 (condition-case error
240 (setq string
241 (epg-encrypt-string
242 context
243 (if (stringp start)
244 (epa-file--encode-coding-string start coding-system)
245 (unless start
246 (setq start (point-min)
247 end (point-max)))
248 (setq buffer (current-buffer))
249 (with-temp-buffer
250 (insert-buffer-substring buffer start end)
251 ;; Translate the region according to
252 ;; `buffer-file-format', as `write-region' would.
253 ;; We can't simply do `write-region' (into a
254 ;; temporary file) here, since it writes out
255 ;; decrypted contents.
256 (format-encode-buffer (with-current-buffer buffer
257 buffer-file-format))
258 (epa-file--encode-coding-string (buffer-string)
259 coding-system)))
260 (if (or (eq epa-file-select-keys t)
261 (and (null epa-file-select-keys)
262 (not (local-variable-p 'epa-file-encrypt-to
263 (current-buffer)))))
264 (epa-select-keys
265 context
266 "Select recipients for encryption.
267 If no one is selected, symmetric encryption will be performed. "
268 recipients)
269 (if epa-file-encrypt-to
270 (epg-list-keys context recipients)))))
271 (error
272 (epa-display-error context)
273 (if (setq entry (assoc file epa-file-passphrase-alist))
274 (setcdr entry nil))
275 (signal 'file-error (cons "Opening output file" (cdr error)))))
276 (epa-file-run-real-handler
277 #'write-region
278 (list string nil file append visit lockname mustbenew))
279 (if (boundp 'last-coding-system-used)
280 (setq last-coding-system-used coding-system))
281 (if (eq visit t)
282 (progn
283 (setq buffer-file-name file)
284 (set-visited-file-modtime))
285 (if (stringp visit)
286 (progn
287 (set-visited-file-modtime)
288 (setq buffer-file-name visit))))
289 (if (or (eq visit t)
290 (eq visit nil)
291 (stringp visit))
292 (message "Wrote %s" buffer-file-name))))
293 (put 'write-region 'epa-file 'epa-file-write-region)
294
295 (defun epa-file-select-keys ()
296 "Select recipients for encryption."
297 (interactive)
298 (setq-local epa-file-encrypt-to
299 (mapcar
300 (lambda (key)
301 (epg-sub-key-id (car (epg-key-sub-key-list key))))
302 (epa-select-keys
303 (epg-make-context)
304 "Select recipients for encryption.
305 If no one is selected, symmetric encryption will be performed. "))))
306
307 ;;;###autoload
308 (defun epa-file-enable ()
309 (interactive)
310 (if (memq epa-file-handler file-name-handler-alist)
311 (message "`epa-file' already enabled")
312 (setq file-name-handler-alist
313 (cons epa-file-handler file-name-handler-alist))
314 (add-hook 'find-file-hook 'epa-file-find-file-hook)
315 (setq auto-mode-alist (cons epa-file-auto-mode-alist-entry auto-mode-alist))
316 (message "`epa-file' enabled")))
317
318 ;;;###autoload
319 (defun epa-file-disable ()
320 (interactive)
321 (if (memq epa-file-handler file-name-handler-alist)
322 (progn
323 (setq file-name-handler-alist
324 (delq epa-file-handler file-name-handler-alist))
325 (remove-hook 'find-file-hook 'epa-file-find-file-hook)
326 (setq auto-mode-alist (delq epa-file-auto-mode-alist-entry
327 auto-mode-alist))
328 (message "`epa-file' disabled"))
329 (message "`epa-file' already disabled")))
330
331 (provide 'epa-file)
332
333 ;;; epa-file.el ends here