Added SILC Thread Queue API
[silc.git] / lib / silcskr / silcskr.c
1 /*
2
3   silcskr.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2005 - 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 #include "silc.h"
21 #include "silcskr.h"
22
23 /************************** Types and definitions ***************************/
24
25 /* Search constraints */
26 typedef enum {
27   SILC_SKR_FIND_PKCS_TYPE,
28   SILC_SKR_FIND_USERNAME,
29   SILC_SKR_FIND_HOST,
30   SILC_SKR_FIND_REALNAME,
31   SILC_SKR_FIND_EMAIL,
32   SILC_SKR_FIND_ORG,
33   SILC_SKR_FIND_COUNTRY,
34   SILC_SKR_FIND_PUBLIC_KEY,
35   SILC_SKR_FIND_CONTEXT,
36   SILC_SKR_FIND_USAGE,          /* Never added as key specific */
37 } SilcSKRFindType;
38
39 /* Hash table key context */
40 typedef struct {
41   SilcSKRFindType type;         /* Type of key */
42   void *data;                   /* Hash table key */
43 } *SilcSKREntry, SilcSKREntryStruct;
44
45 /* Foreach user context when finding entries from hash table */
46 typedef struct {
47   SilcDList list;
48   void *key_context;
49   SilcSKRKeyUsage usage;
50 } SilcSKRFindForeach;
51
52 #if defined(SILC_DEBUG)
53 static const char *find_name[] = {
54   "PKCS TYPE ",
55   "USERNAME  ",
56   "HOST      ",
57   "REALNAME  ",
58   "EMAIL     ",
59   "ORG       ",
60   "COUNTRY   ",
61   "PUBLIC KEY",
62   "CONTEXT   ",
63   "USAGE     ",
64   NULL
65 };
66 #endif /* SILC_DEBUG */
67
68 /************************ Static utility functions **************************/
69
70 #if defined(SILC_DEBUG)
71
72 /* Returns search constraint string */
73
74 static void silc_skr_type_string(SilcSKRFindType type, void *data,
75                                  char *retbuf, SilcUInt32 retbuf_size)
76 {
77   switch (type) {
78   case SILC_SKR_FIND_PKCS_TYPE:
79   case SILC_SKR_FIND_USAGE:
80     silc_snprintf(retbuf, retbuf_size, "[%s] [%d]", find_name[type],
81                   (int)SILC_PTR_TO_32(data));
82     break;
83
84   case SILC_SKR_FIND_PUBLIC_KEY:
85   case SILC_SKR_FIND_CONTEXT:
86     silc_snprintf(retbuf, retbuf_size, "[%s] [%p]", find_name[type], data);
87     break;
88
89   default:
90     silc_snprintf(retbuf, retbuf_size, "[%s] [%s]", find_name[type],
91                   (char *)data);
92   }
93 }
94 #endif /* SILC_DEBUG */
95
96 /* Hash table destructor for search constraints */
97
98 static void silc_skr_find_destructor(void *key, void *context,
99                                      void *user_context)
100 {
101   SilcSKRFindType type = SILC_PTR_TO_32(key);
102   SilcPKCSType pkcs_type = SILC_PTR_TO_32(user_context);
103
104   switch (type) {
105   case SILC_SKR_FIND_PKCS_TYPE:
106   case SILC_SKR_FIND_USAGE:
107   case SILC_SKR_FIND_CONTEXT:
108     break;
109
110   case SILC_SKR_FIND_PUBLIC_KEY:
111     silc_pkcs_public_key_free(context);
112     break;
113
114   default:
115     /* In SILC Public key all entries are referenced from the public key
116        so don't free them.  This test is valid only when removing key
117        from the repository. */
118     if (pkcs_type == SILC_PKCS_SILC)
119       break;
120
121     silc_free(context);
122   }
123 }
124
125 /* Hash table destructor for key entries */
126
127 static void silc_skr_destructor(void *key, void *context, void *user_context)
128 {
129   SilcSKREntry type = key;
130   SilcSKRKeyInternal entry = context;
131   SilcPKCSType pkcs_type = silc_pkcs_get_type(entry->key.key);
132
133   /* Destroy search data, except for SILC_SKR_FIND_PUBLIC_KEY because it
134      shares same context with the key entry. */
135   if (SILC_PTR_TO_32(type->type) != SILC_SKR_FIND_PUBLIC_KEY)
136     silc_skr_find_destructor(SILC_32_TO_PTR(type->type), type->data,
137                              SILC_32_TO_PTR(pkcs_type));
138   silc_free(type);
139
140   /* Destroy key */
141   entry->refcnt--;
142   if (entry->refcnt > 0)
143     return;
144
145   SILC_LOG_DEBUG(("Freeing public key %p", entry->key.key));
146
147   silc_pkcs_public_key_free(entry->key.key);
148   silc_free(entry);
149 }
150
151 /* Hash table hash function for key entries */
152
153 static SilcUInt32 silc_skr_hash(void *key, void *user_context)
154 {
155   SilcSKREntry type = key;
156
157   switch (type->type) {
158   case SILC_SKR_FIND_PKCS_TYPE:
159   case SILC_SKR_FIND_CONTEXT:
160     return type->type + (type->type ^ SILC_PTR_TO_32(type->data));
161     break;
162
163   case SILC_SKR_FIND_PUBLIC_KEY:
164     return type->type + silc_hash_public_key(type->data, user_context);
165     break;
166
167   default:
168     break;
169   }
170
171   return type->type + silc_hash_string_case(type->data, user_context);
172 }
173
174 /* Hash table comparison function for key entries */
175
176 static SilcBool silc_skr_compare(void *key1, void *key2, void *user_context)
177 {
178   SilcSKREntry type1 = key1;
179   SilcSKREntry type2 = key2;
180
181   if (type1->type != type2->type)
182     return FALSE;
183
184   switch (type1->type) {
185   case SILC_SKR_FIND_PKCS_TYPE:
186   case SILC_SKR_FIND_CONTEXT:
187     return type1->data == type2->data;
188     break;
189
190   case SILC_SKR_FIND_PUBLIC_KEY:
191     return silc_hash_public_key_compare(type1->data, type2->data,
192                                         user_context);
193     break;
194
195   default:
196     break;
197   }
198
199   return silc_utf8_strcasecmp((const char *)type1->data,
200                               (const char *)type2->data);
201 }
202
203 /* Foreach function for finding entries in the repository */
204
205 static void silc_skr_find_foreach(void *key, void *context,
206                                   void *user_context)
207 {
208   SilcSKRFindForeach *f = user_context;
209   SilcSKRKeyInternal k = context;
210
211   if (k) {
212     /* If key context is present, it must match the context in the key.
213        This is used only internally when adding keys, to check if the key
214        is added with same context. */
215     if (f->key_context && f->key_context != k->key.key_context)
216       return;
217
218     /* Check for usage bits.  At least one usage bit must be set. */
219     if (f->usage && k->key.usage && (f->usage & k->key.usage) == 0)
220       return;
221
222     silc_dlist_add(f->list, k);
223   }
224 }
225
226 /* Finds entry from repository by search constraint type and data */
227
228 static SilcBool silc_skr_find_entry(SilcSKR skr,
229                                     SilcSKRStatus *status,
230                                     SilcSKRFindType type,
231                                     void *type_data,
232                                     SilcDList *results,
233                                     void *key_context,
234                                     SilcSKRKeyUsage usage)
235 {
236   SilcSKREntryStruct find;
237   SilcSKRFindForeach f;
238
239   f.list = silc_dlist_init();
240   if (!f.list) {
241     *status |= SILC_SKR_NO_MEMORY;
242     return FALSE;
243   }
244   f.key_context = key_context;
245   f.usage = usage;
246
247   find.type = type;
248   find.data = type_data;
249
250   silc_hash_table_find_foreach(skr->keys, (void *)&find,
251                                silc_skr_find_foreach, &f);
252
253   if (!silc_dlist_count(f.list)) {
254     *status |= SILC_SKR_NOT_FOUND;
255     silc_dlist_uninit(f.list);
256     return FALSE;
257   }
258
259   if (results)
260     *results = f.list;
261   else
262     silc_dlist_uninit(f.list);
263
264   return TRUE;
265 }
266
267 /* Add a key by search constraint type to repository */
268
269 static SilcBool silc_skr_add_entry(SilcSKR skr, SilcSKRFindType type,
270                                    void *type_data, SilcSKRKeyInternal key)
271 {
272   SilcSKREntry entry;
273
274 #if defined(SILC_DEBUG)
275   char tmp[256];
276   memset(tmp, 0, sizeof(tmp));
277   silc_skr_type_string(type, type_data, tmp, sizeof(tmp) - 1);
278   SILC_LOG_DEBUG(("Search constraint %s", tmp));
279 #endif /* SILC_DEBUG */
280
281   entry = silc_calloc(1, sizeof(*entry));
282   if (!entry)
283     return FALSE;
284
285   entry->type = type;
286   entry->data = type_data;
287
288   return silc_hash_table_add(skr->keys, entry, key);
289 }
290
291 /* Delete a key by search constraint type from repository */
292
293 static SilcBool silc_skr_del_entry(SilcSKR skr, SilcSKRFindType type,
294                                    void *type_data, SilcSKRKeyInternal key)
295 {
296   SilcSKREntryStruct entry;
297
298   if (!type_data)
299     return FALSE;
300
301   entry.type = type;
302   entry.data = type_data;
303
304   return silc_hash_table_del_by_context(skr->keys, &entry, key);
305 }
306
307 /* This performs AND operation.  Any entry already in `results' that is not
308    in `list' will be removed from `results'. */
309
310 static SilcBool silc_skr_results_and(SilcDList list, SilcSKRStatus *status,
311                                      SilcDList *results)
312 {
313   SilcSKRKeyInternal entry, r;
314
315   if (*results == NULL) {
316     *results = silc_dlist_init();
317     if (*results == NULL) {
318       *status |= SILC_SKR_NO_MEMORY;
319       return FALSE;
320     }
321   }
322
323   /* If results is empty, just add all entries from list to results */
324   if (!silc_dlist_count(*results)) {
325     silc_dlist_start(list);
326     while ((entry = silc_dlist_get(list)) != SILC_LIST_END)
327       silc_dlist_add(*results, entry);
328
329     return TRUE;
330   }
331
332   silc_dlist_start(*results);
333   while ((entry = silc_dlist_get(*results)) != SILC_LIST_END) {
334
335     /* Check if this entry is in list  */
336     silc_dlist_start(list);
337     while ((r = silc_dlist_get(list)) != SILC_LIST_END) {
338       if (r == entry)
339         break;
340     }
341     if (r != SILC_LIST_END)
342       continue;
343
344     /* Remove from results */
345     silc_dlist_del(*results, entry);
346   }
347
348   /* If results became empty, we did not find any key */
349   if (!silc_dlist_count(*results)) {
350     SILC_LOG_DEBUG(("Not all search constraints found"));
351     *status |= SILC_SKR_NOT_FOUND;
352     return FALSE;
353   }
354
355   return TRUE;
356 }
357
358
359 /***************************** SILC Public Key ******************************/
360
361 /* Add SILC style public key to repository */
362
363 static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
364                                        SilcPublicKey public_key,
365                                        SilcSKRKeyUsage usage,
366                                        void *key_context,
367                                        SilcSKRKey *return_key)
368 {
369   SilcSKRKeyInternal key;
370   SilcSKRStatus status = SILC_SKR_ERROR;
371   SilcPublicKeyIdentifier ident;
372   SilcSILCPublicKey silc_pubkey;
373 #if defined(SILC_DEBUG)
374   char tmp[256];
375 #endif /* SILC_DEBUG */
376
377   /* Get the SILC public key */
378   silc_pubkey = silc_pkcs_public_key_get_pkcs(SILC_PKCS_SILC, public_key);
379   ident = &silc_pubkey->identifier;
380
381   SILC_LOG_DEBUG(("Adding SILC public key %p [%s], context %p",
382                   public_key, ident->username, key_context));
383
384   silc_mutex_lock(skr->lock);
385
386   /* Check that this key hasn't been added already */
387   if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
388                           public_key, NULL, key_context, 0)) {
389     silc_mutex_unlock(skr->lock);
390     SILC_LOG_DEBUG(("Key already added"));
391     return status | SILC_SKR_ALREADY_EXIST;
392   }
393
394   /* Allocate key entry */
395   key = silc_calloc(1, sizeof(*key));
396   if (!key) {
397     silc_mutex_unlock(skr->lock);
398     return status | SILC_SKR_NO_MEMORY;
399   }
400
401   key->key.usage = usage;
402   key->key.key = public_key;
403   key->key.key_context = key_context;
404
405 #if defined(SILC_DEBUG)
406   silc_skr_type_string(SILC_SKR_FIND_USAGE, SILC_32_TO_PTR(usage),
407                        tmp, sizeof(tmp) - 1);
408   SILC_LOG_DEBUG((" Search constraint %s", tmp));
409 #endif /* SILC_DEBUG */
410
411   /* Add key specifics */
412
413   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
414                           public_key, key))
415     goto err;
416   key->refcnt++;
417
418   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
419                           SILC_32_TO_PTR(SILC_PKCS_SILC), key))
420     goto err;
421   key->refcnt++;
422
423   if (ident->username) {
424     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_USERNAME,
425                             ident->username, key))
426       goto err;
427     key->refcnt++;
428   }
429
430   if (ident->host) {
431     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_HOST,
432                             ident->host, key))
433       goto err;
434     key->refcnt++;
435   }
436
437   if (ident->realname) {
438     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_REALNAME,
439                             ident->realname, key))
440       goto err;
441     key->refcnt++;
442   }
443
444   if (ident->email) {
445     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_EMAIL,
446                             ident->email, key))
447       goto err;
448     key->refcnt++;
449   }
450
451   if (ident->org) {
452     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_ORG,
453                             ident->org, key))
454       goto err;
455     key->refcnt++;
456   }
457
458   if (ident->country) {
459     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_COUNTRY,
460                             ident->country, key))
461       goto err;
462     key->refcnt++;
463   }
464
465   if (key_context) {
466     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
467                             key_context, key))
468       goto err;
469     key->refcnt++;
470   }
471
472   silc_mutex_unlock(skr->lock);
473
474   if (return_key)
475     *return_key = (SilcSKRKey)key;
476
477   return SILC_SKR_OK;
478
479  err:
480   silc_mutex_unlock(skr->lock);
481   return status;
482 }
483
484 /* Add SILC style public key to repository, and only the public key, not
485    other details from the key. */
486
487 static SilcSKRStatus silc_skr_add_silc_simple(SilcSKR skr,
488                                               SilcPublicKey public_key,
489                                               SilcSKRKeyUsage usage,
490                                               void *key_context,
491                                               SilcSKRKey *return_key)
492 {
493   SilcSKRKeyInternal key;
494   SilcSKRStatus status = SILC_SKR_ERROR;
495 #if defined(SILC_DEBUG)
496   char tmp[256];
497 #endif /* SILC_DEBUG */
498
499   SILC_LOG_DEBUG(("Adding SILC public key"));
500
501   silc_mutex_lock(skr->lock);
502
503   /* Check that this key hasn't been added already */
504   if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
505                           public_key, NULL, key_context, 0)) {
506     silc_mutex_unlock(skr->lock);
507     SILC_LOG_DEBUG(("Key already added"));
508     return status | SILC_SKR_ALREADY_EXIST;
509   }
510
511   /* Allocate key entry */
512   key = silc_calloc(1, sizeof(*key));
513   if (!key) {
514     silc_mutex_unlock(skr->lock);
515     return status | SILC_SKR_NO_MEMORY;
516   }
517
518   key->key.usage = usage;
519   key->key.key = public_key;
520   key->key.key_context = key_context;
521
522 #if defined(SILC_DEBUG)
523   silc_skr_type_string(SILC_SKR_FIND_USAGE, SILC_32_TO_PTR(usage),
524                        tmp, sizeof(tmp) - 1);
525   SILC_LOG_DEBUG(("Search cons %s", tmp));
526 #endif /* SILC_DEBUG */
527
528   /* Add key specifics */
529
530   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
531                           public_key, key))
532     goto err;
533   key->refcnt++;
534
535   if (key_context) {
536     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
537                             key_context, key))
538       goto err;
539     key->refcnt++;
540   }
541
542   silc_mutex_unlock(skr->lock);
543
544   if (return_key)
545     *return_key = (SilcSKRKey)key;
546
547   return SILC_SKR_OK;
548
549  err:
550   silc_mutex_unlock(skr->lock);
551   return status;
552 }
553
554 /* Deletes SILC public key from repository */
555
556 static SilcSKRStatus silc_skr_del_silc_public_key(SilcSKR skr,
557                                                   SilcPublicKey public_key,
558                                                   void *key_context)
559 {
560   SilcSKRStatus status = SILC_SKR_ERROR;
561   SilcPublicKeyIdentifier ident;
562   SilcSILCPublicKey silc_pubkey;
563   SilcSKRKeyInternal key;
564   SilcDList entry;
565
566   /* Get the SILC public key */
567   silc_pubkey = silc_pkcs_public_key_get_pkcs(SILC_PKCS_SILC, public_key);
568   ident = &silc_pubkey->identifier;
569
570   SILC_LOG_DEBUG(("Deleting SILC public key [%s]", ident->username));
571
572   silc_mutex_lock(skr->lock);
573
574   /* Check that this key exists */
575   if (!silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
576                            public_key, &entry, key_context, 0)) {
577     silc_mutex_unlock(skr->lock);
578     SILC_LOG_DEBUG(("Key does not exist"));
579     return status | SILC_SKR_NOT_FOUND;
580   }
581
582   silc_dlist_start(entry);
583   key = silc_dlist_get(entry);
584   silc_dlist_uninit(entry);
585
586   silc_skr_del_entry(skr, SILC_SKR_FIND_PUBLIC_KEY, public_key, key);
587   silc_skr_del_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
588                      SILC_32_TO_PTR(SILC_PKCS_SILC), key);
589   silc_skr_del_entry(skr, SILC_SKR_FIND_USERNAME, ident->username, key);
590   silc_skr_del_entry(skr, SILC_SKR_FIND_HOST, ident->host, key);
591   silc_skr_del_entry(skr, SILC_SKR_FIND_REALNAME, ident->realname, key);
592   silc_skr_del_entry(skr, SILC_SKR_FIND_EMAIL, ident->email, key);
593   silc_skr_del_entry(skr, SILC_SKR_FIND_ORG, ident->org, key);
594   silc_skr_del_entry(skr, SILC_SKR_FIND_COUNTRY, ident->country, key);
595   silc_skr_del_entry(skr, SILC_SKR_FIND_CONTEXT, key_context, key);
596
597   silc_mutex_unlock(skr->lock);
598
599   return SILC_SKR_OK;
600 }
601
602
603 /**************************** Key Repository API ****************************/
604
605 /* Allocate key repository */
606
607 SilcSKR silc_skr_alloc(void)
608 {
609   SilcSKR skr;
610
611   skr = silc_calloc(1, sizeof(*skr));
612   if (!skr)
613     return NULL;
614
615   if (!silc_skr_init(skr)) {
616     silc_skr_free(skr);
617     return NULL;
618   }
619
620   return skr;
621 }
622
623 /* Free key repository */
624
625 void silc_skr_free(SilcSKR skr)
626 {
627   silc_skr_uninit(skr);
628   silc_free(skr);
629 }
630
631 /* Initializes key repository */
632
633 SilcBool silc_skr_init(SilcSKR skr)
634 {
635   if (!silc_mutex_alloc(&skr->lock))
636     return FALSE;
637
638   skr->keys = silc_hash_table_alloc(NULL, 0, silc_skr_hash, NULL,
639                                     silc_skr_compare, NULL,
640                                     silc_skr_destructor, NULL, TRUE);
641   if (!skr->keys)
642     return FALSE;
643
644   return TRUE;
645 }
646
647 /* Uninitializes key repository */
648
649 void silc_skr_uninit(SilcSKR skr)
650 {
651   if (skr->keys)
652     silc_hash_table_free(skr->keys);
653   silc_mutex_free(skr->lock);
654 }
655
656 /* Adds public key to key repository */
657
658 SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
659                                       SilcPublicKey public_key,
660                                       SilcSKRKeyUsage usage,
661                                       void *key_context,
662                                       SilcSKRKey *return_key)
663 {
664   SilcPKCSType type;
665
666   if (!public_key)
667     return SILC_SKR_ERROR;
668
669   type = silc_pkcs_get_type(public_key);
670
671   SILC_LOG_DEBUG(("Adding public key %p to repository", public_key));
672
673   switch (type) {
674
675   case SILC_PKCS_SILC:
676     return silc_skr_add_silc(skr, public_key, usage, key_context, return_key);
677     break;
678
679   default:
680     break;
681   }
682
683   return SILC_SKR_ERROR;
684 }
685
686 /* Adds public key to repository. */
687
688 SilcSKRStatus silc_skr_add_public_key_simple(SilcSKR skr,
689                                              SilcPublicKey public_key,
690                                              SilcSKRKeyUsage usage,
691                                              void *key_context,
692                                              SilcSKRKey *return_key)
693 {
694   SilcPKCSType type;
695
696   if (!public_key)
697     return SILC_SKR_ERROR;
698
699   type = silc_pkcs_get_type(public_key);
700
701   SILC_LOG_DEBUG(("Adding public key %p to repository", public_key));
702
703   switch (type) {
704
705   case SILC_PKCS_SILC:
706     return silc_skr_add_silc_simple(skr, public_key, usage, key_context,
707                                     return_key);
708     break;
709
710   default:
711     break;
712   }
713
714   return SILC_SKR_ERROR;
715 }
716
717 /* Remove key from repository */
718
719 SilcSKRStatus silc_skr_del_public_key(SilcSKR skr,
720                                       SilcPublicKey public_key,
721                                       void *key_context)
722 {
723   SilcPKCSType type;
724
725   if (!public_key)
726     return SILC_SKR_ERROR;
727
728   type = silc_pkcs_get_type(public_key);
729
730   SILC_LOG_DEBUG(("Deleting public key %p from repository", public_key));
731
732   switch (type) {
733
734   case SILC_PKCS_SILC:
735     return silc_skr_del_silc_public_key(skr, public_key, key_context);
736     break;
737
738   default:
739     break;
740   }
741
742   return SILC_SKR_ERROR;
743 }
744
745 /* Reference key */
746
747 void silc_skr_ref_public_key(SilcSKR skr, SilcSKRKey key)
748 {
749   SilcSKRKeyInternal k = (SilcSKRKeyInternal)key;
750
751   silc_mutex_lock(skr->lock);
752   SILC_LOG_DEBUG(("SKR key %p ref %d -> %d", k->refcnt, k->refcnt + 1));
753   k->refcnt++;
754   silc_mutex_unlock(skr->lock);
755 }
756
757 /* Release key reference. */
758
759 void silc_skr_unref_public_key(SilcSKR skr, SilcSKRKey key)
760 {
761   SilcSKRKeyInternal k = (SilcSKRKeyInternal)key;
762
763   silc_mutex_lock(skr->lock);
764
765   SILC_LOG_DEBUG(("SKR key %p ref %d -> %d", k->refcnt, k->refcnt - 1));
766   k->refcnt--;
767
768   if (k->refcnt == 0) {
769     /* If reference is zero, the key has been removed from the repository
770        already.  Just destroy the public key. */
771     silc_pkcs_public_key_free(key->key);
772     silc_free(key);
773   }
774
775   silc_mutex_unlock(skr->lock);
776 }
777
778
779 /************************** Search Constraints API **************************/
780
781 /* Allocate search constraints */
782
783 SilcSKRFind silc_skr_find_alloc(void)
784 {
785   SilcSKRFind find;
786
787   find = silc_calloc(1, sizeof(*find));
788   if (!find)
789     return NULL;
790
791   find->constr = silc_hash_table_alloc(NULL, 0, silc_hash_uint,
792                                        NULL, NULL, NULL,
793                                        silc_skr_find_destructor, NULL, TRUE);
794   if (!find->constr) {
795     silc_skr_find_free(find);
796     return NULL;
797   }
798
799   return find;
800 }
801
802 /* Free search constraints */
803
804 void silc_skr_find_free(SilcSKRFind find)
805 {
806   if (find->constr)
807     silc_hash_table_free(find->constr);
808   silc_free(find);
809 }
810
811 SilcBool silc_skr_find_set_pkcs_type(SilcSKRFind find, SilcPKCSType type)
812 {
813   return silc_hash_table_add(find->constr,
814                              SILC_32_TO_PTR(SILC_SKR_FIND_PKCS_TYPE),
815                              SILC_32_TO_PTR(type));
816 }
817
818 SilcBool silc_skr_find_set_username(SilcSKRFind find, const char *username)
819 {
820   void *c = silc_memdup(username, strlen(username));
821   if (!c)
822     return FALSE;
823   return silc_hash_table_add(find->constr,
824                              SILC_32_TO_PTR(SILC_SKR_FIND_USERNAME), c);
825 }
826
827 SilcBool silc_skr_find_set_host(SilcSKRFind find, const char *host)
828 {
829   void *c = silc_memdup(host, strlen(host));
830   if (!c)
831     return FALSE;
832   return silc_hash_table_add(find->constr,
833                              SILC_32_TO_PTR(SILC_SKR_FIND_HOST), c);
834 }
835
836 SilcBool silc_skr_find_set_realname(SilcSKRFind find, const char *realname)
837 {
838   void *c = silc_memdup(realname, strlen(realname));
839   if (!c)
840     return FALSE;
841   return silc_hash_table_add(find->constr,
842                              SILC_32_TO_PTR(SILC_SKR_FIND_REALNAME), c);
843 }
844
845 SilcBool silc_skr_find_set_email(SilcSKRFind find, const char *email)
846 {
847   void *c = silc_memdup(email, strlen(email));
848   if (!c)
849     return FALSE;
850   return silc_hash_table_add(find->constr,
851                              SILC_32_TO_PTR(SILC_SKR_FIND_EMAIL), c);
852 }
853
854 SilcBool silc_skr_find_set_org(SilcSKRFind find, const char *org)
855 {
856   void *c = silc_memdup(org, strlen(org));
857   if (!c)
858     return FALSE;
859   return silc_hash_table_add(find->constr,
860                              SILC_32_TO_PTR(SILC_SKR_FIND_ORG), c);
861 }
862
863 SilcBool silc_skr_find_set_country(SilcSKRFind find, const char *country)
864 {
865   void *c = silc_memdup(country, strlen(country));
866   if (!c)
867     return FALSE;
868   return silc_hash_table_add(find->constr,
869                              SILC_32_TO_PTR(SILC_SKR_FIND_COUNTRY), c);
870 }
871
872 SilcBool silc_skr_find_set_public_key(SilcSKRFind find,
873                                       SilcPublicKey public_key)
874 {
875   SilcPublicKey pk = silc_pkcs_public_key_copy(public_key);
876   if (!pk)
877     return FALSE;
878   return silc_hash_table_add(find->constr,
879                              SILC_32_TO_PTR(SILC_SKR_FIND_PUBLIC_KEY), pk);
880 }
881
882 SilcBool silc_skr_find_set_context(SilcSKRFind find, void *context)
883 {
884   if (!context)
885     return TRUE;
886   return silc_hash_table_add(find->constr,
887                              SILC_32_TO_PTR(SILC_SKR_FIND_CONTEXT), context);
888 }
889
890 SilcBool silc_skr_find_set_usage(SilcSKRFind find, SilcSKRKeyUsage usage)
891 {
892   if (!usage)
893     return TRUE;
894   return silc_hash_table_add(find->constr,
895                              SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
896                              SILC_32_TO_PTR(usage));
897 }
898
899 /******************************** Search API ********************************/
900
901 /* Finds key(s) by the set search constraints.  The callback will be called
902    once keys has been found. */
903 /* This is now synchronous function but may later change async */
904
905 SilcAsyncOperation silc_skr_find(SilcSKR skr, SilcSchedule schedule,
906                                  SilcSKRFind find,
907                                  SilcSKRFindCallback callback,
908                                  void *callback_context)
909 {
910   SilcSKRStatus status = SILC_SKR_ERROR;
911   SilcHashTableList htl;
912   SilcDList list, results = NULL;
913   void *type, *ctx, *usage = NULL;
914 #if defined(SILC_DEBUG)
915   char tmp[256];
916 #endif /* SILC_DEBUG */
917
918   SILC_LOG_DEBUG(("Finding key from repository"));
919
920   if (!find || !callback)
921     return NULL;
922
923   silc_mutex_lock(skr->lock);
924
925   /* Get usage bits, if searching by them */
926   silc_hash_table_find(find->constr, SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
927                        NULL, &usage);
928
929 #if defined(SILC_DEBUG)
930   if (usage) {
931     memset(tmp, 0, sizeof(tmp));
932     silc_skr_type_string(SILC_SKR_FIND_USAGE, usage, tmp, sizeof(tmp) - 1);
933     SILC_LOG_DEBUG(("Finding key by %s", tmp));
934   }
935 #endif /* SILC_DEBUG */
936
937   silc_hash_table_list(find->constr, &htl);
938   while (silc_hash_table_get(&htl, &type, &ctx)) {
939
940     /* SILC_SKR_FIND_USAGE is handled separately while searching the keys. */
941     if ((SilcSKRFindType)SILC_32_TO_PTR(type) == SILC_SKR_FIND_USAGE)
942       continue;
943
944 #if defined(SILC_DEBUG)
945     memset(tmp, 0, sizeof(tmp));
946     silc_skr_type_string((SilcSKRFindType)SILC_32_TO_PTR(type),
947                          ctx, tmp, sizeof(tmp) - 1);
948     SILC_LOG_DEBUG(("Finding key by %s", tmp));
949 #endif /* SILC_DEBUG */
950
951     /* Find entries by this search constraint */
952     if (!silc_skr_find_entry(skr, &status,
953                              (SilcSKRFindType)SILC_32_TO_PTR(type),
954                              ctx, &list, NULL, SILC_PTR_TO_32(usage))) {
955       SILC_LOG_DEBUG(("Not found"));
956       if (results) {
957         silc_dlist_uninit(results);
958         results = NULL;
959       }
960       break;
961     }
962
963     /* For now, our logic rule is AND.  All constraints must be found
964        to find the key.  Later OR might be added also. */
965     if (!silc_skr_results_and(list, &status, &results)) {
966       SILC_LOG_DEBUG(("Not found"));
967       if (results) {
968         silc_dlist_uninit(results);
969         results = NULL;
970       }
971       silc_dlist_uninit(list);
972       break;
973     }
974
975     silc_dlist_uninit(list);
976   }
977   silc_hash_table_list_reset(&htl);
978
979   silc_mutex_unlock(skr->lock);
980
981   /* Return results */
982   if (!results) {
983     callback(skr, find, status, NULL, callback_context);
984   } else {
985     silc_dlist_start(results);
986     callback(skr, find, SILC_SKR_OK, results, callback_context);
987   }
988
989   return NULL;
990 }