Added SILC Thread Queue API
[silc.git] / lib / silcskr / silcskr.c
index 1ed22beb005b340c51cf271c4855d95b9b856e0a..7459d43242a02a9f49eb4d3aec58c6604cc176e0 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2005 - 2006 Pekka Riikonen
+  Copyright (C) 2005 - 2007 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -20,8 +20,6 @@
 #include "silc.h"
 #include "silcskr.h"
 
-/* XXX Locking, when removing keys */
-
 /************************** Types and definitions ***************************/
 
 /* Search constraints */
@@ -53,16 +51,16 @@ typedef struct {
 
 #if defined(SILC_DEBUG)
 static const char *find_name[] = {
-  "PKCS TYPE",
-  "USERNAME",
-  "HOST",
-  "REALNAME",
-  "EMAIL",
-  "ORG",
-  "COUNTRY",
+  "PKCS TYPE ",
+  "USERNAME  ",
+  "HOST      ",
+  "REALNAME  ",
+  "EMAIL     ",
+  "ORG       ",
+  "COUNTRY   ",
   "PUBLIC KEY",
-  "CONTEXT",
-  "USAGE",
+  "CONTEXT   ",
+  "USAGE     ",
   NULL
 };
 #endif /* SILC_DEBUG */
@@ -80,19 +78,19 @@ static void silc_skr_type_string(SilcSKRFindType type, void *data,
   case SILC_SKR_FIND_PKCS_TYPE:
   case SILC_SKR_FIND_USAGE:
     silc_snprintf(retbuf, retbuf_size, "[%s] [%d]", find_name[type],
-            (int)SILC_PTR_TO_32(data));
+                 (int)SILC_PTR_TO_32(data));
     break;
 
   case SILC_SKR_FIND_PUBLIC_KEY:
+  case SILC_SKR_FIND_CONTEXT:
     silc_snprintf(retbuf, retbuf_size, "[%s] [%p]", find_name[type], data);
     break;
 
   default:
     silc_snprintf(retbuf, retbuf_size, "[%s] [%s]", find_name[type],
-            (char *)data);
+                 (char *)data);
   }
 }
-
 #endif /* SILC_DEBUG */
 
 /* Hash table destructor for search constraints */
@@ -101,6 +99,7 @@ static void silc_skr_find_destructor(void *key, void *context,
                                     void *user_context)
 {
   SilcSKRFindType type = SILC_PTR_TO_32(key);
+  SilcPKCSType pkcs_type = SILC_PTR_TO_32(user_context);
 
   switch (type) {
   case SILC_SKR_FIND_PKCS_TYPE:
@@ -113,6 +112,12 @@ static void silc_skr_find_destructor(void *key, void *context,
     break;
 
   default:
+    /* In SILC Public key all entries are referenced from the public key
+       so don't free them.  This test is valid only when removing key
+       from the repository. */
+    if (pkcs_type == SILC_PKCS_SILC)
+      break;
+
     silc_free(context);
   }
 }
@@ -123,11 +128,13 @@ static void silc_skr_destructor(void *key, void *context, void *user_context)
 {
   SilcSKREntry type = key;
   SilcSKRKeyInternal entry = context;
+  SilcPKCSType pkcs_type = silc_pkcs_get_type(entry->key.key);
 
   /* Destroy search data, except for SILC_SKR_FIND_PUBLIC_KEY because it
      shares same context with the key entry. */
   if (SILC_PTR_TO_32(type->type) != SILC_SKR_FIND_PUBLIC_KEY)
-    silc_skr_find_destructor(SILC_32_TO_PTR(type->type), type->data, NULL);
+    silc_skr_find_destructor(SILC_32_TO_PTR(type->type), type->data,
+                            SILC_32_TO_PTR(pkcs_type));
   silc_free(type);
 
   /* Destroy key */
@@ -135,6 +142,8 @@ static void silc_skr_destructor(void *key, void *context, void *user_context)
   if (entry->refcnt > 0)
     return;
 
+  SILC_LOG_DEBUG(("Freeing public key %p", entry->key.key));
+
   silc_pkcs_public_key_free(entry->key.key);
   silc_free(entry);
 }
@@ -159,7 +168,7 @@ static SilcUInt32 silc_skr_hash(void *key, void *user_context)
     break;
   }
 
-  return type->type + silc_hash_string(type->data, user_context);
+  return type->type + silc_hash_string_case(type->data, user_context);
 }
 
 /* Hash table comparison function for key entries */
