(create_duptable, destroy_duptable)
authorWerner Koch <wk@gnupg.org>
Tue, 2 Mar 2004 08:02:47 +0000 (08:02 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 2 Mar 2004 08:02:47 +0000 (08:02 +0000)
(insert_duptable): New.
(gpgsm_export): Avoid duplicates.

sm/ChangeLog
sm/export.c

index f6716b0..1cda33b 100644 (file)
@@ -1,3 +1,9 @@
+2004-03-02  Werner Koch  <wk@gnupg.org>
+
+       * export.c (create_duptable, destroy_duptable)
+       (insert_duptable): New.
+       (gpgsm_export): Avoid duplicates.
+
 2004-02-26  Werner Koch  <wk@gnupg.org>
 
        * certchain.c (compare_certs): New.
index ab175f1..fa56ec9 100644 (file)
 #endif
 
 
+/* A table tem to store a fingerprint used in a duplicates table.  We
+   don't need to hash here because a fingerprint is alrady a perfect
+   hash value.  This we use the most significant bits to index the
+   table and then use a linked list for the overflow.  Possible
+   enhancement for very large number of certictates: Add a second
+   level table and then resort to a linked list. */
+struct duptable_s
+{
+  struct duptable_s *next;
+
+  /* Note that we only need to store 19 bytes because the first byte
+     is implictly given by the table index (we require at least 8
+     bits). */
+  unsigned char fpr[19];
+};
+typedef struct duptable_s *duptable_t;
+#define DUPTABLE_BITS 12
+#define DUPTABLE_SIZE (1 << DUPTABLE_BITS)
+
 
 static void print_short_info (ksba_cert_t cert, FILE *fp);
 static gpg_error_t export_p12 (const unsigned char *certimg, size_t certimglen,
@@ -51,12 +70,74 @@ static gpg_error_t export_p12 (const unsigned char *certimg, size_t certimglen,
                                FILE **retfp);
 
 
+/* Create a table used to indetify duplicated certificates. */
+static duptable_t *
+create_duptable (void)
+{
+  return xtrycalloc (DUPTABLE_SIZE, sizeof (duptable_t));
+}
+
+static void
+destroy_duptable (duptable_t *table)
+{
+  int idx;
+  duptable_t t, t2;
+
+  if (table)
+    {
+      for (idx=0; idx < DUPTABLE_SIZE; idx++) 
+        for (t = table[idx]; t; t = t2)
+          {
+            t2 = t->next;
+            xfree (t);
+          }
+      xfree (table);
+    }
+}
+
+/* Insert the 20 byte fingerprint FPR into TABLE.  Sets EXITS to true
+   if the fingerprint already exists in the table. */
+static gpg_error_t
+insert_duptable (duptable_t *table, unsigned char *fpr, int *exists)
+{
+  size_t idx;
+  duptable_t t;
+  
+  *exists = 0;
+  idx = fpr[0];
+#if DUPTABLE_BITS > 16
+#error cannot handle a table larger than 16 bits
+#elif DUPTABLE_BITS > 8
+  idx <<= (DUPTABLE_BITS - 8);  
+  idx |= (fpr[1] & ~(~0 << 4)); 
+#endif  
+
+  for (t = table[idx]; t; t = t->next)
+    if (!memcmp (t->fpr, fpr+1, 19))
+      break;
+  if (t)
+    {
+      *exists = 1;
+      return 0;
+    }
+  /* Insert that fingerprint. */
+  t = xtrymalloc (sizeof *t);
+  if (!t)
+    return gpg_error_from_errno (errno);
+  memcpy (t->fpr, fpr+1, 19);
+  t->next = table[idx];
+  table[idx] = t;
+  return 0;
+}
+
+
+
 
 /* Export all certificates or just those given in NAMES. */
 void
 gpgsm_export (CTRL ctrl, STRLIST names, FILE *fp)
 {
-  KEYDB_HANDLE hd;
+  KEYDB_HANDLE hd = NULL;
   KEYDB_SEARCH_DESC *desc = NULL;
   int ndesc;
   Base64Context b64writer = NULL;
@@ -66,6 +147,15 @@ gpgsm_export (CTRL ctrl, STRLIST names, FILE *fp)
   int rc=0;
   int count = 0;
   int i;
+  duptable_t *dtable;
+
+  
+  dtable = create_duptable ();
+  if (!dtable)
+    {
+      log_error ("creating duplicates table failed: %s\n", strerror (errno));
+      goto leave;
+    }
 
   hd = keydb_new (0);
   if (!hd)
@@ -127,8 +217,8 @@ gpgsm_export (CTRL ctrl, STRLIST names, FILE *fp)
       
   while (!(rc = keydb_search (hd, desc, ndesc)))
     {
-      const unsigned char *image;
-      size_t imagelen;
+      unsigned char fpr[20];
+      int exists;
 
       if (!names) 
         desc[0].mode = KEYDB_SEARCH_MODE_NEXT;
@@ -140,53 +230,69 @@ gpgsm_export (CTRL ctrl, STRLIST names, FILE *fp)
           goto leave;
         }
 
-      image = ksba_cert_get_image (cert, &imagelen);
-      if (!image)
+      gpgsm_get_fingerprint (cert, 0, fpr, NULL);
+      rc = insert_duptable (dtable, fpr, &exists);
+      if (rc)
         {
-          log_error ("ksba_cert_get_image failed\n");
+          log_error ("inserting into duplicates table fauiled: %s\n",
+                     gpg_strerror (rc));
           goto leave;
         }
 
-      if (ctrl->create_pem)
+      if (!exists)
         {
-          if (count)
-            putc ('\n', fp);
-          print_short_info (cert, fp);
-          putc ('\n', fp);
-        }
-      count++;
+          const unsigned char *image;
+          size_t imagelen;
 
-      if (!b64writer)
-        {
-          ctrl->pem_name = "CERTIFICATE";
-          rc = gpgsm_create_writer (&b64writer, ctrl, fp, &writer);
-          if (rc)
+          image = ksba_cert_get_image (cert, &imagelen);
+          if (!image)
             {
-              log_error ("can't create writer: %s\n", gpg_strerror (rc));
+              log_error ("ksba_cert_get_image failed\n");
               goto leave;
             }
-        }
 
-      rc = ksba_writer_write (writer, image, imagelen);
-      if (rc)
-        {
-          log_error ("write error: %s\n", gpg_strerror (rc));
-          goto leave;
-        }
 
-      if (ctrl->create_pem)
-        {
-          /* We want one certificate per PEM block */
-          rc = gpgsm_finish_writer (b64writer);
-          if (rc) 
+          if (ctrl->create_pem)
             {
-              log_error ("write failed: %s\n", gpg_strerror (rc));
+              if (count)
+                putc ('\n', fp);
+              print_short_info (cert, fp);
+              putc ('\n', fp);
+            }
+          count++;
+
+          if (!b64writer)
+            {
+              ctrl->pem_name = "CERTIFICATE";
+              rc = gpgsm_create_writer (&b64writer, ctrl, fp, &writer);
+              if (rc)
+                {
+                  log_error ("can't create writer: %s\n", gpg_strerror (rc));
+                  goto leave;
+                }
+            }
+
+          rc = ksba_writer_write (writer, image, imagelen);
+          if (rc)
+            {
+              log_error ("write error: %s\n", gpg_strerror (rc));
               goto leave;
             }
-          gpgsm_destroy_writer (b64writer);
-          b64writer = NULL;
+
+          if (ctrl->create_pem)
+            {
+              /* We want one certificate per PEM block */
+              rc = gpgsm_finish_writer (b64writer);
+              if (rc) 
+                {
+                  log_error ("write failed: %s\n", gpg_strerror (rc));
+                  goto leave;
+                }
+              gpgsm_destroy_writer (b64writer);
+              b64writer = NULL;
+            }
         }
-      
+
       ksba_cert_release (cert); 
       cert = NULL;
     }
@@ -207,6 +313,7 @@ gpgsm_export (CTRL ctrl, STRLIST names, FILE *fp)
   ksba_cert_release (cert);
   xfree (desc);
   keydb_release (hd);
+  destroy_duptable (dtable);
 }
 
 
@@ -228,6 +335,7 @@ gpgsm_p12_export (ctrl_t ctrl, const char *name, FILE *fp)
   int  nread;
   FILE *datafp = NULL;
 
+
   hd = keydb_new (0);
   if (!hd)
     {