* 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.
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.
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} \
--- /dev/null
+/* 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;
+
+\f
+/* 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;
+}
+
+\f
+/* 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;
+}
+
+\f
+/* 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;
+}
#include "key.h"
#include "sema.h"
-#if SIZEOF_UNSIGNED_INT < 4
-#error unsigned int too short to be used as a hash value
-#endif
-
-\f
-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;
-}
-
\f
static const char *
pkalgo_to_string (int algo)
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;
-}
};
\f
-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);
GpgmeError _gpgme_progress_status_handler (GpgmeCtx ctx, GpgmeStatusCode code,
char *args);
+\f
+/* 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);
/* For _gpgme_sema_subsystem_init (). */
#include "sema.h"
-/* For _gpgme_key_cache_init (). */
-#include "key.h"
-
\f
/* Bootstrap the subsystems needed for concurrent operation. This
must be done once at startup. We can not guarantee this using a
return;
_gpgme_sema_subsystem_init ();
- _gpgme_key_cache_init ();
done = 1;
}