gpg: Don't error out if a key occurs multiple times in the keyring.
[gnupg.git] / g10 / export.c
index 94a3256..f7ad1b2 100644 (file)
@@ -46,14 +46,28 @@ struct subkey_list_s
 typedef struct subkey_list_s *subkey_list_t;
 
 
-static int do_export (ctrl_t ctrl,
-                      strlist_t users, int secret, unsigned int options );
+/* An object to track statistics for export operations.  */
+struct export_stats_s
+{
+  ulong count;            /* Number of processed keys.        */
+  ulong secret_count;     /* Number of secret keys seen.      */
+  ulong exported;         /* Number of actual exported keys.  */
+};
+
+
+/* Local prototypes.  */
+static int do_export (ctrl_t ctrl, strlist_t users, int secret,
+                      unsigned int options, export_stats_t stats);
 static int do_export_stream (ctrl_t ctrl, iobuf_t out,
                              strlist_t users, int secret,
                              kbnode_t *keyblock_out, unsigned int options,
-                            int *any);
+                            export_stats_t stats, int *any);
+
+\f
 
 
+/* Option parser for export options.  See parse_options fro
+   details.  */
 int
 parse_export_options(char *str,unsigned int *options,int noisy)
 {
@@ -85,39 +99,102 @@ parse_export_options(char *str,unsigned int *options,int noisy)
 }
 
 
-/****************
- * Export the public keys (to standard out or --output).
- * Depending on opt.armor the output is armored.
- * options are defined in main.h.
- * If USERS is NULL, the complete ring will be exported.  */
+/* Create a new export stats object initialized to zero.  On error
+   returns NULL and sets ERRNO.  */
+export_stats_t
+export_new_stats (void)
+{
+  export_stats_t stats;
+
+  return xtrycalloc (1, sizeof *stats);
+}
+
+
+/* Release an export stats object.  */
+void
+export_release_stats (export_stats_t stats)
+{
+  xfree (stats);
+}
+
+
+/* Print export statistics using the status interface.  */
+void
+export_print_stats (export_stats_t stats)
+{
+  if (!stats)
+    return;
+
+  if (is_status_enabled ())
+    {
+      char buf[15*20];
+
+      snprintf (buf, sizeof buf, "%lu %lu %lu",
+               stats->count,
+               stats->secret_count,
+               stats->exported );
+      write_status_text (STATUS_EXPORT_RES, buf);
+    }
+}
+
+
+/*
+ * Export public keys (to stdout or to --output FILE).
+ *
+ * Depending on opt.armor the output is armored.  OPTIONS are defined
+ * in main.h.  If USERS is NULL, all keys will be exported.  STATS is
+ * either an export stats object for update or NULL.
+ *
+ * This function is the core of "gpg --export".
+ */
 int
-export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options )
+export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options,
+                export_stats_t stats)
 {
-  return do_export (ctrl, users, 0, options );
+  return do_export (ctrl, users, 0, options, stats);
 }
 
-/****************
- * Export to an already opened stream; return -1 if no keys have
- * been exported
+
+/*
+ * Export secret keys (to stdout or to --output FILE).
+ *
+ * Depending on opt.armor the output is armored.  If USERS is NULL,
+ * all secret keys will be exported.  STATS is either an export stats
+ * object for update or NULL.
+ *
+ * This function is the core of "gpg --export-secret-keys".
  */
 int
-export_pubkeys_stream (ctrl_t ctrl, iobuf_t out, strlist_t users,
-                      kbnode_t *keyblock_out, unsigned int options )
+export_seckeys (ctrl_t ctrl, strlist_t users, export_stats_t stats)
 {
-  int any, rc;
+  return do_export (ctrl, users, 1, 0, stats);
+}
 
-  rc = do_export_stream (ctrl, out, users, 0, keyblock_out, options, &any);
-  if (!rc && !any)
-    rc = -1;
-  return rc;
+
+/*
+ * Export secret sub keys (to stdout or to --output FILE).
+ *
+ * This is the same as export_seckeys but replaces the primary key by
+ * a stub key.  Depending on opt.armor the output is armored.  If
+ * USERS is NULL, all secret subkeys will be exported.  STATS is
+ * either an export stats object for update or NULL.
+ *
+ * This function is the core of "gpg --export-secret-subkeys".
+ */
+int
+export_secsubkeys (ctrl_t ctrl, strlist_t users, export_stats_t stats)
+{
+  return do_export (ctrl, users, 2, 0, stats);
 }
 
 
 /*
- * Export a single key into a memory buffer.
+ * Export a single key into a memory buffer.  STATS is either an
+ * export stats object for update or NULL.
  */
 gpg_error_t
 export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options,
+                      export_stats_t stats,
                       kbnode_t *r_keyblock, void **r_data, size_t *r_datalen)
 {
   gpg_error_t err;
@@ -134,7 +211,8 @@ export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options,
     return gpg_error_from_syserror ();
 
   iobuf = iobuf_temp ();
-  err = do_export_stream (ctrl, iobuf, helplist, 0, r_keyblock, options, &any);
+  err = do_export_stream (ctrl, iobuf, helplist, 0, r_keyblock, options,
+                          stats, &any);
   if (!err && !any)
     err = gpg_error (GPG_ERR_NOT_FOUND);
   if (!err)
@@ -166,26 +244,14 @@ export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options,
 }
 
 
-int
-export_seckeys (ctrl_t ctrl, strlist_t users )
-{
-  return do_export (ctrl, users, 1, 0);
-}
-
-int
-export_secsubkeys (ctrl_t ctrl, strlist_t users )
-{
-  return do_export (ctrl, users, 2, 0);
-}
-
-
 /* Export the keys identified by the list of strings in USERS.  If
    Secret is false public keys will be exported.  With secret true
    secret keys will be exported; in this case 1 means the entire
    secret keyblock and 2 only the subkeys.  OPTIONS are the export
    options to apply.  */
 static int
