Add support for gpg --fetch-keys.

This commit is contained in:
Werner Koch 2009-06-16 15:42:37 +00:00
parent 3320cc1742
commit bebd9cbe29
14 changed files with 362 additions and 71 deletions

3
NEWS
View File

@ -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.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View 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>

View 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

View 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.

View 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);

View 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");

View 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;

View 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>

View 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>

View 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

View File

@ -37,13 +37,6 @@
static int verbose;
static const char *
nonnull (const char *s)
{
return s? s :"[none]";
}
static int
show_usage (int ex)
{

129
tests/gpg/pgp-import.c Normal file
View File

@ -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;
}

View File

@ -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)
{

View File

@ -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);
}