Add support for gpg --fetch-keys.
authorWerner Koch <wk@gnupg.org>
Tue, 16 Jun 2009 15:42:37 +0000 (15:42 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 16 Jun 2009 15:42:37 +0000 (15:42 +0000)
14 files changed:
NEWS
doc/ChangeLog
doc/gpgme.texi
src/ChangeLog
src/data.c
src/engine-gpg.c
src/gpgme.h.in
src/version.c
tests/ChangeLog
tests/gpg/Makefile.am
tests/gpg/pgp-export.c
tests/gpg/pgp-import.c [new file with mode: 0644]
tests/gpg/pgp-keylist.c
tests/gpg/t-support.h

diff --git a/NEWS b/NEWS
index 2f6c437..449dcbf 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -48,6 +48,9 @@ Noteworthy changes in version 1.2.0 (unreleased)
  gpgme_op_export_ext            EXTENDED: Arg RESERVED is now a MODE flag.
  gpgme_op_export_keys_start     NEW.
  gpgme_op_export_keys           NEW.
+ GPGME_DATA_ENCODING_URL        NEW.
+ GPGME_DATA_ENCODING_URL0       NEW.
+ GPGME_DATA_ENCODING_URLESC     NEW.
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 
index 6bc623b..5772e68 100644 (file)
@@ -6,6 +6,7 @@
 
        * gpgme.texi (Exporting Keys): Document gpgme_op_export_keys.
        (Importing Keys): Document gpgme_op_import_keys.
+       (Data Buffer Meta-Data): Document URL encodings.
 
 2009-05-28  Marcus Brinkmann  <marcus@g10code.de>
 
index 8ecb449..a0bc20e 100644 (file)
@@ -1934,6 +1934,19 @@ scheme as used by @acronym{MIME} and other protocols.
 @item GPGME_DATA_ENCODING_ARMOR
 This specifies that the data is encoded in an armored form as used by
 OpenPGP and PEM.
+
+@item GPGME_DATA_ENCODING_URL
+The data is a list of linefeed delimited URLs.  This is only useful with
+@code{gpgme_op_import}.
+
+@item GPGME_DATA_ENCODING_URL0
+The data is a list of binary zero delimited URLs.  This is only useful
+with @code{gpgme_op_import}.
+
+@item GPGME_DATA_ENCODING_URLESC
+The data is a list of linefeed delimited URLs with all control and space
+characters percent escaped.  This mode is is not yet implemented.
+
 @end table
 @end deftp
 
index 6861913..93108af 100644 (file)
@@ -5,6 +5,14 @@
 
 2009-06-16  Werner Koch  <wk@g10code.com>
 
+       * version.c: Include stdlib.h. 
+
+       * gpgme.h.in (gpgme_data_encoding_t): Add GPGME_DATA_ENCODING_URL,
+       GPGME_DATA_ENCODING_URLESC, GPGME_DATA_ENCODING_URL0.
+       * data.c (gpgme_data_set_encoding): Adjust for new values.
+       * engine-gpg.c (string_from_data): New.
+       (gpg_import): Implement --fetch-key feature.
+
        * gpgme.h.in (gpgme_op_export_keys_start, gpgme_op_export_keys): New.
        * gpgme.def, libgpgme.vers: Add them.
        * export.c (gpgme_op_export_keys_start, gpgme_op_export_keys): New.
index 18d9c71..759d328 100644 (file)
@@ -191,7 +191,7 @@ gpgme_data_set_encoding (gpgme_data_t dh, gpgme_data_encoding_t enc)
              "encoding=%i", enc);
   if (!dh)
     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
-  if (enc < 0 || enc > GPGME_DATA_ENCODING_ARMOR)
+  if (enc < 0 || enc > GPGME_DATA_ENCODING_URL0)
     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
   dh->encoding = enc;
   return TRACE_ERR (0);
index 3c06edf..1012166 100644 (file)
@@ -1803,6 +1803,107 @@ gpg_genkey (void *engine, gpgme_data_t help_data, int use_armor,
   return err;
 }
 
