2003-04-24 Marcus Brinkmann <marcus@g10code.de>
* Makefile.am (libgpgme_la_SOURCES): Add key-cache.c. * key.c (key_cache_initialized, key_cache_size, key_cache_max_chain_length, ): Removed. (struct key_cache_item_s, key_cache_lock, key_cache, key_cache_unused_items, hash_key, _gpgme_key_cache_add, _gpgme_key_cache_get, gpgme_get_key): Moved to ... * key-cache.c: ... here. New file. * key.h (_gpgme_key_cache_init): Remove prototypes. (_gpgme_key_cache_add,_gpgme_key_cache_get): Move to ... * ops.h: ... here. * version.c: Do not include "key.h". (do_subsystem_inits): Do not call _gpgme_key_cache_init.
This commit is contained in:
parent
5b88552e74
commit
5375105fad
@ -1,5 +1,18 @@
|
||||
2003-04-24 Marcus Brinkmann <marcus@g10code.de>
|
||||
|
||||
* Makefile.am (libgpgme_la_SOURCES): Add key-cache.c.
|
||||
* key.c (key_cache_initialized, key_cache_size,
|
||||
key_cache_max_chain_length, ): Removed.
|
||||
(struct key_cache_item_s, key_cache_lock, key_cache,
|
||||
key_cache_unused_items, hash_key, _gpgme_key_cache_add,
|
||||
_gpgme_key_cache_get, gpgme_get_key): Moved to ...
|
||||
* key-cache.c: ... here. New file.
|
||||
* key.h (_gpgme_key_cache_init): Remove prototypes.
|
||||
(_gpgme_key_cache_add,_gpgme_key_cache_get): Move to ...
|
||||
* ops.h: ... here.
|
||||
* version.c: Do not include "key.h".
|
||||
(do_subsystem_inits): Do not call _gpgme_key_cache_init.
|
||||
|
||||
* mkstatus: Strip trailing comma.
|
||||
* gpgme.h (GpgmeStatus): Pretty print.
|
||||
|
||||
|
@ -77,7 +77,7 @@ libgpgme_la_SOURCES = \
|
||||
op-support.c \
|
||||
encrypt.c encrypt-sign.c decrypt.c decrypt-verify.c verify.c \
|
||||
sign.c passphrase.c progress.c \
|
||||
key.h key.c keylist.c trustlist.c \
|
||||
key.h key.c key-cache.c keylist.c trustlist.c \
|
||||
import.c export.c genkey.c delete.c edit.c \
|
||||
engine.h engine-backend.h engine.c rungpg.c status-table.h \
|
||||
${gpgsm_components} sema.h io.h ${system_components} \
|
||||
|
250
gpgme/key-cache.c
Normal file
250
gpgme/key-cache.c
Normal file
@ -0,0 +1,250 @@
|
||||
/* key-cache.c - Key cache routines.
|
||||
Copyright (C) 2000 Werner Koch (dd9jn)
|
||||
Copyright (C) 2001, 2002, 2003 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 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>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gpgme.h"
|
||||
#include "util.h"
|
||||
#include "ops.h"
|
||||
#include "sema.h"
|
||||
#include "key.h"
|
||||
|
||||
#if SIZEOF_UNSIGNED_INT < 4
|
||||
#error unsigned int too short to be used as a hash value
|
||||
#endif
|
||||
|
||||
#define KEY_CACHE_SIZE 503
|
||||
#define KEY_CACHE_MAX_CHAIN_LENGTH 10
|
||||
|
||||
struct key_cache_item_s
|
||||
{
|
||||
struct key_cache_item_s *next;
|
||||
GpgmeKey key;
|
||||
};
|
||||
|
||||
/* Protects key_cache and key_cache_unused_items. */
|
||||
DEFINE_STATIC_LOCK (key_cache_lock);
|
||||
static struct key_cache_item_s *key_cache[KEY_CACHE_SIZE];
|
||||
static struct key_cache_item_s *key_cache_unused_items;
|
||||
|
||||
|
||||
/* We use the first 4 digits to calculate the hash. */
|
||||
static int
|
||||
hash_key (const char *fpr, unsigned int *rhash)
|
||||
{
|
||||
unsigned int hash;
|
||||
int c;
|
||||
|
||||
if (!fpr)
|
||||
return -1;
|
||||
if ((c = _gpgme_hextobyte (fpr)) == -1)
|
||||
return -1;
|
||||
hash = c;
|
||||
if ((c = _gpgme_hextobyte (fpr+2)) == -1)
|
||||
return -1;
|
||||
hash |= c << 8;
|
||||
if ((c = _gpgme_hextobyte (fpr+4)) == -1)
|
||||
return -1;
|
||||
hash |= c << 16;
|
||||
if ((c = _gpgme_hextobyte (fpr+6)) == -1)
|
||||
return -1;
|
||||
hash |= c << 24;
|
||||
|
||||
*rhash = hash;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Acquire a reference to KEY and add it to the key cache. */
|
||||
void
|
||||
_gpgme_key_cache_add (GpgmeKey key)
|
||||
{
|
||||
struct subkey_s *k;
|
||||
|
||||
LOCK (key_cache_lock);
|
||||
/* Put the key under each fingerprint into the cache. We use the
|
||||
first 4 digits to calculate the hash. */
|
||||
for (k = &key->keys; k; k = k->next)
|
||||
{
|
||||
size_t n;
|
||||
unsigned int hash;
|
||||
struct key_cache_item_s *item;
|
||||
|
||||
if (hash_key (k->fingerprint, &hash))
|
||||
continue;
|
||||
|
||||
hash %= KEY_CACHE_SIZE;
|
||||
for (item = key_cache[hash], n=0; item; item = item->next, n++)
|
||||
{
|
||||
struct subkey_s *k2;
|
||||
if (item->key == key)
|
||||
/* Already in cache. */
|
||||
break;
|
||||
/* Now do a deeper check. */
|
||||
for (k2 = &item->key->keys; k2; k2 = k2->next)
|
||||
{
|
||||
if (k2->fingerprint && !strcmp (k->fingerprint, k2->fingerprint))
|
||||
{
|
||||
/* Okay, replace it with the new copy. */
|
||||
gpgme_key_unref (item->key);
|
||||
item->key = key;
|
||||
gpgme_key_ref (item->key);
|
||||
UNLOCK (key_cache_lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item)
|
||||
continue;
|
||||
|
||||
if (n > KEY_CACHE_MAX_CHAIN_LENGTH)
|
||||
{
|
||||
/* Remove the last entries. */
|
||||
struct key_cache_item_s *last = NULL;
|
||||
|
||||
for (item = key_cache[hash];
|
||||
item && n < KEY_CACHE_MAX_CHAIN_LENGTH;
|
||||
last = item, item = item->next, n++)
|
||||
;
|
||||
|
||||
if (last)
|
||||
{
|
||||
struct key_cache_item_s *next;
|
||||
|
||||
last->next = NULL;
|
||||
for (; item; item = next)
|
||||
{
|
||||
next = item->next;
|
||||
gpgme_key_unref (item->key);
|
||||
item->key = NULL;
|
||||
item->next = key_cache_unused_items;
|
||||
key_cache_unused_items = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item = key_cache_unused_items;
|
||||
if (item)
|
||||
{
|
||||
key_cache_unused_items = item->next;
|
||||
item->next = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
item = malloc (sizeof *item);
|
||||
if (!item)
|
||||
{
|
||||
UNLOCK (key_cache_lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
item->key = key;
|
||||
gpgme_key_ref (key);
|
||||
item->next = key_cache[hash];
|
||||
key_cache[hash] = item;
|
||||
}
|
||||
UNLOCK (key_cache_lock);
|
||||
}
|
||||
|
||||
|
||||
GpgmeKey
|
||||
_gpgme_key_cache_get (const char *fpr)
|
||||
{
|
||||
struct key_cache_item_s *item;
|
||||
unsigned int hash;
|
||||
|
||||
LOCK (key_cache_lock);
|
||||
if (hash_key (fpr, &hash))
|
||||
{
|
||||
UNLOCK (key_cache_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hash %= KEY_CACHE_SIZE;
|
||||
for (item = key_cache[hash]; item; item = item->next)
|
||||
{
|
||||
struct subkey_s *k;
|
||||
|
||||
for (k = &item->key->keys; k; k = k->next)
|
||||
{
|
||||
if (k->fingerprint && !strcmp (k->fingerprint, fpr))
|
||||
{
|
||||
gpgme_key_ref (item->key);
|
||||
UNLOCK (key_cache_lock);
|
||||
return item->key;
|
||||
}
|
||||
}
|
||||
}
|
||||
UNLOCK (key_cache_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* 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 GPGME_Invalid_Value;
|
||||
|
||||
if (strlen (fpr) < 16) /* We have at least a key ID. */
|
||||
return GPGME_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;
|
||||
}
|
||||
}
|
||||
|
||||
/* We need our own context because we have to avoid the user's I/O
|
||||
callback handlers. */
|
||||
/* 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;
|
||||
}
|
261
gpgme/key.c
261
gpgme/key.c
@ -32,225 +32,12 @@
|
||||
#include "key.h"
|
||||
#include "sema.h"
|
||||
|
||||
#if SIZEOF_UNSIGNED_INT < 4
|
||||
#error unsigned int too short to be used as a hash value
|
||||
#endif
|
||||
|
||||
|
||||
struct key_cache_item_s
|
||||
{
|
||||
struct key_cache_item_s *next;
|
||||
GpgmeKey key;
|
||||
};
|
||||
|
||||
/* Protects all key_cache_* variables. */
|
||||
DEFINE_STATIC_LOCK (key_cache_lock);
|
||||
static int key_cache_initialized;
|
||||
static struct key_cache_item_s **key_cache;
|
||||
static size_t key_cache_size;
|
||||
static size_t key_cache_max_chain_length;
|
||||
static struct key_cache_item_s *key_cache_unused_items;
|
||||
|
||||
/* Protects all reference counters in keys. All other accesses to a
|
||||
key are either read only or happen before the key is entered into
|
||||
the cache. */
|
||||
DEFINE_STATIC_LOCK (key_ref_lock);
|
||||
|
||||
static int
|
||||
hash_key (const char *fpr, unsigned int *rhash)
|
||||
{
|
||||
unsigned int hash;
|
||||
int c;
|
||||
|
||||
if (!fpr)
|
||||
return -1;
|
||||
if ((c = _gpgme_hextobyte (fpr)) == -1)
|
||||
return -1;
|
||||
hash = c;
|
||||
if ((c = _gpgme_hextobyte (fpr+2)) == -1)
|
||||
return -1;
|
||||
hash |= c << 8;
|
||||
if ((c = _gpgme_hextobyte (fpr+4)) == -1)
|
||||
return -1;
|
||||
hash |= c << 16;
|
||||
if ((c = _gpgme_hextobyte (fpr+6)) == -1)
|
||||
return -1;
|
||||
hash |= c << 24;
|
||||
|
||||
*rhash = hash;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_gpgme_key_cache_init (void)
|
||||
{
|
||||
LOCK (key_cache_lock);
|
||||
if (!key_cache_initialized)
|
||||
{
|
||||
key_cache_size = 503;
|
||||
key_cache = calloc (key_cache_size, sizeof *key_cache);
|
||||
if (!key_cache)
|
||||
{
|
||||
key_cache_size = 0;
|
||||
key_cache_initialized = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The upper bound for our cache size is
|
||||
key_cache_max_chain_length * key_cache_size. */
|
||||
key_cache_max_chain_length = 10;
|
||||
key_cache_initialized = 1;
|
||||
}
|
||||
}
|
||||
UNLOCK (key_cache_lock);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_gpgme_key_cache_add (GpgmeKey key)
|
||||
{
|
||||
struct subkey_s *k;
|
||||
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
_gpgme_key_cache_init ();
|
||||
|
||||
LOCK (key_cache_lock);
|
||||
/* Check if cache was enabled. */
|
||||
if (!key_cache_size)
|
||||
{
|
||||
UNLOCK (key_cache_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Put the key under each fingerprint into the cache. We use the
|
||||
first 4 digits to calculate the hash. */
|
||||
for (k = &key->keys; k; k = k->next)
|
||||
{
|
||||
size_t n;
|
||||
unsigned int hash;
|
||||
struct key_cache_item_s *item;
|
||||
|
||||
if (hash_key (k->fingerprint, &hash))
|
||||
continue;
|
||||
|
||||
hash %= key_cache_size;
|
||||
for (item = key_cache[hash], n=0; item; item = item->next, n++)
|
||||
{
|
||||
struct subkey_s *k2;
|
||||
if (item->key == key)
|
||||
/* Already in cache. */
|
||||
break;
|
||||
/* Now do a deeper check. */
|
||||
for (k2 = &item->key->keys; k2; k2 = k2->next)
|
||||
{
|
||||
if (k2->fingerprint && !strcmp (k->fingerprint, k2->fingerprint))
|
||||
{
|
||||
/* Okay, replace it with the new copy. */
|
||||
gpgme_key_unref (item->key);
|
||||
item->key = key;
|
||||
gpgme_key_ref (item->key);
|
||||
UNLOCK (key_cache_lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item)
|
||||
continue;
|
||||
|
||||
if (n > key_cache_max_chain_length)
|
||||
{
|
||||
/* Remove the last entries. */
|
||||
struct key_cache_item_s *last = NULL;
|
||||
|
||||
for (item = key_cache[hash];
|
||||
item && n < key_cache_max_chain_length;
|
||||
last = item, item = item->next, n++)
|
||||
;
|
||||
|
||||
if (last)
|
||||
{
|
||||
struct key_cache_item_s *next;
|
||||
|
||||
assert (last->next == item);
|
||||
last->next = NULL;
|
||||
for (; item; item = next)
|
||||
{
|
||||
next = item->next;
|
||||
gpgme_key_unref (item->key);
|
||||
item->key = NULL;
|
||||
item->next = key_cache_unused_items;
|
||||
key_cache_unused_items = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item = key_cache_unused_items;
|
||||
if (item)
|
||||
{
|
||||
key_cache_unused_items = item->next;
|
||||
item->next = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
item = malloc (sizeof *item);
|
||||
if (!item)
|
||||
{
|
||||
UNLOCK (key_cache_lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
item->key = key;
|
||||
gpgme_key_ref (key);
|
||||
item->next = key_cache[hash];
|
||||
key_cache[hash] = item;
|
||||
}
|
||||
UNLOCK (key_cache_lock);
|
||||
}
|
||||
|
||||
|
||||
GpgmeKey
|
||||
_gpgme_key_cache_get (const char *fpr)
|
||||
{
|
||||
struct key_cache_item_s *item;
|
||||
unsigned int hash;
|
||||
|
||||
LOCK (key_cache_lock);
|
||||
/* Check if cache is enabled already. */
|
||||
if (!key_cache_size)
|
||||
{
|
||||
UNLOCK (key_cache_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (hash_key (fpr, &hash))
|
||||
{
|
||||
UNLOCK (key_cache_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hash %= key_cache_size;
|
||||
for (item = key_cache[hash]; item; item = item->next)
|
||||
{
|
||||
struct subkey_s *k;
|
||||
|
||||
for (k = &item->key->keys; k; k = k->next)
|
||||
{
|
||||
if (k->fingerprint && !strcmp (k->fingerprint, fpr))
|
||||
{
|
||||
gpgme_key_ref (item->key);
|
||||
UNLOCK (key_cache_lock);
|
||||
return item->key;
|
||||
}
|
||||
}
|
||||
}
|
||||
UNLOCK (key_cache_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static const char *
|
||||
pkalgo_to_string (int algo)
|
||||
@ -1180,51 +967,3 @@ gpgme_key_sig_get_ulong_attr (GpgmeKey key, int uid_idx, GpgmeAttr what,
|
||||
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 GPGME_Invalid_Value;
|
||||
|
||||
if (strlen (fpr) < 16) /* We have at least a key ID. */
|
||||
return GPGME_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;
|
||||
}
|
||||
}
|
||||
|
||||
/* We need our own context because we have to avoid the user's I/O
|
||||
callback handlers. */
|
||||
/* 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;
|
||||
}
|
||||
|
@ -96,11 +96,6 @@ struct gpgme_key_s
|
||||
};
|
||||
|
||||
|
||||
void _gpgme_key_cache_init (void);
|
||||
void _gpgme_key_cache_add (GpgmeKey key);
|
||||
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_secret_subkey (GpgmeKey key);
|
||||
|
11
gpgme/ops.h
11
gpgme/ops.h
@ -94,6 +94,17 @@ GpgmeError _gpgme_passphrase_start (GpgmeCtx ctx);
|
||||
GpgmeError _gpgme_progress_status_handler (GpgmeCtx ctx, GpgmeStatusCode code,
|
||||
char *args);
|
||||
|
||||
|
||||
/* From key-cache.c. */
|
||||
|
||||
/* Acquire a reference to KEY and add it to the key cache. */
|
||||
void _gpgme_key_cache_add (GpgmeKey key);
|
||||
|
||||
/* Look up a key with fingerprint FPR in the key cache. If such a key
|
||||
is found, a reference is acquired for it and it is returned.
|
||||
Otherwise, NULL is returned. */
|
||||
GpgmeKey _gpgme_key_cache_get (const char *fpr);
|
||||
|
||||
/*-- keylist.c --*/
|
||||
void _gpgme_op_keylist_event_cb (void *data, GpgmeEventIO type, void *type_data);
|
||||
|
||||
|
@ -31,9 +31,6 @@
|
||||
/* For _gpgme_sema_subsystem_init (). */
|
||||
#include "sema.h"
|
||||
|
||||
/* For _gpgme_key_cache_init (). */
|
||||
#include "key.h"
|
||||
|
||||
|
||||
/* Bootstrap the subsystems needed for concurrent operation. This
|
||||
must be done once at startup. We can not guarantee this using a
|
||||
@ -49,7 +46,6 @@ do_subsystem_inits (void)
|
||||
return;
|
||||
|
||||
_gpgme_sema_subsystem_init ();
|
||||
_gpgme_key_cache_init ();
|
||||
done = 1;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user