2002-12-04 Marcus Brinkmann <marcus@g10code.de>

* gpgme.h: Add prototype for gpgme_get_key.
	* key.c (gpgme_get_key): New function.
	* verify.c (gpgme_get_sig_key): Rewrite using gpgme_get_key.

	* gpgme.h: Add prototypes for new interfaces
	gpgme_key_sig_get_string_attr and gpgme_key_get_ulong_attr.
	(enum GpgmeAttr): New attribute GPGME_ATTR_SIG_CLASS.
	* gpgme.c (gpgme_set_keylist_mode): Allow GPGME_KEYLIST_MODE_SIGS.
	* key.h (struct certsig_s): New members ALGO, NAME_PART,
	EMAIL_PART, COMMENT_PART, NAME, SIG_STAT and SIG_CLASS.

	* conversion.c (_gpgme_decode_c_string): Add new parameter LEN.
	Use that to determine if allocation is desired or not.
	* util.h: Adjust prototype of _gpgme_decode_c_string.
	* keylist.c (keylist_colon_handler): Adjust caller of
	_gpgme_decode_c_string.

	* key.h (struct gpgme_key_s): New member last_uid.
	* key.c (_gpgme_key_append_name): Rewritten using
	_gpgme_decode_c_string and the last_uid pointer.
	(my_isdigit): Macro removed.
	(ALLOC_CHUNK): Likewise.
	* keylist.c (set_userid_flags): Use last_uid member of KEY.

	* context.h (struct user_id_s): New member last_certsig.
	* key.h: Add prototype for _gpgme_key_add_certsig.
	* key.c (_gpgme_key_add_certsig): New function.
	(set_user_id_part): Move function before _gpgme_key_add_certsig.
	(parse_user_id): Change first argument to SRC, add new arguments
	NAME, EMAIL and COMMENT.  Change code to use these arguments
	instead going through UID.  Move function before
	_gpgme_add_certsig.
	(parse_x509_user_id): Likewise.
	(_gpgme_key_append_name): Adjust arguments to parse_x509_user_id
	and parse_user_id invocation.
	(one_certsig_as_xml): New function.
	(one_uid_as_xml): Print signatures.
	* context.h (struct gpgme_context_s): New member TMP_UID.
	* keylist.c (keylist_colon_handler): Rewritten, implement "sig"
	record entries.

	* key.c (get_certsig): New function.
	(gpgme_key_sig_get_string_attr): Likewise.
	(gpgme_key_sig_get_ulong_attr): Likewise.

	* keylist.c: Include <ctype.h>.
	(my_isdigit): Macro removed.
	(set_mainkey_trust_info): Use isdigit, not my_isdigit.
	(set_userid_flags): Likewise.
	(set_subkey_trust_info): Likewise.
	(set_ownertrust): Likewise.
	(finish_key): Move function up a bit and remove prototype.

	* rungpg.c (gpg_keylist_ext): Correct precedence of signature
	listing mode.
	(gpg_keylist_ext): Implement signature listing mode.
This commit is contained in:
Marcus Brinkmann 2002-12-04 16:28:34 +00:00
parent 7394638cdb
commit 1b495c5140
12 changed files with 1202 additions and 771 deletions

11
NEWS
View File

@ -1,6 +1,12 @@
Noteworthy changes in version 0.4.0 (unreleased) Noteworthy changes in version 0.4.0 (unreleased)
------------------------------------------------ ------------------------------------------------
* Supports signatures of user IDs in keys via the new
GPGME_KEYLIST_MODE_SIGS keylist mode and the
gpgme_key_sig_get_string_attr and gpgme_key_sig_get_ulong_attr
interfaces. The XML info about a key also includes the signatures
if available.
* New data object interface, which is more flexible and transparent. * New data object interface, which is more flexible and transparent.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -23,6 +29,11 @@ gpgme_op_verify CHANGED: Take different data objects for
gpgme_op_verify_start CHANGED: See gpgme_op_verify. gpgme_op_verify_start CHANGED: See gpgme_op_verify.
gpgme_check_engine REMOVED: Deprecated since 0.3.0. gpgme_check_engine REMOVED: Deprecated since 0.3.0.
gpgme_op_genkey CHANGED: New parameter FPR. gpgme_op_genkey CHANGED: New parameter FPR.
GPGME_KEYLIST_MODE_SIGS NEW
gpgme_key_sig_get_string_attr NEW
gpgme_key_sig_get_ulong_attr NEW
gpgme_get_key NEW
GPGME_ATTR_SIG_CLASS NEW
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Noteworthy changes in version 0.3.13 (2002-11-20) Noteworthy changes in version 0.3.13 (2002-11-20)

View File

@ -1,3 +1,62 @@
2002-12-04 Marcus Brinkmann <marcus@g10code.de>
* gpgme.h: Add prototype for gpgme_get_key.
* key.c (gpgme_get_key): New function.
* verify.c (gpgme_get_sig_key): Rewrite using gpgme_get_key.
* gpgme.h: Add prototypes for new interfaces
gpgme_key_sig_get_string_attr and gpgme_key_get_ulong_attr.
(enum GpgmeAttr): New attribute GPGME_ATTR_SIG_CLASS.
* gpgme.c (gpgme_set_keylist_mode): Allow GPGME_KEYLIST_MODE_SIGS.
* key.h (struct certsig_s): New members ALGO, NAME_PART,
EMAIL_PART, COMMENT_PART, NAME, SIG_STAT and SIG_CLASS.
* conversion.c (_gpgme_decode_c_string): Add new parameter LEN.
Use that to determine if allocation is desired or not.
* util.h: Adjust prototype of _gpgme_decode_c_string.
* keylist.c (keylist_colon_handler): Adjust caller of
_gpgme_decode_c_string.
* key.h (struct gpgme_key_s): New member last_uid.
* key.c (_gpgme_key_append_name): Rewritten using
_gpgme_decode_c_string and the last_uid pointer.
(my_isdigit): Macro removed.
(ALLOC_CHUNK): Likewise.
* keylist.c (set_userid_flags): Use last_uid member of KEY.
* context.h (struct user_id_s): New member last_certsig.
* key.h: Add prototype for _gpgme_key_add_certsig.
* key.c (_gpgme_key_add_certsig): New function.
(set_user_id_part): Move function before _gpgme_key_add_certsig.
(parse_user_id): Change first argument to SRC, add new arguments
NAME, EMAIL and COMMENT. Change code to use these arguments
instead going through UID. Move function before
_gpgme_add_certsig.
(parse_x509_user_id): Likewise.
(_gpgme_key_append_name): Adjust arguments to parse_x509_user_id
and parse_user_id invocation.
(one_certsig_as_xml): New function.
(one_uid_as_xml): Print signatures.
* context.h (struct gpgme_context_s): New member TMP_UID.
* keylist.c (keylist_colon_handler): Rewritten, implement "sig"
record entries.
* key.c (get_certsig): New function.
(gpgme_key_sig_get_string_attr): Likewise.
(gpgme_key_sig_get_ulong_attr): Likewise.
* keylist.c: Include <ctype.h>.
(my_isdigit): Macro removed.
(set_mainkey_trust_info): Use isdigit, not my_isdigit.
(set_userid_flags): Likewise.
(set_subkey_trust_info): Likewise.
(set_ownertrust): Likewise.
(finish_key): Move function up a bit and remove prototype.
* rungpg.c (gpg_keylist_ext): Correct precedence of signature
listing mode.
(gpg_keylist_ext): Implement signature listing mode.
2002-11-25 Marcus Brinkmann <marcus@g10code.de> 2002-11-25 Marcus Brinkmann <marcus@g10code.de>
* rungpg.c (_gpgme_gpg_spawn): Do not set parent fds to -1. * rungpg.c (_gpgme_gpg_spawn): Do not set parent fds to -1.

View File

@ -1,23 +1,22 @@
/* context.h /* context.h
* Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2000 Werner Koch (dd9jn)
* Copyright (C) 2001, 2002 g10 Code GmbH Copyright (C) 2001, 2002 g10 Code GmbH
*
* This file is part of GPGME. This file is part of GPGME.
*
* GPGME is free software; you can redistribute it and/or modify GPGME is free software; you can redistribute it and/or modify it
* it under the terms of the GNU General Public License as published by under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version. (at your option) any later version.
*
* GPGME is distributed in the hope that it will be useful, GPGME is distributed in the hope that it will be useful, but
* but WITHOUT ANY WARRANTY; without even the implied warranty of WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. General Public License for more details.
*
* You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software along with GPGME; if not, write to the Free Software Foundation,
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
*/
#ifndef CONTEXT_H #ifndef CONTEXT_H
#define CONTEXT_H #define CONTEXT_H
@ -93,6 +92,7 @@ struct gpgme_context_s
/* Used by keylist.c. */ /* Used by keylist.c. */
GpgmeKey tmp_key; GpgmeKey tmp_key;
struct user_id_s *tmp_uid;
/* Something new is available. */ /* Something new is available. */
volatile int key_cond; volatile int key_cond;
struct key_queue_item_s *key_queue; struct key_queue_item_s *key_queue;
@ -124,6 +124,7 @@ struct user_id_s
unsigned int invalid : 1; unsigned int invalid : 1;
GpgmeValidity validity; GpgmeValidity validity;
struct certsig_s *certsigs; struct certsig_s *certsigs;
struct certsig_s *last_certsig;
const char *name_part; /* All 3 point into strings behind name */ const char *name_part; /* All 3 point into strings behind name */
const char *email_part; /* or to read-only strings. */ const char *email_part; /* or to read-only strings. */
const char *comment_part; const char *comment_part;