+/* Return the next DELIM delimited string from DATA as a C-string.
+   The caller needs to provide the address of a pointer variable which
+   he has to set to NULL before the first call.  After the last call
+   to this function, this function needs to be called once more with
+   DATA set to NULL so that the function can release its internal
+   state.  After that the pointer variable is free for use again.
+   Note that we use a delimiter and thus a trailing delimiter is not
+   required.  DELIM may not be changed after the first call. */
+static const char *
+string_from_data (gpgme_data_t data, int delim, 
+                  void **helpptr, gpgme_error_t *r_err)
+{
+#define MYBUFLEN 2000 /* Fixme: We don't support URLs longer than that.  */
+  struct {
+    int  eof_seen;
+    int  nbytes;      /* Length of the last returned string including
+                         the delimiter. */
+    int  buflen;      /* Valid length of BUF.  */
+    char buf[MYBUFLEN+1];  /* Buffer with one byte extra space.  */
+  } *self;
+  char *p;
+  int nread;
+
+  *r_err = 0;
+  if (!data)
+    {
+      if (*helpptr)
+        {
+          free (*helpptr);
+          *helpptr = NULL;
+        }
+      return NULL;
+    }
+
+  if (*helpptr)
+    self = *helpptr;
+  else
+    {
+      self = malloc (sizeof *self);
+      if (!self)
+        {
+          *r_err = gpg_error_from_syserror ();
+          return NULL;
+        }
+      *helpptr = self;
+      self->eof_seen = 0;
+      self->nbytes = 0;
+      self->buflen = 0;
+    }
+
+  if (self->eof_seen)
+    return NULL;
+
+  assert (self->nbytes <= self->buflen);
+  memmove (self->buf, self->buf + self->nbytes, self->buflen - self->nbytes);
+  self->buflen -= self->nbytes;
+  self->nbytes = 0;
+
+  do
+    {
+      /* Fixme: This is fairly infective scanning because we may scan
+         the buffer several times.  */
+      p = memchr (self->buf, delim, self->buflen);
+      if (p)
+        {
+          *p = 0;
+          self->nbytes = p - self->buf + 1;
+          return self->buf;
+        }
+
+      if ( !(MYBUFLEN - self->buflen) )
+        {
+          /* Not enough space - URL too long.  */
+          *r_err = gpg_error (GPG_ERR_TOO_LARGE);
+          return NULL;
+        }
+
+      nread = gpgme_data_read (data, self->buf + self->buflen, 
+                               MYBUFLEN - self->buflen);
+      if (nread < 0)
+        {
+          *r_err = gpg_error_from_syserror ();
+          return NULL;
+        }
+      self->buflen += nread;
+    }
+  while (nread);
+
+  /* EOF reached.  If we have anything in the buffer, append a Nul and
+     return it. */
+  self->eof_seen = 1;
+  if (self->buflen)
+    {
+      self->buf[self->buflen] = 0;  /* (we allocated one extra byte)  */
+      return self->buf;
+    }
+  return NULL;
+#undef MYBUFLEN
+}
+
+
 
 static gpgme_error_t
 gpg_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
@@ -1810,10 +1911,13 @@ gpg_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
   engine_gpg_t gpg = engine;
   gpgme_error_t err;
   int idx;
+  gpgme_data_encoding_t dataenc;
 
   if (keydata && keyarray)
     gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed.  */
 
+  dataenc = gpgme_data_get_encoding (keydata);
+
   if (keyarray)
     {
       err = add_arg (gpg, "--recv-keys");
@@ -1831,6 +1935,38 @@ gpg_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
             err = add_arg (gpg, keyarray[idx]->subkeys->keyid);
         }
     }
+  else if (dataenc == GPGME_DATA_ENCODING_URL
+           || dataenc == GPGME_DATA_ENCODING_URL0)
+    {
+      void *helpptr;
+      const char *string;
+      gpgme_error_t xerr;
+      int delim = (dataenc == GPGME_DATA_ENCODING_URL)? '\n': 0;
+
+      /* FIXME: --fetch-keys is probably not correct because it can't
+         grok all kinds of URLs.  On Unix it should just work but on
+         Windows we will build the command line and that may fail for
+         some embedded control characters.  It is anyway limited to
+         the maximum size of the command line.  We need another
+         command which can take its input from a file.  Maybe we
+         should use an option to gpg to modify such commands (ala
+         --multifile).  */
+      err = add_arg (gpg, "--fetch-keys");
+      if (!err)
+        err = add_arg (gpg, "--");
+      helpptr = NULL;
+      while (!err
+             && (string = string_from_data (keydata, delim, &helpptr, &xerr)))
+        err = add_arg (gpg, string);
+      if (!err)
+        err = xerr;
+      string_from_data (NULL, delim, &helpptr, &xerr);
+    }
+  else if (dataenc == GPGME_DATA_ENCODING_URLESC)
+    {
+      /* Already escaped URLs are not yet supported.  */
+      err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+    }
   else
     {
       err = add_arg (gpg, "--import");
index ff9bba0..e02a98c 100644 (file)
@@ -185,7 +185,10 @@ typedef enum
     GPGME_DATA_ENCODING_NONE   = 0,    /* Not specified.  */
     GPGME_DATA_ENCODING_BINARY = 1,
     GPGME_DATA_ENCODING_BASE64 = 2,
-    GPGME_DATA_ENCODING_ARMOR  = 3     /* Either PEM or OpenPGP Armor.  */
+    GPGME_DATA_ENCODING_ARMOR  = 3,    /* Either PEM or OpenPGP Armor.  */
+    GPGME_DATA_ENCODING_URL    = 4,     /* LF delimited URL list.        */
+    GPGME_DATA_ENCODING_URLESC = 5,     /* Ditto, but percent escaped.   */
+    GPGME_DATA_ENCODING_URL0   = 6      /* Nul delimited URL list.       */
   }
 gpgme_data_encoding_t;
 