-do_export (ctrl_t ctrl, strlist_t users, int secret, unsigned int options )
+do_export (ctrl_t ctrl, strlist_t users, int secret, unsigned int options,
+           export_stats_t stats)
 {
   IOBUF out = NULL;
   int any, rc;
@@ -198,18 +264,14 @@ do_export (ctrl_t ctrl, strlist_t users, int secret, unsigned int options )
   if (rc)
     return rc;
 
-  /* We don't want an Armor for DANE format.  */
-  if (!(options & EXPORT_DANE_FORMAT))
+  if ( opt.armor )
     {
-      if ( opt.armor )
-        {
-          afx = new_armor_context ();
-          afx->what = secret? 5 : 1;
-          push_armor_filter (afx, out);
-        }
+      afx = new_armor_context ();
+      afx->what = secret? 5 : 1;
+      push_armor_filter (afx, out);
     }
 
-  rc = do_export_stream (ctrl, out, users, secret, NULL, options, &any );
+  rc = do_export_stream (ctrl, out, users, secret, NULL, options, stats, &any);
 
   if ( rc || !any )
     iobuf_cancel (out);
@@ -747,6 +809,22 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
   goto leave;
 }
 
+
+/* Print an "EXPORTED" status line.  PK is the primary public key.  */
+static void
+print_status_exported (PKT_public_key *pk)
+{
+  char *hexfpr;
+
+  if (!is_status_enabled ())
+    return;
+
+  hexfpr = hexfingerprint (pk, NULL, 0);
+  write_status_text (STATUS_EXPORTED, hexfpr? hexfpr : "[?]");
+  xfree (hexfpr);
+}
+
+
 /* Export the keys identified by the list of strings in USERS to the
    stream OUT.  If Secret is false public keys will be exported.  With
    secret true secret keys will be exported; in this case 1 means the
@@ -758,7 +836,8 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
    key has been exported true is stored at ANY. */
 static int
 do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret,
-                 kbnode_t *keyblock_out, unsigned int options, int *any)
+                 kbnode_t *keyblock_out, unsigned int options,
+                  export_stats_t stats, int *any)
 {
   gpg_error_t err = 0;
   PACKET pkt;
@@ -771,10 +850,20 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret,
   strlist_t sl;
   gcry_cipher_hd_t cipherhd = NULL;
   char *cache_nonce = NULL;
+  struct export_stats_s dummystats;
 
+  if (!stats)
+    stats = &dummystats;
   *any = 0;
   init_packet (&pkt);
   kdbhd = keydb_new ();
+  if (!kdbhd)
+    return gpg_error_from_syserror ();
+
+  /* For the DANE format override the options.  */
+  if ((options & EXPORT_DANE_FORMAT))
+    options = (EXPORT_DANE_FORMAT | EXPORT_MINIMAL | EXPORT_CLEAN);
+
 
   if (!users)
     {
@@ -853,8 +942,6 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret,
       err = keydb_search (kdbhd, desc, ndesc, &descindex);
       if (!users)
         desc[0].mode = KEYDB_SEARCH_MODE_NEXT;
-      if (gpg_err_code (err) == GPG_ERR_LEGACY_KEY)
-        continue;  /* Skip PGP2 keys.  */
       if (err)
         break;
 
@@ -862,8 +949,6 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret,
       release_kbnode (keyblock);
       keyblock = NULL;
       err = keydb_get_keyblock (kdbhd, &keyblock);
-      if (gpg_err_code (err) == GPG_ERR_LEGACY_KEY)
-        continue;  /* Skip PGP2 keys.  */
       if (err)
         {
           log_error (_("error reading keyblock: %s\n"), gpg_strerror (err));
@@ -876,6 +961,7 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret,
           log_error ("public key packet not found in keyblock - skipped\n");
           continue;
         }
+      stats->count++;
       setup_main_keyids (keyblock);  /* gpg_format_keydesc needs it.  */
       pk = node->pkt->pkt.public_key;
       keyid_from_pk (pk, keyid);
@@ -905,6 +991,7 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret,
                         "not yet supported - skipped\n", keystr (keyid));
               continue;
             }
+          stats->secret_count++;
         }
 
       /* Always do the cleaning on the public key part if requested.
@@ -1108,6 +1195,11 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret,
                     }
 
                   err = build_packet (out, node->pkt);
+                  if (!err && node->pkt->pkttype == PKT_PUBLIC_KEY)
+                    {
+                      stats->exported++;
+                      print_status_exported (node->pkt->pkt.public_key);
+                    }
                 }
               else if (!err)
                 {
@@ -1163,6 +1255,11 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret,
                     goto unwraperror;
 
                   err = build_packet (out, node->pkt);
+                  if (!err && node->pkt->pkttype == PKT_PUBLIC_KEY)
+                    {
+                      stats->exported++;
+                      print_status_exported (node->pkt->pkt.public_key);
+                    }
                   goto unwraperror_leave;
 
                 unwraperror:
@@ -1200,8 +1297,14 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret,
           else
             {
               err = build_packet (out, node->pkt);
+              if (!err && node->pkt->pkttype == PKT_PUBLIC_KEY)
+                {
+                  stats->exported++;
+                  print_status_exported (node->pkt->pkt.public_key);
+                }
             }
 
+
           if (err)
             {
               log_error ("build_packet(%d) failed: %s\n",