View File

@ -57,11 +57,21 @@ _gpgme_hextobyte (const byte *str)
} }
/* Decode the C formatted string SRC and store the result in the
buffer *DESTP which is LEN bytes long. If LEN is zero, then a
large enough buffer is allocated with malloc and *DESTP is set to
the result. Currently, LEN is only used to specify if allocation
is desired or not, the caller is expected to make sure that *DESTP
is large enough if LEN is not zero. */
GpgmeError GpgmeError
_gpgme_decode_c_string (const char *src, char **destp) _gpgme_decode_c_string (const char *src, char **destp, int len)
{ {
char *dest; char *dest;
if (len)
dest = *destp;
else
{
/* We can malloc a buffer of the same length, because the converted /* We can malloc a buffer of the same length, because the converted
string will never be larger. */ string will never be larger. */
dest = malloc (strlen (src) + 1); dest = malloc (strlen (src) + 1);
@ -69,6 +79,7 @@ _gpgme_decode_c_string (const char *src, char **destp)
return mk_error (Out_Of_Core); return mk_error (Out_Of_Core);
*destp = dest; *destp = dest;
}
while (*src) while (*src)
{ {

View File

@ -1,25 +1,26 @@
/* gpgme.c - GnuPG Made Easy /* gpgme.c - GnuPG Made Easy
* Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2000 Werner Koch (dd9jn)
* Copyright (C) 2001, 2002 g10 Code GmbH Copyright (C) 2001, 2002 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 General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
This file is part of GPGME.
GPGME is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 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
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GPGME; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#if HAVE_CONFIG_H
#include <config.h> #include <config.h>
#endif
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -348,7 +349,8 @@ gpgme_set_keylist_mode (GpgmeCtx ctx, int mode)
return mk_error (Invalid_Value); return mk_error (Invalid_Value);
if (!((mode & GPGME_KEYLIST_MODE_LOCAL) if (!((mode & GPGME_KEYLIST_MODE_LOCAL)
|| (mode & GPGME_KEYLIST_MODE_EXTERN))) || (mode & GPGME_KEYLIST_MODE_EXTERN)
|| (mode & GPGME_KEYLIST_MODE_SIGS)))
return mk_error (Invalid_Value); return mk_error (Invalid_Value);
ctx->keylist_mode = mode; ctx->keylist_mode = mode;

View File

@ -196,7 +196,8 @@ typedef enum
GPGME_ATTR_CHAINID = 28, GPGME_ATTR_CHAINID = 28,
GPGME_ATTR_SIG_STATUS = 29, GPGME_ATTR_SIG_STATUS = 29,
GPGME_ATTR_ERRTOK = 30, GPGME_ATTR_ERRTOK = 30,
GPGME_ATTR_SIG_SUMMARY = 31 GPGME_ATTR_SIG_SUMMARY = 31,
GPGME_ATTR_SIG_CLASS = 32
} }
GpgmeAttr; GpgmeAttr;
@ -611,6 +612,13 @@ GpgmeError gpgme_data_rewind (GpgmeData dh);
/* Key and trust functions. */ /* Key and trust functions. */
/* Get the key with the fingerprint FPR from the key cache or from the
crypto backend. If FORCE_UPDATE is true, force a refresh of the
key from the crypto backend and replace the key in the cache, if
any. If SECRET is true, get the secret key. */
GpgmeError gpgme_get_key (GpgmeCtx ctx, const char *fpr, GpgmeKey *r_key,
int secret, int force_update);
/* Acquire a reference to KEY. */ /* Acquire a reference to KEY. */
void gpgme_key_ref (GpgmeKey key); void gpgme_key_ref (GpgmeKey key);
@ -635,6 +643,21 @@ const char *gpgme_key_get_string_attr (GpgmeKey key, GpgmeAttr what,
unsigned long gpgme_key_get_ulong_attr (GpgmeKey key, GpgmeAttr what, unsigned long gpgme_key_get_ulong_attr (GpgmeKey key, GpgmeAttr what,
const void *reserved, int idx); const void *reserved, int idx);
/* Return the value of the attribute WHAT of a signature on user ID
UID_IDX in KEY, which has to be representable by a string. IDX
specifies the signature. */
const char *gpgme_key_sig_get_string_attr (GpgmeKey key, int uid_idx,
GpgmeAttr what,
const void *reserved, int idx);
/* Return the value of the attribute WHAT of a signature on user ID
UID_IDX in KEY, which has to be representable by an unsigned
integer string. IDX specifies the signature. */
unsigned long gpgme_key_sig_get_ulong_attr (GpgmeKey key, int uid_idx,
GpgmeAttr what,
const void *reserved, int idx);
/* Release the trust item ITEM. */ /* Release the trust item ITEM. */
void gpgme_trust_item_release (GpgmeTrustItem item); void gpgme_trust_item_release (GpgmeTrustItem item);

View File

@ -1,25 +1,26 @@
/* key.c - Key and keyList objects /* key.c - Key objects.
* Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2000 Werner Koch (dd9jn)
* Copyright (C) 2001, 2002 g10 Code GmbH Copyright (C) 2001, 2002 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 General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
This file is part of GPGME.
GPGME is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 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
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GPGME; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#if HAVE_CONFIG_H
#include <config.h> #include <config.h>
#endif
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -31,13 +32,11 @@
#include "key.h" #include "key.h"
#include "sema.h" #include "sema.h"
#define ALLOC_CHUNK 1024
#define my_isdigit(a) ((a) >='0' && (a) <= '9')
#if SIZEOF_UNSIGNED_INT < 4 #if SIZEOF_UNSIGNED_INT < 4
#error unsigned int too short to be used as a hash value #error unsigned int too short to be used as a hash value
#endif #endif
struct key_cache_item_s struct key_cache_item_s
{ {
struct key_cache_item_s *next; struct key_cache_item_s *next;
@ -82,6 +81,7 @@ hash_key (const char *fpr, unsigned int *rhash)
return 0; return 0;
} }
void void
_gpgme_key_cache_init (void) _gpgme_key_cache_init (void)
{ {
@ -251,7 +251,7 @@ _gpgme_key_cache_get (const char *fpr)
return NULL; return NULL;
} }
static const char * static const char *
pkalgo_to_string (int algo) pkalgo_to_string (int algo)
{ {
@ -288,12 +288,14 @@ key_new (GpgmeKey *r_key, int secret)
return 0; return 0;
} }
GpgmeError GpgmeError
_gpgme_key_new (GpgmeKey *r_key) _gpgme_key_new (GpgmeKey *r_key)
{ {
return key_new (r_key, 0); return key_new (r_key, 0);
} }
GpgmeError GpgmeError
_gpgme_key_new_secret (GpgmeKey *r_key) _gpgme_key_new_secret (GpgmeKey *r_key)
{ {
@ -317,7 +319,7 @@ gpgme_key_ref (GpgmeKey key)
UNLOCK (key_ref_lock); UNLOCK (key_ref_lock);
} }
static struct subkey_s * static struct subkey_s *
add_subkey (GpgmeKey key, int secret) add_subkey (GpgmeKey key, int secret)
{ {
@ -354,7 +356,173 @@ _gpgme_key_add_secret_subkey (GpgmeKey key)
return add_subkey (key, 1); return add_subkey (key, 1);
} }
static char *
set_user_id_part (char *tail, const char *buf, size_t len)
{
while (len && (buf[len - 1] == ' ' || buf[len - 1] == '\t'))
len--;
for (; len; len--)
*tail++ = *buf++;
*tail++ = 0;
return tail;
}
static void
parse_user_id (const char *src, const char **name, const char **email,
const char **comment, char *tail)
{
const char *start = NULL;
int in_name = 0;
int in_email = 0;
int in_comment = 0;
while (*src)
{
if (in_email)
{
if (*src == '<')
/* Not legal but anyway. */
in_email++;
else if (*src == '>')
{
if (!--in_email && !*email)
{
*email = tail;
tail = set_user_id_part (tail, start, src - start);
}
}
}
else if (in_comment)
{
if (*src == '(')
in_comment++;
else if (*src == ')')
{
if (!--in_comment && !*comment)
{
*comment = tail;
tail = set_user_id_part (tail, start, src - start);
}
}
}
else if (*src == '<')
{
if (in_name)
{
if (!*name)
{
*name = tail;
tail = set_user_id_part (tail, start, src - start);
}
in_name = 0;
}
in_email = 1;
start = src + 1;
}
else if (*src == '(')
{
if (in_name)
{
if (!*name)
{
*name = tail;
tail = set_user_id_part (tail, start, src - start);
}
in_name = 0;
}
in_comment = 1;
start = src + 1;
}
else if (!in_name && *src != ' ' && *src != '\t')
{
in_name = 1;
start = src;
}
src++;
}
if (in_name)
{
if (!*name)
{
*name = tail;
tail = set_user_id_part (tail, start, src - start);
}
}
/* Let unused parts point to an EOS. */
tail--;
if (!*name)
*name = tail;
if (!*email)
*email = tail;
if (!*comment)
*comment = tail;
}
static void
parse_x509_user_id (const char *src, const char **name, const char **email,
const char **comment, char *tail)
{
if (*src == '<' && src[strlen (src) - 1] == '>')
*email = src;
/* Let unused parts point to an EOS. */
tail--;
if (!*name)
*name = tail;
if (!*email)
*email = tail;
if (!*comment)
*comment = tail;
}
struct certsig_s *
_gpgme_key_add_certsig (GpgmeKey key, char *src)
{
int src_len = src ? strlen (src) : 0;
struct user_id_s *uid;
struct certsig_s *certsig;
assert (key); /* XXX */
uid = key->last_uid;
assert (uid); /* XXX */
/* We can malloc a buffer of the same length, because the converted
string will never be larger. Actually we allocate it twice the
size, so that we are able to store the parsed stuff there too. */
certsig = calloc (1, sizeof (*certsig) + 2 * src_len + 3);
if (!certsig)
return NULL;
if (src)
{
char *dst = certsig->name;
_gpgme_decode_c_string (src, &dst, src_len + 1);
dst += src_len + 1;
if (key->x509)
parse_x509_user_id (src, &certsig->name_part, &certsig->email_part,
&certsig->comment_part, dst);
else
parse_user_id (src, &certsig->name_part, &certsig->email_part,
&certsig->comment_part, dst);
}
if (!uid->certsigs)
uid->certsigs = certsig;
if (uid->last_certsig)
uid->last_certsig->next = certsig;
uid->last_certsig = certsig;
return certsig;
}
/** /**
* gpgme_key_release: * gpgme_key_release:
* @key: Key Object or NULL * @key: Key Object or NULL
@ -406,6 +574,7 @@ gpgme_key_release (GpgmeKey key)
free (key); free (key);
} }
/** /**
* gpgme_key_unref: * gpgme_key_unref:
* @key: Key Object * @key: Key Object
@ -418,237 +587,41 @@ gpgme_key_unref (GpgmeKey key)
gpgme_key_release (key); gpgme_key_release (key);
} }
static char * /* Take a name from the --with-colon listing, remove certain escape
set_user_id_part (char *tail, const char *buf, size_t len) sequences sequences and put it into the list of UIDs. */
{
while (len && (buf[len-1] == ' ' || buf[len-1] == '\t'))
len--;
for (; len; len--)
*tail++ = *buf++;
*tail++ = 0;
return tail;
}
static void
parse_user_id (struct user_id_s *uid, char *tail)
{
const char *s, *start=NULL;
int in_name = 0;
int in_email = 0;
int in_comment = 0;
for (s = uid->name; *s; s++)
{
if (in_email)
{
if (*s == '<')
/* Not legal but anyway. */
in_email++;
else if (*s == '>')
{
if (!--in_email)
{
if (!uid->email_part)
{
uid->email_part = tail;
tail = set_user_id_part ( tail, start, s-start );
}
}
}
}
else if (in_comment)
{
if (*s == '(')
in_comment++;
else if (*s== ')')
{
if (!--in_comment)
{
if (!uid->comment_part)
{
uid->comment_part = tail;
tail = set_user_id_part ( tail, start, s-start );
}
}
}
}
else if (*s == '<')
{
if (in_name)
{
if (!uid->name_part)
{
uid->name_part = tail;
tail = set_user_id_part (tail, start, s-start);
}
in_name = 0;
}
in_email = 1;
start = s+1;
}
else if (*s == '(')
{
if (in_name)
{
if (!uid->name_part)
{
uid->name_part = tail;
tail = set_user_id_part (tail, start, s-start );
}
in_name = 0;
}
in_comment = 1;
start = s+1;
}
else if (!in_name && *s != ' ' && *s != '\t')
{
in_name = 1;
start = s;
}
}
if (in_name)
{
if (!uid->name_part)
{
uid->name_part = tail;
tail = set_user_id_part (tail, start, s-start);
}
}
/* Let unused parts point to an EOS. */
tail--;
if (!uid->name_part)
uid->name_part = tail;
if (!uid->email_part)
uid->email_part = tail;
if (!uid->comment_part)
uid->comment_part = tail;
}
static void
parse_x509_user_id (struct user_id_s *uid, char *tail)
{
const char *s;
s=uid->name;
if (*s == '<' && s[strlen (s) - 1] == '>')
uid->email_part = s;
/* Let unused parts point to an EOS. */
tail--;
if (!uid->name_part)
uid->name_part = tail;
if (!uid->email_part)
uid->email_part = tail;
if (!uid->comment_part)
uid->comment_part = tail;
}
/*
* Take a name from the --with-colon listing, remove certain escape sequences
* sequences and put it into the list of UIDs
*/
GpgmeError GpgmeError
_gpgme_key_append_name (GpgmeKey key, const char *s) _gpgme_key_append_name (GpgmeKey key, const char *src)
{ {
struct user_id_s *uid; struct user_id_s *uid;
char *d; char *dst;
int src_len = strlen (src);
assert (key); assert (key);
/* We can malloc a buffer of the same length, because the converted /* We can malloc a buffer of the same length, because the converted
string will never be larger. Actually we allocate it twice the string will never be larger. Actually we allocate it twice the
size, so that we are able to store the parsed stuff there too. */ size, so that we are able to store the parsed stuff there too. */
uid = malloc (sizeof *uid + 2*strlen (s)+3); uid = malloc (sizeof (*uid) + 2 * src_len + 3);
if (!uid) if (!uid)
return mk_error (Out_Of_Core); return mk_error (Out_Of_Core);
memset (uid, 0, sizeof *uid); memset (uid, 0, sizeof *uid);
d = uid->name;
while (*s) dst = uid->name;
{ _gpgme_decode_c_string (src, &dst, src_len + 1);
if (*s != '\\')
*d++ = *s++; dst += src_len + 1;
else if (s[1] == '\\')
{
s++;
*d++ = *s++;
}
else if (s[1] == 'n')
{
s += 2;
*d++ = '\n';
}
else if (s[1] == 'r')
{
s += 2;
*d++ = '\r';
}
else if (s[1] == 'v')
{
s += 2;
*d++ = '\v';
}
else if (s[1] == 'b')
{
s += 2;
*d++ = '\b';
}
else if (s[1] == '0')
{
/* Hmmm: no way to express this */
s += 2;
*d++ = '\\';
*d++ = '\0';
}
else if (s[1] == 'x' && isxdigit (s[2]) && isxdigit (s[3]))
{
int val = _gpgme_hextobyte (&s[2]);
if (val == -1)
{
/* Should not happen. */
*d++ = *s++;
*d++ = *s++;
*d++ = *s++;
*d++ = *s++;
}
else
{
if (!val)
{
*d++ = '\\';
*d++ = '\0';
}
else
*(byte*)d++ = val;
s += 4;
}
}
else
{
/* should not happen */
s++;
*d++ = '\\';
*d++ = *s++;
}
}
*d++ = 0;
if (key->x509) if (key->x509)
parse_x509_user_id (uid, d); parse_x509_user_id (src, &uid->name_part, &uid->email_part,
&uid->comment_part, dst);
else else
parse_user_id (uid, d); parse_user_id (src, &uid->name_part, &uid->email_part,
&uid->comment_part, dst);
if (key->uids) if (!key->uids)
{
struct user_id_s *u = key->uids;
while (u->next)
u = u->next;
u->next = uid;
}
else
key->uids = uid; key->uids = uid;
if (key->last_uid)
key->last_uid->next = uid;
key->last_uid = uid;
return 0; return 0;
} }
@ -662,6 +635,7 @@ add_otag (GpgmeData d, const char *tag)
_gpgme_data_append_string (d, ">"); _gpgme_data_append_string (d, ">");
} }
static void static void
add_ctag (GpgmeData d, const char *tag) add_ctag (GpgmeData d, const char *tag)
{ {
@ -670,6 +644,7 @@ add_ctag (GpgmeData d, const char *tag)
_gpgme_data_append_string (d, ">\n"); _gpgme_data_append_string (d, ">\n");
} }
static void static void
add_tag_and_string (GpgmeData d, const char *tag, const char *string) add_tag_and_string (GpgmeData d, const char *tag, const char *string)
{ {
@ -678,6 +653,7 @@ add_tag_and_string (GpgmeData d, const char *tag, const char *string)
add_ctag (d, tag); add_ctag (d, tag);
} }
static void static void
add_tag_and_uint (GpgmeData d, const char *tag, unsigned int val) add_tag_and_uint (GpgmeData d, const char *tag, unsigned int val)
{ {
@ -686,6 +662,7 @@ add_tag_and_uint (GpgmeData d, const char *tag, unsigned int val)
add_tag_and_string (d, tag, buf); add_tag_and_string (d, tag, buf);
} }
static void static void
add_tag_and_time (GpgmeData d, const char *tag, time_t val) add_tag_and_time (GpgmeData d, const char *tag, time_t val)
{ {
@ -697,22 +674,55 @@ add_tag_and_time (GpgmeData d, const char *tag, time_t val)
add_tag_and_string (d, tag, buf); add_tag_and_string (d, tag, buf);
} }
static void static void
one_uid_as_xml (GpgmeData d, struct user_id_s *u) one_certsig_as_xml (GpgmeData data, struct certsig_s *certsig)
{ {
_gpgme_data_append_string (d, " <userid>\n"); _gpgme_data_append_string (data, " <signature>\n");
if (u->invalid) if (certsig->flags.invalid)
_gpgme_data_append_string (d, " <invalid/>\n"); _gpgme_data_append_string (data, " <invalid/>\n");
if (u->revoked) if (certsig->flags.revoked)
_gpgme_data_append_string (d, " <revoked/>\n"); _gpgme_data_append_string (data, " <revoked/>\n");
add_tag_and_string (d, "raw", u->name); if (certsig->flags.expired)
if (*u->name_part) _gpgme_data_append_string (data, " <expired/>\n");
add_tag_and_string (d, "name", u->name_part); add_tag_and_string (data, "keyid", certsig->keyid);
if (*u->email_part) add_tag_and_uint (data, "algo", certsig->algo);
add_tag_and_string (d, "email", u->email_part); add_tag_and_time (data, "created", certsig->timestamp);
if (*u->comment_part) add_tag_and_time (data, "expire", certsig->expires_at);
add_tag_and_string (d, "comment", u->comment_part); if (*certsig->name)
_gpgme_data_append_string (d, " </userid>\n"); add_tag_and_string (data, "raw", certsig->name);
if (*certsig->name_part)
add_tag_and_string (data, "name", certsig->name_part);
if (*certsig->email_part)
add_tag_and_string (data, "email", certsig->email_part);
if (*certsig->comment_part)
add_tag_and_string (data, "comment", certsig->comment_part);
_gpgme_data_append_string (data, " </signature>\n");
}
static void
one_uid_as_xml (GpgmeData data, struct user_id_s *uid)
{
struct certsig_s *certsig;
_gpgme_data_append_string (data, " <userid>\n");
if (uid->invalid)
_gpgme_data_append_string (data, " <invalid/>\n");
if (uid->revoked)
_gpgme_data_append_string (data, " <revoked/>\n");
add_tag_and_string (data, "raw", uid->name);
if (*uid->name_part)
add_tag_and_string (data, "name", uid->name_part);
if (*uid->email_part)
add_tag_and_string (data, "email", uid->email_part);
if (*uid->comment_part)
add_tag_and_string (data, "comment", uid->comment_part);
/* Now the signatures. */
for (certsig = uid->certsigs; certsig; certsig = certsig->next)
one_certsig_as_xml (data, certsig);
_gpgme_data_append_string (data, " </userid>\n");
} }
@ -972,6 +982,7 @@ gpgme_key_get_string_attr (GpgmeKey key, GpgmeAttr what,
case GPGME_ATTR_SIG_STATUS: case GPGME_ATTR_SIG_STATUS:
case GPGME_ATTR_SIG_SUMMARY: case GPGME_ATTR_SIG_SUMMARY:
case GPGME_ATTR_ERRTOK: case GPGME_ATTR_ERRTOK:
case GPGME_ATTR_SIG_CLASS:
/* Not of any use here. */ /* Not of any use here. */
break; break;
} }
@ -1102,3 +1113,154 @@ gpgme_key_get_ulong_attr (GpgmeKey key, GpgmeAttr what,
return val; return val;
} }
static struct certsig_s *
get_certsig (GpgmeKey key, int uid_idx, int idx)
{
struct user_id_s *uid;
struct certsig_s *certsig;
if (!key || uid_idx < 0 || idx < 0)
return NULL;
uid = key->uids;
while (uid && uid_idx > 0)
{
uid = uid->next;
uid_idx--;
}
if (!uid)
return NULL;
certsig = uid->certsigs;
while (certsig && idx > 0)
{
certsig = certsig->next;
idx--;
}
return certsig;
}
const char *
gpgme_key_sig_get_string_attr (GpgmeKey key, int uid_idx, GpgmeAttr what,
const void *reserved, int idx)
{
struct certsig_s *certsig = get_certsig (key, uid_idx, idx);
if (!certsig || reserved)
return NULL;
switch (what)
{
case GPGME_ATTR_KEYID:
return certsig->keyid;
case GPGME_ATTR_ALGO:
return pkalgo_to_string (certsig->algo);
case GPGME_ATTR_USERID:
return certsig->name;
case GPGME_ATTR_NAME:
return certsig->name_part;
case GPGME_ATTR_EMAIL:
return certsig->email_part;
case GPGME_ATTR_COMMENT:
return certsig->comment_part;
default:
return NULL;
}
}
unsigned long
gpgme_key_sig_get_ulong_attr (GpgmeKey key, int uid_idx, GpgmeAttr what,
const void *reserved, int idx)
{
struct certsig_s *certsig = get_certsig (key, uid_idx, idx);
if (!certsig || reserved)
return 0;
switch (what)
{
case GPGME_ATTR_ALGO:
return (unsigned long) certsig->algo;
case GPGME_ATTR_CREATED:
return certsig->timestamp < 0 ? 0L : (unsigned long) certsig->timestamp;
case GPGME_ATTR_EXPIRE:
return certsig->expires_at < 0 ? 0L : (unsigned long) certsig->expires_at;
case GPGME_ATTR_KEY_REVOKED:
return certsig->flags.revoked;
case GPGME_ATTR_KEY_INVALID:
return certsig->flags.invalid;
case GPGME_ATTR_KEY_EXPIRED:
return certsig->flags.expired;
case GPGME_ATTR_SIG_CLASS:
return certsig->sig_class;
case GPGME_ATTR_SIG_STATUS:
return certsig->sig_stat;
default:
return 0;
}
}
/* Get the key with the fingerprint FPR from the key cache or from the
crypto backend. If FORCE_UPDATE is true, force a refresh of the
key from the crypto backend and replace the key in the cache, if
any. If SECRET is true, get the secret key. */
GpgmeError
gpgme_get_key (GpgmeCtx ctx, const char *fpr, GpgmeKey *r_key,
int secret, int force_update)
{
GpgmeCtx listctx;
GpgmeError err;
if (!ctx || !r_key)
return mk_error (Invalid_Value);
if (ctx->pending)
return mk_error (Busy);
if (strlen (fpr) < 16) /* We have at least a key ID. */
return mk_error (Invalid_Key);
if (!force_update)
{
*r_key = _gpgme_key_cache_get (fpr);
if (*r_key)
{
/* If the primary UID (if available) has no signatures, and
we are in the signature listing keylist mode, then try to
update the key below before returning. */
if (!((ctx->keylist_mode & GPGME_KEYLIST_MODE_SIGS)
&& (*r_key)->uids && !(*r_key)->uids->certsigs))
return 0;
}
}
/* Fixme: This can be optimized by keeping an internal context
used for such key listings. */
err = gpgme_new (&listctx);
if (err)
return err;
gpgme_set_protocol (listctx, gpgme_get_protocol (ctx));
gpgme_set_keylist_mode (listctx, ctx->keylist_mode);
err = gpgme_op_keylist_start (listctx, fpr, secret);
if (!err)
err = gpgme_op_keylist_next (listctx, r_key);
gpgme_release (listctx);
return err;
}