index f777da4..b2d795a 100644 (file)
@@ -22,6 +22,7 @@
 #if HAVE_CONFIG_H
 #include <config.h>
 #endif
+#include <stdlib.h>
 #include <string.h>
 #include <limits.h>
 #include <ctype.h>
index c9ab133..fcdf156 100644 (file)
@@ -1,5 +1,9 @@
 2009-06-16  Werner Koch  <wk@g10code.com>
 
+       * gpg/pgp-import.c: New.
+       * gpg/t-support.h (print_import_result, nonnull): Factored out
+       from other tools.
+
        * gpg/pgp-export.c, gpg/pgp-keylist.c: New.
 
 2009-06-09  Werner Koch  <wk@g10code.com>
index fdb023c..441e80c 100644 (file)
@@ -51,7 +51,7 @@ t_thread1_LDADD = ../../src/libgpgme-pthread.la
 
 # We don't run t-genkey in the test suite, because it takes too long
 # The other programs are used for debugging.
-noinst_PROGRAMS = $(TESTS) t-genkey pgp-keylist pgp-export
+noinst_PROGRAMS = $(TESTS) t-genkey pgp-keylist pgp-export pgp-import
 
 mkdemodirs: mkdemodirs.in Makefile
        sed -e 's,[@]GPG[@],$(GPG),g' < $(srcdir)/mkdemodirs.in > mkdemodirs
index 9911c18..a0b3e8a 100644 (file)
 static int verbose;
 
 
