From bebd9cbe29cf6db15469502b5c6b1f83c93513ef Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 16 Jun 2009 15:42:37 +0000 Subject: [PATCH] Add support for gpg --fetch-keys. --- NEWS | 3 + doc/ChangeLog | 1 + doc/gpgme.texi | 13 ++++ src/ChangeLog | 8 +++ src/data.c | 2 +- src/engine-gpg.c | 136 ++++++++++++++++++++++++++++++++++++++++ src/gpgme.h.in | 5 +- src/version.c | 1 + tests/ChangeLog | 4 ++ tests/gpg/Makefile.am | 2 +- tests/gpg/pgp-export.c | 7 --- tests/gpg/pgp-import.c | 129 +++++++++++++++++++++++++++++++++++++ tests/gpg/pgp-keylist.c | 61 ------------------ tests/gpg/t-support.h | 61 ++++++++++++++++++ 14 files changed, 362 insertions(+), 71 deletions(-) create mode 100644 tests/gpg/pgp-import.c diff --git a/NEWS b/NEWS index 2f6c437..449dcbf 100644 --- 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. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/ChangeLog b/doc/ChangeLog index 6bc623b..5772e68 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -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 diff --git a/doc/gpgme.texi b/doc/gpgme.texi index 8ecb449..a0bc20e 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -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 diff --git a/src/ChangeLog b/src/ChangeLog index 6861913..93108af 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -5,6 +5,14 @@ 2009-06-16 Werner Koch + * 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. diff --git a/src/data.c b/src/data.c index 18d9c71..759d328 100644 --- a/src/data.c +++ b/src/data.c @@ -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); diff --git a/src/engine-gpg.c b/src/engine-gpg.c index 3c06edf..1012166 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -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"); diff --git a/src/gpgme.h.in b/src/gpgme.h.in index ff9bba0..e02a98c 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -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; diff --git a/src/version.c b/src/version.c index f777da4..b2d795a 100644 --- a/src/version.c +++ b/src/version.c @@ -22,6 +22,7 @@ #if HAVE_CONFIG_H #include #endif +#include #include #include #include diff --git a/tests/ChangeLog b/tests/ChangeLog index c9ab133..fcdf156 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,5 +1,9 @@ 2009-06-16 Werner Koch + * 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 diff --git a/tests/gpg/Makefile.am b/tests/gpg/Makefile.am index fdb023c..441e80c 100644 --- a/tests/gpg/Makefile.am +++ b/tests/gpg/Makefile.am @@ -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 diff --git a/tests/gpg/pgp-export.c b/tests/gpg/pgp-export.c index 9911c18..a0b3e8a 100644 --- a/tests/gpg/pgp-export.c +++ b/tests/gpg/pgp-export.c @@ -37,13 +37,6 @@ 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 index 0000000..b9d6860 --- /dev/null +++ b/tests/gpg/pgp-import.c @@ -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 . +*/ + +/* 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 +#endif + +#include +#include +#include + +#include + +#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; +} diff --git a/tests/gpg/pgp-keylist.c b/tests/gpg/pgp-keylist.c index 2645f48..4978b8d 100644 --- a/tests/gpg/pgp-keylist.c +++ b/tests/gpg/pgp-keylist.c @@ -37,67 +37,6 @@ 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) { diff --git a/tests/gpg/t-support.h b/tests/gpg/t-support.h index 362b857..13475f2 100644 --- a/tests/gpg/t-support.h +++ b/tests/gpg/t-support.h @@ -48,6 +48,13 @@ 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); +} + -- 2.26.2