doc/
authorMarcus Brinkmann <mb@g10code.com>
Thu, 9 May 2002 03:38:12 +0000 (03:38 +0000)
committerMarcus Brinkmann <mb@g10code.com>
Thu, 9 May 2002 03:38:12 +0000 (03:38 +0000)
2002-05-09  Marcus Brinkmann  <marcus@g10code.de>

* gpgme.texi (Overview): Replace note about thread-safeness.
(Multi Threading): New section.

gpgme/
2002-05-08  Marcus Brinkmann  <marcus@g10code.de>

* w32-util.c: New static variable GET_PATH_LOCK.
(_gpgme_get_gpg_path): Remove superfluous NULL initializer.
Take lock while determining path.
(_gpgme_get_gpgsm_path): Likewise.
* version.c (do_subsystem_inits): Set DONE to 1 after
initialization.
(gpgme_get_engine_info): New variable ENGINE_INFO_LOCK.  Take lock
while determining engine info.
* rungpg.c (_gpgme_gpg_get_version): New variable
GPG_VERSION_LOCK.  Take the lock while determining the program
version.
* posix-io.c: Include "sema.h".
(_gpgme_io_spawn): New variable FIXED_SIGNALS_LOCK.  Take the lock
while fixing the signals.
(_gpgme_io_select): Make READFDS and WRITEFDS non-static.
* key.c: Include "sema.h".  New globals KEY_CACHE_LOCK and
KEY_REF_LOCK.
(capabilities_to_string): Make STRINGS very const.
(_gpgme_key_cache_add): Lock the key cache.
(_gpgme_key_cache_get): Likewise.
(gpgme_key_ref, gpgme_key_release): Lock the key_ref_lock.
* import.c (append_xml_impinfo): Make IMPORTED_FIELDS and
IMPORT_RES_FIELDS very const.  Make FIELD and FIELD_NAME a litle
const.
* engine.c (_gpgme_engine_get_info): New variable
ENGINE_INFO_LOCK.  Take lock while determining engine info.
* engine-gpgsm.c: Include "sema.h".
(_gpgme_gpgsm_get_version): New variable GPGSM_VERSION_LOCK.  Take
lock while getting program version.

13 files changed:
TODO
doc/ChangeLog
doc/gpgme.texi
gpgme/ChangeLog
gpgme/engine-gpgsm.c
gpgme/engine.c
gpgme/gpgme.h
gpgme/import.c
gpgme/key.c
gpgme/posix-io.c
gpgme/rungpg.c
gpgme/version.c
gpgme/w32-util.c

diff --git a/TODO b/TODO
index 07d0cba..6bcd659 100644 (file)
--- a/TODO
+++ b/TODO
@@ -8,8 +8,6 @@ Hey Emacs, this is -*- outline -*- mode!
 * Allow to use GTK's main loop instead of the select stuff in
   wait.c
 
-* add locking to the key cache?
-
 * cleanup the namespace - we use log_* assuan_* ascii_* mutex_*
   But those are only used internally.  Some linker tricks should make
   it possible to hide them from the user (didn't work last time, try
@@ -30,6 +28,7 @@ Hey Emacs, this is -*- outline -*- mode!
 *** For pipemode, make sure to release the pipemode callback data object.
 
 * Operations
+** gpgme_wait needs to be made thread safe!!!
 ** Export status handler need much more work.
 ** Import should return a useful error when one happened.
 ** Genkey should return something more useful than General_Error.
index 19b141e..a5c7c5e 100644 (file)
@@ -1,3 +1,8 @@
+2002-05-09  Marcus Brinkmann  <marcus@g10code.de>
+
+       * gpgme.texi (Overview): Replace note about thread-safeness.
+       (Multi Threading): New section.
+
 2002-05-03  Werner Koch  <wk@gnupg.org>
 
        * gpgme.texi (Manipulating Data Buffers): Changed some data types
index 0061274..9606eed 100644 (file)
@@ -104,6 +104,7 @@ Preparation
 * Header::                        What header file you need to include.
 * Building the Source::           Compiler options to be used.
 * Library Version Check::         Getting and verifying the library version.
+* Multi Threading::               How GPGME can be used in an MT environment.
 
 Protocols and Engines
 
@@ -278,11 +279,9 @@ including listing keys, querying their attributes, generating,
 importing, exporting and deleting keys, and acquiring information
 about the trust path.
 
-@cindex thread-safeness
-@cindex multi-threading
-@strong{Caution:} The @acronym{GPGME} library is not thread-safe.  It
-will be to some extent in the future, but currently great care has to
-be taken if @acronym{GPGME} is used in a multi-threaded environment.
+With some precautions, @acronym{GPGME} can be used in a multi-threaded
+environment, although it is not completely thread safe and thus needs
+the support of the application.
 
 
 @node Preparation
@@ -298,6 +297,7 @@ of the library are verified.
 * Header::                        What header file you need to include.
 * Building the Source::           Compiler options to be used.
 * Library Version Check::         Getting and verifying the library version.
+* Multi Threading::               How GPGME can be used in an MT environment.
 @end menu
 
 
@@ -402,6 +402,81 @@ features are provided by the installed version of the library.
 @end deftypefun
 
 
+@node Multi Threading
+@section Multi Threading
+@cindex thread-safeness
+@cindex multi-threading
+
+The @acronym{GPGME} library is not entirely thread-safe, but it can
+still be used in a multi-threaded environment if some care is taken.
+If the following requirements are met, there should be no race
+conditions to worry about:
+
+@itemize @bullet
+@item
+The function @code{gpgme_check_version} must be called before any
+other function in the library, because it initializes the locking
+subsystem in @acronym{GPGME}.  To achieve this in all generality, it
+is necessary to synchronize the call to this function with all other
+calls to functions in the library, using the synchronization
+mechanisms available in your thread library.  Otherwise, specific
+compiler or CPU memory cache optimizations could lead to the situation
+where a thread is started and uses @acronym{GPGME} before the effects
+of the initialization are visible for this thread.  It doesn't even
+suffice to call @code{gpgme_check_version} before creating this other
+thread@footnote{In SMP systems the new thread could be started on
+another CPU before the effects of the initialization are seen by that
+CPU's memory cache.  Not doing proper synchronization here leads to
+the same problems the double-checked locking idiom has.  You might
+find that if you don't do proper synchronization, it still works in
+most configurations.  Don't let this fool you.  Someday it might lead
+to subtle bugs when someone tries it on a DEC Alpha or an SMP
+machine.}.
+
+For example, if you are using POSIX threads, each thread that wants to
+call functions in @acronym{GPGME} could call the following function
+before any function in the library:
+
+@example
+#include <pthread.h>
+
+void
+initialize_gpgme (void)
+{
+  static int gpgme_init;
+  static pthread_mutext_t gpgme_init_lock = PTHREAD_MUTEX_INITIALIZER;
+
+  pthread_mutex_lock (&gpgme_init_lock);
+  if (!gpgme_init)
+    {
+      gpgme_check_version ();
+      gpgme_init = 1;
+    }
+  pthread_mutex_unlock (&gpgme_init_lock);
+}
+@end example
+
+@item
+Any @code{GpgmeData}, @code{GpgmeCtx} and @code{GpgmeRecipients}
+object must only be accessed by one thread at a time.  If multiple
+threads want to deal with the same object, the caller has to make sure
+that operations on this object are fully synchronized.
+
+@item
+Only one thread is allowed to call @code{gpgme_wait} at a time.  If
+multiple threads call this function, the caller must make sure that
+all invocations are fully synchronized.
+
+@item
+Unfortunately, the last rule implies that all calls to one of the
+following functions have to be fully synchronized together with
+@code{gpgme_wait}, as they call @code{gpgme_wait} internally: All
+functions @code{gpgme_op_FOO} that have a corresponding
+@code{gpgme_op_FOO_start} function, @code{gpgme_op_keylist_next},
+@code{gpgme_op_trustlist_next}.
+@end itemize
+
+
 @node Protocols and Engines
 @chapter Protocols and Engines
 @cindex protocol
index 200dfea..0696272 100644 (file)
@@ -1,5 +1,37 @@
 2002-05-08  Marcus Brinkmann  <marcus@g10code.de>
 
+       * w32-util.c: New static variable GET_PATH_LOCK.
+       (_gpgme_get_gpg_path): Remove superfluous NULL initializer.
+       Take lock while determining path.
+       (_gpgme_get_gpgsm_path): Likewise.
+       * version.c (do_subsystem_inits): Set DONE to 1 after
+       initialization.
+       (gpgme_get_engine_info): New variable ENGINE_INFO_LOCK.  Take lock
+       while determining engine info.
+       * rungpg.c (_gpgme_gpg_get_version): New variable
+       GPG_VERSION_LOCK.  Take the lock while determining the program
+       version.
+       * posix-io.c: Include "sema.h".
+       (_gpgme_io_spawn): New variable FIXED_SIGNALS_LOCK.  Take the lock
+       while fixing the signals.
+       (_gpgme_io_select): Make READFDS and WRITEFDS non-static.
+       * key.c: Include "sema.h".  New globals KEY_CACHE_LOCK and
+       KEY_REF_LOCK.
+       (capabilities_to_string): Make STRINGS very const.
+       (_gpgme_key_cache_add): Lock the key cache.
+       (_gpgme_key_cache_get): Likewise.
+       (gpgme_key_ref, gpgme_key_release): Lock the key_ref_lock.
+       * import.c (append_xml_impinfo): Make IMPORTED_FIELDS and
+       IMPORT_RES_FIELDS very const.  Make FIELD and FIELD_NAME a litle
+       const.
+       * engine.c (_gpgme_engine_get_info): New variable
+       ENGINE_INFO_LOCK.  Take lock while determining engine info.
+       * engine-gpgsm.c: Include "sema.h".
+       (_gpgme_gpgsm_get_version): New variable GPGSM_VERSION_LOCK.  Take
+       lock while getting program version.
+
+2002-05-08  Marcus Brinkmann  <marcus@g10code.de>
+
        * debug.h: New file.
        * Makefile.am (libgpgme_la_SOURCES): Add debug.h.
        * util.h: Removed all prototypes and declarations related to
index 7ecbf43..bb2e553 100644 (file)
@@ -48,6 +48,7 @@
 #include "wait.h"
 #include "io.h"
 #include "key.h"
+#include "sema.h"
 
 #include "engine-gpgsm.h"
 
@@ -99,10 +100,12 @@ const char *
 _gpgme_gpgsm_get_version (void)
 {
   static const char *gpgsm_version;
+  DEFINE_STATIC_LOCK (gpgsm_version_lock);
 
-  /* FIXME: Locking.  */
+  LOCK (gpgsm_version_lock);
   if (!gpgsm_version)
     gpgsm_version = _gpgme_get_program_version (_gpgme_get_gpgsm_path ());
+  UNLOCK (gpgsm_version_lock);
 
   return gpgsm_version;
 }
index c1dd630..ce5164b 100644 (file)
@@ -36,6 +36,7 @@
 #include "rungpg.h"
 #include "engine-gpgsm.h"
 
+
 struct engine_object_s
   {
     GpgmeProtocol protocol;
@@ -50,6 +51,7 @@ struct engine_object_s
       } engine;
 };
 
