diff --git a/gpgme/ChangeLog b/gpgme/ChangeLog index 59eed259..1b4d0b5f 100644 --- a/gpgme/ChangeLog +++ b/gpgme/ChangeLog @@ -1,5 +1,18 @@ 2003-04-24 Marcus Brinkmann + * 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. diff --git a/gpgme/Makefile.am b/gpgme/Makefile.am index 364897f1..f36872e2 100644 --- a/gpgme/Makefile.am +++ b/gpgme/Makefile.am @@ -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} \ diff --git a/gpgme/key-cache.c b/gpgme/key-cache.c new file mode 100644 index 00000000..0322ef22 --- /dev/null +++ b/gpgme/key-cache.c @@ -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 +#endif +#include +#include + +#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; +} diff --git a/gpgme/key.c b/gpgme/key.c index 7a2b7544..af87ac95 100644 --- a/gpgme/key.c +++ b/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; -} diff --git a/gpgme/key.h b/gpgme/key.h index c9e60416..4b60b608 100644 --- a/gpgme/key.h +++ b/gpgme/key.h @@ -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); diff --git a/gpgme/ops.h b/gpgme/ops.h index b3377a2d..c21de0bf 100644 --- a/gpgme/ops.h +++ b/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); diff --git a/gpgme/version.c b/gpgme/version.c index 0efdf40f..d64e67a3 100644 --- a/gpgme/version.c +++ b/gpgme/version.c @@ -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; }