@@ -262,6 +271,13 @@ static SilcBool silc_skr_add_entry(SilcSKR skr, SilcSKRFindType type,
 {
   SilcSKREntry entry;
 
+#if defined(SILC_DEBUG)
+  char tmp[256];
+  memset(tmp, 0, sizeof(tmp));
+  silc_skr_type_string(type, type_data, tmp, sizeof(tmp) - 1);
+  SILC_LOG_DEBUG(("Search constraint %s", tmp));
+#endif /* SILC_DEBUG */
+
   entry = silc_calloc(1, sizeof(*entry));
   if (!entry)
     return FALSE;
@@ -272,23 +288,98 @@ static SilcBool silc_skr_add_entry(SilcSKR skr, SilcSKRFindType type,
   return silc_hash_table_add(skr->keys, entry, key);
 }
 
+/* Delete a key by search constraint type from repository */
+
+static SilcBool silc_skr_del_entry(SilcSKR skr, SilcSKRFindType type,
+                                  void *type_data, SilcSKRKeyInternal key)
+{
+  SilcSKREntryStruct entry;
+
+  if (!type_data)
+    return FALSE;
+
+  entry.type = type;
+  entry.data = type_data;
+
+  return silc_hash_table_del_by_context(skr->keys, &entry, key);
+}
+
+/* This performs AND operation.  Any entry already in `results' that is not
+   in `list' will be removed from `results'. */
+
+static SilcBool silc_skr_results_and(SilcDList list, SilcSKRStatus *status,
+                                    SilcDList *results)
+{
+  SilcSKRKeyInternal entry, r;
+
+  if (*results == NULL) {
+    *results = silc_dlist_init();
+    if (*results == NULL) {
+      *status |= SILC_SKR_NO_MEMORY;
+      return FALSE;
+    }
+  }
+
+  /* If results is empty, just add all entries from list to results */
+  if (!silc_dlist_count(*results)) {
+    silc_dlist_start(list);
+    while ((entry = silc_dlist_get(list)) != SILC_LIST_END)
+      silc_dlist_add(*results, entry);
+
+    return TRUE;
+  }
+
+  silc_dlist_start(*results);
+  while ((entry = silc_dlist_get(*results)) != SILC_LIST_END) {
+
+    /* Check if this entry is in list  */
+    silc_dlist_start(list);
+    while ((r = silc_dlist_get(list)) != SILC_LIST_END) {
+      if (r == entry)
+       break;
+    }
+    if (r != SILC_LIST_END)
+      continue;
+
+    /* Remove from results */
+    silc_dlist_del(*results, entry);
+  }
+
+  /* If results became empty, we did not find any key */
+  if (!silc_dlist_count(*results)) {
+    SILC_LOG_DEBUG(("Not all search constraints found"));
+    *status |= SILC_SKR_NOT_FOUND;
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+
+/***************************** SILC Public Key ******************************/
+
 /* Add SILC style public key to repository */
 
 static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
                                       SilcPublicKey public_key,
                                       SilcSKRKeyUsage usage,
-                                      void *key_context)
+                                      void *key_context,
+                                      SilcSKRKey *return_key)
 {
   SilcSKRKeyInternal key;
   SilcSKRStatus status = SILC_SKR_ERROR;
   SilcPublicKeyIdentifier ident;
   SilcSILCPublicKey silc_pubkey;
+#if defined(SILC_DEBUG)
+  char tmp[256];
+#endif /* SILC_DEBUG */
 
   /* Get the SILC public key */
-  silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
+  silc_pubkey = silc_pkcs_public_key_get_pkcs(SILC_PKCS_SILC, public_key);
   ident = &silc_pubkey->identifier;
 
-  SILC_LOG_DEBUG(("Adding SILC public key [%s]", ident->username));
+  SILC_LOG_DEBUG(("Adding SILC public key %p [%s], context %p",
+                 public_key, ident->username, key_context));
 
   silc_mutex_lock(skr->lock);
 
@@ -311,6 +402,12 @@ static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
   key->key.key = public_key;
   key->key.key_context = key_context;
 
+#if defined(SILC_DEBUG)
+  silc_skr_type_string(SILC_SKR_FIND_USAGE, SILC_32_TO_PTR(usage),
+                      tmp, sizeof(tmp) - 1);
+  SILC_LOG_DEBUG((" Search constraint %s", tmp));
+#endif /* SILC_DEBUG */
+
   /* Add key specifics */
 
   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
@@ -374,6 +471,9 @@ static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
 
   silc_mutex_unlock(skr->lock);
 
+  if (return_key)
+    *return_key = (SilcSKRKey)key;
+
   return SILC_SKR_OK;
 
  err:
@@ -387,10 +487,14 @@ static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
 static SilcSKRStatus silc_skr_add_silc_simple(SilcSKR skr,
                                              SilcPublicKey public_key,
                                              SilcSKRKeyUsage usage,
-                                             void *key_context)
+                                             void *key_context,
+                                             SilcSKRKey *return_key)
 {
   SilcSKRKeyInternal key;
   SilcSKRStatus status = SILC_SKR_ERROR;
+#if defined(SILC_DEBUG)
+  char tmp[256];
+#endif /* SILC_DEBUG */
 
   SILC_LOG_DEBUG(("Adding SILC public key"));
 
@@ -415,6 +519,12 @@ static SilcSKRStatus silc_skr_add_silc_simple(SilcSKR skr,
   key->key.key = public_key;
   key->key.key_context = key_context;
 
+#if defined(SILC_DEBUG)
+  silc_skr_type_string(SILC_SKR_FIND_USAGE, SILC_32_TO_PTR(usage),
+                      tmp, sizeof(tmp) - 1);
+  SILC_LOG_DEBUG(("Search cons %s", tmp));
+#endif /* SILC_DEBUG */
+
   /* Add key specifics */
 
   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
@@ -431,6 +541,9 @@ static SilcSKRStatus silc_skr_add_silc_simple(SilcSKR skr,
 
   silc_mutex_unlock(skr->lock);
 
+  if (return_key)
+    *return_key = (SilcSKRKey)key;
+
   return SILC_SKR_OK;
 
  err:
@@ -438,55 +551,52 @@ static SilcSKRStatus silc_skr_add_silc_simple(SilcSKR skr,
   return status;
 }
 
-/* This performs AND operation.  Any entry already in `results' that is not
-   in `list' will be removed from `results'. */
+/* Deletes SILC public key from repository */
 
-static SilcBool silc_skr_results_and(SilcDList list, SilcSKRStatus *status,
-                                    SilcDList *results)
+static SilcSKRStatus silc_skr_del_silc_public_key(SilcSKR skr,
+                                                 SilcPublicKey public_key,
+                                                 void *key_context)
 {
-  SilcSKRKeyInternal entry, r;
-
-  if (*results == NULL) {
-    *results = silc_dlist_init();
-    if (*results == NULL) {
-      *status |= SILC_SKR_NO_MEMORY;
-      return FALSE;
-    }
-  }
-
-  /* If results is empty, just add all entries from list to results */
-  if (!silc_dlist_count(*results)) {
-    silc_dlist_start(list);
-    while ((entry = silc_dlist_get(list)) != SILC_LIST_END)
-      silc_dlist_add(*results, entry);
+  SilcSKRStatus status = SILC_SKR_ERROR;
+  SilcPublicKeyIdentifier ident;
+  SilcSILCPublicKey silc_pubkey;
+  SilcSKRKeyInternal key;
+  SilcDList entry;
 
-    return TRUE;
-  }
+  /* Get the SILC public key */
+  silc_pubkey = silc_pkcs_public_key_get_pkcs(SILC_PKCS_SILC, public_key);
+  ident = &silc_pubkey->identifier;
 
-  silc_dlist_start(*results);
-  while ((entry = silc_dlist_get(*results)) != SILC_LIST_END) {
+  SILC_LOG_DEBUG(("Deleting SILC public key [%s]", ident->username));
 
-    /* Check if this entry is in list  */
-    silc_dlist_start(list);
-    while ((r = silc_dlist_get(list)) != SILC_LIST_END) {
-      if (r == entry)
-       break;
-    }
-    if (r != SILC_LIST_END)
-      continue;
+  silc_mutex_lock(skr->lock);
 
-    /* Remove from results */
-    silc_dlist_del(*results, entry);
+  /* Check that this key exists */
+  if (!silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
+                          public_key, &entry, key_context, 0)) {
+    silc_mutex_unlock(skr->lock);
+    SILC_LOG_DEBUG(("Key does not exist"));
+    return status | SILC_SKR_NOT_FOUND;
   }
 
-  /* If results became empty, we did not find any key */
-  if (!silc_dlist_count(*results)) {
-    SILC_LOG_DEBUG(("Not all search constraints found"));
-    *status |= SILC_SKR_NOT_FOUND;
-    return FALSE;
-  }
+  silc_dlist_start(entry);
+  key = silc_dlist_get(entry);
+  silc_dlist_uninit(entry);
+
+  silc_skr_del_entry(skr, SILC_SKR_FIND_PUBLIC_KEY, public_key, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
+                    SILC_32_TO_PTR(SILC_PKCS_SILC), key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_USERNAME, ident->username, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_HOST, ident->host, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_REALNAME, ident->realname, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_EMAIL, ident->email, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_ORG, ident->org, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_COUNTRY, ident->country, key);
+  silc_skr_del_entry(skr, SILC_SKR_FIND_CONTEXT, key_context, key);
 
-  return TRUE;
+  silc_mutex_unlock(skr->lock);
+
+  return SILC_SKR_OK;
 }
 
 
@@ -494,7 +604,7 @@ static SilcBool silc_skr_results_and(SilcDList list, SilcSKRStatus *status,
 
 /* Allocate key repository */
 
-SilcSKR silc_skr_alloc(SilcSchedule scheduler)
+SilcSKR silc_skr_alloc(void)
 {
   SilcSKR skr;
 
@@ -502,7 +612,7 @@ SilcSKR silc_skr_alloc(SilcSchedule scheduler)
   if (!skr)
     return NULL;
 
-  if (!silc_skr_init(skr, scheduler)) {
+  if (!silc_skr_init(skr)) {
     silc_skr_free(skr);
     return NULL;
   }
@@ -520,17 +630,12 @@ void silc_skr_free(SilcSKR skr)
 
 /* Initializes key repository */
 
-SilcBool silc_skr_init(SilcSKR skr, SilcSchedule scheduler)
+SilcBool silc_skr_init(SilcSKR skr)
 {
-  if (!scheduler)
-    return FALSE;
-
-  skr->scheduler = scheduler;
-
   if (!silc_mutex_alloc(&skr->lock))
     return FALSE;
 
-  skr->keys = silc_hash_table_alloc(0, silc_skr_hash, NULL,
+  skr->keys = silc_hash_table_alloc(NULL, 0, silc_skr_hash, NULL,
                                    silc_skr_compare, NULL,
                                    silc_skr_destructor, NULL, TRUE);
   if (!skr->keys)
@@ -553,7 +658,8 @@ void silc_skr_uninit(SilcSKR skr)
 SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
                                      SilcPublicKey public_key,
                                      SilcSKRKeyUsage usage,
-                                     void *key_context)
+                                     void *key_context,
+                                     SilcSKRKey *return_key)
 {
   SilcPKCSType type;
 
@@ -562,12 +668,12 @@ SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
 
   type = silc_pkcs_get_type(public_key);
 
-  SILC_LOG_DEBUG(("Adding public key to repository"));
+  SILC_LOG_DEBUG(("Adding public key %p to repository", public_key));
 
   switch (type) {
 
   case SILC_PKCS_SILC:
-    return silc_skr_add_silc(skr, public_key, usage, key_context);
+    return silc_skr_add_silc(skr, public_key, usage, key_context, return_key);
     break;
 
   default:
@@ -582,7 +688,37 @@ SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
 SilcSKRStatus silc_skr_add_public_key_simple(SilcSKR skr,
                                             SilcPublicKey public_key,
                                             SilcSKRKeyUsage usage,
-                                            void *key_context)
+                                            void *key_context,
+                                            SilcSKRKey *return_key)
+{
+  SilcPKCSType type;
+
+  if (!public_key)
+    return SILC_SKR_ERROR;
+
+  type = silc_pkcs_get_type(public_key);
+
+  SILC_LOG_DEBUG(("Adding public key %p to repository", public_key));
+
+  switch (type) {
+
+  case SILC_PKCS_SILC:
+    return silc_skr_add_silc_simple(skr, public_key, usage, key_context,
+                                   return_key);
+    break;
+
+  default:
+    break;
+  }
+
+  return SILC_SKR_ERROR;
+}
+
+/* Remove key from repository */
+
+SilcSKRStatus silc_skr_del_public_key(SilcSKR skr,
+                                     SilcPublicKey public_key,
+                                     void *key_context)
 {
   SilcPKCSType type;
 
@@ -591,12 +727,12 @@ SilcSKRStatus silc_skr_add_public_key_simple(SilcSKR skr,
 
   type = silc_pkcs_get_type(public_key);
 
-  SILC_LOG_DEBUG(("Adding public key to repository"));
+  SILC_LOG_DEBUG(("Deleting public key %p from repository", public_key));
 
   switch (type) {
 
   case SILC_PKCS_SILC:
-    return silc_skr_add_silc_simple(skr, public_key, usage, key_context);
+    return silc_skr_del_silc_public_key(skr, public_key, key_context);
     break;
 
   default:
@@ -606,6 +742,39 @@ SilcSKRStatus silc_skr_add_public_key_simple(SilcSKR skr,
   return SILC_SKR_ERROR;
 }
 
+/* Reference key */
+
+void silc_skr_ref_public_key(SilcSKR skr, SilcSKRKey key)
+{
+  SilcSKRKeyInternal k = (SilcSKRKeyInternal)key;
+
+  silc_mutex_lock(skr->lock);
+  SILC_LOG_DEBUG(("SKR key %p ref %d -> %d", k->refcnt, k->refcnt + 1));
+  k->refcnt++;
+  silc_mutex_unlock(skr->lock);
+}
+
+/* Release key reference. */
+
+void silc_skr_unref_public_key(SilcSKR skr, SilcSKRKey key)
+{
+  SilcSKRKeyInternal k = (SilcSKRKeyInternal)key;
+
+  silc_mutex_lock(skr->lock);
+
+  SILC_LOG_DEBUG(("SKR key %p ref %d -> %d", k->refcnt, k->refcnt - 1));
+  k->refcnt--;
+
+  if (k->refcnt == 0) {
+    /* If reference is zero, the key has been removed from the repository
+       already.  Just destroy the public key. */
+    silc_pkcs_public_key_free(key->key);
+    silc_free(key);
+  }
+
+  silc_mutex_unlock(skr->lock);
+}
+
 
 /************************** Search Constraints API **************************/
 
@@ -619,7 +788,8 @@ SilcSKRFind silc_skr_find_alloc(void)
   if (!find)
     return NULL;
 
-  find->constr = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
+  find->constr = silc_hash_table_alloc(NULL, 0, silc_hash_uint,
+                                      NULL, NULL, NULL,
                                       silc_skr_find_destructor, NULL, TRUE);
   if (!find->constr) {
     silc_skr_find_free(find);
@@ -711,6 +881,8 @@ SilcBool silc_skr_find_set_public_key(SilcSKRFind find,
 
 SilcBool silc_skr_find_set_context(SilcSKRFind find, void *context)
 {
+  if (!context)
+    return TRUE;
   return silc_hash_table_add(find->constr,
                             SILC_32_TO_PTR(SILC_SKR_FIND_CONTEXT), context);
 }
@@ -730,7 +902,8 @@ SilcBool silc_skr_find_set_usage(SilcSKRFind find, SilcSKRKeyUsage usage)
    once keys has been found. */
 /* This is now synchronous function but may later change async */
 
-SilcAsyncOperation silc_skr_find(SilcSKR skr, SilcSKRFind find,
+SilcAsyncOperation silc_skr_find(SilcSKR skr, SilcSchedule schedule,
+                                SilcSKRFind find,
                                 SilcSKRFindCallback callback,
                                 void *callback_context)
 {
@@ -738,6 +911,9 @@ SilcAsyncOperation silc_skr_find(SilcSKR skr, SilcSKRFind find,
   SilcHashTableList htl;
   SilcDList list, results = NULL;
   void *type, *ctx, *usage = NULL;
+#if defined(SILC_DEBUG)
+  char tmp[256];
+#endif /* SILC_DEBUG */
 
   SILC_LOG_DEBUG(("Finding key from repository"));
 
@@ -750,21 +926,28 @@ SilcAsyncOperation silc_skr_find(SilcSKR skr, SilcSKRFind find,
   silc_hash_table_find(find->constr, SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
                       NULL, &usage);
 
+#if defined(SILC_DEBUG)
+  if (usage) {
+    memset(tmp, 0, sizeof(tmp));
+    silc_skr_type_string(SILC_SKR_FIND_USAGE, usage, tmp, sizeof(tmp) - 1);
+    SILC_LOG_DEBUG(("Finding key by %s", tmp));
+  }
+#endif /* SILC_DEBUG */
+
   silc_hash_table_list(find->constr, &htl);
   while (silc_hash_table_get(&htl, &type, &ctx)) {
 
+    /* SILC_SKR_FIND_USAGE is handled separately while searching the keys. */
+    if ((SilcSKRFindType)SILC_32_TO_PTR(type) == SILC_SKR_FIND_USAGE)
+      continue;
+
 #if defined(SILC_DEBUG)
-    char tmp[256];
     memset(tmp, 0, sizeof(tmp));
     silc_skr_type_string((SilcSKRFindType)SILC_32_TO_PTR(type),
                         ctx, tmp, sizeof(tmp) - 1);
     SILC_LOG_DEBUG(("Finding key by %s", tmp));
 #endif /* SILC_DEBUG */
 
-    /* SILC_SKR_FIND_USAGE is handled separately while searching the keys. */
-    if ((SilcSKRFindType)SILC_32_TO_PTR(type) == SILC_SKR_FIND_USAGE)
-      continue;
-
     /* Find entries by this search constraint */
     if (!silc_skr_find_entry(skr, &status,
                             (SilcSKRFindType)SILC_32_TO_PTR(type),