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