X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=epg.el;h=3ba189ff49cecead62558434214b246b13868f3c;hb=5944a44f16964313efa58244a1ad0e62f32237fb;hp=ba5cc04f2ca4b8dd153621a6d6da2d2e62d01711;hpb=9dfb007e62a86cb53bedea570f8f0e47ff73df01;p=elisp%2Fepg.git diff --git a/epg.el b/epg.el index ba5cc04..3ba189f 100644 --- a/epg.el +++ b/epg.el @@ -46,6 +46,7 @@ (defvar epg-key-id nil) (defvar epg-context nil) (defvar epg-debug nil) +(defvar epg-debug-buffer nil) ;; from gnupg/include/cipher.h (defconst epg-cipher-algorithm-alist @@ -405,11 +406,25 @@ This function is for internal use only." (setcdr entry value) (epg-context-set-result context (cons (cons name value) result))))) +(defun epg-signature-to-string (signature) + (format "%s signature from %s %s%s" + (capitalize (symbol-name (epg-signature-status signature))) + (epg-signature-key-id signature) + (epg-signature-user-id signature) + (if (epg-signature-validity signature) + (format " (trust %s)" + (epg-signature-validity signature)) + ""))) + +(defun epg-verify-result-to-string (verify-result) + (mapconcat #'epg-signature-to-string verify-result "\n")) + (defun epg-start (context args) "Start `epg-gpg-program' in a subprocess with given ARGS." (let* ((args (append (list "--no-tty" "--status-fd" "1" - "--command-fd" "0") + "--command-fd" "0" + "--yes") (if (epg-context-armor context) '("--armor")) (if (epg-context-textmode context) '("--textmode")) (if (epg-context-output-file context) @@ -422,7 +437,9 @@ This function is for internal use only." process) (if epg-debug (save-excursion - (set-buffer (get-buffer-create " *epg-debug*")) + (unless epg-debug-buffer + (setq epg-debug-buffer (generate-new-buffer " *epg-debug*"))) + (set-buffer epg-debug-buffer) (goto-char (point-max)) (insert (format "%s %s\n" epg-gpg-program (mapconcat #'identity args " "))))) @@ -447,7 +464,9 @@ This function is for internal use only." (defun epg-process-filter (process input) (if epg-debug (save-excursion - (set-buffer (get-buffer-create " *epg-debug*")) + (unless epg-debug-buffer + (setq epg-debug-buffer (generate-new-buffer " *epg-debug*"))) + (set-buffer epg-debug-buffer) (goto-char (point-max)) (insert input))) (if (buffer-live-p (process-buffer process)) @@ -457,7 +476,7 @@ This function is for internal use only." (insert input) (goto-char epg-read-point) (beginning-of-line) - (while (looking-at ".*\n") ;the input line is finished + (while (looking-at ".*\n") ;the input line finished (save-excursion (if (looking-at "\\[GNUPG:] \\([A-Z_]+\\) ?\\(.*\\)") (let* ((status (match-string 1)) @@ -489,15 +508,16 @@ This function is for internal use only." epg-pending-status-list) (accept-process-output (epg-context-process context) 1)))) -(defun epg-wait-for-completion (context &optional no-eof) - (if (and (not no-eof) - (eq (process-status (epg-context-process context)) 'run)) - (process-send-eof (epg-context-process context))) +(defun epg-wait-for-completion (context) (while (eq (process-status (epg-context-process context)) 'run) ;; We can't use accept-process-output instead of sit-for here ;; because it may cause an interrupt during the sentinel execution. (sit-for 0.1))) +(defun epg-flush (context) + (if (eq (process-status (epg-context-process context)) 'run) + (process-send-eof (epg-context-process context)))) + (defun epg-reset (context) (if (and (epg-context-process context) (buffer-live-p (process-buffer (epg-context-process context)))) @@ -530,23 +550,26 @@ This function is for internal use only." (setq epg-key-id 'PIN)) (defun epg-status-GET_HIDDEN (process string) - (let ((passphrase - (funcall (if (consp (epg-context-passphrase-callback epg-context)) - (car (epg-context-passphrase-callback epg-context)) - (epg-context-passphrase-callback epg-context)) - epg-key-id - (if (consp (epg-context-passphrase-callback epg-context)) - (cdr (epg-context-passphrase-callback epg-context))))) - string) - (if passphrase - (unwind-protect - (progn - (setq string (concat passphrase "\n")) - (fillarray passphrase 0) - (setq passphrase nil) - (process-send-string process string)) - (if string - (fillarray string 0)))))) + (if (and epg-key-id + (string-match "\\`passphrase\\." string)) + (let ((passphrase + (funcall + (if (consp (epg-context-passphrase-callback epg-context)) + (car (epg-context-passphrase-callback epg-context)) + (epg-context-passphrase-callback epg-context)) + epg-key-id + (if (consp (epg-context-passphrase-callback epg-context)) + (cdr (epg-context-passphrase-callback epg-context))))) + string) + (if passphrase + (unwind-protect + (progn + (setq string (concat passphrase "\n")) + (fillarray passphrase 0) + (setq passphrase nil) + (process-send-string process string)) + (if string + (fillarray string 0))))))) (defun epg-status-GET_BOOL (process string) (let ((entry (assoc string epg-prompt-alist))) @@ -717,6 +740,21 @@ This function is for internal use only." (cons (cons 'delete-problem (string-to-number (match-string 1 string))) (epg-context-result-for epg-context 'error))))) +(defun epg-status-SIG_CREATED (process string) + (if (string-match "\\`\\([DCS]\\) \\([0-9]+\\) \\([0-9]+\\) \ +\\([0-9A-Fa-F][0-9A-Fa-F]\\) \\(.*\\) " string) + (epg-context-set-result-for + epg-context 'sign + (cons (list (cons 'type (string-to-char (match-string 1 string))) + (cons 'pubkey-algorithm + (string-to-number (match-string 2 string))) + (cons 'digest-algorithm + (string-to-number (match-string 3 string))) + (cons 'class (string-to-number (match-string 4 string) 16)) + (cons 'creation-time (match-string 5 string)) + (cons 'fingerprint (substring string (match-end 0)))) + (epg-context-result-for epg-context 'sign))))) + (defun epg-passphrase-callback-function (key-id handback) (read-passwd (if (eq key-id 'SYM) @@ -905,7 +943,7 @@ If PLAIN is nil, it returns the result as a string." (epg-context-set-output-file context (epg-make-temp-file "epg-output"))) (epg-start-decrypt context (epg-make-data-from-file cipher)) - (epg-wait-for-completion context t) + (epg-wait-for-completion context) (if (epg-context-result-for context 'error) (error "Decrypt failed: %S" (epg-context-result-for context 'error))) @@ -926,6 +964,7 @@ If PLAIN is nil, it returns the result as a string." (epg-context-set-output-file context (epg-make-temp-file "epg-output")) (epg-start-decrypt context (epg-make-data-from-file input-file)) + (epg-flush context) (epg-wait-for-completion context) (if (epg-context-result-for context 'error) (error "Decrypt failed: %S" @@ -986,7 +1025,7 @@ For a normal or a clear text signature, SIGNED-TEXT should be nil." (epg-make-data-from-file signed-text)) (epg-start-verify context (epg-make-data-from-file signature))) - (epg-wait-for-completion context t) + (epg-wait-for-completion context) (unless plain (epg-read-output context))) (unless plain @@ -1014,6 +1053,7 @@ For a normal or a clear text signature, SIGNED-TEXT should be nil." (epg-make-data-from-file input-file) (epg-make-data-from-string signed-text))) (epg-start-verify context (epg-make-data-from-string signature))) + (epg-flush context) (epg-wait-for-completion context) (epg-read-output context)) (epg-delete-output-file context) @@ -1044,9 +1084,12 @@ If you are unsure, use synchronous version of this function "--detach-sign" "--sign"))) (apply #'nconc - (mapcar (lambda (signer) - (list "-u" signer)) - (epg-context-signers context))) + (mapcar + (lambda (signer) + (list "-u" + (epg-sub-key-id + (car (epg-key-sub-key-list signer))))) + (epg-context-signers context))) (if (epg-data-file plain) (list (epg-data-file plain))))) (epg-wait-for-status context '("BEGIN_SIGNING")) @@ -1069,7 +1112,7 @@ Otherwise, it makes a normal signature." (epg-context-set-output-file context (epg-make-temp-file "epg-output"))) (epg-start-sign context (epg-make-data-from-file plain) mode) - (epg-wait-for-completion context t) + (epg-wait-for-completion context) (if (epg-context-result-for context 'error) (error "Sign failed: %S" (epg-context-result-for context 'error))) @@ -1090,6 +1133,7 @@ Otherwise, it makes a normal signature." (epg-context-set-output-file context (epg-make-temp-file "epg-output")) (epg-start-sign context (epg-make-data-from-string plain) mode) + (epg-flush context) (epg-wait-for-completion context) (if (epg-context-result-for context 'error) (error "Sign failed: %S" @@ -1121,14 +1165,17 @@ If you are unsure, use synchronous version of this function (list "-u" signer)) (epg-context-signers context))))) (apply #'nconc - (mapcar (lambda (recipient) - (list "-r" recipient)) - recipients)) + (mapcar + (lambda (recipient) + (list "-r" + (epg-sub-key-id + (car (epg-key-sub-key-list recipient))))) + recipients)) (if (epg-data-file plain) (list (epg-data-file plain))))) (if sign - (epg-wait-for-status context '("BEGIN_SIGNING"))) - (epg-wait-for-status context '("BEGIN_ENCRYPTION")) + (epg-wait-for-status context '("BEGIN_SIGNING")) + (epg-wait-for-status context '("BEGIN_ENCRYPTION"))) (if (and (epg-data-string plain) (eq (process-status (epg-context-process context)) 'run)) (process-send-string (epg-context-process context) @@ -1148,7 +1195,7 @@ If RECIPIENTS is nil, it performs symmetric encryption." (epg-make-temp-file "epg-output"))) (epg-start-encrypt context (epg-make-data-from-file plain) recipients sign always-trust) - (epg-wait-for-completion context t) + (epg-wait-for-completion context) (if (epg-context-result-for context 'error) (error "Encrypt failed: %S" (epg-context-result-for context 'error))) @@ -1169,6 +1216,7 @@ If RECIPIENTS is nil, it performs symmetric encryption." (epg-make-temp-file "epg-output")) (epg-start-encrypt context (epg-make-data-from-string plain) recipients sign always-trust) + (epg-flush context) (epg-wait-for-completion context) (if (epg-context-result-for context 'error) (error "Encrypt failed: %S" @@ -1178,31 +1226,47 @@ If RECIPIENTS is nil, it performs symmetric encryption." (epg-reset context))) ;;;###autoload -(defun epg-start-export-keys (context pattern) +(defun epg-start-export-keys (context keys) "Initiate an export keys operation. If you use this function, you will need to wait for the completion of `epg-gpg-program' by using `epg-wait-for-completion' and call `epg-reset' to clear a temporaly output file. If you are unsure, use synchronous version of this function -`epg-export-keys' instead." +`epg-export-keys-to-file' or `epg-export-keys-to-string' instead." (epg-context-set-result context nil) - (epg-context-set-output-file context (epg-make-temp-file "epg-output")) - (epg-start context (list "--export" pattern))) + (epg-start context (cons "--export" + (mapcar + (lambda (key) + (epg-sub-key-id + (car (epg-key-sub-key-list key)))) + keys)))) ;;;###autoload -(defun epg-export-keys (context pattern) - "Extract public keys matched with PATTERN and return them." +(defun epg-export-keys-to-file (context keys file) + "Extract public KEYS." (unwind-protect (progn - (epg-start-export-keys context pattern) + (if keys + (epg-context-set-output-file context file) + (epg-context-set-output-file context + (epg-make-temp-file "epg-output"))) + (epg-start-export-keys context keys) (epg-wait-for-completion context) (if (epg-context-result-for context 'error) (error "Export keys failed")) - (epg-read-output context)) + (unless file + (epg-read-output context))) + (unless file + (epg-delete-output-file context)) (epg-reset context))) ;;;###autoload +(defun epg-export-keys-to-string (context keys) + "Extract public KEYS and return them as a string." + (epg-export-keys-to-file context keys nil)) + +;;;###autoload (defun epg-start-import-keys (context keys) "Initiate an import keys operation. KEYS is a data object. @@ -1214,7 +1278,7 @@ If you are unsure, use synchronous version of this function `epg-import-keys-from-file' or `epg-import-keys-from-string' instead." (epg-context-set-result context nil) (epg-context-set-output-file context (epg-make-temp-file "epg-output")) - (epg-start context (append (list "--import") (epg-data-file keys))) + (epg-start context (list "--import" (epg-data-file keys))) (if (and (epg-data-string keys) (eq (process-status (epg-context-process context)) 'run)) (process-send-string (epg-context-process context) @@ -1224,7 +1288,9 @@ If you are unsure, use synchronous version of this function (unwind-protect (progn (epg-start-import-keys context keys) - (epg-wait-for-completion context (epg-data-file keys)) + (if (epg-data-file keys) + (epg-flush context)) + (epg-wait-for-completion context) (if (epg-context-result-for context 'error) (error "Import keys failed")) (epg-read-output context)) @@ -1241,28 +1307,31 @@ If you are unsure, use synchronous version of this function (epg-import-keys-1 context (epg-make-data-from-string keys))) ;;;###autoload -(defun epg-start-delete-key (context key &optional allow-secret) - "Initiate an delete key operation. +(defun epg-start-delete-keys (context keys &optional allow-secret) + "Initiate an delete keys operation. If you use this function, you will need to wait for the completion of `epg-gpg-program' by using `epg-wait-for-completion' and call `epg-reset' to clear a temporaly output file. If you are unsure, use synchronous version of this function -`epg-delete-key' instead." +`epg-delete-keys' instead." (epg-context-set-result context nil) - (epg-context-set-output-file context (epg-make-temp-file "epg-output")) - (epg-start context (list (if allow-secret + (epg-start context (cons (if allow-secret "--delete-secret-key" "--delete-key") - (epg-sub-key-id (car (epg-key-sub-key-list key)))))) + (mapcar + (lambda (key) + (epg-sub-key-id + (car (epg-key-sub-key-list key)))) + keys)))) ;;;###autoload -(defun epg-delete-key (context key &optional allow-secret) - "Delete KEY from the key ring." +(defun epg-delete-keys (context keys &optional allow-secret) + "Delete KEYS from the key ring." (unwind-protect (progn - (epg-start-delete-key context key) - (epg-wait-for-completion context t) + (epg-start-delete-keys context keys) + (epg-wait-for-completion context) (if (epg-context-result-for context 'error) (error "Delete key failed"))) (epg-reset context)))