+
 struct reap_s
 {
   struct reap_s *next;
@@ -61,6 +63,7 @@ struct reap_s
 static struct reap_s *reap_list;
 DEFINE_STATIC_LOCK (reap_list_lock);
 
+
 /* Get the path of the engine for PROTOCOL.  */
 const char *
 _gpgme_engine_get_path (GpgmeProtocol proto)
@@ -76,6 +79,7 @@ _gpgme_engine_get_path (GpgmeProtocol proto)
     }
 }
 
+
 /* Get the version number of the engine for PROTOCOL.  */
 const char *
 _gpgme_engine_get_version (GpgmeProtocol proto)
@@ -91,6 +95,7 @@ _gpgme_engine_get_version (GpgmeProtocol proto)
     }
 }
 
+
 GpgmeError
 gpgme_engine_check_version (GpgmeProtocol proto)
 {
@@ -105,6 +110,7 @@ gpgme_engine_check_version (GpgmeProtocol proto)
     }
 }
 
+
 const char *
 _gpgme_engine_get_info (GpgmeProtocol proto)
 {
@@ -115,36 +121,35 @@ _gpgme_engine_get_info (GpgmeProtocol proto)
     " </engine>\n";
   static const char *const strproto[3] = { "OpenPGP", "CMS", NULL };
   static const char *engine_info[3];  /* FIXME: MAX_PROTO + 1*/
-  const char *path;
-  const char *version;
-  char *info;
+  DEFINE_STATIC_LOCK (engine_info_lock);
 
   if (proto > 2 /* FIXME MAX_PROTO */ || !strproto[proto])
     return NULL;
 
-  /* FIXME: Make sure that only one instance does run.  */
-  if (engine_info[proto])
-    return engine_info[proto];
-
-  path = _gpgme_engine_get_path (proto);
-  version = _gpgme_engine_get_version (proto);
-
-  if (!path || !version)
-    return NULL;
-
-  info = xtrymalloc (strlen(fmt) + strlen(strproto[proto]) + strlen(path)
-                     + strlen (version) + 1);
-  if (!info)
-    info = " <engine>\n"
-      "  <error>Out of core</error>\n"
-      " </engine>";
-  else
-    sprintf (info, fmt, strproto[proto], version, path);
-  engine_info[proto] = info;
+  LOCK (engine_info_lock);
+  if (!engine_info[proto])
+    {
+      const char *path = _gpgme_engine_get_path (proto);
+      const char *version = _gpgme_engine_get_version (proto);
 
+      if (path && version)
+       {
+         char *info = xtrymalloc (strlen (fmt) + strlen (strproto[proto])
+                                  + strlen (path) + strlen (version) + 1);
+         if (!info)
+           info = " <engine>\n"
+             "  <error>Out of core</error>\n"
+             " </engine>";
+         else
+           sprintf (info, fmt, strproto[proto], version, path);
+         engine_info[proto] = info;
+       }
+    }
+  UNLOCK (engine_info_lock);
   return engine_info[proto];
 }
 
+
 GpgmeError
 _gpgme_engine_new (GpgmeProtocol proto, EngineObject *r_engine)
 {
@@ -193,6 +198,7 @@ _gpgme_engine_new (GpgmeProtocol proto, EngineObject *r_engine)
   return err;
 }
 
+
 void
 _gpgme_engine_release (EngineObject engine)
 {
@@ -561,6 +567,7 @@ _gpgme_engine_start (EngineObject engine, void *opaque)
   return 0;
 }
 
+\f
 void
 _gpgme_engine_add_child_to_reap_list (void *buf, int buflen, pid_t pid)
 {
@@ -572,10 +579,10 @@ _gpgme_engine_add_child_to_reap_list (void *buf, int buflen, pid_t pid)
   memset (child, 0, sizeof *child);
   child->pid = pid;
   child->entered = time (NULL);
-  LOCK(reap_list_lock);
+  LOCK (reap_list_lock);
   child->next = reap_list;
   reap_list = child;
-  UNLOCK(reap_list_lock);
+  UNLOCK (reap_list_lock);
 }
 
 static void
