* epa.el: Added header.
[elisp/epg.git] / epg.el
diff --git a/epg.el b/epg.el
index 110bbeb..605f0d1 100644 (file)
--- a/epg.el
+++ b/epg.el
@@ -1,3 +1,30 @@
+;;; epg.el --- EasyPG, yet another GnuPG interface.
+;; Copyright (C) 1999, 2000, 2002, 2003, 2004,
+;;   2005, 2006 Free Software Foundation, Inc.
+;; Copyright (C) 2006 Daiki Ueno
+
+;; Author: Daiki Ueno <ueno@unixuser.org>
+;; Keywords: PGP, GnuPG
+
+;; This file is part of EasyPG.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
+
+;;; Code:
+
 (defgroup epg ()
   "EasyPG, yet another GnuPG interface.")
 
 (defvar epg-debug nil)
 
 (defvar epg-colons-pub-spec
-  '((trust "[^:]")
+  '((validity "[^:]")
     (length "[0-9]+" 0 string-to-number)
     (algorithm "[0-9]+" 0 string-to-number)
     (key-id "[^:]+")
     (creation-date "[0-9]+")
     (expiration-date "[0-9]+")
     nil
-    (ownertrust "[^:]")
+    (owner-trust "[^:]")
     nil
     nil
-    (capability "[escaESCA]*"))
+    (capability "[escaESCA]+"))
   "The schema of keylisting output whose type is \"pub\".
 This is used by `epg-list-keys'.")
 
 (defvar epg-colons-sec-spec
-  '((trust "[^:]")
+  '((validity "[^:]")
     (length "[0-9]+" 0 string-to-number)
     (algorithm "[0-9]+" 0 string-to-number)
     (key-id "[^:]+")
     (creation-date "[0-9]+")
     (expiration-date "[0-9]+")
     nil
-    (ownertrust "[^:]"))
+    (owner-trust "[^:]")
+    nil
+    nil
+    (capability "[escaESCA]+"))
 "The schema of keylisting output whose type is \"sec\".
 This is used by `epg-list-keys'.")
 
 (defvar epg-colons-uid-spec
-  '((trust "[^:]")
+  '((validity "[^:]")
     nil
     nil
     nil
@@ -57,12 +87,77 @@ This is used by `epg-list-keys'.")
     (user-id "[^:]+"))
   "The schema of keylisting output whose type is \"uid\".
 This is used by `epg-list-keys'.")
-    
+
+(defvar epg-colons-fpr-spec
+  '(nil
+    nil
+    nil
+    nil
+    nil
+    nil
+    nil
+    nil
+    (fingerprint "[^:]+"))
+  "The schema of keylisting output whose type is \"fpr\".
+This is used by `epg-list-keys'.")
+
+(defconst epg-cipher-algorithm-alist
+  '((0 . "NONE")
+    (1 . "IDEA")
+    (2 . "3DES")
+    (3 . "CAST5")
+    (4 . "BLOWFISH")
+    (7 . "AES")
+    (8 . "AES192")
+    (9 . "AES256")
+    (10 . "TWOFISH")
+    (110 . "DUMMY")))
+
+(defconst epg-pubkey-algorithm-alist
+  '((1 . "RSA")
+    (2 . "RSA_E")
+    (3 . "RSA_S")
+    (16 . "ELGAMAL_E")
+    (17 . "DSA")
+    (20 . "ELGAMAL")))
+
+(defconst epg-digest-algorithm-alist
+  '((1 . "MD5")
+    (2 . "SHA1")
+    (3 . "RMD160")
+    (8 . "SHA256")
+    (9 . "SHA384")
+    (10 . "SHA512")))
+
+(defconst epg-compress-algorithm-alist
+  '((0 . "NONE")
+    (1 . "ZIP")
+    (2 . "ZLIB")
+    (3 . "BZIP2")))
+
+(defvar epg-prompt-alist nil)
+
+(defun epg-make-data-from-file (file)
+  "Make a data object from FILE."
+  (vector file nil))
+
+(defun epg-make-data-from-string (string)
+  "Make a data object from STRING."
+  (vector nil string))
+
+(defun epg-data-file (data)
+  "Return the file of DATA."
+  (aref data 0))
+
+(defun epg-data-string (data)
+  "Return the string of DATA."
+  (aref data 1))
+
 (defun epg-make-context (&optional protocol armor textmode include-certs)
   "Return a context object."
   (vector protocol armor textmode include-certs
-         (cons #'epg-passphrase-callback-function nil)
-         (cons #'epg-progress-callback-function nil)
+         #'epg-passphrase-callback-function
+         #'epg-progress-callback-function
          nil nil nil nil))
 
 (defun epg-context-protocol (context)
@@ -82,11 +177,11 @@ This is used by `epg-list-keys'.")
 message."
   (aref context 3))
 
