more crypt simplifications
[apps/madmutt.git] / lib-crypt / crypt-gpgme.c
index 7b6787e..5bd3173 100644 (file)
@@ -6,52 +6,29 @@
  * Copyright (C) 2001  Thomas Roessler <roessler@guug.de>
  *                     Oliver Ehli <elmy@acm.org>
  * Copyright (C) 2002, 2003, 2004 g10 Code GmbH
- *
- * This file is part of mutt-ng, see http://www.muttng.org/.
- * It's licensed under the GNU General Public License,
- * please see the file GPL in the top level source directory.
+ */
+/*
+ * Copyright © 2006 Pierre Habouzit
  */
 
 #include <lib-lib/lib-lib.h>
 
-#ifdef HAVE_LOCALE_H
-#  include <locale.h>
-#endif
-#ifdef HAVE_LANGINFO_D_T_FMT
-#  include <langinfo.h>
-#endif
-#ifdef HAVE_SYS_RESOURCE_H
-#  include <sys/resource.h>
-#endif
-
 #include <gpgme.h>
 
 #include <lib-mime/mime.h>
-
 #include <lib-ui/curses.h>
 #include <lib-ui/enter.h>
 #include <lib-ui/menu.h>
 
+#include "crypt.h"
+
 #include "lib.h"
 #include "alias.h"
-#include <lib-crypt/crypt.h>
 #include "handler.h"
 #include "copy.h"
 #include "pager.h"
 #include "recvattach.h"
 #include "sort.h"
-#include "crypt-gpgme.h"
-
-/*
- * Helper macros.
- */
-#define digitp(p)   (*(p) >= '0' && *(p) <= '9')
-#define hexdigitp(a) (digitp (a)                     \
-                      || (*(a) >= 'A' && *(a) <= 'F')  \
-                      || (*(a) >= 'a' && *(a) <= 'f'))
-#define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
-                     *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
-#define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
 
 /* Values used for comparing addresses. */
 #define CRYPT_KV_VALID    1
@@ -94,28 +71,6 @@ typedef struct crypt_entry {
 static struct crypt_cache *id_defaults = NULL;
 static gpgme_key_t signature_key = NULL;
 
-/* Initialization.  */
-void crypt_init(void)
-{
-    /* Make sure that gpg-agent is running.  */
-    if (!getenv ("GPG_AGENT_INFO")) {
-        mutt_error ("\nUsing GPGME backend, although no gpg-agent is running");
-        if (mutt_any_key_to_continue (NULL) == -1)
-            mutt_exit (1);
-    }
-}
-
-/* Show a message that a backend will be invoked. */
-void crypt_invoke_message (int type)
-{
-    if (type & APPLICATION_PGP) {
-        mutt_message _("Invoking PGP...");
-    }
-    else if (type & APPLICATION_SMIME) {
-        mutt_message _("Invoking S/MIME...");
-    }
-}
-
 /*
  * General helper functions.
  */
@@ -510,6 +465,33 @@ static char *data_object_to_tempfile (gpgme_data_t data, FILE ** ret_fp)
 }
 
 