-static const char *
-nonnull (const char *s)
-{
-  return s? s :"[none]";
-}
-
-
 static int
 show_usage (int ex)
 {
diff --git a/tests/gpg/pgp-import.c b/tests/gpg/pgp-import.c
new file mode 100644 (file)
index 0000000..b9d6860
--- /dev/null
@@ -0,0 +1,129 @@
+/* pgp-import.c  - Helper to run an import command
+   Copyright (C) 2008, 2009 g10 Code GmbH
+
+   This file is part of GPGME.
+   GPGME is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of
+   the License, or (at your option) any later version.
+   
+   GPGME 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
+   Lesser General Public License for more details.
+   
+   You should have received a copy of the GNU Lesser General Public
+   License along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* We need to include config.h so that we know whether we are building
+   with large file system (LFS) support. */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <gpgme.h>
+
+#define PGM "pgp-import"
+
+#include "t-support.h"
+
+
+static int verbose;
+
+
+static int
+show_usage (int ex)
+{
+  fputs ("usage: " PGM " [options] FILENAMEs\n\n"
+         "Options:\n"
+         "  --verbose        run in verbose mode\n"
+         "  --url            import from given URLs\n"
+         "  -0               URLs are delimited by a nul\n"
+         , stderr);
+  exit (ex);
+}
+
+int 
+main (int argc, char **argv)
+{
+  int last_argc = -1;
+  gpgme_error_t err;
+  gpgme_ctx_t ctx;
+  int url_mode = 0;
+  int nul_mode = 0;
+  gpgme_import_result_t impres;
+  gpgme_data_t data;
+
+  if (argc)
+    { argc--; argv++; }
+  while (argc && last_argc != argc )
+    {
+      last_argc = argc;
+      if (!strcmp (*argv, "--"))
+        {
+          argc--; argv++;
+          break;
+        }
+      else if (!strcmp (*argv, "--help"))
+        show_usage (0);
+      else if (!strcmp (*argv, "--verbose"))
+        {
+          verbose = 1;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--url"))
+        {
+          url_mode = 1;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "-0"))
+        {
+          nul_mode = 1;
+          argc--; argv++;
+        }
+      else if (!strncmp (*argv, "--", 2))
+        show_usage (1);
+      
+    }          
+  if (!argc)
+    show_usage (1);
+
+  init_gpgme (GPGME_PROTOCOL_OpenPGP);
+
+  err = gpgme_new (&ctx);
+  fail_if_err (err);
+  gpgme_set_protocol (ctx, GPGME_PROTOCOL_OpenPGP);
+
+  for (; argc; argc--, argv++)
+    {
+      printf ("reading file `%s'\n", *argv);
+      err = gpgme_data_new_from_file (&data, *argv, 1);
+      fail_if_err (err);
+
+      if (url_mode)
+        gpgme_data_set_encoding (data, (nul_mode? GPGME_DATA_ENCODING_URL0
+                                        : GPGME_DATA_ENCODING_URL));
+      
+      err = gpgme_op_import (ctx, data);
+      fail_if_err (err);
+      impres = gpgme_op_import_result (ctx);
+      if (!impres)
+        {
+          fprintf (stderr, PGM ": no import result returned\n");
+          exit (1);
+        }
+      print_import_result (impres);
+      
+      gpgme_data_release (data);
+    }
+
+  gpgme_release (ctx);
+  return 0;
+}
index 2645f48..4978b8d 100644 (file)
 static int verbose;
 
 
-static const char *
-nonnull (const char *s)
-{
-  return s? s :"[none]";
-}
-
-
-static void
-print_import_result (gpgme_import_result_t r)
-{
-  gpgme_import_status_t st;
-
-  printf ("key import results:\n"
-          "        considered: %d\n"
-          "        no user id: %d\n"
-          "          imported: %d\n"
-          "      imported_rsa: %d\n"
-          "         unchanged: %d\n"
-          "      new user ids: %d\n"
-          "       new subkeys: %d\n"
-          "    new signatures: %d\n"
-          "   new revocations: %d\n"
-          "       secret read: %d\n"
-          "   secret imported: %d\n"
-          "  secret unchanged: %d\n"
-          "  skipped new keys: %d\n"
-          "      not imported: %d\n",
-          r->considered,
-          r->no_user_id,
-          r->imported,
-          r->imported_rsa,
-          r->unchanged,
-          r->new_user_ids,
-          r->new_sub_keys,
-          r->new_signatures,
-          r->new_revocations,
-          r->secret_read,
-          r->secret_imported,
-          r->secret_unchanged,
-          r->skipped_new_keys,
-          r->not_imported);
-
-  for (st=r->imports; st; st = st->next)
-    {
-      printf ("  fpr: %s err: %d (%s) status:", nonnull (st->fpr),
-              st->result, gpg_strerror (st->result));
-      if (st->status & GPGME_IMPORT_NEW)
-        fputs (" new", stdout);
-      if (st->status & GPGME_IMPORT_UID)
-        fputs (" uid", stdout);
-      if (st->status & GPGME_IMPORT_SIG)
-        fputs (" sig", stdout);
-      if (st->status & GPGME_IMPORT_SUBKEY)
-        fputs (" subkey", stdout);
-      if (st->status & GPGME_IMPORT_SECRET)
-        fputs (" secret", stdout);
-      putchar ('\n');
-    }
-}
-
-
 static int
 show_usage (int ex)
 {
index 362b857..13475f2 100644 (file)
   while (0)
 
 
+static const char *
+nonnull (const char *s)
+{
+  return s? s :"[none]";
+}
+
+
 void
 print_data (gpgme_data_t dh)
 {
@@ -113,3 +120,57 @@ init_gpgme (gpgme_protocol_t proto)
   err = gpgme_engine_check_version (proto);
   fail_if_err (err);
 }
+
+
+void
+print_import_result (gpgme_import_result_t r)
+{
+  gpgme_import_status_t st;
+
+  for (st=r->imports; st; st = st->next)
+    {
+      printf ("  fpr: %s err: %d (%s) status:", nonnull (st->fpr),
+              st->result, gpg_strerror (st->result));
+      if (st->status & GPGME_IMPORT_NEW)
+        fputs (" new", stdout);
+      if (st->status & GPGME_IMPORT_UID)
+        fputs (" uid", stdout);
+      if (st->status & GPGME_IMPORT_SIG)
+        fputs (" sig", stdout);
+      if (st->status & GPGME_IMPORT_SUBKEY)
+        fputs (" subkey", stdout);
+      if (st->status & GPGME_IMPORT_SECRET)
+        fputs (" secret", stdout);
+      putchar ('\n');
+    }
+  printf ("key import summary:\n"
+          "        considered: %d\n"
+          "        no user id: %d\n"
+          "          imported: %d\n"
+          "      imported_rsa: %d\n"
+          "         unchanged: %d\n"
+          "      new user ids: %d\n"
+          "       new subkeys: %d\n"
+          "    new signatures: %d\n"
+          "   new revocations: %d\n"
+          "       secret read: %d\n"
+          "   secret imported: %d\n"
+          "  secret unchanged: %d\n"
+          "  skipped new keys: %d\n"
+          "      not imported: %d\n",
+          r->considered,
+          r->no_user_id,
+          r->imported,
+          r->imported_rsa,
+          r->unchanged,
+          r->new_user_ids,
+          r->new_sub_keys,
+          r->new_signatures,
+          r->new_revocations,
+          r->secret_read,
+          r->secret_imported,
+          r->secret_unchanged,
+          r->skipped_new_keys,
+          r->not_imported);
+}
+