* epg.el (epg-export-keys-to-file): Fixed a typo.
[elisp/epg.git] / epg-pgp50i.el
index 893e73e..2341670 100644 (file)
     ("Pass phrase is good." .
      "GOOD_PASSPHRASE")
     ("Cannot decrypt message.  It can only be decrypted by:" .
-     "CANNOT_DECRYPT")))
-
-(defvar epg-pgp50i-status nil)
+     "CANNOT_DECRYPT")
+    ("Good signature made .* by key:" .
+     "GOOD_SIGNATURE")
+    ("BAD signature made .* by key:" .
+     "BAD_SIGNATURE")
+    ("Error .* checking signature:  " .
+     "ERROR_SIGNATURE")
+    ("Signature by unknown keyid: " .
+     "UNKNOWN_SIGNATURE")))
 
 (defun epg-pgp50i--start (context program args)
-  (let ((args (append '("--headers" "--language=us" "--batchmode=0" "--force")
+  (let ((args (append '("--headers" "--language=us")
                      (if (epg-context-armor context) '("--armor"))
                      (if (epg-context-textmode context) '("--textmode"))
                      (if (epg-context-output-file context)
     (with-current-buffer buffer
       (make-local-variable 'epg-read-point)
       (setq epg-read-point (point-min))
+      (make-local-variable 'epg-process-filter-running)
+      (setq epg-process-filter-running nil)
       (make-local-variable 'epg-pending-status-list)
       (setq epg-pending-status-list nil)
       (make-local-variable 'epg-key-id)
       (setq epg-key-id nil)
       (make-local-variable 'epg-context)
-      (setq epg-context context)
-      (make-local-variable 'epg-pgp50i-status)
-      (setq epg-pgp50i-status nil))
+      (setq epg-context context))
     (unwind-protect
        (progn
          (set-default-file-modes 448)
        (set-buffer (process-buffer process))
        (goto-char (point-max))
        (insert input)
-       (unless epg-pgp50i-status
-         (goto-char epg-read-point)
-         (while (not (eobp))
-           (save-excursion
-             (if (looking-at
-                  "\\(PRI\\|INF\\|QRY\\|STA\\|WRN\\|ERR\\):  \\(.*\\)")
-                 (let ((message (match-string 2))
-                       (pointer epg-pgp50i-message-alist)
-                       status symbol)
-                   (while pointer
-                     (if (string-match (car (car pointer)) message)
-                         (setq status (cdr (car pointer))
-                               pointer nil))
-                     (setq pointer (cdr pointer)))
-                   (when status
-                     (unless (looking-at ".*\n")
-                       (end-of-line)
-                       (insert "\n"))
-                     (if (member status epg-pending-status-list)
-                         (setq epg-pending-status-list nil))
-                     (setq symbol (intern-soft (concat "epg-pgp50i--status-"
-                                                       status)))
-                     (if (and symbol
-                              (fboundp symbol))
-                         (unwind-protect
-                             (progn
-                               (setq epg-pgp50i-status status)
-                               (funcall symbol epg-context message))
-                           (setq epg-pgp50i-status nil)))))))
-           (forward-line)
-           (setq epg-read-point (point)))))))
-
-(defun epg-pgp50i--wait-for-line (context)
+       (unless epg-process-filter-running
+         (unwind-protect
+             (progn
+               (setq epg-process-filter-running t)
+               (goto-char epg-read-point)
+               (while (not (eobp))
+                 (if (looking-at
+                      "^\\(PRI\\|INF\\|QRY\\|STA\\|WRN\\|ERR\\):  \\(.*\\)")
+                     (let ((message (match-string 2))
+                           (pointer epg-pgp50i-message-alist)
+                           status symbol)
+                       (while pointer
+                         (if (string-match (car (car pointer)) message)
+                             (setq status (cdr (car pointer))
+                                   pointer nil))
+                         (setq pointer (cdr pointer)))
+                       (when status
+                         (unless (looking-at ".*\n")
+                           (end-of-line)
+                           (insert "\n"))
+                         (if (member status epg-pending-status-list)
+                             (setq epg-pending-status-list nil))
+                         (setq symbol (intern-soft (concat
+                                                    "epg-pgp50i--status-"
+                                                    status)))
+                         (if (and symbol
+                                  (fboundp symbol))
+                             (funcall symbol epg-context message)))))
+                 (forward-line)
+                 (setq epg-read-point (point))))
+           (setq epg-process-filter-running nil))))))
+
+(defun epg-pgp50i--read-line (context)
   (if (eq (process-status (epg-context-process context)) 'run)
       (save-excursion
        (set-buffer (process-buffer (epg-context-process context)))
+       (forward-line)
        (goto-char epg-read-point)
-       (beginning-of-line 2)
-       (while (and (eq (process-status (epg-context-process context)) 'run)
-                   (not (if (looking-at ".*\n")
-                            (setq epg-read-point (point)))))
-         (accept-process-output (epg-context-process context) 1))
-       (buffer-substring (point) (progn (end-of-line) (point))))))
-
-(defun epg-pgp50i--status-ENTER_PASSPHRASE (context status)
+       (if (looking-at ".*\n")
+           (buffer-substring (point) (progn (end-of-line) (point)))))))
+
+(defun epg-pgp50i--status-ENTER_PASSPHRASE (context message)
   (epg--status-GET_HIDDEN context "passphrase."))
 
-(defun epg-pgp50i--status-NEED_PASSPHRASE_TO_DECRYPT_KEY (context status)
-  (let ((line (epg-pgp50i--wait-for-line context))
-       user-id entry)
+(defun epg-pgp50i--read-key (context)
+  (let ((line (epg-pgp50i--read-line context))
+       key-id user-id-list)
     (when (and line
               (string-match "[ 0-9]+ bits, Key ID \\([0-9A-F]+\\)" line))
-      (setq epg-key-id (match-string 1 line)
-           line (epg-pgp50i--wait-for-line context))
-      (when (and line
-                (string-match "\"\\([^\"]+\\)\"" line))
-       (setq user-id (match-string 1 line)
-             entry (assoc epg-key-id epg-user-id-alist))
-       (if entry
-           (setcdr entry user-id)
-         (setq epg-user-id-alist (cons (cons epg-key-id user-id)
-                                       epg-user-id-alist)))))))
+      (setq key-id (match-string 1 line))
+      (while (and (setq line (epg-pgp50i--read-line context))
+                 (string-match "\"\\([^\"]+\\)\"" line))
+       (setq user-id-list (cons (match-string 1 line) user-id-list)))
+      (cons key-id user-id-list))))
+
+(defun epg-pgp50i--status-NEED_PASSPHRASE_TO_DECRYPT_KEY (context message)
+  (let* ((key (epg-pgp50i--read-key context))
+        (entry (assoc (car key) epg-user-id-alist)))
+    (if entry
+       (setcdr entry (car (cdr key)))
+      (setq epg-user-id-alist (cons (cons (car key) (car (cdr key)))
+                                   epg-user-id-alist)))
+    (setq epg-key-id (car key))))
+
+(defun epg-pgp50i--status-CANNOT_DECRYPT (context message)
+  (epg-context-set-result-for
+   context 'error
+   (cons (cons 'decryption-failed
+              (epg-pgp50i--read-key context))
+        (epg-context-result-for context 'error))))
+
+(defun epg-pgp50i--parse-time (string)
+  (if (string-match "\\`\\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\) \
+\\([0-9][0-9]\\):\\([0-9][0-9]\\) GMT\\'" string)
+      (encode-time 0
+                  (string-to-number (match-string 5 string))
+                  (string-to-number (match-string 4 string))
+                  (string-to-number (match-string 3 string))
+                  (string-to-number (match-string 2 string))
+                  (string-to-number (match-string 1 string))
+                  0)))
+
+(defun epg-pgp50i--status-GOOD_SIGNATURE (context message)
+  (if (string-match "Good signature made \\(.*\\) by key:" message)
+      (let ((time (epg-pgp50i--parse-time (match-string 1 message)))
+           (key (epg-pgp50i--read-key context)))
+       (epg--status-*SIG context 'good (concat (car key) " " (car (cdr key))))
+       (epg-signature-set-creation-time
+        (car (epg-context-result-for context 'verify))
+        time))))
+
+(defun epg-pgp50i--status-BAD_SIGNATURE (context message)
+  (if (string-match "BAD signature made \\(.*\\) by key:" message)
+      (let ((time (epg-pgp50i--parse-time (match-string 1 message)))
+           (key (epg-pgp50i--read-key context)))
+       (epg--status-*SIG context 'good (concat (car key) " " (car (cdr key))))
+       (epg-signature-set-creation-time
+        (car (epg-context-result-for context 'verify))
+        time))))
 
 (defadvice epg-start-decrypt
   (around epg-pgp50i activate)
   (if (eq (epg-context-protocol (ad-get-arg 0)) 'CMS)
       ad-do-it
-    (unless (epg-data-file (ad-get-arg 1))
-      (error "Not a file"))
-    (epg-context-set-operation context 'decrypt)
-    (epg-context-set-result (ad-get-arg 0) nil)
-    (epg-pgp50i--start context epg-pgp50i-pgpv-program
-                      (list (epg-data-file (ad-get-arg 1))))))
+    (let ((context (ad-get-arg 0))
+         (cipher (ad-get-arg 1)))
+      (unless (epg-data-file cipher)
+       (error "Not a file"))
+      (epg-context-set-operation context 'decrypt)
+      (epg-context-set-result context nil)
+      (epg-pgp50i--start context epg-pgp50i-pgpv-program
+                        (list  "--batchmode=0" "--force"
+                               (epg-data-file cipher))))))
 
 (defadvice epg-start-verify
   (around epg-pgp50i activate)
   (if (eq (epg-context-protocol (ad-get-arg 0)) 'CMS)
       ad-do-it
-    (error "Not implemented yet")))
+    (let ((context (ad-get-arg 0))
+         (signature (ad-get-arg 1))
+         (signed-text (ad-get-arg 2)))
+      (epg-context-set-operation context 'verify)
+      (epg-context-set-result context nil)
+      (if signed-text
+         ;; Detached signature.
+         (if (epg-data-file signed-text)
+             (epg-pgp50i--start context
+                                epg-pgp50i-pgpv-program
+                                (list  "--batchmode=1" "--force"
+                                       (epg-data-file signature)
+                                       (epg-data-file signed-text)))
+           (epg-pgp50i--start context
+                              epg-pgp50i-pgpv-program
+                              (list  "--batchmode=1" "--force"
+                                     (epg-data-file signature)))
+           (if (eq (process-status (epg-context-process context)) 'run)
+               (process-send-string (epg-context-process context)
+                                    (epg-data-string signed-text)))
+           (if (eq (process-status (epg-context-process context)) 'run)
+               (process-send-eof (epg-context-process context))))
+       ;; Normal (or cleartext) signature.
+       (if (epg-data-file signature)
+           (epg-pgp50i--start context
+                              epg-pgp50i-pgpv-program
+                              (list "--batchmode=1" "--force"
+                                    (epg-data-file signature)))
+         (epg-pgp50i--start context
+                            epg-pgp50i-pgpv-program
+                            (list "--batchmode=1" "--force"))
+         (if (eq (process-status (epg-context-process context)) 'run)
+             (process-send-string (epg-context-process context)
+                                  (epg-data-string signature)))
+         (if (eq (process-status (epg-context-process context)) 'run)
+             (process-send-eof (epg-context-process context))))))))
 
 (defadvice epg-start-sign
   (around epg-pgp50i activate)