index 71b2956..74487c8 100644 (file)
@@ -43,7 +43,7 @@ extern "C" {
    AM_PATH_GPGME macro) check that this header matches the installed
    library.  Warning: Do not edit the next line.  configure will do
    that for you!  */
-#define GPGME_VERSION "0.3.6"
+#define GPGME_VERSION "0.3.7-cvs"
 
 
 /* The opaque data types used by GPGME.  */
index 6d07051..338cc1d 100644 (file)
@@ -53,14 +53,14 @@ static void
 append_xml_impinfo (GpgmeData *rdh, GpgStatusCode code, char *args)
 {
 #define MAX_IMPORTED_FIELDS 14
-  static char *imported_fields[MAX_IMPORTED_FIELDS]
+  static const char *const imported_fields[MAX_IMPORTED_FIELDS]
     = { "keyid", "username", 0 };
-  static char *import_res_fields[MAX_IMPORTED_FIELDS]
+  static const char *const import_res_fields[MAX_IMPORTED_FIELDS]
     = { "count", "no_user_id", "imported", "imported_rsa",
        "unchanged", "n_uids", "n_subk", "n_sigs", "s_sigsn_revoc",
        "sec_read", "sec_imported", "sec_dups", "skipped_new", 0 };
-  char *field[MAX_IMPORTED_FIELDS];
-  char **field_name = 0;
+  const char *field[MAX_IMPORTED_FIELDS];
+  const char *const *field_name = 0;
   GpgmeData dh;
   int i;
 
index 01506de..0b3b787 100644 (file)
 #include "util.h"
 #include "ops.h"
 #include "key.h"
+#include "sema.h"
 
 #define ALLOC_CHUNK 1024
-#define my_isdigit(a) ( (a) >='0' && (a) <= '9' )
+#define my_isdigit(a) ((a) >='0' && (a) <= '9')
 
 #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;
+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
-hextobyte ( const byte *s )
+hextobyte (const byte *s)
 {
-    int c;
-
-    if ( *s >= '0' && *s <= '9' )
-       c = 16 * (*s - '0');
-    else if ( *s >= 'A' && *s <= 'F' )
-       c = 16 * (10 + *s - 'A');
-    else if ( *s >= 'a' && *s <= 'f' )
-       c = 16 * (10 + *s - 'a');
-    else
-       return -1;
-    s++;
-    if ( *s >= '0' && *s <= '9' )
-       c += *s - '0';
-    else if ( *s >= 'A' && *s <= 'F' )
-       c += 10 + *s - 'A';
-    else if ( *s >= 'a' && *s <= 'f' )
-       c += 10 + *s - 'a';
-    else
-       return -1;
-    return c;
+  int c;
+
+  if (*s >= '0' && *s <= '9')
+    c = 16 * (*s - '0');
+  else if (*s >= 'A' && *s <= 'F')
+    c = 16 * (10 + *s - 'A');
+  else if (*s >= 'a' && *s <= 'f')
+    c = 16 * (10 + *s - 'a');
+  else
+    return -1;
+  s++;
+  if (*s >= '0' && *s <= '9')
+    c += *s - '0';
+  else if (*s >= 'A' && *s <= 'F')
+    c += 10 + *s - 'A';
+  else if (*s >= 'a' && *s <= 'f')
+    c += 10 + *s - 'a';
+  else
+    return -1;
+  return c;
 }
 
 static int
 hash_key (const char *fpr, unsigned int *rhash)
 {
-    unsigned int hash;
-    int c;
-
-    if ( !fpr )                         return -1;
-    if ( (c = hextobyte(fpr)) == -1 )   return -1;
-    hash = c;
-    if ( (c = hextobyte(fpr+2)) == -1 ) return -1;
-    hash |= c << 8;
-    if ( (c = hextobyte(fpr+4)) == -1 ) return -1;
-    hash |= c << 16;
-    if ( (c = hextobyte(fpr+6)) == -1 ) return -1;
-    hash |= c << 24;
-
-    *rhash = hash;
-    return 0;
+  unsigned int hash;
+  int c;
+
+  if (!fpr)
+    return -1;
+  if ((c = hextobyte (fpr)) == -1)
+    return -1;
+  hash = c;
+  if ((c = hextobyte (fpr+2)) == -1)
+    return -1;
+  hash |= c << 8;
+  if ((c = hextobyte (fpr+4)) == -1)
+    return -1;
+  hash |= c << 16;
+  if ((c = hextobyte (fpr+6)) == -1)
+    return -1;
+  hash |= c << 24;
+
+  *rhash = hash;
+  return 0;
 }
 
 void
 _gpgme_key_cache_init (void)
 {
-    if (key_cache_initialized)
-        return;
-    key_cache_size = 503;
-    key_cache = xtrycalloc (key_cache_size, sizeof *key_cache);
-    if (!key_cache) {
-        key_cache_size = 0;
-        key_cache_initialized = 1;
-        return;
+  LOCK (key_cache_lock);
+  if (!key_cache_initialized)
+    {
+      key_cache_size = 503;
+      key_cache = xtrycalloc (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;
+       }
     }
-    /*
-     * 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;
-
-    /* FIXME: add locking */
-    if (!key_cache_initialized)
-        _gpgme_key_cache_init ();
-    if (!key_cache_size)
-        return; /* cache was not enabled */
-
-    /* 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) 
-                break; /* already in cache */
-            /* 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);
-                    return;
+  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 (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;
+      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;
+      item = key_cache_unused_items;
+      if (item)
+       {
+         key_cache_unused_items = item->next;
+         item->next = NULL;
         }
-        else {
-            item = xtrymalloc (sizeof *item);
-            if (!item)
-                return; /* out of core */
+      else
+       {
+         item = xtrymalloc (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;
+
+      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;
+  struct key_cache_item_s *item;
+  unsigned int hash;
 
-    if (!key_cache_size)
-        return NULL; /* cache not (yet) enabled */
+  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))
-        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;
+  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);
-                return item->key;
+      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;
             }
         }
     }
-    return NULL;
+  UNLOCK (key_cache_lock);
+  return NULL;
 }
 
 
 static const char *