+/* FIXME: stolen from gpgme to avoid "ambiguous identity" errors */
+static gpgme_error_t
+gpgme_get_key2 (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
+              int secret)
+{
+  gpgme_ctx_t listctx;
+  gpgme_error_t err;
+
+  if (!ctx || !r_key || !fpr)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if (strlen (fpr) < 8)        /* We have at least a key ID.  */
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  /* FIXME: We use our own context because we have to avoid the user's
+     I/O callback handlers.  */
+  err = gpgme_new (&listctx);
+  if (err)
+    return err;
+  gpgme_set_protocol (listctx, gpgme_get_protocol (ctx));
+  err = gpgme_op_keylist_start (listctx, fpr, secret);
+  if (!err)
+    err = gpgme_op_keylist_next (listctx, r_key);
+  gpgme_release (listctx);
+  return err;
+}
+
 /* Create a GpgmeRecipientSet from the keys in the string KEYLIST.
    The keys must be space delimited. */
 static gpgme_key_t *create_recipient_set (const char *keylist,
@@ -542,13 +524,13 @@ static gpgme_key_t *create_recipient_set (const char *keylist,
              key. */
           buf[i - 1] = 0;
 
-          err = gpgme_get_key (context, buf, &key, 0);
+          err = gpgme_get_key2 (context, buf, &key, 0);
           if (!err)
             key->uids->validity = GPGME_VALIDITY_FULL;
           buf[i - 1] = '!';
         }
         else
-          err = gpgme_get_key (context, buf, &key, 0);
+          err = gpgme_get_key2 (context, buf, &key, 0);
 
         if (!err) {
           p_realloc(&rset, rset_n + 1);
@@ -875,7 +857,7 @@ BODY *crypt_pgp_encrypt_message (BODY * a, char *keylist, int sign)
 
 /* Encrypt the mail body A to all keys given as space separated
    fingerprints in KEYLIST and return the S/MIME encrypted body.  */
-BODY *smime_gpgme_build_smime_entity (BODY * a, char *keylist)
+BODY *crypt_smime_build_smime_entity (BODY * a, char *keylist)
 {
   char *outfile = NULL;
   BODY *t;
@@ -1099,7 +1081,7 @@ static void show_one_sig_validity (gpgme_ctx_t ctx, int idx, STATE * s)
 /* Show information about one signature.  This fucntion is called with
    the context CTX of a sucessful verification operation and the
    enumerator IDX which should start at 0 and incremete for each
-   call/signature. 
+   call/signature.
 
    Return values are: 0 for normal procession, 1 for a bad signature,
    2 for a signature with a warning or -1 for no more signature.  */
@@ -1138,7 +1120,7 @@ static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE * s)
     if (gpg_err_code (sig->status) != GPG_ERR_NO_ERROR)
       anybad = 1;
 
-    err = gpgme_get_key (ctx, fpr, &key, 0);    /* secret key?  */
+    err = gpgme_get_key2 (ctx, fpr, &key, 0);    /* secret key?  */
     if (!err) {
       uid = (key->uids && key->uids->uid) ? key->uids->uid : "[?]";
       if (!signature_key)
@@ -1215,8 +1197,7 @@ static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE * s)
 
 /* Do the actual verification step. With IS_SMIME set to true we
    assume S/MIME (surprise!) */
-static int verify_one (BODY * sigbdy, STATE * s,
-                       const char *tempfile, int is_smime)
+int crypt_verify_one(BODY *sigbdy, STATE *s, FILE *fp, int is_smime)
 {
   int badsig = -1;
   int anywarn = 0;
@@ -1233,7 +1214,7 @@ static int verify_one (BODY * sigbdy, STATE * s,
   if (is_smime)
     gpgme_data_set_encoding (signature, GPGME_DATA_ENCODING_BASE64);
 
-  err = gpgme_data_new_from_file (&message, tempfile, 1);
+  err = gpgme_data_new_from_stream(&message, fp);
   if (err) {
     gpgme_data_release (signature);
     mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
@@ -1313,16 +1294,6 @@ static int verify_one (BODY * sigbdy, STATE * s,
   return badsig ? 1 : anywarn ? 2 : 0;
 }
 
-int crypt_pgp_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
-{
-  return verify_one (sigbdy, s, tempfile, 0);
-}
-
-int crypt_smime_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
-{
-  return verify_one (sigbdy, s, tempfile, 1);
-}
-
 /*
  * Implementation of `decrypt_part'.
  */
@@ -1620,7 +1591,7 @@ int crypt_smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b,
 }
 
 
-/* 
+/*
  * Implementation of `pgp_check_traditional'.
  */
 
@@ -1695,14 +1666,14 @@ int crypt_pgp_check_traditional (FILE * fp, BODY * b, int tagged_only)
 
 /* Implementation of `application_handler'. */
 
-/* 
+/*
   Copy a clearsigned message, and strip the signature and PGP's
   dash-escaping.
-  
+
   XXX - charset handling: We assume that it is safe to do
   character set decoding first, dash decoding second here, while
   we do it the other way around in the main handler.
-  
+
   (Note that we aren't worse than Outlook & Cie in this, and also
   note that we can successfully handle anything produced by any
   existing versions of mutt.)  */
@@ -1918,7 +1889,7 @@ int crypt_pgp_application_pgp_handler (BODY * m, STATE * s)
 
       /*
        * Now, copy cleartext to the screen.  NOTE - we expect that PGP
-       * outputs utf-8 cleartext.  This may not always be true, but it 
+       * outputs utf-8 cleartext.  This may not always be true, but it
        * seems to be a reasonable guess.
        */
 
@@ -1980,12 +1951,10 @@ int crypt_pgp_application_pgp_handler (BODY * m, STATE * s)
   return (err);
 }
 
-/* 
- * Implementation of `encrypted_handler'.
- */
+/* Implementation of `encrypted_handler'. */
 
 /* MIME handler for pgp/mime encrypted messages. */
-int pgp_gpgme_encrypted_handler (BODY * a, STATE * s)
+int crypt_pgp_encrypted_handler (BODY * a, STATE * s)
 {
   char tempfile[_POSIX_PATH_MAX];
   FILE *fpout;
@@ -2034,7 +2003,7 @@ int pgp_gpgme_encrypted_handler (BODY * a, STATE * s)
       s->fpin = savefp;
     }
 
-    /* 
+    /*
      * if a multipart/signed is the _only_ sub-part of a
      * multipart/encrypted, cache signature verification
      * status.
@@ -2093,7 +2062,7 @@ int crypt_smime_application_smime_handler (BODY * a, STATE * s)
       s->fpin = savefp;
     }
 
-    /* 
+    /*
      * if a multipart/signed is the _only_ sub-part of a
      * multipart/encrypted, cache signature verification
      * status.
@@ -2125,7 +2094,7 @@ int crypt_smime_application_smime_handler (BODY * a, STATE * s)
 
 /*
  * Format an entry on the CRYPT key selection menu.
- * 
+ *
  * %n  number
  * %k  key id          %K      key id of the principal key
  * %u  user id
@@ -2525,7 +2494,7 @@ static const unsigned char *parse_dn_part (struct dn_array_s *array,
 
   if (*string == '#') {         /* hexstring */
     string++;
-    for (s = string; hexdigitp (s); s++)
+    for (s = string; hexval(*s) >= 0; s++)
       s++;
     n = s - string;
     if (!n || (n & 1))
@@ -2534,7 +2503,7 @@ static const unsigned char *parse_dn_part (struct dn_array_s *array,
     p = p_new(unsigned char, n + 1);
     array->value = (char *) p;
     for (s1 = string; n; s1 += 2, n--)
-      *p++ = xtoi_2 (s1);
+      *p++ = (hexval(*s1) << 8) | hexval(*s1);
     *p = 0;
   }
   else {                        /* regular v3 quoted string */
@@ -2545,7 +2514,7 @@ static const unsigned char *parse_dn_part (struct dn_array_s *array,
             || *s == '<' || *s == '>' || *s == '#' || *s == ';'
             || *s == '\\' || *s == '\"' || *s == ' ')
           n++;
-        else if (hexdigitp (s) && hexdigitp (s + 1)) {
+        else if (hexval(*s) >= 0 && hexval(*s + 1) >= 0) {
           s++;
           n++;
         }
@@ -2566,8 +2535,8 @@ static const unsigned char *parse_dn_part (struct dn_array_s *array,
     for (s = string; n; s++, n--) {
       if (*s == '\\') {
         s++;
-        if (hexdigitp (s)) {
-          *p++ = xtoi_2 (s);
+        if (hexval(*s) >= 0) {
+          *p++ = (hexval(*s) << 8) | hexval(*s + 1);
           s++;
         }
         else
@@ -3107,26 +3076,6 @@ static crypt_key_t *get_candidates (string_list_t * hints, unsigned int app,
       if (key_check_cap (key, KEY_CAP_CAN_SIGN))
         flags |= KEYFLAG_CANSIGN;
 
-#if 0                           /* DISABLED code */
-      if (!flags) {
-        /* Bug in gpg.  Capabilities are not listed for secret
-           keys.  Try to deduce them from the algorithm. */
-
-        switch (key->subkeys[0].pubkey_algo) {
-        case GPGME_PK_RSA:
-          flags |= KEYFLAG_CANENCRYPT;
-          flags |= KEYFLAG_CANSIGN;
-          break;
-        case GPGME_PK_ELG_E:
-          flags |= KEYFLAG_CANENCRYPT;
-          break;
-        case GPGME_PK_DSA:
-          flags |= KEYFLAG_CANSIGN;
-          break;
-        }
-      }
-#endif /* DISABLED code */
-
       for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
         k = p_new(crypt_key_t, 1);
         k->kobj = key;
@@ -3487,17 +3436,17 @@ static crypt_key_t *crypt_getkeybyaddr (address_t * a, short abilities,
   if (matches) {
     if (the_valid_key && !multi && !weak
         && !(invalid && option (OPTPGPSHOWUNUSABLE))) {
-      /* 
+      /*
        * There was precisely one strong match on a valid ID, there
        * were no valid keys with weak matches, and we aren't
        * interested in seeing invalid keys.
-       * 
+       *
        * Proceed without asking the user.
        */
       k = crypt_copy_key (the_valid_key);
     }
     else {
-      /* 
+      /*
        * Else: Ask the user.
        */
       k = crypt_select_key (matches, a, NULL, app, forced_valid);
@@ -3729,17 +3678,37 @@ static char *find_keys (address_t * to, address_t * cc, address_t * bcc,
   return (keylist);
 }
 
-char *crypt_pgp_findkeys (address_t * to, address_t * cc, address_t * bcc)
+int crypt_get_keys (HEADER * msg, char **keylist)
 {
-  return find_keys (to, cc, bcc, APPLICATION_PGP);
-}
+  /* Do a quick check to make sure that we can find all of the encryption
+   * keys if the user has requested this service.
+   */
 
-char *crypt_smime_findkeys (address_t * to, address_t * cc, address_t * bcc)
-{
-  return find_keys (to, cc, bcc, APPLICATION_SMIME);
+  *keylist = NULL;
+
+  if (msg->security & ENCRYPT) {
+    if (msg->security & APPLICATION_PGP) {
+      set_option(OPTPGPCHECKTRUST);
+      *keylist = find_keys(msg->env->to, msg->env->cc, msg->env->bcc,
+                           APPLICATION_PGP);
+      unset_option(OPTPGPCHECKTRUST);
+      if (!*keylist)
+          return -1;
+    }
+
+    if (msg->security & APPLICATION_SMIME) {
+      *keylist = find_keys(msg->env->to, msg->env->cc, msg->env->bcc,
+                           APPLICATION_SMIME);
+      if (!*keylist)
+          return -1;
+    }
+  }
+
+  return (0);
 }
 
-static int gpgme_send_menu (HEADER * msg, int *redraw, int is_smime)
+
+int crypt_send_menu (HEADER * msg, int *redraw, int is_smime)
 {
   crypt_key_t *p;
   char input_signas[STRING];
@@ -3754,12 +3723,12 @@ static int gpgme_send_menu (HEADER * msg, int *redraw, int is_smime)
     choice =
       mutt_multi_choice (_
                          ("S/MIME (e)ncrypt, (s)ign, sign (a)s, (b)oth, (p)gp or (c)lear?"),
-                         _("esabpfc"));
+                         _("esabpc"));
   else
     choice =
       mutt_multi_choice (_
                          ("PGP (e)ncrypt, (s)ign, sign (a)s, (b)oth, s/(m)ime or (c)lear?"),
-                         _("esabmfc"));
+                         _("esabmc"));
 
   switch (choice) {
   case 1:                      /* (e)ncrypt */
@@ -3773,7 +3742,6 @@ static int gpgme_send_menu (HEADER * msg, int *redraw, int is_smime)
     break;
 
   case 3:                      /* sign (a)s */
-/*      unset_option(OPTCRYPTCHECKTRUST); */
     if ((p = crypt_ask_for_key (_("Sign as: "), NULL, KEYFLAG_CANSIGN,
                                 is_smime ? APPLICATION_SMIME :
                                 APPLICATION_PGP, NULL))) {
@@ -3797,31 +3765,18 @@ static int gpgme_send_menu (HEADER * msg, int *redraw, int is_smime)
     break;
 
   case 6:                      /* (c)lear */
-    msg->security = 0;
-    break;
+    return msg->security = 0;
   }
 
-  if (choice == 6 || choice == 7);
-  else if (is_smime) {
+  if (is_smime) {
     msg->security &= ~APPLICATION_PGP;
     msg->security |= APPLICATION_SMIME;
-  }
-  else {
+  } else {
     msg->security &= ~APPLICATION_SMIME;
     msg->security |= APPLICATION_PGP;
   }
 
-  return (msg->security);
-}
-
-int crypt_pgp_send_menu(HEADER * msg, int *redraw)
-{
-  return gpgme_send_menu(msg, redraw, 0);
-}
-
-int crypt_smime_send_menu(HEADER * msg, int *redraw)
-{
-  return gpgme_send_menu (msg, redraw, 1);
+  return msg->security;
 }
 
 int crypt_smime_verify_sender (HEADER * h)
@@ -3869,13 +3824,13 @@ int crypt_smime_verify_sender (HEADER * h)
   return ret;
 }
 
-static void invoke_import(const char *fname, int smime)
+void crypt_invoke_import(FILE *stream, int smime)
 {
     gpgme_ctx_t ctx = create_gpgme_context(smime);
     gpgme_data_t data;
     gpgme_error_t err;
 
-    err = gpgme_data_new_from_file(&data, fname, 1);
+    err = gpgme_data_new_from_stream(&data, stream);
     if (err) {
         mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
         gpgme_release(ctx);
@@ -3895,38 +3850,24 @@ static void invoke_import(const char *fname, int smime)
     return;
 }
 
-void crypt_pgp_invoke_import(const char *fname)
+static void pgp_extract_keys_from_attachment(FILE * fp, BODY * top)
 {
-    invoke_import(fname, 0);
-}
-
-void crypt_smime_invoke_import(const char *fname)
-{
-    invoke_import(fname, 1);
-}
+    STATE s;
+    FILE *tmpfp = tmpfile();
 
-static void pgp_extract_keys_from_attachment (FILE * fp, BODY * top)
-{
-  STATE s;
-  FILE *tempfp;
-  char tempfname[_POSIX_PATH_MAX];
-
-  tempfp = m_tempfile(tempfname, sizeof(tempfname), NONULL(MCore.tmpdir), NULL);
-  if (tempfp == NULL) {
-    mutt_perror (_("Can't create temporary file"));
-    return;
-  }
-
-  p_clear(&s, 1);
-
-  s.fpin = fp;
-  s.fpout = tempfp;
+    if (tmpfp == NULL) {
+        mutt_perror (_("Can't create temporary file"));
+        return;
+    }
 
-  mutt_body_handler (top, &s);
+    p_clear(&s, 1);
+    s.fpin  = fp;
+    s.fpout = tmpfp;
+    mutt_body_handler(top, &s);
 
-  m_fclose(&tempfp);
-  crypt_pgp_invoke_import(tempfname);
-  mutt_unlink (tempfname);
+    rewind(tmpfp);
+    crypt_invoke_import(tmpfp, 0);
+    m_fclose(&tmpfp);
 }
 
 void crypt_pgp_extract_keys_from_attachment_list(FILE * fp, int tag, BODY * top)
@@ -3945,3 +3886,24 @@ void crypt_pgp_extract_keys_from_attachment_list(FILE * fp, int tag, BODY * top)
   unset_option (OPTDONTHANDLEPGPKEYS);
 }
 
+
+/* TODO */
+
+/* fixme: needs documentation. */
+void crypt_pgp_invoke_getkeys (address_t * addr)
+{
+}
+
+/* Generate a PGP public key attachment. */
+BODY *crypt_pgp_make_key_attachment (char *tempf)
+{
+    return NULL;
+}
+
+/* S/MIME */
+
+/* fixme: Needs documentation. */
+void crypt_smime_getkeys (ENVELOPE * env)
+{
+}
+