View File

@ -1,23 +1,22 @@
/* key.h /* key.h - Key handling interface.
* Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2000 Werner Koch (dd9jn)
* Copyright (C) 2001 g10 Code GmbH Copyright (C) 2001, 2002 g10 Code GmbH
*
* This file is part of GPGME. This file is part of GPGME.
*
* GPGME is free software; you can redistribute it and/or modify GPGME is free software; you can redistribute it and/or modify it
* it under the terms of the GNU General Public License as published by under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version. (at your option) any later version.
*
* GPGME is distributed in the hope that it will be useful, GPGME is distributed in the hope that it will be useful, but
* but WITHOUT ANY WARRANTY; without even the implied warranty of WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. General Public License for more details.
*
* You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software along with GPGME; if not, write to the Free Software Foundation,
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
*/
#ifndef KEY_H #ifndef KEY_H
#define KEY_H #define KEY_H
@ -26,22 +25,36 @@
#include "types.h" #include "types.h"
#include "context.h" #include "context.h"
struct certsig_s {
struct certsig_s
{
struct certsig_s *next; struct certsig_s *next;
struct { struct
{
unsigned int revoked : 1; unsigned int revoked : 1;
unsigned int expired : 1; unsigned int expired : 1;
unsigned int invalid : 1; unsigned int invalid : 1;
unsigned int exportable : 1;
} flags; } flags;
unsigned int algo;
char keyid[16 + 1]; char keyid[16 + 1];
time_t timestamp; /* -1 for invalid, 0 for not available */ time_t timestamp; /* -1 for invalid, 0 for not available. */
time_t expires_at; /* 0 for does not expires */ time_t expires_at; /* 0 for no expiration. */
GpgmeSigStat sig_stat;
unsigned int sig_class;
const char *name_part; /* All 3 point into strings behind name */
const char *email_part; /* or to read-only strings. */
const char *comment_part;
char name[1];
}; };
struct subkey_s {
struct subkey_s
{
struct subkey_s *next; struct subkey_s *next;
unsigned int secret:1; unsigned int secret:1;
struct { struct
{
unsigned int revoked : 1; unsigned int revoked : 1;
unsigned int expired : 1; unsigned int expired : 1;
unsigned int disabled : 1; unsigned int disabled : 1;
@ -53,13 +66,16 @@ struct subkey_s {
unsigned int key_algo; unsigned int key_algo;
unsigned int key_len; unsigned int key_len;
char keyid[16 + 1]; char keyid[16 + 1];
char *fingerprint; /* malloced hex digits */ char *fingerprint; /* Malloced hex digits. */
time_t timestamp; /* -1 for invalid, 0 for not available */ time_t timestamp; /* -1 for invalid, 0 for not available. */
time_t expires_at; /* 0 for does not expires */ time_t expires_at; /* 0 for does not expires. */
}; };
struct gpgme_key_s {
struct { struct gpgme_key_s
{
struct
{
unsigned int revoked : 1; unsigned int revoked : 1;
unsigned int expired : 1; unsigned int expired : 1;
unsigned int disabled : 1; unsigned int disabled : 1;
@ -71,23 +87,24 @@ struct gpgme_key_s {
unsigned int ref_count; unsigned int ref_count;
unsigned int secret : 1; unsigned int secret : 1;
unsigned int x509 : 1; unsigned int x509 : 1;
char *issuer_serial; /* malloced string used only with X.509 */ char *issuer_serial; /* Malloced string used only with X.509. */
char *issuer_name; /* ditto */ char *issuer_name; /* Ditto. */
char *chain_id; /* ditto */ char *chain_id; /* Ditto. */
GpgmeValidity otrust; /* only used with OpenPGP */ GpgmeValidity otrust; /* Only used with OpenPGP. */
struct subkey_s keys; struct subkey_s keys;
struct user_id_s *uids; struct user_id_s *uids;
struct user_id_s *last_uid;
}; };
void _gpgme_key_cache_init (void); void _gpgme_key_cache_init (void);
void _gpgme_key_cache_add (GpgmeKey key); void _gpgme_key_cache_add (GpgmeKey key);
GpgmeKey _gpgme_key_cache_get (const char *fpr); GpgmeKey _gpgme_key_cache_get (const char *fpr);
struct certsig_s *_gpgme_key_add_certsig (GpgmeKey key, char *src);
struct subkey_s *_gpgme_key_add_subkey (GpgmeKey key); struct subkey_s *_gpgme_key_add_subkey (GpgmeKey key);
struct subkey_s *_gpgme_key_add_secret_subkey (GpgmeKey key); struct subkey_s *_gpgme_key_add_secret_subkey (GpgmeKey key);
GpgmeError _gpgme_key_append_name ( GpgmeKey key, const char *s ); GpgmeError _gpgme_key_append_name (GpgmeKey key, const char *str);
#endif /* KEY_H */ #endif /* KEY_H */

View File

@ -1,46 +1,45 @@
/* keylist.c - key listing /* keylist.c - Listing keys.
* Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2000 Werner Koch (dd9jn)
* Copyright (C) 2001, 2002 g10 Code GmbH Copyright (C) 2001, 2002 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 General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
This file is part of GPGME.
GPGME is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 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
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GPGME; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#if HAVE_CONFIG_H
#include <config.h> #include <config.h>
#endif
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <assert.h> #include <assert.h>
#include <ctype.h>
#include "util.h" #include "util.h"
#include "context.h" #include "context.h"
#include "ops.h" #include "ops.h"
#include "key.h" #include "key.h"
#define my_isdigit(a) ( (a) >='0' && (a) <= '9' )
struct keylist_result_s struct keylist_result_s
{ {
int truncated; int truncated;
GpgmeData xmlinfo; GpgmeData xmlinfo;
}; };
static void finish_key ( GpgmeCtx ctx );
void void
_gpgme_release_keylist_result (KeylistResult result) _gpgme_release_keylist_result (KeylistResult result)
@ -50,6 +49,7 @@ _gpgme_release_keylist_result (KeylistResult result)
free (result); free (result);
} }
/* Append some XML info. args is currently ignore but we might want /* Append some XML info. args is currently ignore but we might want
to add more information in the future (like source of the to add more information in the future (like source of the
keylisting. With args of NULL the XML structure is closed. */ keylisting. With args of NULL the XML structure is closed. */
@ -78,15 +78,11 @@ append_xml_keylistinfo (GpgmeData *rdh, char *args)
return; return;
} }
_gpgme_data_append_string (dh, _gpgme_data_append_string (dh, " <keylisting>\n <truncated/>\n");
" <keylisting>\n"
" <truncated/>\n"
);
} }
static void static void
keylist_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args) keylist_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args)
{ {
@ -117,124 +113,218 @@ keylist_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args)
} }
} }
static time_t static time_t
parse_timestamp (char *p) parse_timestamp (char *timestamp)
{ {
if (!*p) if (!*timestamp)
return 0; return 0;
return (time_t)strtoul (p, NULL, 10); return (time_t) strtoul (timestamp, NULL, 10);
} }
static void static void
set_mainkey_trust_info (GpgmeKey key, const char *s) set_mainkey_trust_info (GpgmeKey key, const char *src)
{ {
/* Look at letters and stop at the first digit. */ /* Look at letters and stop at the first digit. */
for (; *s && !my_isdigit (*s); s++) while (*src && !isdigit (*src))
{ {
switch (*s) switch (*src)
{ {
case 'e': key->keys.flags.expired = 1; break; case 'e':
case 'r': key->keys.flags.revoked = 1; break; key->keys.flags.expired = 1;
case 'd': key->keys.flags.disabled = 1; break; break;
case 'i': key->keys.flags.invalid = 1; break;
case 'r':
key->keys.flags.revoked = 1;
break;
case 'd':
key->keys.flags.disabled = 1;
break;
case 'i':
key->keys.flags.invalid = 1;
break;
} }
src++;
} }
} }
static void static void
set_userid_flags (GpgmeKey key, const char *s) set_userid_flags (GpgmeKey key, const char *src)
{ {
struct user_id_s *u = key->uids; struct user_id_s *uid = key->last_uid;
assert (u);
while (u->next)
u = u->next;
assert (uid);
/* Look at letters and stop at the first digit. */ /* Look at letters and stop at the first digit. */
for (; *s && !my_isdigit (*s); s++) while (*src && !isdigit (*src))
{ {
switch (*s) switch (*src)
{ {
case 'r': u->revoked = 1; break; case 'r':
case 'i': u->invalid = 1; break; uid->revoked = 1;
break;
case 'n': u->validity = GPGME_VALIDITY_NEVER; break; case 'i':
case 'm': u->validity = GPGME_VALIDITY_MARGINAL; break; uid->invalid = 1;
case 'f': u->validity = GPGME_VALIDITY_FULL; break; break;
case 'u': u->validity = GPGME_VALIDITY_ULTIMATE; break;
case 'n':
uid->validity = GPGME_VALIDITY_NEVER;
break;
case 'm':
uid->validity = GPGME_VALIDITY_MARGINAL;
break;
case 'f':
uid->validity = GPGME_VALIDITY_FULL;
break;
case 'u':
uid->validity = GPGME_VALIDITY_ULTIMATE;
break;
} }
src++;
} }
} }
static void static void
set_subkey_trust_info (struct subkey_s *k, const char *s) set_subkey_trust_info (struct subkey_s *subkey, const char *src)
{ {
/* Look at letters and stop at the first digit. */ /* Look at letters and stop at the first digit. */
for (; *s && !my_isdigit (*s); s++) while (*src && !isdigit (*src))
{ {
switch (*s) switch (*src)
{ {
case 'e': k->flags.expired = 1; break; case 'e':
case 'r': k->flags.revoked = 1; break; subkey->flags.expired = 1;
case 'd': k->flags.disabled = 1; break; break;
case 'i': k->flags.invalid = 1; break;
case 'r':
subkey->flags.revoked = 1;
break;
case 'd':
subkey->flags.disabled = 1;
break;
case 'i':
subkey->flags.invalid = 1;
break;
} }
src++;
} }
} }
static void static void
set_mainkey_capability (GpgmeKey key, const char *s) set_mainkey_capability (GpgmeKey key, const char *src)
{ {
for (; *s ; s++) while (*src)
{ {
switch (*s) switch (*src)
{ {
case 'e': key->keys.flags.can_encrypt = 1; break; case 'e':
case 's': key->keys.flags.can_sign = 1; break; key->keys.flags.can_encrypt = 1;
case 'c': key->keys.flags.can_certify = 1; break; break;
case 'E': key->gloflags.can_encrypt = 1; break;
case 'S': key->gloflags.can_sign = 1; break; case 's':
case 'C': key->gloflags.can_certify = 1; break; key->keys.flags.can_sign = 1;
break;
case 'c':
key->keys.flags.can_certify = 1;
break;
case 'E':
key->gloflags.can_encrypt = 1;
break;
case 'S':
key->gloflags.can_sign = 1;
break;
case 'C':
key->gloflags.can_certify = 1;
break;
} }
src++;
} }
} }
static void static void
set_subkey_capability ( struct subkey_s *k, const char *s) set_subkey_capability (struct subkey_s *subkey, const char *src)
{ {
for (; *s; s++) while (*src)
{ {
switch (*s) switch (*src)
{ {
case 'e': k->flags.can_encrypt = 1; break; case 'e':
case 's': k->flags.can_sign = 1; break; subkey->flags.can_encrypt = 1;
case 'c': k->flags.can_certify = 1; break; break;
case 's':
subkey->flags.can_sign = 1;
break;
case 'c':
subkey->flags.can_certify = 1;
break;
} }
src++;
} }
} }
static void static void
set_ownertrust (GpgmeKey key, const char *s) set_ownertrust (GpgmeKey key, const char *src)
{ {
/* Look at letters and stop at the first digit. */ /* Look at letters and stop at the first digit. */
for (; *s && !my_isdigit (*s); s++) while (*src && !isdigit (*src))
{ {
switch (*s) switch (*src)
{ {
case 'n': key->otrust = GPGME_VALIDITY_NEVER; break; case 'n':
case 'm': key->otrust = GPGME_VALIDITY_MARGINAL; break; key->otrust = GPGME_VALIDITY_NEVER;
case 'f': key->otrust = GPGME_VALIDITY_FULL; break; break;
case 'u': key->otrust = GPGME_VALIDITY_ULTIMATE; break;
default : key->otrust = GPGME_VALIDITY_UNKNOWN; break; case 'm':
key->otrust = GPGME_VALIDITY_MARGINAL;
break;
case 'f':
key->otrust = GPGME_VALIDITY_FULL;
break;
case 'u':
key->otrust = GPGME_VALIDITY_ULTIMATE;
break;
default:
key->otrust = GPGME_VALIDITY_UNKNOWN;
break;
}
src++;
} }
} }
/* We have read an entire key into ctx->tmp_key and should now finish
it. It is assumed that this releases ctx->tmp_key. */
static void
finish_key (GpgmeCtx ctx)
{
GpgmeKey key = ctx->tmp_key;
ctx->tmp_key = NULL;
if (key)
_gpgme_engine_io_event (ctx->engine, GPGME_EVENT_NEXT_KEY, key);
} }
@ -242,67 +332,67 @@ set_ownertrust (GpgmeKey key, const char *s)
static void static void
keylist_colon_handler (GpgmeCtx ctx, char *line) keylist_colon_handler (GpgmeCtx ctx, char *line)
{ {
char *p, *pend;
int field = 0;
enum enum
{ {
RT_NONE, RT_SIG, RT_UID, RT_SUB, RT_PUB, RT_FPR, RT_SSB, RT_SEC, RT_NONE, RT_SIG, RT_UID, RT_SUB, RT_PUB, RT_FPR, RT_SSB, RT_SEC,
RT_CRT, RT_CRS RT_CRT, RT_CRS, RT_REV
} }
rectype = RT_NONE; rectype = RT_NONE;
#define NR_FIELDS 13
char *field[NR_FIELDS];
int fields = 0;
GpgmeKey key = ctx->tmp_key; GpgmeKey key = ctx->tmp_key;
int i; struct subkey_s *subkey = NULL;
const char *trust_info = NULL; struct certsig_s *certsig = NULL;
struct subkey_s *sk = NULL;
DEBUG3 ("keylist_colon_handler ctx = %p, key = %p, line = %s\n",
ctx, key, line ? line : "(null)");
DEBUG3 ("keylist_colon_handler ctx=%p, key=%p, line=%s\n", ctx, key,
line? line: "(null)");
if (ctx->error) if (ctx->error)
return; return;
if (!line) if (!line)
{ {
/* EOF */ /* End Of File. */
finish_key (ctx); finish_key (ctx);
return; return;
} }
for (p = line; p; p = pend) while (line && fields < NR_FIELDS)
{ {
field++; field[fields++] = line;
pend = strchr (p, ':'); line = strchr (line, ':');
if (pend) if (line)
*pend++ = 0; *(line++) = '\0';
if (field == 1)
{
if (!strcmp (p, "sig"))
rectype = RT_SIG;
else if (!strcmp (p, "uid") && key)
{
rectype = RT_UID;
key = ctx->tmp_key;
} }
else if (!strcmp (p, "sub") && key)
if (!strcmp (field[0], "sig"))
rectype = RT_SIG;
else if (!strcmp (field[0], "rev"))
rectype = RT_REV;
else if (!strcmp (field[0], "uid") && key)
rectype = RT_UID;
else if (!strcmp (field[0], "sub") && key)
{ {
/* Start a new subkey. */ /* Start a new subkey. */
rectype = RT_SUB; rectype = RT_SUB;
if (!(sk = _gpgme_key_add_subkey (key))) if (!(subkey = _gpgme_key_add_subkey (key)))
{ {
ctx->error = mk_error (Out_Of_Core); ctx->error = mk_error (Out_Of_Core);
return; return;
} }
} }
else if (!strcmp (p, "ssb") && key) else if (!strcmp (field[0], "ssb") && key)
{ {
/* Start a new secret subkey. */ /* Start a new secret subkey. */
rectype = RT_SSB; rectype = RT_SSB;
if (!(sk = _gpgme_key_add_secret_subkey (key))) if (!(subkey = _gpgme_key_add_secret_subkey (key)))
{ {
ctx->error = mk_error (Out_Of_Core); ctx->error = mk_error (Out_Of_Core);
return; return;
} }
} }
else if (!strcmp (p, "pub")) else if (!strcmp (field[0], "pub"))
{ {
/* Start a new keyblock. */ /* Start a new keyblock. */
if (_gpgme_key_new (&key)) if (_gpgme_key_new (&key))
@ -316,7 +406,7 @@ keylist_colon_handler (GpgmeCtx ctx, char *line)
assert (!ctx->tmp_key); assert (!ctx->tmp_key);
ctx->tmp_key = key; ctx->tmp_key = key;
} }
else if (!strcmp (p, "sec")) else if (!strcmp (field[0], "sec"))
{ {
/* Start a new keyblock, */ /* Start a new keyblock, */
if (_gpgme_key_new_secret (&key)) if (_gpgme_key_new_secret (&key))
@ -330,7 +420,7 @@ keylist_colon_handler (GpgmeCtx ctx, char *line)
assert (!ctx->tmp_key); assert (!ctx->tmp_key);
ctx->tmp_key = key; ctx->tmp_key = key;
} }
else if (!strcmp (p, "crt")) else if (!strcmp (field[0], "crt"))
{ {
/* Start a new certificate. */ /* Start a new certificate. */
if (_gpgme_key_new (&key)) if (_gpgme_key_new (&key))
@ -345,7 +435,7 @@ keylist_colon_handler (GpgmeCtx ctx, char *line)
assert (!ctx->tmp_key); assert (!ctx->tmp_key);
ctx->tmp_key = key; ctx->tmp_key = key;
} }
else if (!strcmp (p, "crs")) else if (!strcmp (field[0], "crs"))
{ {
/* Start a new certificate. */ /* Start a new certificate. */
if (_gpgme_key_new_secret (&key)) if (_gpgme_key_new_secret (&key))
@ -360,171 +450,240 @@ keylist_colon_handler (GpgmeCtx ctx, char *line)
assert (!ctx->tmp_key); assert (!ctx->tmp_key);
ctx->tmp_key = key; ctx->tmp_key = key;
} }
else if (!strcmp (p, "fpr") && key) else if (!strcmp (field[0], "fpr") && key)
rectype = RT_FPR; rectype = RT_FPR;
else else
rectype = RT_NONE; rectype = RT_NONE;
}
else if (rectype == RT_PUB || rectype == RT_SEC /* Only look at signatures immediately following a user ID. For
|| rectype == RT_CRT || rectype == RT_CRS) this, clear the user ID pointer when encountering anything but a
signature. */
if (rectype != RT_SIG && rectype != RT_REV)
ctx->tmp_uid = NULL;
switch (rectype)
{ {
switch (field) case RT_CRT:
case RT_CRS:
/* Field 8 has the X.509 serial number. */
if (fields >= 8)
{ {
case 2: /* trust info */ key->issuer_serial = strdup (field[7]);
trust_info = p;
set_mainkey_trust_info (key, trust_info);
break;
case 3: /* key length */
i = atoi (p);
if (i > 1) /* ignore invalid values */
key->keys.key_len = i;
break;
case 4: /* pubkey algo */
i = atoi (p);
if (i >= 1 && i < 128)
key->keys.key_algo = i;
break;
case 5: /* long keyid */
if (strlen (p) == DIM(key->keys.keyid) - 1)
strcpy (key->keys.keyid, p);
break;
case 6: /* timestamp (seconds) */
key->keys.timestamp = parse_timestamp (p);
break;
case 7: /* expiration time (seconds) */
key->keys.expires_at = parse_timestamp (p);
break;
case 8: /* X.509 serial number */
if (rectype == RT_CRT || rectype == RT_CRS)
{
key->issuer_serial = strdup (p);
if (!key->issuer_serial) if (!key->issuer_serial)
ctx->error = mk_error (Out_Of_Core); ctx->error = mk_error (Out_Of_Core);
} }
break;
case 9: /* ownertrust */ /* Field 10 is not used for gpg due to --fixed-list-mode option
set_ownertrust (key, p); but GPGSM stores the issuer name. */
break; if (fields >= 10 && _gpgme_decode_c_string (field[9],
case 10: &key->issuer_name, 0))
/* Not used for gpg due to --fixed-list-mode option but
GPGSM stores the issuer name. */
if (rectype == RT_CRT || rectype == RT_CRS)
if (_gpgme_decode_c_string (p, &key->issuer_name))
ctx->error = mk_error (Out_Of_Core); ctx->error = mk_error (Out_Of_Core);
break; /* Fall through! */
case 11: /* signature class */
break; case RT_PUB:
case 12: /* capabilities */ case RT_SEC:
set_mainkey_capability (key, p); /* Field 2 has the trust info. */
break; if (fields >= 2)
case 13: set_mainkey_trust_info (key, field[1]);
pend = NULL; /* we can stop here */
break; /* Field 3 has the key length. */
} if (fields >= 3)
}
else if ((rectype == RT_SUB || rectype== RT_SSB) && sk)
{ {
switch (field) int i = atoi (field[2]);
/* Ignore invalid values. */
if (i > 1)
key->keys.key_len = i;
}
/* Field 4 has the public key algorithm. */
if (fields >= 4)
{ {
case 2: /* trust info */ int i = atoi (field[3]);
set_subkey_trust_info (sk, p);
break;
case 3: /* key length */
i = atoi (p);
if (i > 1) /* ignore invalid values */
sk->key_len = i;
break;
case 4: /* pubkey algo */
i = atoi (p);
if (i >= 1 && i < 128) if (i >= 1 && i < 128)
sk->key_algo = i; key->keys.key_algo = i;
break;
case 5: /* long keyid */
if (strlen (p) == DIM(sk->keyid) - 1)
strcpy (sk->keyid, p);
break;
case 6: /* timestamp (seconds) */
sk->timestamp = parse_timestamp (p);
break;
case 7: /* expiration time (seconds) */
sk->expires_at = parse_timestamp (p);
break;
case 8: /* reserved (LID) */
break;
case 9: /* ownertrust */
break;
case 10:/* user ID n/a for a subkey */
break;
case 11: /* signature class */
break;
case 12: /* capability */
set_subkey_capability (sk, p);
break;
case 13:
pend = NULL; /* we can stop here */
break;
} }
}
else if (rectype == RT_UID) /* Field 5 has the long keyid. */
{ if (fields >= 5 && strlen (field[4]) == DIM(key->keys.keyid) - 1)
switch (field) strcpy (key->keys.keyid, field[4]);
{
case 2: /* trust info */ /* Field 6 has the timestamp (seconds). */
trust_info = p; /*save for later */ if (fields >= 6)
key->keys.timestamp = parse_timestamp (field[5]);
/* Field 7 has the expiration time (seconds). */
if (fields >= 7)
key->keys.expires_at = parse_timestamp (field[6]);
/* Field 9 has the ownertrust. */
if (fields >= 9)
set_ownertrust (key, field[8]);
/* Field 11 has the signature class. */
/* Field 12 has the capabilities. */
if (fields >= 12)
set_mainkey_capability (key, field[11]);
break; break;
case 10: /* user ID */
if (_gpgme_key_append_name (key, p)) case RT_SUB:
/* The only kind of error we can get*/ case RT_SSB:
/* Field 2 has the trust info. */
if (fields >= 2)
set_subkey_trust_info (subkey, field[1]);
/* Field 3 has the key length. */
if (fields >= 3)
{
int i = atoi (field[2]);
/* Ignore invalid values. */
if (i > 1)
subkey->key_len = i;
}
/* Field 4 has the public key algorithm. */
if (fields >= 4)
{
int i = atoi (field[3]);
if (i >= 1 && i < 128)
subkey->key_algo = i;
}
/* Field 5 has the long keyid. */
if (fields >= 5 && strlen (field[4]) == DIM(subkey->keyid) - 1)
strcpy (subkey->keyid, field[4]);
/* Field 6 has the timestamp (seconds). */
if (fields >= 6)
subkey->timestamp = parse_timestamp (field[5]);
/* Field 7 has the expiration time (seconds). */
if (fields >= 7)
subkey->expires_at = parse_timestamp (field[6]);
/* Field 8 is reserved (LID). */
/* Field 9 has the ownertrust. */
/* Field 10, the user ID, is n/a for a subkey. */
/* Field 11 has the signature class. */
/* Field 12 has the capabilities. */
if (fields >= 12)
set_subkey_capability (subkey, field[11]);
break;
case RT_UID:
/* Field 2 has the trust info, and field 10 has the user ID. */
if (fields >= 10)
{
if (_gpgme_key_append_name (key, field[9]))
ctx->error = mk_error (Out_Of_Core); ctx->error = mk_error (Out_Of_Core);
else else
{ {
if (trust_info) if (field[1])
set_userid_flags (key, trust_info); set_userid_flags (key, field[1]);
ctx->tmp_uid = key->last_uid;
}
} }
pend = NULL; /* we can stop here */
break; break;
}
} case RT_FPR:
else if (rectype == RT_FPR) /* Field 10 has the fingerprint (take only the first one). */
if (fields >= 10 && !key->keys.fingerprint && field[9] && *field[9])
{ {
switch (field) key->keys.fingerprint = strdup (field[9]);
{
case 10: /* fingerprint (take only the first one)*/
if (!key->keys.fingerprint && *p)
{
key->keys.fingerprint = strdup (p);
if (!key->keys.fingerprint) if (!key->keys.fingerprint)
ctx->error = mk_error (Out_Of_Core); ctx->error = mk_error (Out_Of_Core);
} }
break;
case 13: /* gpgsm chain ID (take only the first one)*/ /* Field 13 has the gpgsm chain ID (take only the first one). */
if (!key->chain_id && *p) if (fields >= 13 && !key->chain_id && *field[12])
{ {
key->chain_id = strdup (p); key->chain_id = strdup (field[12]);
if (!key->chain_id) if (!key->chain_id)
ctx->error = mk_error (Out_Of_Core); ctx->error = mk_error (Out_Of_Core);
} }
pend = NULL; /* that is all we want */ break;
case RT_SIG:
case RT_REV:
if (!ctx->tmp_uid)
return;
/* Start a new (revoked) signature. */
assert (ctx->tmp_uid == key->last_uid);
certsig = _gpgme_key_add_certsig (key, (fields >= 10) ? field[9] : NULL);
if (!certsig)
{
ctx->error = mk_error (Out_Of_Core);
return;
}
/* Field 2 has the calculated trust ('!', '-', '?', '%'). */
if (fields >= 2)
switch (field[1][0])
{
case '!':
certsig->sig_stat = GPGME_SIG_STAT_GOOD;
break;
case '-':
certsig->sig_stat = GPGME_SIG_STAT_BAD;
break;
case '?':
certsig->sig_stat = GPGME_SIG_STAT_NOKEY;
break;
case '%':
certsig->sig_stat = GPGME_SIG_STAT_ERROR;
break;
default:
certsig->sig_stat = GPGME_SIG_STAT_NONE;
break; break;
} }
}
}
}
/* Field 4 has the public key algorithm. */
/* if (fields >= 4)
* We have read an entire key into ctx->tmp_key and should now finish
* it. It is assumed that this releases ctx->tmp_key.
*/
static void
finish_key (GpgmeCtx ctx)
{ {
GpgmeKey key = ctx->tmp_key; int i = atoi (field[3]);
if (i >= 1 && i < 128)
certsig->algo = i;
}
ctx->tmp_key = NULL; /* Field 5 has the long keyid. */
if (fields >= 5 && strlen (field[4]) == DIM(certsig->keyid) - 1)
strcpy (certsig->keyid, field[4]);
if (key) /* Field 6 has the timestamp (seconds). */
_gpgme_engine_io_event (ctx->engine, GPGME_EVENT_NEXT_KEY, key); if (fields >= 6)
certsig->timestamp = parse_timestamp (field[5]);
/* Field 7 has the expiration time (seconds). */
if (fields >= 7)
certsig->expires_at = parse_timestamp (field[6]);
/* Field 11 has the signature class (eg, 0x30 means revoked). */
if (fields >= 11)
if (field[10][0] && field[10][1])
{
int class = _gpgme_hextobyte (field[10]);
if (class >= 0)
{
certsig->sig_class = class;
if (class == 0x30)
certsig->flags.revoked = 1;
}
if (field[10][2] == 'x')
certsig->flags.exportable = 1;
}
break;
case RT_NONE:
/* Unknown record. */
break;
}
} }
@ -593,9 +752,9 @@ gpgme_op_keylist_start (GpgmeCtx ctx, const char *pattern, int secret_only)
if (err) if (err)
goto leave; goto leave;
/* We don't want to use the verbose mode as this will also print /* We don't want to use the verbose mode as this will also print the
the key signatures which is in most cases not needed and furthermore we key signatures which is in most cases not needed and furthermore
just ignore those lines - This should speed up things */ we just ignore those lines - This should speed up things. */
_gpgme_engine_set_verbosity (ctx->engine, 0); _gpgme_engine_set_verbosity (ctx->engine, 0);
err = _gpgme_engine_op_keylist (ctx->engine, pattern, secret_only, err = _gpgme_engine_op_keylist (ctx->engine, pattern, secret_only,
@ -647,15 +806,16 @@ gpgme_op_keylist_ext_start (GpgmeCtx ctx, const char *pattern[],
if (err) if (err)
goto leave; goto leave;
/* We don't want to use the verbose mode as this will also print /* We don't want to use the verbose mode as this will also print the
the key signatures which is in most cases not needed and furthermore we key signatures which is in most cases not needed and furthermore
just ignore those lines - This should speed up things */ we just ignore those lines - This should speed up things. */
_gpgme_engine_set_verbosity (ctx->engine, 0); _gpgme_engine_set_verbosity (ctx->engine, 0);
err = _gpgme_engine_op_keylist_ext (ctx->engine, pattern, secret_only, err = _gpgme_engine_op_keylist_ext (ctx->engine, pattern, secret_only,
reserved, ctx->keylist_mode); reserved, ctx->keylist_mode);
if (!err) /* And kick off the process. */ /* And kick off the process. */
if (!err)
err = _gpgme_engine_start (ctx->engine, ctx); err = _gpgme_engine_start (ctx->engine, ctx);
leave: leave:

View File

@ -1486,10 +1486,9 @@ gpg_keylist (void *engine, const char *pattern, int secret_only,
if (!err) if (!err)
err = add_arg (gpg, "--with-fingerprint"); err = add_arg (gpg, "--with-fingerprint");
if (!err) if (!err)
err = add_arg (gpg, (keylist_mode & GPGME_KEYLIST_MODE_SIGS) ? err = add_arg (gpg, secret_only ? "--list-secret-keys"
"--check-sigs" : : ((keylist_mode & GPGME_KEYLIST_MODE_SIGS)
secret_only ? "--list-secret-keys" ? "--check-sigs" : "--list-keys"));
: "--list-keys");
/* Tell the gpg object about the data. */ /* Tell the gpg object about the data. */
if (!err) if (!err)
@ -1519,7 +1518,9 @@ gpg_keylist_ext (void *engine, const char *pattern[], int secret_only,
if (!err) if (!err)
err = add_arg (gpg, "--with-fingerprint"); err = add_arg (gpg, "--with-fingerprint");
if (!err) if (!err)
err = add_arg (gpg, secret_only ? "--list-secret-keys" : "--list-keys"); err = add_arg (gpg, secret_only ? "--list-secret-keys"
: ((keylist_mode & GPGME_KEYLIST_MODE_SIGS)
? "--check-sigs" : "--list-keys"));
/* Tell the gpg object about the data. */ /* Tell the gpg object about the data. */
if (!err) if (!err)

View File

@ -85,7 +85,13 @@ FILE *fopencookie (void *cookie, const char *opentype,
/*-- conversion.c --*/ /*-- conversion.c --*/
GpgmeError _gpgme_decode_c_string (const char *src, char **destp); /* Decode the C formatted string SRC and store the result in the
buffer *DESTP which is LEN bytes long. If LEN is zero, then a
large enough buffer is allocated with malloc and *DESTP is set to
the result. Currently, LEN is only used to specify if allocation
is desired or not, the caller is expected to make sure that *DESTP
is large enough if LEN is not zero. */
GpgmeError _gpgme_decode_c_string (const char *src, char **destp, int len);
int _gpgme_hextobyte (const byte *str); int _gpgme_hextobyte (const byte *str);
#endif /* UTIL_H */ #endif /* UTIL_H */

View File

@ -643,42 +643,20 @@ gpgme_get_sig_ulong_attr (GpgmeCtx c, int idx, GpgmeAttr what, int reserved)
* indicate that there are no more signatures. * indicate that there are no more signatures.
**/ **/
GpgmeError GpgmeError
gpgme_get_sig_key (GpgmeCtx c, int idx, GpgmeKey *r_key) gpgme_get_sig_key (GpgmeCtx ctx, int idx, GpgmeKey *r_key)
{ {
VerifyResult result; VerifyResult result;
GpgmeError err = 0;
if (!c || !r_key) if (!ctx || !r_key)
return mk_error (Invalid_Value); return mk_error (Invalid_Value);
if (c->pending || !c->result.verify) if (ctx->pending || !ctx->result.verify)
return mk_error (Busy); return mk_error (Busy);
for (result = c->result.verify; for (result = ctx->result.verify;
result && idx > 0; result = result->next, idx--) result && idx > 0; result = result->next, idx--)
; ;
if (!result) if (!result)
return mk_error (EOF); return mk_error (EOF);
if (strlen(result->fpr) < 16) /* We have at least a key ID. */ return gpgme_get_key (ctx, result->fpr, r_key, 0, 0);
return mk_error (Invalid_Key);
*r_key = _gpgme_key_cache_get (result->fpr);
if (!*r_key)
{
GpgmeCtx listctx;
/* Fixme: This can be optimized by keeping an internal context
used for such key listings. */
err = gpgme_new (&listctx);
if (err)
return err;
gpgme_set_protocol (listctx, gpgme_get_protocol (c));
gpgme_set_keylist_mode (listctx, c->keylist_mode);
err = gpgme_op_keylist_start (listctx, result->fpr, 0);
if (!err)
err = gpgme_op_keylist_next (listctx, r_key);
gpgme_release (listctx);
} }
return err;
}