-pkalgo_to_string ( int algo )
+pkalgo_to_string (int algo)
 {
-    switch (algo) {
-      case 1: 
-      case 2:
-      case 3: return "RSA";
-      case 16:
-      case 20: return "ElG";
-      case 17: return "DSA";
-      default: return "Unknown";
+  switch (algo)
+    {
+    case 1: 
+    case 2:
+    case 3:
+      return "RSA";
+    case 16:
+    case 20:
+      return "ElG";
+    case 17:
+      return "DSA";
+    default:
+      return "Unknown";
     }
 }
 
 
 static GpgmeError
-key_new ( GpgmeKey *r_key, int secret )
+key_new (GpgmeKey *r_key, int secret)
 {
-    GpgmeKey key;
-
-    *r_key = NULL;
-    key = xtrycalloc ( 1, sizeof *key );
-    if (!key)
-        return mk_error (Out_Of_Core);
-    key->ref_count = 1;
-    *r_key = key;
-    if (secret)
-        key->secret = 1;
-    return 0;
+  GpgmeKey key;
+
+  *r_key = NULL;
+  key = xtrycalloc (1, sizeof *key);
+  if (!key)
+    return mk_error (Out_Of_Core);
+  key->ref_count = 1;
+  *r_key = key;
+  if (secret)
+    key->secret = 1;
+  return 0;
 }
 
 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
-_gpgme_key_new_secret ( GpgmeKey *r_key )
+_gpgme_key_new_secret (GpgmeKey *r_key)
 {
-    return key_new ( r_key, 1 );
+  return key_new (r_key, 1);
 }
 
 
@@ -278,48 +333,52 @@ _gpgme_key_new_secret ( GpgmeKey *r_key )
  * Use this function to bump the reference counter.
  **/
 void
-gpgme_key_ref ( GpgmeKey key )
+gpgme_key_ref (GpgmeKey key)
 {
-    return_if_fail (key);
-    key->ref_count++;
+  return_if_fail (key);
+  LOCK (key_ref_lock);
+  key->ref_count++;
+  UNLOCK (key_ref_lock);
 }
 
 
 static struct subkey_s *
 add_subkey (GpgmeKey key, int secret)
 {
-    struct subkey_s *k, *kk;
-
-    k = xtrycalloc (1, sizeof *k);
-    if (!k)
-        return NULL;
-
-    if( !(kk=key->keys.next) )
-        key->keys.next = k;
-    else {
-        while ( kk->next )
-            kk = kk->next;
-        kk->next = k;
+  struct subkey_s *k, *kk;
+
+  k = xtrycalloc (1, sizeof *k);
+  if (!k)
+    return NULL;
+
+  if(!(kk = key->keys.next))
+    key->keys.next = k;
+  else
+    {
+      while (kk->next)
+       kk = kk->next;
+      kk->next = k;
     }
-    if (secret)
-        k->secret = 1;
-    return k;
+  if (secret)
+    k->secret = 1;
+  return k;
 }
 
+
 struct subkey_s *
 _gpgme_key_add_subkey (GpgmeKey key)
 {
-    return add_subkey (key, 0);
+  return add_subkey (key, 0);
 }
 
+
 struct subkey_s *
 _gpgme_key_add_secret_subkey (GpgmeKey key)
 {
-    return add_subkey (key, 1);
+  return add_subkey (key, 1);
 }
 
 
-
 /**
  * gpgme_key_release:
  * @key: Key Object or NULL
@@ -330,32 +389,39 @@ _gpgme_key_add_secret_subkey (GpgmeKey key)
  * as well as for every gpgme_key_ref() done on the key object.
  **/
 void
-gpgme_key_release ( GpgmeKey key )
+gpgme_key_release (GpgmeKey key)
 {
-    struct user_id_s *u, *u2;
-    struct subkey_s *k, *k2;
+  struct user_id_s *u, *u2;
+  struct subkey_s *k, *k2;
 
-    if (!key)
-        return;
+  if (!key)
+    return;
 
-    assert (key->ref_count);
-    if ( --key->ref_count )
-        return;
+  LOCK (key_ref_lock);
+  assert (key->ref_count);
+  if (--key->ref_count)
+    {
+      UNLOCK (key_ref_lock);
+      return;
+    }
+  UNLOCK (key_ref_lock);
 
-    xfree (key->keys.fingerprint);
-    for (k = key->keys.next; k; k = k2 ) {
-        k2 = k->next;
-        xfree (k->fingerprint);
-        xfree (k);
+  xfree (key->keys.fingerprint);
+  for (k = key->keys.next; k; k = k2)
+    {
+      k2 = k->next;
+      xfree (k->fingerprint);
+      xfree (k);
     }
-    for (u = key->uids; u; u = u2 ) {
-        u2 = u->next;
-        xfree (u);
+  for (u = key->uids; u; u = u2)
+    {
+      u2 = u->next;
+      xfree (u);
     }
-    xfree (key->issuer_serial);
-    xfree (key->issuer_name);
-    xfree (key->chain_id);
-    xfree (key);
+  xfree (key->issuer_serial);
+  xfree (key->issuer_name);
+  xfree (key->chain_id);
+  xfree (key);
 }
 
 /**
@@ -367,111 +433,129 @@ gpgme_key_release ( GpgmeKey key )
 void
 gpgme_key_unref (GpgmeKey key)
 {
-    gpgme_key_release (key);
+  gpgme_key_release (key);
 }
 
 
 static char *
-set_user_id_part ( char *tail, const char *buf, size_t len )
+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;
+  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 )
+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 == '<' )
-                in_email++; /* not legal but anyway */
-            else if (*s== '>') {
-                if ( !--in_email ) {
-                    if (!uid->email_part) {
+  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) {
+                     }
+                 }
+             }
+         }
+        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 ) {
+                     }
+                 }
+             }
+         }
+        else if (*s == '<')
+         {
+            if (in_name)
+             {
+                if (!uid->name_part)
+                 {
                     uid->name_part = tail;
-                    tail = set_user_id_part (tail, start, s-start );
-                }
+                    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 ) {
+         }
+        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' ) {
+         }
+        else if (!in_name && *s != ' ' && *s != '\t')
+         {
             in_name = 1;
             start = s;
-        }    
-    }
-
-    if ( in_name ) {
-        if ( !uid->name_part ) {
+         }    
+      }
+    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 = set_user_id_part (tail, start, s-start);
+         }
+      }
+    /* Let unused parts point to an EOS.  */
     tail--;
     if (!uid->name_part)
-        uid->name_part = tail;
+      uid->name_part = tail;
     if (!uid->email_part)
-        uid->email_part = tail;
+      uid->email_part = tail;
     if (!uid->comment_part)
-        uid->comment_part = tail;
-
+      uid->comment_part = tail;
 }
 
 static void
-parse_x509_user_id ( struct user_id_s *uid, char *tail )
+parse_x509_user_id (struct user_id_s *uid, char *tail)
 {
   const char *s;
 
   s=uid->name; 
-  if (*s == '<' && s[strlen(s)-1] == '>')
+  if (*s == '<' && s[strlen (s) - 1] == '>')
     uid->email_part = s;
   
-  /* let unused parts point to an EOS */ 
+  /* Let unused parts point to an EOS.  */
   tail--;
   if (!uid->name_part)
     uid->name_part = tail;
@@ -584,64 +668,64 @@ _gpgme_key_append_name (GpgmeKey key, const char *s)
 
 
 static void
-add_otag ( GpgmeData d, const char *tag )
+add_otag (GpgmeData d, const char *tag)
 {
-    _gpgme_data_append_string ( d, "    <" );
-    _gpgme_data_append_string ( d, tag );
-    _gpgme_data_append_string ( d, ">" );
+  _gpgme_data_append_string (d, "    <");
+  _gpgme_data_append_string (d, tag);
+  _gpgme_data_append_string (d, ">");
 }
 
 static void
-add_ctag ( GpgmeData d, const char *tag )
+add_ctag (GpgmeData d, const char *tag)
 {
-    _gpgme_data_append_string ( d, "</" );
-    _gpgme_data_append_string ( d, tag );
-    _gpgme_data_append_string ( d, ">\n" );
+  _gpgme_data_append_string (d, "</");
+  _gpgme_data_append_string (d, tag);
+  _gpgme_data_append_string (d, ">\n");
 }
 
 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)
 {
-    add_otag (d, tag);
-    _gpgme_data_append_string_for_xml ( d, string );
-    add_ctag (d, tag); 
+  add_otag (d, tag);
+  _gpgme_data_append_string_for_xml (d, string);
+  add_ctag (d, tag); 
 }
 
 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)
 {
-    char buf[30];
-    sprintf (buf, "%u", val );
-    add_tag_and_string ( d, tag, buf );
+  char buf[30];
+  sprintf (buf, "%u", val);
+  add_tag_and_string (d, tag, buf);
 }
 
 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)
 {
-    char buf[30];
+  char buf[30];
 
-    if (!val || val == (time_t)-1 )
-        return;
-    sprintf (buf, "%lu", (unsigned long)val );
-    add_tag_and_string ( d, tag, buf );
+  if (!val || val == (time_t) - 1)
+    return;
+  sprintf (buf, "%lu", (unsigned long) val);
+  add_tag_and_string (d, tag, buf);
 }
 
 static void
 one_uid_as_xml (GpgmeData d, struct user_id_s *u)
 {
-    _gpgme_data_append_string (d, "  <userid>\n");
-    if ( u->invalid )
-        _gpgme_data_append_string ( d, "    <invalid/>\n");
-    if ( u->revoked )
-        _gpgme_data_append_string ( d, "    <revoked/>\n");
-    add_tag_and_string ( d, "raw", u->name );
-    if ( *u->name_part )
-        add_tag_and_string ( d, "name", u->name_part );
-    if ( *u->email_part )
-        add_tag_and_string ( d, "email", u->email_part );
-    if ( *u->comment_part )
-        add_tag_and_string ( d, "comment", u->comment_part );
-    _gpgme_data_append_string (d, "  </userid>\n");
+  _gpgme_data_append_string (d, "  <userid>\n");
+  if (u->invalid)
+    _gpgme_data_append_string (d, "    <invalid/>\n");
+  if (u->revoked)
+    _gpgme_data_append_string (d, "    <revoked/>\n");
+  add_tag_and_string (d, "raw", u->name);
+  if (*u->name_part)
+    add_tag_and_string (d, "name", u->name_part);
+  if (*u->email_part)
+    add_tag_and_string (d, "email", u->email_part);
+  if (*u->comment_part)
+    add_tag_and_string (d, "comment", u->comment_part);
+  _gpgme_data_append_string (d, "  </userid>\n");
 }
 
 