-(defun epg-context-passphrase-callback-info (context)
+(defun epg-context-passphrase-callback (context)
   "Return the function used to query passphrase."
   (aref context 4))
 
-(defun epg-context-progress-callback-info (context)
+(defun epg-context-progress-callback (context)
   "Return the function which handles progress update."
   (aref context 5))
 
@@ -124,14 +219,14 @@ This function is for internal use only."
  "Set how many certificates should be included in an S/MIME signed message."
   (aset context 3 include-certs))
 
-(defun epg-context-set-passphrase-callback-info (context
-                                                passphrase-callback-info)
+(defun epg-context-set-passphrase-callback (context
+                                                passphrase-callback)
   "Set the function used to query passphrase."
-  (aset context 4 passphrase-callback-info))
+  (aset context 4 passphrase-callback))
 
-(defun epg-context-set-progress-callback-info (context progress-callback-info)
+(defun epg-context-set-progress-callback (context progress-callback)
   "Set the function which handles progress update."
-  (aset context 5 progress-callback-info))
+  (aset context 5 progress-callback))
 
 (defun epg-context-set-signers (context signers)
  "Set the list of key-id for singning."
@@ -153,7 +248,7 @@ This function is for internal use only."
 
 (defun epg-make-signature (status key-id user-id)
   "Return a signature object."
-  (vector status key-id user-id nil))
+  (vector status key-id user-id nil nil))
 
 (defun epg-signature-status (signature)
   "Return the status code of SIGNATURE."
@@ -171,6 +266,10 @@ This function is for internal use only."
   "Return the validity of SIGNATURE."
   (aref signature 3))
 
+(defun epg-signature-fingerprint (signature)
+  "Return the fingerprint of SIGNATURE."
+  (aref signature 4))
+
 (defun epg-signature-set-status (signature status)
  "Set the status code of SIGNATURE."
   (aset signature 0 status))
@@ -187,6 +286,10 @@ This function is for internal use only."
  "Set the validity of SIGNATURE."
   (aset signature 3 validity))
 
+(defun epg-signature-set-fingerprint (signature fingerprint)
+ "Set the fingerprint of SIGNATURE."
+  (aset signature 4 fingerprint))
+
 (defun epg-context-result-for (context name)
   (cdr (assq name (epg-context-result context))))
 
