aee05424ad2eb27a6fdc2c53eeff3266020f3f8e
[elisp/epg.git] / epg.el
1 (defgroup epg ()
2   "EasyPG, yet another GnuPG interface.")
3
4 (defcustom epg-gpg-program "gpg"
5   "The `gpg' executable."
6   :group 'epg
7   :type 'string)
8
9 (defvar epg-user-id nil
10   "GnuPG ID of your default identity.")
11
12 (defvar epg-user-id-alist nil
13   "An alist mapping from key ID to user ID.")
14
15 (defvar epg-read-point nil)
16 (defvar epg-pending-status-list nil)
17 (defvar epg-key-id nil)
18 (defvar epg-context nil)
19 (defvar epg-debug nil)
20
21 (defvar epg-colons-pub-spec
22   '((trust "[^:]")
23     (length "[0-9]+" 0 string-to-number)
24     (algorithm "[0-9]+" 0 string-to-number)
25     (key-id "[^:]+")
26     (creation-date "[0-9]+")
27     (expiration-date "[0-9]+")
28     nil
29     (ownertrust "[^:]")
30     nil
31     nil
32     (capability "[escaESCA]*"))
33   "The schema of keylisting output whose type is \"pub\".
34 This is used by `epg-list-keys'.")
35
36 (defvar epg-colons-sec-spec
37   '((trust "[^:]")
38     (length "[0-9]+" 0 string-to-number)
39     (algorithm "[0-9]+" 0 string-to-number)
40     (key-id "[^:]+")
41     (creation-date "[0-9]+")
42     (expiration-date "[0-9]+")
43     nil
44     (ownertrust "[^:]"))
45 "The schema of keylisting output whose type is \"sec\".
46 This is used by `epg-list-keys'.")
47
48 (defvar epg-colons-uid-spec
49   '((trust "[^:]")
50     nil
51     nil
52     nil
53     (creation-date "[0-9]+")
54     (expiration-date "[0-9]+")
55     (hash "[^:]+")
56     nil
57     (user-id "[^:]+"))
58   "The schema of keylisting output whose type is \"uid\".
59 This is used by `epg-list-keys'.")
60     
61 (defun epg-make-context (&optional protocol armor textmode include-certs)
62   "Return a context object."
63   (vector protocol armor textmode include-certs
64           (cons #'epg-passphrase-callback-function nil)
65           (cons #'epg-progress-callback-function nil)
66           nil nil nil nil))
67
68 (defun epg-context-protocol (context)
69   "Return the protocol used within the context."
70   (aref context 0))
71
72 (defun epg-context-armor (context)
73   "Return t if the output shouled be ASCII armored in the CONTEXT context."
74   (aref context 1))
75
76 (defun epg-context-textmode (context)
77   "Return t if canonical text mode should be used in the CONTEXT context."
78   (aref context 2))
79
80 (defun epg-context-include-certs (context)
81   "Return how many certificates should be included in an S/MIME signed
82 message."
83   (aref context 3))
84
85 (defun epg-context-passphrase-callback-info (context)
86   "Return the function used to query passphrase."
87   (aref context 4))
88
89 (defun epg-context-progress-callback-info (context)
90   "Return the function which handles progress update."
91   (aref context 5))
92
93 (defun epg-context-signers (context)
94   "Return the list of key-id for singning."
95   (aref context 6))
96
97 (defun epg-context-process (context)
98   "Return the process object of `epg-gpg-program'.
99 This function is for internal use only."
100   (aref context 7))
101
102 (defun epg-context-output-file (context)
103   "Return the output file of `epg-gpg-program'.
104 This function is for internal use only."
105   (aref context 8))
106
107 (defun epg-context-result (context)
108   "Return the result of the previous cryptographic operation."
109   (aref context 9))
110
111 (defun epg-context-set-protocol (context protocol)
112   "Set the protocol used within the context."
113   (aset context 0 protocol))
114
115 (defun epg-context-set-armor (context armor)
116   "Specify if the output shouled be ASCII armored in the CONTEXT context."
117   (aset context 1 armor))
118
119 (defun epg-context-set-textmode (context textmode)
120   "Specify if canonical text mode should be used in the CONTEXT context."
121   (aset context 2 textmode))
122
123 (defun epg-context-set-include-certs (context include-certs)
124  "Set how many certificates should be included in an S/MIME signed message."
125   (aset context 3 include-certs))
126
127 (defun epg-context-set-passphrase-callback-info (context
128                                                  passphrase-callback-info)
129   "Set the function used to query passphrase."
130   (aset context 4 passphrase-callback-info))
131
132 (defun epg-context-set-progress-callback-info (context progress-callback-info)
133   "Set the function which handles progress update."
134   (aset context 5 progress-callback-info))
135
136 (defun epg-context-set-signers (context signers)
137  "Set the list of key-id for singning."
138   (aset context 6 signers))
139
140 (defun epg-context-set-process (context process)
141   "Set the process object of `epg-gpg-program'.
142 This function is for internal use only."
143   (aset context 7 process))
144
145 (defun epg-context-set-output-file (context output-file)
146   "Set the output file of `epg-gpg-program'.
147 This function is for internal use only."
148   (aset context 8 output-file))
149
150 (defun epg-context-set-result (context result)
151   "Set the result of the previous cryptographic operation."
152   (aset context 9 result))
153
154 (defun epg-make-signature (status key-id user-id)
155   "Return a signature object."
156   (vector status key-id user-id nil nil))
157
158 (defun epg-signature-status (signature)
159   "Return the status code of SIGNATURE."
160   (aref signature 0))
161
162 (defun epg-signature-key-id (signature)
163   "Return the key-id of SIGNATURE."
164   (aref signature 1))
165
166 (defun epg-signature-user-id (signature)
167   "Return the user-id of SIGNATURE."
168   (aref signature 2))
169   
170 (defun epg-signature-validity (signature)
171   "Return the validity of SIGNATURE."
172   (aref signature 3))
173
174 (defun epg-signature-fingerprint (signature)
175   "Return the fingerprint of SIGNATURE."
176   (aref signature 4))
177
178 (defun epg-signature-set-status (signature status)
179  "Set the status code of SIGNATURE."
180   (aset signature 0 status))
181
182 (defun epg-signature-set-key-id (signature key-id)
183  "Set the key-id of SIGNATURE."
184   (aset signature 1 key-id))
185
186 (defun epg-signature-set-user-id (signature user-id)
187  "Set the user-id of SIGNATURE."
188   (aset signature 2 user-id))
189   
190 (defun epg-signature-set-validity (signature validity)
191  "Set the validity of SIGNATURE."
192   (aset signature 3 validity))
193
194 (defun epg-signature-set-fingerprint (signature fingerprint)
195  "Set the fingerprint of SIGNATURE."
196   (aset signature 4 fingerprint))
197
198 (defun epg-context-result-for (context name)
199   (cdr (assq name (epg-context-result context))))
200
201 (defun epg-context-set-result-for (context name value)
202   (let* ((result (epg-context-result context))
203          (entry (assq name result)))
204     (if entry
205         (setcdr entry value)
206       (epg-context-set-result context (cons (cons name value) result)))))
207
208 (defun epg-start (context args)
209   "Start `epg-gpg-program' in a subprocess with given ARGS."
210   (let* ((args (append (list "--no-tty"
211                              "--status-fd" "1"
212                              "--command-fd" "0"
213                              "--yes") ; overwrite
214                        (if (epg-context-armor context) '("--armor"))
215                        (if (epg-context-textmode context) '("--textmode"))
216                        (if (epg-context-output-file context)
217                            (list "--output" (epg-context-output-file context)))
218                        args))
219          (coding-system-for-write 'binary)
220          process-connection-type
221          (orig-mode (default-file-modes))
222          (buffer (generate-new-buffer " *epg*"))
223          process)
224     (with-current-buffer buffer
225       (make-local-variable 'epg-read-point)
226       (setq epg-read-point (point-min))
227       (make-local-variable 'epg-pending-status-list)
228       (setq epg-pending-status-list nil)
229       (make-local-variable 'epg-key-id)
230       (setq epg-key-id nil)
231       (make-local-variable 'epg-context)
232       (setq epg-context context))
233     (unwind-protect
234         (progn
235           (set-default-file-modes 448)
236           (setq process
237                 (apply #'start-process "epg" buffer epg-gpg-program args)))
238       (set-default-file-modes orig-mode))
239     (set-process-filter process #'epg-process-filter)
240     (epg-context-set-process context process)))
241
242 (defun epg-process-filter (process input)
243   (if epg-debug
244       (save-excursion
245         (set-buffer (get-buffer-create  " *epg-debug*"))
246         (goto-char (point-max))
247         (insert input)))
248   (if (buffer-live-p (process-buffer process))
249       (save-excursion
250         (set-buffer (process-buffer process))
251         (goto-char (point-max))
252         (insert input)
253         (goto-char epg-read-point)
254         (beginning-of-line)
255         (while (looking-at ".*\n")      ;the input line is finished
256           (save-excursion
257             (if (looking-at "\\[GNUPG:] \\([A-Z_]+\\) ?\\(.*\\)")
258                 (let* ((status (match-string 1))
259                        (string (match-string 2))
260                        (symbol (intern-soft (concat "epg-status-" status))))
261                   (if (member status epg-pending-status-list)
262                       (setq epg-pending-status-list nil))
263                   (if (and symbol
264                            (fboundp symbol))
265                       (funcall symbol process string)))))
266           (forward-line))
267         (setq epg-read-point (point)))))
268
269 (defun epg-read-output (context)
270   (with-temp-buffer
271     (if (fboundp 'set-buffer-multibyte)
272         (set-buffer-multibyte nil))
273     (if (file-exists-p (epg-context-output-file context))
274         (let ((coding-system-for-read (if (epg-context-textmode context)
275                                           'raw-text
276                                         'binary)))
277           (insert-file-contents (epg-context-output-file context))
278           (buffer-string)))))
279
280 (defun epg-wait-for-status (context status-list)
281   (with-current-buffer (process-buffer (epg-context-process context))
282     (setq epg-pending-status-list status-list)
283     (while (and (eq (process-status (epg-context-process context)) 'run)
284                 epg-pending-status-list)
285       (accept-process-output (epg-context-process context) 1))))
286
287 (defun epg-wait-for-completion (context)
288   (if (eq (process-status (epg-context-process context)) 'run)
289       (process-send-eof (epg-context-process context)))
290   (while (eq (process-status (epg-context-process context)) 'run)
291     ;; We can't use accept-process-output instead of sit-for here
292     ;; because it may cause an interrupt during the sentinel execution.
293     (sit-for 0.1)))
294
295 (defun epg-reset (context)
296   (if (and (epg-context-process context)
297            (buffer-live-p (process-buffer (epg-context-process context))))
298       (kill-buffer (process-buffer (epg-context-process context))))
299   (epg-context-set-process context nil)
300   (if (file-exists-p (epg-context-output-file context))
301       (delete-file (epg-context-output-file context))))
302
303 (defun epg-status-USERID_HINT (process string)
304   (if (string-match "\\`\\([^ ]+\\) \\(.*\\)" string)
305       (let* ((key-id (match-string 1 string))
306              (user-id (match-string 2 string))
307              (entry (assoc key-id epg-user-id-alist)))
308         (if entry
309             (setcdr entry user-id)
310           (setq epg-user-id-alist (cons (cons key-id user-id)
311                                         epg-user-id-alist))))))
312
313 (defun epg-status-NEED_PASSPHRASE (process string)
314   (if (string-match "\\`\\([^ ]+\\)" string)
315       (setq epg-key-id (match-string 1 string))))
316
317 (defun epg-status-NEED_PASSPHRASE_SYM (process string)
318   (setq epg-key-id 'SYM))
319
320 (defun epg-status-NEED_PASSPHRASE_PIN (process string)
321   (setq epg-key-id 'PIN))
322
323 (defun epg-status-GET_HIDDEN (process string)
324   (let ((passphrase
325          (funcall (car (epg-context-passphrase-callback-info epg-context))
326                   epg-key-id
327                   (cdr (epg-context-passphrase-callback-info epg-context))))
328         string)
329     (if passphrase
330         (unwind-protect
331             (progn
332               (setq string (concat passphrase "\n"))
333               (fillarray passphrase 0)
334               (setq passphrase nil)
335               (process-send-string process string))
336           (if string
337               (fillarray string 0))))))
338
339 (defun epg-status-GOODSIG (process string)
340   (if (string-match "\\`\\([^ ]+\\) \\(.*\\)" string)
341       (epg-context-set-result-for
342        epg-context
343        'verify
344        (cons (epg-make-signature 'good
345                                  (match-string 1 string)
346                                  (match-string 2 string))
347              (epg-context-result-for epg-context 'verify)))))
348
349 (defun epg-status-EXPSIG (process string)
350   (if (string-match "\\`\\([^ ]+\\) \\(.*\\)" string)
351       (epg-context-set-result-for
352        epg-context
353        'verify
354        (cons (epg-make-signature 'expired
355                                  (match-string 1 string)
356                                  (match-string 2 string))
357              (epg-context-result-for epg-context 'verify)))))
358
359 (defun epg-status-EXPKEYSIG (process string)
360   (if (string-match "\\`\\([^ ]+\\) \\(.*\\)" string)
361       (epg-context-set-result-for
362        epg-context
363        'verify
364        (cons (epg-make-signature 'expired-key
365                                  (match-string 1 string)
366                                  (match-string 2 string))
367              (epg-context-result-for epg-context 'verify)))))
368
369 (defun epg-status-REVKEYSIG (process string)
370   (if (string-match "\\`\\([^ ]+\\) \\(.*\\)" string)
371       (epg-context-set-result-for
372        epg-context
373        'verify
374        (cons (epg-make-signature 'revoked-key
375                                  (match-string 1 string)
376                                  (match-string 2 string))
377              (epg-context-result-for epg-context 'verify)))))
378
379 (defun epg-status-BADSIG (process string)
380   (if (string-match "\\`\\([^ ]+\\) \\(.*\\)" string)
381       (epg-context-set-result-for
382        epg-context
383        'verify
384        (cons (epg-make-signature 'bad
385                                  (match-string 1 string)
386                                  (match-string 2 string))
387              (epg-context-result-for epg-context 'verify)))))
388
389 (defun epg-status-VALIDSIG (process string)
390   (let ((signature (car (epg-context-result-for epg-context 'verify))))
391     (if (and signature
392              (eq (epg-signature-status signature) 'good)
393              (string-match "\\`\\([^ ]+\\) " string))
394         (epg-signature-set-fingerprint signature (match-string 1 string)))))
395
396 (defun epg-status-TRUST_UNDEFINED (process string)
397   (let ((signature (car (epg-context-result-for epg-context 'verify))))
398     (if (and signature
399              (eq (epg-signature-status signature) 'good))
400         (epg-signature-set-validity signature 'undefined))))
401
402 (defun epg-status-TRUST_NEVER (process string)
403   (let ((signature (car (epg-context-result-for epg-context 'verify))))
404     (if (and signature
405              (eq (epg-signature-status signature) 'good))
406         (epg-signature-set-validity signature 'never))))
407
408 (defun epg-status-TRUST_MARGINAL (process string)
409   (let ((signature (car (epg-context-result-for epg-context 'verify))))
410     (if (and signature
411              (eq (epg-signature-status signature) 'marginal))
412         (epg-signature-set-validity signature 'marginal))))
413
414 (defun epg-status-TRUST_FULLY (process string)
415   (let ((signature (car (epg-context-result-for epg-context 'verify))))
416     (if (and signature
417              (eq (epg-signature-status signature) 'good))
418         (epg-signature-set-validity signature 'fully))))
419
420 (defun epg-status-TRUST_ULTIMATE (process string)
421   (let ((signature (car (epg-context-result-for epg-context 'verify))))
422     (if (and signature
423              (eq (epg-signature-status signature) 'good))
424         (epg-signature-set-validity signature 'ultimate))))
425
426 (defun epg-status-PROGRESS (process string)
427   (if (string-match "\\`\\([^ ]+\\) \\([^ ]\\) \\([0-9]+\\) \\([0-9]+\\)"
428                     string)
429       (funcall (car (epg-context-progress-callback-info epg-context))
430                (match-string 1 string)
431                (match-string 2 string)
432                (string-to-number (match-string 3 string))
433                (string-to-number (match-string 4 string))
434                (cdr (epg-context-progress-callback-info epg-context)))))
435
436 (defun epg-status-DECRYPTION_FAILED (process string)
437   (epg-context-set-result-for
438    epg-context 'error
439    (cons 'decryption-failed
440          (epg-context-result-for epg-context 'error))))
441
442 (defun epg-status-NODATA (process string)
443   (epg-context-set-result-for
444    epg-context 'error
445    (cons (cons 'no-data (string-to-number string))
446          (epg-context-result-for epg-context 'error))))
447
448 (defun epg-status-UNEXPECTED (process string)
449   (epg-context-set-result-for
450    epg-context 'error
451    (cons (cons 'unexpected (string-to-number string))
452          (epg-context-result-for epg-context 'error))))
453
454 (defun epg-status-KEYEXPIRED (process string)
455   (epg-context-set-result-for
456    epg-context 'error
457    (cons (cons 'key-expired string)
458          (epg-context-result-for epg-context 'error))))
459
460 (defun epg-status-KEYREVOKED (process string)
461   (epg-context-set-result-for
462    epg-context 'error
463    (cons 'key-revoked
464          (epg-context-result-for epg-context 'error))))
465
466 (defun epg-status-BADARMOR (process string)
467   (epg-context-set-result-for
468    epg-context 'error
469    (cons 'bad-armor
470          (epg-context-result-for epg-context 'error))))
471
472 (defun epg-passphrase-callback-function (key-id handback)
473   (read-passwd
474    (if (eq key-id 'SYM)
475        "Passphrase for symmetric encryption: "
476      (if (eq key-id 'PIN)
477          "Passphrase for PIN: "
478        (let ((entry (assoc key-id epg-user-id-alist)))
479          (if entry
480              (format "Passphrase for %s %s: " key-id (cdr entry))
481            (format "Passphrase for %s: " key-id)))))))
482
483 (defun epg-progress-callback-function (what char current total handback)
484   (message "%s: %d%%/%d%%" what current total))
485
486 (defun epg-list-keys (name &optional secret)
487   "List keys associated with STRING."
488   (let ((args (list "--with-colons" "--no-greeting" "--batch"
489                     "--fixed-list-mode"
490                     (if secret "--list-secret-keys" "--list-keys")
491                     name))
492         keys type symbol pointer)
493     (with-temp-buffer
494       (apply #'call-process epg-gpg-program nil (list t nil) nil args)
495       (goto-char (point-min))
496       (while (looking-at "\\([a-z][a-z][a-z]\\):\\(.*\\)")
497         (setq type (match-string 1)
498               symbol (intern-soft (format "epg-colons-%s-spec" type)))
499         (if (member type '("pub" "sec"))
500             (setq keys (cons nil keys)))
501         (if (and symbol
502                  (boundp symbol))
503             (setcar keys (cons (cons (intern type)
504                                      (epg-parse-colons
505                                       (symbol-value symbol)
506                                       (match-string 2)))
507                                (car keys))))
508         (forward-line)))
509     (setq pointer keys)
510     (while pointer
511       (setcar pointer (nreverse (car pointer)))
512       (setq pointer (cdr pointer)))
513     (nreverse keys)))
514
515 (defun epg-parse-colons (alist string)
516   (let ((index 0)
517         result)
518     (while (and alist
519                 (or (null (car alist))
520                     (eq index
521                         (string-match
522                          (concat "\\(" (nth 1 (car alist)) "\\)?:")
523                          string index))))
524       (if (car alist)
525           (progn
526             (setq index (match-end 0))
527             (if (match-beginning 1)
528                 (setq result
529                       (cons (cons (car (car alist))
530                                   (funcall (or (nth 3 (car alist)) #'identity)
531                                            (match-string
532                                             (1+ (or (nth 2 (car alist)) 0))
533                                             string)))
534                             result))))
535         (setq index (1+ index)))
536       (setq alist (cdr alist)))
537     (nreverse result)))
538
539 (if (fboundp 'make-temp-file)
540     (defalias 'epg-make-temp-file 'make-temp-file)
541   ;; stolen from poe.el.
542   (defun epg-make-temp-file (prefix)
543     "Create a temporary file.
544 The returned file name (created by appending some random characters at the end
545 of PREFIX, and expanding against `temporary-file-directory' if necessary),
546 is guaranteed to point to a newly created empty file.
547 You can then use `write-region' to write new data into the file."
548     (let (tempdir tempfile)
549       (unwind-protect
550           (let (file)
551             ;; First, create a temporary directory.
552             (while (condition-case ()
553                        (progn
554                          (setq tempdir (make-temp-name
555                                         (concat
556                                          (file-name-directory prefix)
557                                          "DIR")))
558                          ;; return nil or signal an error.
559                          (make-directory tempdir))
560                      ;; let's try again.
561                      (file-already-exists t)))
562             (set-file-modes tempdir 448)
563             ;; Second, create a temporary file in the tempdir.
564             ;; There *is* a race condition between `make-temp-name'
565             ;; and `write-region', but we don't care it since we are
566             ;; in a private directory now.
567             (setq tempfile (make-temp-name (concat tempdir "/EMU")))
568             (write-region "" nil tempfile nil 'silent)
569             (set-file-modes tempfile 384)
570             ;; Finally, make a hard-link from the tempfile.
571             (while (condition-case ()
572                        (progn
573                          (setq file (make-temp-name prefix))
574                          ;; return nil or signal an error.
575                          (add-name-to-file tempfile file))
576                      ;; let's try again.
577                      (file-already-exists t)))
578             file)
579         ;; Cleanup the tempfile.
580         (and tempfile
581              (file-exists-p tempfile)
582              (delete-file tempfile))
583         ;; Cleanup the tempdir.
584         (and tempdir
585              (file-directory-p tempdir)
586              (delete-directory tempdir))))))
587
588 ;;;###autoload
589 (defun epg-start-decrypt (context input-file)
590   "Initiate a decrypt operation on INPUT-FILE.
591
592 If you use this function, you will need to wait for the completion of
593 `epg-gpg-program' by using `epg-wait-for-completion' and call
594 `epg-reset' to clear a temporaly output file.
595 If you are unsure, use synchronous version of this function
596 `epg-decrypt-string' instead."
597   (epg-context-set-result context nil)
598   (epg-context-set-output-file context (epg-make-temp-file "epg-output"))
599   (epg-start context
600              (list "--decrypt" input-file))
601   (epg-wait-for-status context '("BEGIN_DECRYPTION")))
602
603 ;;;###autoload
604 (defun epg-decrypt-file (context input-file)
605   "Decrypt INPUT-FILE and return the plain text."
606   (unwind-protect
607       (progn
608         (epg-start-decrypt context input-file)
609         (epg-wait-for-completion context)
610         (if (epg-context-result-for context 'error)
611             (error "Decryption failed"))
612         (epg-read-output context))
613     (epg-reset context)))
614
615 ;;;###autoload
616 (defun epg-decrypt-string (context string)
617   "Decrypt STRING and return the plain text."
618   (let ((input-file (epg-make-temp-file "epg-input"))
619         (coding-system-for-write 'binary))
620     (unwind-protect
621         (progn
622           (write-region string nil input-file)
623           (epg-decrypt-file context input-file))
624       (if (file-exists-p input-file)
625           (delete-file input-file)))))
626
627 ;;;###autoload
628 (defun epg-start-verify (context signature &optional string)
629   "Initiate a verify operation on SIGNATURE.
630
631 For a detached signature, both SIGNATURE and STRING should be string.
632 For a normal or a clear text signature, STRING should be nil.
633
634 If you use this function, you will need to wait for the completion of
635 `epg-gpg-program' by using `epg-wait-for-completion' and call
636 `epg-reset' to clear a temporaly output file.
637 If you are unsure, use synchronous version of this function
638 `epg-verify-string' instead."
639   (epg-context-set-result context nil)
640   (epg-context-set-output-file context (epg-make-temp-file "epg-output"))
641   (if string
642       ;; Detached signature.
643       (progn
644         (epg-start context
645                    (append (list "--verify")
646                            (list signature "-")))
647         (if (eq (process-status (epg-context-process context)) 'run)
648             (process-send-string (epg-context-process context) string)))
649     ;; Normal (or cleartext) signature.
650     (epg-start context (list "--verify"))
651     (if (eq (process-status (epg-context-process context)) 'run)
652         (process-send-string (epg-context-process context) signature))))
653
654 ;;;###autoload
655 (defun epg-verify-file (context input-file &optional string)
656   "Verify INPUT-FILE.
657
658 For a detached signature, both INPUT-FILE and STRING should be string.
659 For a normal or a clear text signature, STRING should be nil."
660   (unwind-protect
661       (progn
662         (epg-start-verify context input-file string)
663         (epg-wait-for-completion context)
664         (epg-context-result-for context 'verify))
665     (epg-reset context)))
666
667 ;;;###autoload
668 (defun epg-verify-string (context signature &optional string)
669   "Verify SIGNATURE.
670
671 For a detached signature, both SIGNATURE and STRING should be string.
672 For a normal or a clear text signature, STRING should be nil."
673   (let ((input-file (epg-make-temp-file "epg-input"))
674         (coding-system-for-write 'binary))
675     (unwind-protect
676         (progn
677           (if string
678               (write-region signature nil input-file))
679           (epg-verify-file context input-file string))
680       (if (file-exists-p input-file)
681           (delete-file input-file)))))
682
683 ;;;###autoload
684 (defun epg-start-sign (context string &optional mode)
685   "Initiate a sign operation on STRING.
686
687 If optional 3rd argument MODE is 'clearsign, it makes a clear text signature.
688 If MODE is t or 'detached, it makes a detached signature.
689 Otherwise, it makes a normal signature.
690
691 If you use this function, you will need to wait for the completion of
692 `epg-gpg-program' by using `epg-wait-for-completion' and call
693 `epg-reset' to clear a temporaly output file.
694 If you are unsure, use synchronous version of this function
695 `epg-sign-string' instead."
696   (epg-context-set-result context nil)
697   (epg-context-set-output-file context (epg-make-temp-file "epg-output"))
698   (epg-start context
699              (append (list (if (eq mode 'clearsign)
700                                "--clearsign"
701                              (if (or (eq mode t) (eq mode 'detached))
702                                  "--detach-sign"
703                                "--sign")))
704                      (apply #'nconc
705                             (mapcar (lambda (signer)
706                                       (list "-u" signer))
707                                     (epg-context-signers context)))))
708   (epg-wait-for-status context '("BEGIN_SIGNING"))
709   (if (eq (process-status (epg-context-process context)) 'run)
710       (process-send-string (epg-context-process context) string)))
711
712 ;;;###autoload
713 (defun epg-sign-string (context string &optional mode)
714   "Sign STRING and return the output as string.
715 If optional 3rd argument MODE is 'clearsign, it makes a clear text signature.
716 If MODE is t or 'detached, it makes a detached signature.
717 Otherwise, it makes a normal signature."
718   (unwind-protect
719       (progn
720         (epg-start-sign context string mode)
721         (epg-wait-for-completion context)
722         (if (epg-context-result-for context 'error)
723             (error "Sign failed"))
724         (epg-read-output context))
725     (epg-reset context)))
726
727 ;;;###autoload
728 (defun epg-start-encrypt (context string recipients
729                                   &optional sign always-trust)
730   "Initiate an encrypt operation on STRING.
731 If RECIPIENTS is nil, it performs symmetric encryption.
732
733 If you use this function, you will need to wait for the completion of
734 `epg-gpg-program' by using `epg-wait-for-completion' and call
735 `epg-reset' to clear a temporaly output file.
736 If you are unsure, use synchronous version of this function
737 `epg-encrypt-string' instead."
738   (epg-context-set-result context nil)
739   (epg-context-set-output-file context (epg-make-temp-file "epg-output"))
740   (epg-start context
741              (append (if always-trust '("--always-trust"))
742                      (if recipients '("--encrypt") '("--symmetric"))
743                      (if sign
744                          (cons "--sign"
745                                (apply #'nconc
746                                       (mapcar (lambda (signer)
747                                                 (list "-u" signer))
748                                               (epg-context-signers context)))))
749                      (apply #'nconc
750                             (mapcar (lambda (recipient)
751                                       (list "-r" recipient))
752                                     recipients))))
753   (if sign
754       (epg-wait-for-status context '("BEGIN_SIGNING"))
755     (if (null recipients)
756         (epg-wait-for-status context '("BEGIN_ENCRYPTION"))))
757   (if (eq (process-status (epg-context-process context)) 'run)
758       (process-send-string (epg-context-process context) string)))
759
760 ;;;###autoload
761 (defun epg-encrypt-string (context string recipients
762                                    &optional sign always-trust)
763   "Encrypt STRING.
764 If RECIPIENTS is nil, it performs symmetric encryption."
765   (unwind-protect
766       (progn
767         (epg-start-encrypt context string recipients sign always-trust)
768         (epg-wait-for-completion context)
769         (if (epg-context-result-for context 'error)
770             (error "Encrypt failed"))
771         (epg-read-output context))
772     (epg-reset context)))
773
774 ;;;###autoload
775 (defun epg-start-export-keys (context pattern)
776   "Initiate an export keys operation.
777
778 If you use this function, you will need to wait for the completion of
779 `epg-gpg-program' by using `epg-wait-for-completion' and call
780 `epg-reset' to clear a temporaly output file.
781 If you are unsure, use synchronous version of this function
782 `epg-export-keys' instead."
783   (epg-context-set-result context nil)
784   (epg-context-set-output-file context (epg-make-temp-file "epg-output"))
785   (epg-start context (list "--export" pattern)))
786
787 ;;;###autoload
788 (defun epg-export-keys (context pattern)
789   "Extract public keys matched with PATTERN and return them."
790   (unwind-protect
791       (progn
792         (epg-start-export-keys context pattern)
793         (epg-wait-for-completion context)
794         (if (epg-context-result-for context 'error)
795             (error "Export keys failed"))
796         (epg-read-output context))
797     (epg-reset context)))
798
799 ;;;###autoload
800 (defun epg-start-import-keys (context keys)
801   "Initiate an import key operation.
802
803 If you use this function, you will need to wait for the completion of
804 `epg-gpg-program' by using `epg-wait-for-completion' and call
805 `epg-reset' to clear a temporaly output file.
806 If you are unsure, use synchronous version of this function
807 `epg-import-keys' instead."
808   (epg-context-set-result context nil)
809   (epg-context-set-output-file context (epg-make-temp-file "epg-output"))
810   (epg-start context (list "--import"))
811   (if (eq (process-status (epg-context-process context)) 'run)
812       (process-send-string (epg-context-process context) keys)))
813
814 ;;;###autoload
815 (defun epg-import-keys (context keys)
816   "Add KEYS."
817   (unwind-protect
818       (progn
819         (epg-start-import-keys context keys)
820         (epg-wait-for-completion context)
821         (if (epg-context-result-for context 'error)
822             (error "Import keys failed"))
823         (epg-read-output context))
824     (epg-reset context)))
825
826 (provide 'epg)
827
828 ;;; epg.el ends here