@@ -656,37 +740,37 @@ one_uid_as_xml (GpgmeData d, struct user_id_s *u)
  *                a NULL passed as @key
  **/
 char *
-gpgme_key_get_as_xml ( GpgmeKey key )
+gpgme_key_get_as_xml (GpgmeKey key)
 {
   GpgmeData d;
   struct user_id_s *u;
   struct subkey_s *k;
   
-  if ( !key )
+  if (!key)
     return NULL;
   
-  if ( gpgme_data_new ( &d ) )
+  if (gpgme_data_new (&d))
     return NULL;
   
-  _gpgme_data_append_string ( d, "<GnupgKeyblock>\n"
-                              "  <mainkey>\n" );
-  if ( key->keys.secret )
-    _gpgme_data_append_string ( d, "    <secret/>\n");
-  if ( key->keys.flags.invalid )
-    _gpgme_data_append_string ( d, "    <invalid/>\n");
-  if ( key->keys.flags.revoked )
-    _gpgme_data_append_string ( d, "    <revoked/>\n");
-  if ( key->keys.flags.expired )
-    _gpgme_data_append_string ( d, "    <expired/>\n");
-  if ( key->keys.flags.disabled )
-    _gpgme_data_append_string ( d, "    <disabled/>\n");
-  add_tag_and_string (d, "keyid", key->keys.keyid );   
+  _gpgme_data_append_string (d, "<GnupgKeyblock>\n"
+                            "  <mainkey>\n");
+  if (key->keys.secret)
+    _gpgme_data_append_string (d, "    <secret/>\n");
+  if (key->keys.flags.invalid)
+    _gpgme_data_append_string (d, "    <invalid/>\n");
+  if (key->keys.flags.revoked)
+    _gpgme_data_append_string (d, "    <revoked/>\n");
+  if (key->keys.flags.expired)
+    _gpgme_data_append_string (d, "    <expired/>\n");
+  if (key->keys.flags.disabled)
+    _gpgme_data_append_string (d, "    <disabled/>\n");
+  add_tag_and_string (d, "keyid", key->keys.keyid);
   if (key->keys.fingerprint)
-    add_tag_and_string (d, "fpr", key->keys.fingerprint );
-  add_tag_and_uint (d, "algo", key->keys.key_algo );
-  add_tag_and_uint (d, "len", key->keys.key_len );
-  add_tag_and_time (d, "created", key->keys.timestamp );
-  add_tag_and_time (d, "expire", key->keys.expires_at );
+    add_tag_and_string (d, "fpr", key->keys.fingerprint);
+  add_tag_and_uint (d, "algo", key->keys.key_algo);
+  add_tag_and_uint (d, "len", key->keys.key_len);
+  add_tag_and_time (d, "created", key->keys.timestamp);
+  add_tag_and_time (d, "expire", key->keys.expires_at);
   if (key->issuer_serial)
     add_tag_and_string (d, "serial", key->issuer_serial);
   if (key->issuer_name)
@@ -694,35 +778,35 @@ gpgme_key_get_as_xml ( GpgmeKey key )
   if (key->chain_id)
     add_tag_and_string (d, "chainid", key->chain_id);
   _gpgme_data_append_string (d, "  </mainkey>\n");
-  
+
   /* Now the user IDs.  */
   for (u = key->uids; u; u = u->next)
     one_uid_as_xml (d,u);
   
-  /* and now the subkeys */
-  for (k=key->keys.next; k; k = k->next )
+  /* And now the subkeys.  */
+  for (k = key->keys.next; k; k = k->next)
     {
       _gpgme_data_append_string (d, "  <subkey>\n");
-      if ( k->secret )
-        _gpgme_data_append_string ( d, "    <secret/>\n");
-      if ( k->flags.invalid )
-        _gpgme_data_append_string ( d, "    <invalid/>\n");
-      if ( k->flags.revoked )
-        _gpgme_data_append_string ( d, "    <revoked/>\n");
-      if ( k->flags.expired )
-        _gpgme_data_append_string ( d, "    <expired/>\n");
-      if ( k->flags.disabled )
-        _gpgme_data_append_string ( d, "    <disabled/>\n");
-      add_tag_and_string (d, "keyid", k->keyid );   
+      if (k->secret)
+        _gpgme_data_append_string (d, "    <secret/>\n");
+      if (k->flags.invalid)
+        _gpgme_data_append_string (d, "    <invalid/>\n");
+      if (k->flags.revoked)
+        _gpgme_data_append_string (d, "    <revoked/>\n");
+      if (k->flags.expired)
+        _gpgme_data_append_string (d, "    <expired/>\n");
+      if (k->flags.disabled)
+        _gpgme_data_append_string (d, "    <disabled/>\n");
+      add_tag_and_string (d, "keyid", k->keyid);
       if (k->fingerprint)
-        add_tag_and_string (d, "fpr", k->fingerprint );
-      add_tag_and_uint (d, "algo", k->key_algo );
-      add_tag_and_uint (d, "len", k->key_len );
-      add_tag_and_time (d, "created", k->timestamp );
-      add_tag_and_time (d, "expire", k->expires_at );
+        add_tag_and_string (d, "fpr", k->fingerprint);
+      add_tag_and_uint (d, "algo", k->key_algo);
+      add_tag_and_uint (d, "len", k->key_len);
+      add_tag_and_time (d, "created", k->timestamp);
+      add_tag_and_time (d, "expire", k->expires_at);
       _gpgme_data_append_string (d, "  </subkey>\n");
     }
-  _gpgme_data_append_string ( d, "</GnupgKeyblock>\n" );
+  _gpgme_data_append_string (d, "</GnupgKeyblock>\n");
   
   return _gpgme_data_release_and_return_string (d);
 }