@@ -201,8 +304,7 @@ This function is for internal use only."
   "Start `epg-gpg-program' in a subprocess with given ARGS."
   (let* ((args (append (list "--no-tty"
                             "--status-fd" "1"
-                            "--command-fd" "0"
-                            "--yes") ; overwrite
+                            "--command-fd" "0")
                       (if (epg-context-armor context) '("--armor"))
                       (if (epg-context-textmode context) '("--textmode"))
                       (if (epg-context-output-file context)
@@ -288,10 +390,12 @@ This function is for internal use only."
   (if (and (epg-context-process context)
           (buffer-live-p (process-buffer (epg-context-process context))))
       (kill-buffer (process-buffer (epg-context-process context))))
-  (epg-context-set-process context nil)
-  (if (file-exists-p (epg-context-output-file context))
-      (delete-file (epg-context-output-file context)))
-  (aset context 9 nil))
+  (epg-context-set-process context nil))
+
+(defun epg-delete-output-file (context)
+  (if (and (epg-context-output-file context)
+          (file-exists-p (epg-context-output-file context)))
+      (delete-file (epg-context-output-file context))))
 
 (defun epg-status-USERID_HINT (process string)
   (if (string-match "\\`\\([^ ]+\\) \\(.*\\)" string)
@@ -315,9 +419,12 @@ This function is for internal use only."
 
 (defun epg-status-GET_HIDDEN (process string)
   (let ((passphrase
-        (funcall (car (epg-context-passphrase-callback-info epg-context))
+        (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
-                 (cdr (epg-context-passphrase-callback-info epg-context))))
+                 (if (consp (epg-context-passphrase-callback epg-context))
+                     (cdr (epg-context-passphrase-callback epg-context)))))
        string)
     (if passphrase
        (unwind-protect
@@ -329,6 +436,17 @@ This function is for internal use only."
          (if string
              (fillarray string 0))))))
 
+(defun epg-status-GET_BOOL (process string)
+  (let ((entry (assoc string epg-prompt-alist)))
+    (if (y-or-n-p (if entry (cdr entry) (concat string "? ")))
+       (process-send-string process "y\n")
+      (process-send-string process "n\n"))))
+
+(defun epg-status-GET_LINE (process string)
+  (let* ((entry (assoc string epg-prompt-alist))
+        (string (read-string (if entry (cdr entry) (concat string ": ")))))
+    (process-send-string process (concat string "\n"))))
+
 (defun epg-status-GOODSIG (process string)
   (if (string-match "\\`\\([^ ]+\\) \\(.*\\)" string)
       (epg-context-set-result-for
@@ -379,11 +497,18 @@ This function is for internal use only."
                                 (match-string 2 string))
             (epg-context-result-for epg-context 'verify)))))
 
+(defun epg-status-VALIDSIG (process string)
+  (let ((signature (car (epg-context-result-for epg-context 'verify))))
+    (if (and signature
+            (eq (epg-signature-status signature) 'good)
+            (string-match "\\`\\([^ ]+\\) " string))
+       (epg-signature-set-fingerprint signature (match-string 1 string)))))
+
 (defun epg-status-TRUST_UNDEFINED (process string)
   (let ((signature (car (epg-context-result-for epg-context 'verify))))
     (if (and signature
             (eq (epg-signature-status signature) 'good))
-       (epg-signature-set-validity signature 'unknown))))
+       (epg-signature-set-validity signature 'undefined))))
 
 (defun epg-status-TRUST_NEVER (process string)
   (let ((signature (car (epg-context-result-for epg-context 'verify))))
@@ -401,23 +526,26 @@ This function is for internal use only."
   (let ((signature (car (epg-context-result-for epg-context 'verify))))
     (if (and signature
             (eq (epg-signature-status signature) 'good))
-       (epg-signature-set-validity signature 'full))))
+       (epg-signature-set-validity signature 'fully))))
 
 (defun epg-status-TRUST_ULTIMATE (process string)
   (let ((signature (car (epg-context-result-for epg-context 'verify))))
     (if (and signature
             (eq (epg-signature-status signature) 'good))
-       (epg-signature-set-validity signature 'full))))
+       (epg-signature-set-validity signature 'ultimate))))
 
 (defun epg-status-PROGRESS (process string)
   (if (string-match "\\`\\([^ ]+\\) \\([^ ]\\) \\([0-9]+\\) \\([0-9]+\\)"
                    string)
-      (funcall (car (epg-context-progress-callback-info epg-context))
+      (funcall (if (consp (epg-context-progress-callback epg-context))
+                  (car (epg-context-progress-callback epg-context))
+                (epg-context-progress-callback epg-context))
               (match-string 1 string)
               (match-string 2 string)
               (string-to-number (match-string 3 string))
               (string-to-number (match-string 4 string))
-              (cdr (epg-context-progress-callback-info epg-context)))))
+              (if (consp (epg-context-progress-callback epg-context))
+                  (cdr (epg-context-progress-callback epg-context))))))
 
 (defun epg-status-DECRYPTION_FAILED (process string)
   (epg-context-set-result-for
@@ -469,17 +597,37 @@ This function is for internal use only."
 (defun epg-progress-callback-function (what char current total handback)
   (message "%s: %d%%/%d%%" what current total))
 
+(defun epg-configuration ()
+  "Return a list of internal configuration parameters of `epg-gpg-program'."
+  (let (config type)
+    (with-temp-buffer
+      (apply #'call-process epg-gpg-program nil (list t nil) nil
+            '("--with-colons" "--list-config"))
+      (goto-char (point-min))
+      (while (re-search-forward "^cfg:\\([^:]+\\):\\(.*\\)" nil t)
+       (setq type (intern (match-string 1))
+             config (cons (cons type
+                                (if (memq type
+                                          '(pubkey cipher digest compress))
+                                    (mapcar #'string-to-number
+                                            (delete "" (split-string
+                                                        (match-string 2)
+                                                        ";")))
+                                  (match-string 2)))
+                          config))))
+    config))
+
 (defun epg-list-keys (name &optional secret)
   "List keys associated with STRING."
-  (let ((args (list "--with-colons" "--no-greeting" "--batch"
-                   "--fixed-list-mode"
-                   (if secret "--list-secret-keys" "--list-keys")
-                   name))
+  (let ((args (append (list "--with-colons" "--no-greeting" "--batch"
+                           "--fixed-list-mode" "--with-fingerprint"
+                           (if secret "--list-secret-keys" "--list-keys"))
+                     (if name (list name))))
        keys type symbol pointer)
     (with-temp-buffer
       (apply #'call-process epg-gpg-program nil (list t nil) nil args)
       (goto-char (point-min))
-      (while (looking-at "\\([a-z][a-z][a-z]\\):\\(.*\\)")
+      (while (re-search-forward "^\\([a-z][a-z][a-z]\\):\\(.*\\)" nil t)
        (setq type (match-string 1)
              symbol (intern-soft (format "epg-colons-%s-spec" type)))
        (if (member type '("pub" "sec"))
@@ -572,91 +720,151 @@ You can then use `write-region' to write new data into the file."
             (delete-directory tempdir))))))
 
 ;;;###autoload
-(defun epg-start-decrypt (context input-file)
-  "Initiate a decrypt operation on INPUT-FILE.
+(defun epg-start-decrypt (context cipher)
+  "Initiate a decrypt operation on CIPHER.
+CIPHER is a data object.
 
 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-decrypt-string' instead."
-  (epg-context-set-output-file context (epg-make-temp-file "epg-output"))
-  (epg-start context
-            (list "--decrypt" input-file))
+`epg-decrypt-file' or `epg-decrypt-string' instead."
+  (unless (epg-data-file cipher)
+    (error "Not a file"))
+  (epg-context-set-result context nil)
+  (epg-start context (list "--decrypt" (epg-data-file cipher)))
   (epg-wait-for-status context '("BEGIN_DECRYPTION")))
 
 ;;;###autoload
-(defun epg-decrypt-file (context input-file)
-  "Decrypt INPUT-FILE and return the plain text."
+(defun epg-decrypt-file (context cipher plain)
+  "Decrypt a file CIPHER and store the result to a file PLAIN.
+If PLAIN is nil, it returns the result as a string."
   (unwind-protect
       (progn
-       (epg-start-decrypt context input-file)
+       (if plain
+           (epg-context-set-output-file context plain)
+         (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)
        (if (epg-context-result-for context 'error)
            (error "Decryption failed"))
-       (epg-read-output context))
+       (unless plain
+         (epg-read-output context)))
+    (unless plain
+      (epg-delete-output-file context))
     (epg-reset context)))
 
 ;;;###autoload
-(defun epg-decrypt-string (context string)
-  "Decrypt STRING and return the plain text."
+(defun epg-decrypt-string (context cipher)
+  "Decrypt a string CIPHER and return the plain text."
   (let ((input-file (epg-make-temp-file "epg-input"))
        (coding-system-for-write 'binary))
     (unwind-protect
        (progn
-         (write-region string nil input-file)
-         (epg-decrypt-file context input-file))
+         (write-region cipher nil input-file)
+         (epg-context-set-output-file context
+                                      (epg-make-temp-file "epg-output"))
+         (epg-start-decrypt context (epg-make-data-from-file input-file))
+         (epg-wait-for-completion context)
+         (if (epg-context-result-for context 'error)
+             (error "Decryption failed"))
+         (epg-read-output context))
+      (epg-delete-output-file context)
       (if (file-exists-p input-file)
-         (delete-file input-file)))))
+         (delete-file input-file))
+      (epg-reset context))))
 
 ;;;###autoload
-(defun epg-start-verify (context signature &optional string)
+(defun epg-start-verify (context signature &optional signed-text)
   "Initiate a verify operation on SIGNATURE.
+SIGNATURE and SIGNED-TEXT are a data object if they are specified.
 
-For a detached signature, both SIGNATURE and STRING should be string.
-For a normal or a clear text signature, STRING should be nil.
+For a detached signature, both SIGNATURE and SIGNED-TEXT should be set.
+For a normal or a clear text signature, SIGNED-TEXT should be nil.
 
 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-verify-string' instead."
-  (epg-context-set-output-file context (epg-make-temp-file "epg-output"))
-  (if string
+`epg-verify-file' or `epg-verify-string' instead."
+  (epg-context-set-result context nil)
+  (if signed-text
       ;; Detached signature.
-      (progn
-       (epg-start context
-                  (append (list "--verify")
-                          (list signature "-")))
+      (if (epg-data-file signed-text)
+         (epg-start context (list "--verify" (epg-data-file signature)
+                                  (epg-data-file signed-text)))
+       (epg-start context (list "--verify" (epg-data-file signature) "-"))
        (if (eq (process-status (epg-context-process context)) 'run)
-           (process-send-string (epg-context-process context) string)))
+           (process-send-string (epg-context-process context)
+                                (epg-data-string signed-text))))
     ;; Normal (or cleartext) signature.
-    (epg-start context (list "--verify"))
-    (if (eq (process-status (epg-context-process context)) 'run)
-       (process-send-string (epg-context-process context) signature))))
+    (if (epg-data-file signature)
+       (epg-start context (list "--verify" (epg-data-file signature)))
+      (epg-start context (list "--verify"))
+      (if (eq (process-status (epg-context-process context)) 'run)
+         (process-send-string (epg-context-process context)
+                              (epg-data-string signature))))))
 
 ;;;###autoload
-(defun epg-verify-string (context signature &optional string)
-  "Verify SIGNATURE.
+(defun epg-verify-file (context signature &optional signed-text plain)
+  "Verify a file SIGNATURE.
+SIGNED-TEXT and PLAIN are also a file if they are specified.
 
-For a detached signature, both SIGNATURE and STRING should be string.
-For a normal or a clear text signature, STRING should be nil."
-  (let ((input-file (epg-make-temp-file "epg-input"))
-       (coding-system-for-write 'binary))
+For a detached signature, both SIGNATURE and SIGNED-TEXT should be string.
+For a normal or a clear text signature, SIGNED-TEXT should be nil."
+  (unwind-protect
+      (progn
+       (if plain
+           (epg-context-set-output-file context plain)
+         (epg-context-set-output-file context
+                                      (epg-make-temp-file "epg-output")))
+       (if signed-text
+           (epg-start-verify context
+                             (epg-make-data-from-file signature)
+                             (epg-make-data-from-file signed-text))
+         (epg-start-verify context
+                           (epg-make-data-from-file signature)))
+       (epg-wait-for-completion context)
+       (unless plain
+         (epg-read-output context)))
+    (unless plain
+      (epg-delete-output-file context))
+    (epg-reset context)))
+
+;;;###autoload
+(defun epg-verify-string (context signature &optional signed-text)
+  "Verify a string SIGNATURE.
+SIGNED-TEXT is a string if it is specified.
+
+For a detached signature, both SIGNATURE and SIGNED-TEXT should be string.
+For a normal or a clear text signature, SIGNED-TEXT should be nil."
+  (let ((coding-system-for-write 'binary)
+       input-file)
     (unwind-protect
        (progn
-         (if string
-             (write-region signature nil input-file))
-         (epg-start-verify context input-file string)
+         (epg-context-set-output-file context
+                                      (epg-make-temp-file "epg-output"))
+         (if signed-text
+             (progn
+               (setq input-file (epg-make-temp-file "epg-signature"))
+               (write-region signature nil input-file)
+               (epg-start-verify context
+                                 (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-wait-for-completion context)
-         (epg-context-result-for context 'verify))
-      (epg-reset context)
-      (if (file-exists-p input-file)
-         (delete-file input-file)))))
+         (epg-read-output context))
+      (epg-delete-output-file context)
+      (if (and input-file
+              (file-exists-p input-file))
+         (delete-file input-file))
+      (epg-reset context))))
 
 ;;;###autoload
-(defun epg-start-sign (context string &optional mode)
-  "Initiate a sign operation on STRING.
+(defun epg-start-sign (context plain &optional mode)
+  "Initiate a sign operation on PLAIN.
+PLAIN is a data object.
 
 If optional 3rd argument MODE is 'clearsign, it makes a clear text signature.
 If MODE is t or 'detached, it makes a detached signature.
@@ -666,8 +874,8 @@ 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-sign-string' instead."
-  (epg-context-set-output-file context (epg-make-temp-file "epg-output"))
+`epg-sign-file' or `epg-sign-string' instead."
+  (epg-context-set-result context nil)
   (epg-start context
             (append (list (if (eq mode 'clearsign)
                               "--clearsign"
@@ -677,38 +885,69 @@ If you are unsure, use synchronous version of this function
                     (apply #'nconc
                            (mapcar (lambda (signer)
                                      (list "-u" signer))
-                                   (epg-context-signers context)))))
+                                   (epg-context-signers context)))
+                    (if (epg-data-file plain)
+                        (list (epg-data-file plain)))))
   (epg-wait-for-status context '("BEGIN_SIGNING"))
-  (if (eq (process-status (epg-context-process context)) 'run)
-      (process-send-string (epg-context-process context) string)))
+  (if (and (epg-data-string plain)
+          (eq (process-status (epg-context-process context)) 'run))
+      (process-send-string (epg-context-process context)
+                          (epg-data-string plain))))
+
+;;;###autoload
+(defun epg-sign-file (context plain signature &optional mode)
+  "Sign a file PLAIN and store the result to a file SIGNATURE.
+If SIGNATURE is nil, it returns the result as a string.
+If optional 3rd argument MODE is 'clearsign, it makes a clear text signature.
+If MODE is t or 'detached, it makes a detached signature.
+Otherwise, it makes a normal signature."
+  (unwind-protect
+      (progn
+       (if signature
+           (epg-context-set-output-file context 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)
+       (if (epg-context-result-for context 'error)
+           (error "Sign failed"))
+       (unless signature
+         (epg-read-output context)))
+    (unless signature
+      (epg-delete-output-file context))
+    (epg-reset context)))
 
 ;;;###autoload
-(defun epg-sign-string (context string &optional mode)
-  "Sign STRING and return the output as string.
+(defun epg-sign-string (context plain &optional mode)
+  "Sign a string PLAIN and return the output as string.
 If optional 3rd argument MODE is 'clearsign, it makes a clear text signature.
 If MODE is t or 'detached, it makes a detached signature.
 Otherwise, it makes a normal signature."
   (unwind-protect
       (progn
-       (epg-start-sign context string mode)
+       (epg-context-set-output-file context
+                                    (epg-make-temp-file "epg-output"))
+       (epg-start-sign context (epg-make-data-from-string plain) mode)
        (epg-wait-for-completion context)
        (if (epg-context-result-for context 'error)
            (error "Sign failed"))
        (epg-read-output context))
+    (epg-delete-output-file context)
     (epg-reset context)))
 
 ;;;###autoload
-(defun epg-start-encrypt (context string recipients
+(defun epg-start-encrypt (context plain recipients
                                  &optional sign always-trust)
-  "Initiate an encrypt operation on STRING.
+  "Initiate an encrypt operation on PLAIN.
+PLAIN is a data object.
 If RECIPIENTS is nil, it performs symmetric encryption.
 
 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-encrypt-string' instead."
-  (epg-context-set-output-file context (epg-make-temp-file "epg-output"))
+`epg-encrypt-file' or `epg-encrypt-string' instead."
+  (epg-context-set-result context nil)
   (epg-start context
             (append (if always-trust '("--always-trust"))
                     (if recipients '("--encrypt") '("--symmetric"))
@@ -721,26 +960,57 @@ If you are unsure, use synchronous version of this function
                     (apply #'nconc
                            (mapcar (lambda (recipient)
                                      (list "-r" recipient))
-                                   recipients))))
+                                   recipients))
+                    (if (epg-data-file plain)
+                        (list (epg-data-file plain)))))
   (if sign
       (epg-wait-for-status context '("BEGIN_SIGNING"))
     (if (null recipients)
        (epg-wait-for-status context '("BEGIN_ENCRYPTION"))))
-  (if (eq (process-status (epg-context-process context)) 'run)
-      (process-send-string (epg-context-process context) string)))
+  (if (and (epg-data-string plain)
+          (eq (process-status (epg-context-process context)) 'run))
+      (process-send-string (epg-context-process context)
+                          (epg-data-string plain))))
 
 ;;;###autoload
-(defun epg-encrypt-string (context string recipients
+(defun epg-encrypt-file (context plain recipients
+                                cipher &optional sign always-trust)
+  "Encrypt a file PLAIN and store the result to a file CIPHER.
+If CIPHER is nil, it returns the result as a string.
+If RECIPIENTS is nil, it performs symmetric encryption."
+  (unwind-protect
+      (progn
+       (if cipher
+           (epg-context-set-output-file context cipher)
+         (epg-context-set-output-file context
+                                      (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)
+       (if (epg-context-result-for context 'error)
+           (error "Encrypt failed"))
+       (unless cipher
+         (epg-read-output context)))
+    (unless cipher
+      (epg-delete-output-file context))
+    (epg-reset context)))
+
+;;;###autoload
+(defun epg-encrypt-string (context plain recipients
                                   &optional sign always-trust)
-  "Encrypt STRING.
+  "Encrypt a string PLAIN.
 If RECIPIENTS is nil, it performs symmetric encryption."
   (unwind-protect
       (progn
-       (epg-start-encrypt context string recipients sign always-trust)
+       (epg-context-set-output-file context
+                                    (epg-make-temp-file "epg-output"))
+       (epg-start-encrypt context (epg-make-data-from-string plain)
+                          recipients sign always-trust)
        (epg-wait-for-completion context)
        (if (epg-context-result-for context 'error)
            (error "Encrypt failed"))
        (epg-read-output context))
+    (epg-delete-output-file context)
     (epg-reset context)))
 
 ;;;###autoload
@@ -752,6 +1022,7 @@ If you use this function, you will need to wait for the completion of
 `epg-reset' to clear a temporaly output file.
 If you are unsure, use synchronous version of this function
 `epg-export-keys' 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)))
 
@@ -769,21 +1040,23 @@ If you are unsure, use synchronous version of this function
 
 ;;;###autoload
 (defun epg-start-import-keys (context keys)
-  "Initiate an import key operation.
+  "Initiate an import keys operation.
+KEYS is a data object.
 
 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-import-keys' instead."
+`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 (list "--import"))
-  (if (eq (process-status (epg-context-process context)) 'run)
-      (process-send-string (epg-context-process context) keys)))
-
-;;;###autoload
-(defun epg-import-keys (context keys)
-  "Add KEYS."
+  (epg-start context (append (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)
+                          (epg-data-string keys))))
+  
+(defun epg-import-keys-1 (context keys)
   (unwind-protect
       (progn
        (epg-start-import-keys context keys)
@@ -793,6 +1066,16 @@ If you are unsure, use synchronous version of this function
        (epg-read-output context))
     (epg-reset context)))
 
+;;;###autoload
+(defun epg-import-keys-from-file (context keys)
+  "Add keys from a file KEYS."
+  (epg-import-keys-1 context (epg-make-data-from-file keys)))
+
+;;;###autoload
+(defun epg-import-keys-from-string (context keys)
+  "Add keys from a string KEYS."
+  (epg-import-keys-1 context (epg-make-data-from-string keys)))
+
 (provide 'epg)
 
 ;;; epg.el ends here