]> code.delx.au - gnu-emacs/blob - lisp/epa-file.el
Merge from emacs-24; up to 117698
[gnu-emacs] / lisp / epa-file.el
1 ;;; epa-file.el --- the EasyPG Assistant, transparent file encryption -*- lexical-binding: t -*-
2 ;; Copyright (C) 2006-2014 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 ;;;###autoload
86 (defun epa-file-handler (operation &rest args)
87 (save-match-data
88 (let ((op (get operation 'epa-file)))
89 (if op
90 (apply op args)
91 (epa-file-run-real-handler operation args)))))
92
93 (defun epa-file-run-real-handler (operation args)
94 (let ((inhibit-file-name-handlers
95 (cons 'epa-file-handler
96 (and (eq inhibit-file-name-operation operation)
97 inhibit-file-name-handlers)))
98 (inhibit-file-name-operation operation))
99 (apply operation args)))
100
101 (defun epa-file-decode-and-insert (string file visit beg end replace)
102 (if (fboundp 'decode-coding-inserted-region)
103 (save-restriction
104 (narrow-to-region (point) (point))
105 (insert (if enable-multibyte-characters
106 (string-to-multibyte string)
107 string))
108 (decode-coding-inserted-region
109 (point-min) (point-max)
110 (substring file 0 (string-match epa-file-name-regexp file))
111 visit beg end replace))
112 (insert (epa-file--decode-coding-string string (or coding-system-for-read
113 'undecided)))))
114
115 (defvar epa-file-error nil)
116 (defun epa-file--find-file-not-found-function ()
117 (let ((error epa-file-error))
118 (save-window-excursion
119 (kill-buffer))
120 (signal 'file-error
121 (cons "Opening input file" (cdr error)))))
122
123 (defvar last-coding-system-used)
124 (defun epa-file-insert-file-contents (file &optional visit beg end replace)
125 (barf-if-buffer-read-only)
126 (if (and visit (or beg end))
127 (error "Attempt to visit less than an entire file"))
128 (setq file (expand-file-name file))
129 (let* ((local-copy
130 (condition-case nil
131 (epa-file-run-real-handler #'file-local-copy (list file))
132 (error)))
133 (local-file (or local-copy file))
134 (context (epg-make-context))
135 (buf (current-buffer))
136 string length entry)
137 (if visit
138 (setq buffer-file-name file))
139 (epg-context-set-passphrase-callback
140 context
141 (cons #'epa-file-passphrase-callback-function
142 local-file))
143 (epg-context-set-progress-callback
144 context
145 (cons #'epa-progress-callback-function
146 (format "Decrypting %s" file)))
147 (if epa-pinentry-mode
148 (setf (epg-context-pinentry-mode context) epa-pinentry-mode))
149 (unwind-protect
150 (progn
151 (if replace
152 (goto-char (point-min)))
153 (condition-case error
154 (setq string (epg-decrypt-file context local-file nil))
155 (error
156 (epa-display-error context)
157 (if (setq entry (assoc file epa-file-passphrase-alist))
158 (setcdr entry nil))
159 ;; If the decryption program can't be found,
160 ;; signal that as a non-file error
161 ;; so that find-file-noselect-1 won't handle it.
162 ;; Borrowed from jka-compr.el.
163 (if (and (eq (car error) 'file-error)
164 (equal (cadr error) "Searching for program"))
165 (error "Decryption program `%s' not found"
166 (nth 3 error)))
167 ;; Hack to prevent find-file from opening empty buffer
168 ;; when decryption failed (bug#6568). See the place
169 ;; where `find-file-not-found-functions' are called in
170 ;; `find-file-noselect-1'.
171 (when (file-exists-p local-file)
172 (setq-local epa-file-error error)
173 (add-hook 'find-file-not-found-functions
174 'epa-file--find-file-not-found-function
175 nil t))
176 (signal 'file-error
177 (cons "Opening input file" (cdr error)))))
178 (set-buffer buf) ;In case timer/filter changed/killed it (bug#16029)!
179 (setq-local epa-file-encrypt-to
180 (mapcar #'car (epg-context-result-for
181 context 'encrypted-to)))
182 (if (or beg end)
183 (setq string (substring string (or beg 0) end)))
184 (save-excursion
185 ;; If visiting, bind off buffer-file-name so that
186 ;; file-locking will not ask whether we should
187 ;; really edit the buffer.
188 (let ((buffer-file-name
189 (if visit nil buffer-file-name)))
190 (save-restriction
191 (narrow-to-region (point) (point))
192 (epa-file-decode-and-insert string file visit beg end replace)
193 (setq length (- (point-max) (point-min))))
194 (if replace
195 (delete-region (point) (point-max))))
196 (if visit
197 (set-visited-file-modtime))))
198 (if (and local-copy
199 (file-exists-p local-copy))
200 (delete-file local-copy)))
201 (list file length)))
202 (put 'insert-file-contents 'epa-file 'epa-file-insert-file-contents)
203
204 (defun epa-file-write-region (start end file &optional append visit lockname
205 mustbenew)
206 (if append
207 (error "Can't append to the file"))
208 (setq file (expand-file-name file))
209 (let* ((coding-system (or coding-system-for-write
210 (if (fboundp 'select-safe-coding-system)
211 ;; This is needed since Emacs 22 has
212 ;; no-conversion setting for *.gpg in
213 ;; `auto-coding-alist'.
214 (let ((buffer-file-name
215 (file-name-sans-extension file)))
216 (select-safe-coding-system
217 (point-min) (point-max)))
218 buffer-file-coding-system)))
219 (context (epg-make-context))
220 (coding-system-for-write 'binary)
221 string entry
222 (recipients
223 (cond
224 ((listp epa-file-encrypt-to) epa-file-encrypt-to)
225 ((stringp epa-file-encrypt-to) (list epa-file-encrypt-to))))
226 buffer)
227 (epg-context-set-passphrase-callback
228 context
229 (cons #'epa-file-passphrase-callback-function
230 file))
231 (epg-context-set-progress-callback
232 context
233 (cons #'epa-progress-callback-function
234 (format "Encrypting %s" file)))
235 (setf (epg-context-armor context) epa-armor)
236 (if epa-pinentry-mode
237 (setf (epg-context-pinentry-mode context) epa-pinentry-mode))
238 (condition-case error
239 (setq string
240 (epg-encrypt-string
241 context
242 (if (stringp start)
243 (epa-file--encode-coding-string start coding-system)
244 (unless start
245 (setq start (point-min)
246 end (point-max)))
247 (setq buffer (current-buffer))
248 (with-temp-buffer
249 (insert-buffer-substring buffer start end)
250 ;; Translate the region according to
251 ;; `buffer-file-format', as `write-region' would.
252 ;; We can't simply do `write-region' (into a
253 ;; temporary file) here, since it writes out
254 ;; decrypted contents.
255 (format-encode-buffer (with-current-buffer buffer
256 buffer-file-format))
257 (epa-file--encode-coding-string (buffer-string)
258 coding-system)))
259 (if (or (eq epa-file-select-keys t)
260 (and (null epa-file-select-keys)
261 (not (local-variable-p 'epa-file-encrypt-to
262 (current-buffer)))))
263 (epa-select-keys
264 context
265 "Select recipients for encryption.
266 If no one is selected, symmetric encryption will be performed. "
267 recipients)
268 (if epa-file-encrypt-to
269 (epg-list-keys context recipients)))))
270 (error
271 (epa-display-error context)
272 (if (setq entry (assoc file epa-file-passphrase-alist))
273 (setcdr entry nil))
274 (signal 'file-error (cons "Opening output file" (cdr error)))))
275 (epa-file-run-real-handler
276 #'write-region
277 (list string nil file append visit lockname mustbenew))
278 (if (boundp 'last-coding-system-used)
279 (setq last-coding-system-used coding-system))
280 (if (eq visit t)
281 (progn
282 (setq buffer-file-name file)
283 (set-visited-file-modtime))
284 (if (stringp visit)
285 (progn
286 (set-visited-file-modtime)
287 (setq buffer-file-name visit))))
288 (if (or (eq visit t)
289 (eq visit nil)
290 (stringp visit))
291 (message "Wrote %s" buffer-file-name))))
292 (put 'write-region 'epa-file 'epa-file-write-region)
293
294 (defun epa-file-select-keys ()
295 "Select recipients for encryption."
296 (interactive)
297 (setq-local epa-file-encrypt-to
298 (mapcar
299 (lambda (key)
300 (epg-sub-key-id (car (epg-key-sub-key-list key))))
301 (epa-select-keys
302 (epg-make-context)
303 "Select recipients for encryption.
304 If no one is selected, symmetric encryption will be performed. "))))
305
306 ;;;###autoload
307 (defun epa-file-enable ()
308 (interactive)
309 (if (memq epa-file-handler file-name-handler-alist)
310 (message "`epa-file' already enabled")
311 (setq file-name-handler-alist
312 (cons epa-file-handler file-name-handler-alist))
313 (add-hook 'find-file-hook 'epa-file-find-file-hook)
314 (setq auto-mode-alist (cons epa-file-auto-mode-alist-entry auto-mode-alist))
315 (message "`epa-file' enabled")))
316
317 ;;;###autoload
318 (defun epa-file-disable ()
319 (interactive)
320 (if (memq epa-file-handler file-name-handler-alist)
321 (progn
322 (setq file-name-handler-alist
323 (delq epa-file-handler file-name-handler-alist))
324 (remove-hook 'find-file-hook 'epa-file-find-file-hook)
325 (setq auto-mode-alist (delq epa-file-auto-mode-alist-entry
326 auto-mode-alist))
327 (message "`epa-file' disabled"))
328 (message "`epa-file' already disabled")))
329
330 (provide 'epa-file)
331
332 ;;; epa-file.el ends here