@@ -731,23 +815,23 @@ gpgme_key_get_as_xml ( GpgmeKey key )
 static const char *
 capabilities_to_string (struct subkey_s *k)
 {
-    static char *strings[8] = {
-        "",
-        "c",
-        "s",
-        "sc",
-        "e",
-        "ec",
-        "es",
-        "esc"
+  static const char *const strings[8] =
+    {
+      "",
+      "c",
+      "s",
+      "sc",
+      "e",
+      "ec",
+      "es",
+      "esc"
     };
-    return strings[  (!!k->flags.can_encrypt << 2)
-                   | (!!k->flags.can_sign    << 1)
-                   | (!!k->flags.can_certify     ) ];
+  return strings[(!!k->flags.can_encrypt << 2)
+                | (!!k->flags.can_sign    << 1)
+                | (!!k->flags.can_certify     )];
 }
 
 
-
 /**
  * gpgme_key_get_string_attr:
  * @key: Key Object
@@ -764,116 +848,133 @@ capabilities_to_string (struct subkey_s *k)
  * as the key object itself is valid.
  **/
 const char *
-gpgme_key_get_string_attr ( GpgmeKey key, GpgmeAttr what,
-                            const void *reserved, int idx )
+gpgme_key_get_string_attr (GpgmeKey key, GpgmeAttr what,
+                          const void *reserved, int idx)
 {
-    const char *val = NULL;
-    struct subkey_s *k;
-    struct user_id_s *u;
-
-    if (!key)
-        return NULL;
-    if (reserved)
-        return NULL;
-    if (idx < 0)
-        return NULL;
-
-    switch (what) {
-      case GPGME_ATTR_KEYID:
-        for (k=&key->keys; k && idx; k=k->next, idx-- )
-            ;
-        if (k) 
-            val = k->keyid;
-        break;
-      case GPGME_ATTR_FPR:
-        for (k=&key->keys; k && idx; k=k->next, idx-- )
-            ;
-        if (k) 
-            val = k->fingerprint;
-        break;
-      case GPGME_ATTR_ALGO:    
-        for (k=&key->keys; k && idx; k=k->next, idx-- )
-            ;
-        if (k) 
-            val = pkalgo_to_string (k->key_algo);
-        break;
-      case GPGME_ATTR_LEN:     
-      case GPGME_ATTR_CREATED: 
-      case GPGME_ATTR_EXPIRE:  
-        break; /* use another get function */
-      case GPGME_ATTR_OTRUST:  
-        val = "[fixme]";
-        break;
-      case GPGME_ATTR_USERID:  
-        for (u=key->uids; u && idx; u=u->next, idx-- )
-            ;
-        val = u? u->name : NULL;
-        break;
-      case GPGME_ATTR_NAME:   
-        for (u=key->uids; u && idx; u=u->next, idx-- )
-            ;
-        val = u? u->name_part : NULL;
-        break;
-      case GPGME_ATTR_EMAIL:
-        for (u=key->uids; u && idx; u=u->next, idx-- )
-            ;
-        val = u? u->email_part : NULL;
-        break;
-      case GPGME_ATTR_COMMENT:
-        for (u=key->uids; u && idx; u=u->next, idx-- )
-            ;
-        val = u? u->comment_part : NULL;
-        break;
-      case GPGME_ATTR_VALIDITY:
-        for (u=key->uids; u && idx; u=u->next, idx-- )
-            ;
-        if (u) {
-            switch (u->validity) {
-              case GPGME_VALIDITY_UNKNOWN:   val = "?"; break;
-              case GPGME_VALIDITY_UNDEFINED: val = "q"; break;
-              case GPGME_VALIDITY_NEVER:     val = "n"; break;
-              case GPGME_VALIDITY_MARGINAL:  val = "m"; break;
-              case GPGME_VALIDITY_FULL:      val = "f"; break;
-              case GPGME_VALIDITY_ULTIMATE:  val = "u"; break;
+  const char *val = NULL;
+  struct subkey_s *k;
+  struct user_id_s *u;
+
+  if (!key)
+    return NULL;
+  if (reserved)
+    return NULL;
+  if (idx < 0)
+    return NULL;
+
+  switch (what)
+    {
+    case GPGME_ATTR_KEYID:
+      for (k = &key->keys; k && idx; k = k->next, idx--)
+       ;
+      if (k) 
+       val = k->keyid;
+      break;
+    case GPGME_ATTR_FPR:
+      for (k = &key->keys; k && idx; k = k->next, idx--)
+       ;
+      if (k) 
+       val = k->fingerprint;
+      break;
+    case GPGME_ATTR_ALGO:    
+      for (k = &key->keys; k && idx; k=k->next, idx--)
+       ;
+      if (k) 
+       val = pkalgo_to_string (k->key_algo);
+      break;
+    case GPGME_ATTR_LEN:     
+    case GPGME_ATTR_CREATED: 
+    case GPGME_ATTR_EXPIRE:  
+      /* Use another get function.  */
+      break;
+    case GPGME_ATTR_OTRUST:
+      val = "[fixme]";
+      break;
+    case GPGME_ATTR_USERID:  
+      for (u = key->uids; u && idx; u = u->next, idx--)
+       ;
+      val = u ? u->name : NULL;
+      break;
+    case GPGME_ATTR_NAME:   
+      for (u = key->uids; u && idx; u = u->next, idx--)
+       ;
+      val = u ? u->name_part : NULL;
+      break;
+    case GPGME_ATTR_EMAIL:
+      for (u = key->uids; u && idx; u = u->next, idx--)
+       ;
+      val = u ? u->email_part : NULL;
+      break;
+    case GPGME_ATTR_COMMENT:
+      for (u = key->uids; u && idx; u = u->next, idx--)
+       ;
+      val = u ? u->comment_part : NULL;
+      break;
+    case GPGME_ATTR_VALIDITY:
+      for (u = key->uids; u && idx; u = u->next, idx--)
+       ;
+      if (u)
+       {
+         switch (u->validity)
+           {
+           case GPGME_VALIDITY_UNKNOWN:
+             val = "?";
+             break;
+           case GPGME_VALIDITY_UNDEFINED:
+             val = "q";
+             break;
+           case GPGME_VALIDITY_NEVER:
+             val = "n";
+             break;
+           case GPGME_VALIDITY_MARGINAL:
+             val = "m";
+             break;
+           case GPGME_VALIDITY_FULL:
+             val = "f";
+             break;
+           case GPGME_VALIDITY_ULTIMATE:
+             val = "u";
+             break;
             }
         }
-        break;
-      case GPGME_ATTR_LEVEL:  /* not used here */
-      case GPGME_ATTR_TYPE:
-      case GPGME_ATTR_KEY_REVOKED:
-      case GPGME_ATTR_KEY_INVALID:
-      case GPGME_ATTR_KEY_EXPIRED:
-      case GPGME_ATTR_KEY_DISABLED:
-      case GPGME_ATTR_UID_REVOKED:
-      case GPGME_ATTR_UID_INVALID:
-      case GPGME_ATTR_CAN_ENCRYPT:
-      case GPGME_ATTR_CAN_SIGN:
-      case GPGME_ATTR_CAN_CERTIFY:
-        break;
-      case GPGME_ATTR_IS_SECRET:
-        if (key->secret)
-            val = "1";
-        break;
-      case GPGME_ATTR_KEY_CAPS:    
-        for (k=&key->keys; k && idx; k=k->next, idx-- )
-            ;
-        if (k) 
-            val = capabilities_to_string (k);
-        break;
-      case GPGME_ATTR_SERIAL:
-        val = key->issuer_serial;
-        break;
-      case GPGME_ATTR_ISSUER:
-        val = key->issuer_name;
-        break;
-      case GPGME_ATTR_CHAINID:
-        val = key->chain_id;
-        break;
-      case GPGME_ATTR_SIG_STATUS:
-        /* not of any use here */
-        break;
+      break;
+    case GPGME_ATTR_LEVEL:
+    case GPGME_ATTR_TYPE:
+    case GPGME_ATTR_KEY_REVOKED:
+    case GPGME_ATTR_KEY_INVALID:
+    case GPGME_ATTR_KEY_EXPIRED:
+    case GPGME_ATTR_KEY_DISABLED:
+    case GPGME_ATTR_UID_REVOKED:
+    case GPGME_ATTR_UID_INVALID:
+    case GPGME_ATTR_CAN_ENCRYPT:
+    case GPGME_ATTR_CAN_SIGN:
+    case GPGME_ATTR_CAN_CERTIFY:
+      /* Not used here.  */
+      break;
+    case GPGME_ATTR_IS_SECRET:
+      if (key->secret)
+       val = "1";
+      break;
+    case GPGME_ATTR_KEY_CAPS:    
+      for (k = &key->keys; k && idx; k = k->next, idx--)
+       ;
+      if (k) 
+       val = capabilities_to_string (k);
+      break;
+    case GPGME_ATTR_SERIAL:
+      val = key->issuer_serial;
+      break;
+    case GPGME_ATTR_ISSUER:
+      val = key->issuer_name;
+      break;
+    case GPGME_ATTR_CHAINID:
+      val = key->chain_id;
+      break;
+    case GPGME_ATTR_SIG_STATUS:
+      /* Not of any use here.  */
+      break;
     }
-    return val;
+  return val;
 }
 
 
@@ -894,101 +995,102 @@ gpgme_key_get_string_attr ( GpgmeKey key, GpgmeAttr what,
  * Return value: 0 or the requested value.
  **/
 unsigned long
-gpgme_key_get_ulong_attr ( GpgmeKey key, GpgmeAttr what,
-                           const void *reserved, int idx )
+gpgme_key_get_ulong_attr (GpgmeKey key, GpgmeAttr what,
+                         const void *reserved, int idx)
 {
-    unsigned long val = 0;
-    struct subkey_s *k;
-    struct user_id_s *u;
-
-    if (!key)
-        return 0;
-    if (reserved)
-        return 0;
-    if (idx < 0)
-        return 0;
-
-    switch (what) {
-      case GPGME_ATTR_ALGO:    
-        for (k=&key->keys; k && idx; k=k->next, idx-- )
-            ;
-        if (k) 
-            val = (unsigned long)k->key_algo;
-        break;
-      case GPGME_ATTR_LEN:     
-        for (k=&key->keys; k && idx; k=k->next, idx-- )
-            ;
-        if (k) 
-            val = (unsigned long)k->key_len;
-        break;
-      case GPGME_ATTR_CREATED: 
-        for (k=&key->keys; k && idx; k=k->next, idx-- )
-            ;
-        if (k) 
-            val = k->timestamp < 0? 0L:(unsigned long)k->timestamp;
-        break;
-      case GPGME_ATTR_EXPIRE: 
-        for (k=&key->keys; k && idx; k=k->next, idx-- )
-            ;
-        if (k) 
-            val = k->expires_at < 0? 0L:(unsigned long)k->expires_at;
-        break;
-      case GPGME_ATTR_VALIDITY:
-        for (u=key->uids; u && idx; u=u->next, idx-- )
-            ;
-        if (u)
-            val = u->validity;
-        break;
-      case GPGME_ATTR_IS_SECRET:
-        val = !!key->secret;
-        break;
-      case GPGME_ATTR_KEY_REVOKED:
-        for (k=&key->keys; k && idx; k=k->next, idx-- )
-            ;
-        if (k) 
-            val = k->flags.revoked;
-        break;
-      case GPGME_ATTR_KEY_INVALID:
-        for (k=&key->keys; k && idx; k=k->next, idx-- )
-            ;
-        if (k) 
-            val = k->flags.invalid;
-        break;
-      case GPGME_ATTR_KEY_EXPIRED:
-        for (k=&key->keys; k && idx; k=k->next, idx-- )
-            ;
-        if (k) 
-            val = k->flags.expired;
-        break;
-      case GPGME_ATTR_KEY_DISABLED:
-        for (k=&key->keys; k && idx; k=k->next, idx-- )
-            ;
-        if (k) 
-            val = k->flags.disabled;
-        break;
-      case GPGME_ATTR_UID_REVOKED:
-        for (u=key->uids; u && idx; u=u->next, idx-- )
-            ;
-        if (u)
-            val = u->revoked;
-        break;
-      case GPGME_ATTR_UID_INVALID:
-        for (u=key->uids; u && idx; u=u->next, idx-- )
-            ;
-        if (u)
-            val = u->invalid;
-        break;
-      case GPGME_ATTR_CAN_ENCRYPT:
-        val = key->gloflags.can_encrypt;
-        break;
-      case GPGME_ATTR_CAN_SIGN:
-        val = key->gloflags.can_sign;
-        break;
-      case GPGME_ATTR_CAN_CERTIFY:
-        val = key->gloflags.can_certify;
-        break;
-      default:
-        break;
+  unsigned long val = 0;
+  struct subkey_s *k;
+  struct user_id_s *u;
+
+  if (!key)
+    return 0;
+  if (reserved)
+    return 0;
+  if (idx < 0)
+    return 0;
+
+  switch (what)
+    {
+    case GPGME_ATTR_ALGO:    
+      for (k = &key->keys; k && idx; k=k->next, idx--)
+       ;
+      if (k) 
+       val = (unsigned long) k->key_algo;
+      break;
+    case GPGME_ATTR_LEN:     
+      for (k = &key->keys; k && idx; k = k->next, idx--)
+       ;
+      if (k) 
+       val = (unsigned long) k->key_len;
+      break;
+    case GPGME_ATTR_CREATED: 
+      for (k = &key->keys; k && idx; k = k->next, idx--)
+       ;
+      if (k) 
+       val = k->timestamp < 0 ? 0L : (unsigned long) k->timestamp;
+      break;
+    case GPGME_ATTR_EXPIRE: 
+      for (k = &key->keys; k && idx; k = k->next, idx--)
+       ;
+      if (k) 
+       val = k->expires_at < 0 ? 0L : (unsigned long) k->expires_at;
+      break;
+    case GPGME_ATTR_VALIDITY:
+      for (u = key->uids; u && idx; u = u->next, idx--)
+       ;
+      if (u)
+       val = u->validity;
+      break;
+    case GPGME_ATTR_IS_SECRET:
+      val = !!key->secret;
+      break;
+    case GPGME_ATTR_KEY_REVOKED:
+      for (k = &key->keys; k && idx; k = k->next, idx--)
+       ;
+      if (k) 
+       val = k->flags.revoked;
+      break;
+    case GPGME_ATTR_KEY_INVALID:
+      for (k = &key->keys; k && idx; k = k->next, idx--)
+       ;
+      if (k) 
+       val = k->flags.invalid;
+      break;
+    case GPGME_ATTR_KEY_EXPIRED:
+      for (k = &key->keys; k && idx; k = k->next, idx--)
+       ;
+      if (k) 
+       val = k->flags.expired;
+      break;
+    case GPGME_ATTR_KEY_DISABLED:
+      for (k = &key->keys; k && idx; k = k->next, idx--)
+       ;
+      if (k) 
+       val = k->flags.disabled;
+      break;
+    case GPGME_ATTR_UID_REVOKED:
+      for (u = key->uids; u && idx; u = u->next, idx--)
+       ;
+      if (u)
+       val = u->revoked;
+      break;
+    case GPGME_ATTR_UID_INVALID:
+      for (u = key->uids; u && idx; u = u->next, idx--)
+       ;
+      if (u)
+       val = u->invalid;
+      break;
+    case GPGME_ATTR_CAN_ENCRYPT:
+      val = key->gloflags.can_encrypt;
+      break;
+    case GPGME_ATTR_CAN_SIGN:
+      val = key->gloflags.can_sign;
+      break;
+    case GPGME_ATTR_CAN_CERTIFY:
+      val = key->gloflags.can_certify;
+      break;
+    default:
+      break;
     }
-    return val;
+  return val;
 }
index be418d2..fbfbd22 100644 (file)
@@ -36,6 +36,7 @@
 
 #include "util.h"
 #include "io.h"
+#include "sema.h"
 
 static struct
 {
@@ -149,10 +150,12 @@ _gpgme_io_spawn (const char *path, char **argv,
                 struct spawn_fd_item_s *fd_child_list,
                 struct spawn_fd_item_s *fd_parent_list)
 {
-  static volatile int fixed_signals;
+  static int fixed_signals;
+  DEFINE_STATIC_LOCK (fixed_signals_lock);
   pid_t pid;
   int i;
 
+  LOCK (fixed_signals_lock);
   if (!fixed_signals)
     { 
       struct sigaction act;
@@ -166,8 +169,8 @@ _gpgme_io_spawn (const char *path, char **argv,
          sigaction (SIGPIPE, &act, NULL);
         }
       fixed_signals = 1;
-      /* XXX: This is not really MT safe.  */
     }
+  UNLOCK (fixed_signals_lock);
 
   pid = fork ();
   if (pid == -1) 
@@ -285,8 +288,8 @@ _gpgme_io_kill (int pid, int hard)
 int
 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds)
 {
-  static fd_set readfds;
-  static fd_set writefds;
+  fd_set readfds;
+  fd_set writefds;
   int any, i, max_fd, n, count;
   struct timeval timeout = { 1, 0 }; /* Use a 1s timeout.  */
   void *dbg_help = NULL;
@@ -314,7 +317,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds)
         }
       else if (fds[i].for_write)
        {
-         assert (!FD_ISSET ( fds[i].fd, &writefds));
+         assert (!FD_ISSET (fds[i].fd, &writefds));
          FD_SET (fds[i].fd, &writefds);
          if (fds[i].fd > max_fd)
            max_fd = fds[i].fd;
index 668ed26..1d6aa2b 100644 (file)
@@ -167,11 +167,12 @@ const char *
 _gpgme_gpg_get_version (void)
 {
   static const char *gpg_version;
+  DEFINE_STATIC_LOCK (gpg_version_lock);
 
-  /* FIXME: Locking.  */
+  LOCK (gpg_version_lock);
   if (!gpg_version)
     gpg_version = _gpgme_get_program_version (_gpgme_get_gpg_path ());
-
+  UNLOCK (gpg_version_lock);
   return gpg_version;
 }
 
index 104f71d..2b2874b 100644 (file)
@@ -43,6 +43,7 @@ do_subsystem_inits (void)
     return;
   _gpgme_sema_subsystem_init ();
   _gpgme_key_cache_init ();
+  done = 1;
 }
 
 static const char*
@@ -148,45 +149,50 @@ const char *
 gpgme_get_engine_info ()
 {
   static const char *engine_info;
-  const char *openpgp_info = _gpgme_engine_get_info (GPGME_PROTOCOL_OpenPGP);
-  const char *cms_info = _gpgme_engine_get_info (GPGME_PROTOCOL_CMS);
-  char *info;
+  DEFINE_STATIC_LOCK (engine_info_lock);
 
-  /* FIXME: Make sure that only one instance does run.  */
-  if (engine_info)
-    return engine_info;
-
-  if (!openpgp_info && !cms_info)
-    info = "<EngineInfo>\n</EngineInfo>\n";
-  else if (!openpgp_info || !cms_info)
+  LOCK (engine_info_lock);
+  if (!engine_info)
     {
-      const char *fmt = "<EngineInfo>\n"
-        "%s"
-        "</EngineInfo>\n";
+      const char *openpgp_info = _gpgme_engine_get_info (GPGME_PROTOCOL_OpenPGP);
+      const char *cms_info = _gpgme_engine_get_info (GPGME_PROTOCOL_CMS);
+      char *info;
 
-      info = xtrymalloc (strlen(fmt) + strlen(openpgp_info
-                                              ? openpgp_info : cms_info) + 1);
-      if (info)
-        sprintf (info, fmt, openpgp_info ? openpgp_info : cms_info);
-    }
-  else
-    {
-      const char *fmt = "<EngineInfo>\n"
-        "%s%s"
-        "</EngineInfo>\n";
-      info = xtrymalloc (strlen(fmt) + strlen(openpgp_info)
-                         + strlen (cms_info) + 1);
-      if (info)
-        sprintf (info, fmt, openpgp_info, cms_info);
+      if (!openpgp_info && !cms_info)
+       info = "<EngineInfo>\n</EngineInfo>\n";
+      else if (!openpgp_info || !cms_info)
+       {
+         const char *fmt = "<EngineInfo>\n"
+           "%s"
+           "</EngineInfo>\n";
+
+         info = xtrymalloc (strlen (fmt)
+                            + strlen (openpgp_info
+                                     ? openpgp_info : cms_info) + 1);
+         if (info)
+           sprintf (info, fmt, openpgp_info ? openpgp_info : cms_info);
+       }
+      else
+       {
+         const char *fmt = "<EngineInfo>\n"
+           "%s%s"
+           "</EngineInfo>\n";
+         info = xtrymalloc (strlen (fmt) + strlen (openpgp_info)
+                            + strlen (cms_info) + 1);
+         if (info)
+           sprintf (info, fmt, openpgp_info, cms_info);
+       }
+      if (!info)
+       info = "<EngineInfo>\n"
+         "  <error>Out of core</error>\n"
+         "</EngineInfo>\n";
+      engine_info = info;
     }
-  if (!info)
-    info = "<EngineInfo>\n"
-      "  <error>Out of core</error>\n"
-      "</EngineInfo>\n";
-  engine_info = info;
+  UNLOCK (engine_info_lock);
   return engine_info;
 }
 
+
 /**
  * gpgme_check_engine:
  * 
index c6e1931..a0a4224 100644 (file)
@@ -37,6 +37,8 @@
 
 #include "util.h"
 
+DEFINE_STATIC_LOCK (get_path_lock);
+
 /* Return a string from the Win32 Registry or NULL in case of error.
   Caller must release the return value.  A NULL for root is an alias
   for HKEY_CURRENT_USER.  */
@@ -112,27 +114,31 @@ find_program_in_registry (const char *name)
 const char *
 _gpgme_get_gpg_path (void)
 {
-  static char *gpg_program = NULL;
-    
+  static char *gpg_program;
+
+  LOCK (get_path_lock);
   if (!gpg_program)
     gpg_program = find_program_in_registry ("gpgProgram");
 #ifdef GPG_PATH
   if (!gpg_program)
     gpg_program = GPG_PATH;
 #endif
+  UNLOCK (get_path_lock);
   return gpg_program;
 }
 
 const char *
 _gpgme_get_gpgsm_path (void)
 {
-  static char *gpgsm_program = NULL;
-    
+  static char *gpgsm_program;
+
+  LOCK (get_path_lock);
   if (!gpgsm_program)
     gpgsm_program = find_program_in_registry ("gpgsmProgram");
 #ifdef GPGSM_PATH
   if (!gpgsm_program)
     gpgsm_program = GPGSM_PATH;
 #endif
+  UNLOCK (get_path_lock);
   return gpgsm_program;
 }