updates
[silc.git] / apps / silcd / idlist.c
1 /*
2
3   idlist.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2000 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; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "serverincludes.h"
23 #include "idlist.h"
24
25 /******************************************************************************
26
27                              Common functions
28
29 ******************************************************************************/
30
31 /* This function is used to add keys and stuff to common ID entry data
32    structure. */
33
34 void silc_idlist_add_data(void *entry, SilcIDListData idata)
35 {
36   SilcIDListData data = (SilcIDListData)entry;
37   data->send_key = idata->send_key;
38   data->receive_key = idata->receive_key;
39   data->hmac = idata->hmac;
40   data->hmac_key = idata->hmac_key;
41   data->hmac_key_len = idata->hmac_key_len;
42   data->pkcs = idata->pkcs;
43   data->public_key = idata->public_key;
44   data->last_receive = idata->last_receive;
45   data->last_sent = idata->last_sent;
46   data->registered = idata->registered;
47 }
48
49 /* Free's all data in the common ID entry data structure. */
50
51 void silc_idlist_del_data(void *entry)
52 {
53   SilcIDListData idata = (SilcIDListData)entry;
54   if (idata->send_key)
55     silc_cipher_free(idata->send_key);
56   if (idata->receive_key)
57     silc_cipher_free(idata->receive_key);
58   if (idata->hmac)
59     silc_hmac_free(idata->hmac);
60   if (idata->hmac_key) {
61     memset(idata->hmac_key, 0, idata->hmac_key_len);
62     silc_free(idata->hmac_key);
63   }
64   if (idata->pkcs)
65     silc_pkcs_free(idata->pkcs);
66   if (idata->public_key)
67     silc_pkcs_public_key_free(idata->public_key);
68 }
69
70 /******************************************************************************
71
72                           Server entry functions
73
74 ******************************************************************************/
75
76 /* Add new server entry. This adds the new server entry to ID cache and
77    returns the allocated entry object or NULL on error. This is called
78    when new server connects to us. We also add ourselves to cache with
79    this function. */
80
81 SilcServerEntry 
82 silc_idlist_add_server(SilcIDList id_list, 
83                        char *server_name, int server_type,
84                        SilcServerID *id, SilcServerEntry router,
85                        void *connection)
86 {
87   SilcServerEntry server;
88
89   SILC_LOG_DEBUG(("Adding new server entry"));
90
91   server = silc_calloc(1, sizeof(*server));
92   server->server_name = server_name;
93   server->server_type = server_type;
94   server->id = id;
95   server->router = router;
96   server->connection = connection;
97
98   if (!silc_idcache_add(id_list->servers, server->server_name, SILC_ID_SERVER,
99                         (void *)server->id, (void *)server, TRUE)) {
100     silc_free(server);
101     return NULL;
102   }
103
104   return server;
105 }
106
107 /* Finds server by Server ID */
108
109 SilcServerEntry
110 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
111                               SilcIDCacheEntry *ret_entry)
112 {
113   SilcIDCacheEntry id_cache = NULL;
114   SilcServerEntry server;
115
116   if (!id)
117     return NULL;
118
119   SILC_LOG_DEBUG(("Finding server by ID"));
120
121   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id, 
122                                    SILC_ID_SERVER, &id_cache))
123     return NULL;
124
125   server = (SilcServerEntry)id_cache->context;
126
127   if (ret_entry)
128     *ret_entry = id_cache;
129
130   return server;
131 }
132
133 /* Replaces old Server ID with new one */ 
134
135 SilcServerEntry
136 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
137                               SilcServerID *new_id)
138 {
139   SilcIDCacheEntry id_cache = NULL;
140   SilcServerEntry server;
141
142   if (!old_id || !new_id)
143     return NULL;
144
145   SILC_LOG_DEBUG(("Replacing Server ID"));
146
147   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id, 
148                                    SILC_ID_SERVER, &id_cache))
149     return NULL;
150
151   server = (SilcServerEntry)id_cache->context;
152   silc_free(server->id);
153   server->id = new_id;
154   id_cache->id = (void *)new_id;
155
156   return server;
157 }
158
159 /* Removes and free's server entry from ID list */
160
161 void silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
162 {
163   if (entry) {
164     /* Remove from cache */
165     if (entry->id)
166       silc_idcache_del_by_id(id_list->servers, SILC_ID_SERVER, 
167                              (void *)entry->id);
168
169     /* Free data */
170     if (entry->server_name)
171       silc_free(entry->server_name);
172     if (entry->id)
173       silc_free(entry->id);
174   }
175 }
176
177 /******************************************************************************
178
179                           Client entry functions
180
181 ******************************************************************************/
182
183 /* Add new client entry. This adds the client entry to ID cache system
184    and returns the allocated client entry or NULL on error.  This is
185    called when new client connection is accepted to the server. */
186
187 SilcClientEntry
188 silc_idlist_add_client(SilcIDList id_list, unsigned char *nickname, 
189                        char *username, char *userinfo, SilcClientID *id, 
190                        SilcServerEntry router, void *connection)
191 {
192   SilcClientEntry client;
193
194   SILC_LOG_DEBUG(("Adding new client entry"));
195
196   client = silc_calloc(1, sizeof(*client));
197   client->nickname = nickname;
198   client->username = username;
199   client->userinfo = userinfo;
200   client->id = id;
201   client->router = router;
202   client->connection = connection;
203   silc_list_init(client->channels, struct SilcChannelClientEntryStruct, 
204                  client_list);
205
206   if (!silc_idcache_add(id_list->clients, nickname, SILC_ID_CLIENT,
207                         (void *)client->id, (void *)client, TRUE)) {
208     silc_free(client);
209     return NULL;
210   }
211
212   return client;
213 }
214
215 /* Free client entry. This free's everything and removes the entry
216    from ID cache. Call silc_idlist_del_data before calling this one. */
217
218 void silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
219 {
220   if (entry) {
221     /* Remove from cache */
222     if (entry->id)
223       silc_idcache_del_by_id(id_list->clients, SILC_ID_CLIENT, 
224                              (void *)entry->id);
225
226     /* Free data */
227     if (entry->nickname)
228       silc_free(entry->nickname);
229     if (entry->username)
230       silc_free(entry->username);
231     if (entry->userinfo)
232       silc_free(entry->userinfo);
233     if (entry->id)
234       silc_free(entry->id);
235   }
236 }
237
238 /* Returns all clients matching requested nickname. Number of clients is
239    returned to `clients_count'. Caller must free the returned table. */
240
241 SilcClientEntry *
242 silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
243                                     char *server, unsigned int *clients_count)
244 {
245   SilcIDCacheList list = NULL;
246   SilcIDCacheEntry id_cache = NULL;
247   SilcClientEntry *clients;
248   int i;
249
250   if (!silc_idcache_find_by_data(id_list->clients, nickname, &list))
251     return NULL;
252
253   clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
254
255   i = 0;
256   silc_idcache_list_first(list, &id_cache);
257   clients[i++] = (SilcClientEntry)id_cache->context;
258
259   while (silc_idcache_list_next(list, &id_cache))
260     clients[i++] = (SilcClientEntry)id_cache->context;
261   
262   silc_idcache_list_free(list);
263   
264   if (clients_count)
265     *clients_count = i;
266
267   return clients;
268 }
269
270 /* Returns all clients matching requested nickname. Number of clients is
271    returned to `clients_count'. Caller must free the returned table. */
272
273 SilcClientEntry *
274 silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
275                                 SilcHash md5hash,
276                                 unsigned int *clients_count)
277 {
278   SilcIDCacheList list = NULL;
279   SilcIDCacheEntry id_cache = NULL;
280   SilcClientEntry *clients;
281   unsigned char hash[32];
282   int i;
283
284   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
285
286   if (!silc_idcache_find_by_data(id_list->clients, hash, &list))
287     return NULL;
288
289   clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
290
291   i = 0;
292   silc_idcache_list_first(list, &id_cache);
293   clients[i++] = (SilcClientEntry)id_cache->context;
294
295   while (silc_idcache_list_next(list, &id_cache))
296     clients[i++] = (SilcClientEntry)id_cache->context;
297   
298   silc_idcache_list_free(list);
299   
300   if (clients_count)
301     *clients_count = i;
302
303   return clients;
304 }
305
306 /* Finds client entry by nickname. */
307
308 SilcClientEntry
309 silc_idlist_find_client_by_nickname(SilcIDList id_list, char *nickname,
310                                     char *server, SilcIDCacheEntry *ret_entry)
311 {
312   SilcIDCacheList list = NULL;
313   SilcIDCacheEntry id_cache = NULL;
314   SilcClientEntry client = NULL;
315
316   SILC_LOG_DEBUG(("Finding client by nickname"));
317
318   if (server) {
319     if (!silc_idcache_find_by_data(id_list->clients, nickname, &list))
320       return NULL;
321
322 #if 0
323     while (silc_idcache_list_next(list, &id_cache)) {
324       client = (SilcClientEntry)id_cache->context;
325
326       if (!strcmp(server, XXX, strlen(server)))
327         break;
328
329       client = NULL;
330     }
331 #endif
332
333    silc_idcache_list_free(list);
334
335    if (!client)
336      return NULL;
337   } else {
338     if (!silc_idcache_find_by_data_one(id_list->clients, nickname, &id_cache))
339       return NULL;
340
341     client = (SilcClientEntry)id_cache->context;
342
343     if (ret_entry)
344       *ret_entry = id_cache;
345   }
346
347   return client;
348 }
349
350 /* Finds client by nickname hash. */
351
352 SilcClientEntry
353 silc_idlist_find_client_by_hash(SilcIDList id_list, char *nickname,
354                                 SilcHash md5hash, SilcIDCacheEntry *ret_entry)
355 {
356   SilcIDCacheList list = NULL;
357   SilcIDCacheEntry id_cache = NULL;
358   SilcClientEntry client = NULL;
359   unsigned char hash[32];
360
361   SILC_LOG_DEBUG(("Finding client by hash"));
362
363   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
364
365   if (!silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY, 
366                                SILC_ID_CLIENT, &list))
367     return NULL;
368
369   if (!silc_idcache_list_first(list, &id_cache)) {
370     silc_idcache_list_free(list);
371     return NULL;
372   }
373
374   while (id_cache) {
375     client = (SilcClientEntry)id_cache->context;
376     
377     if (client && !SILC_ID_COMPARE_HASH(client->id, hash))
378       break;
379
380     id_cache = NULL;
381     client = NULL;
382
383     if (!silc_idcache_list_next(list, &id_cache))
384       break;
385   }
386   
387   silc_idcache_list_free(list);
388
389   if (ret_entry)
390     *ret_entry = id_cache;
391
392   return client;
393 }
394
395 /* Finds client by Client ID */
396
397 SilcClientEntry
398 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
399                               SilcIDCacheEntry *ret_entry)
400 {
401   SilcIDCacheEntry id_cache = NULL;
402   SilcClientEntry client;
403
404   if (!id)
405     return NULL;
406
407   SILC_LOG_DEBUG(("Finding client by ID"));
408
409   if (!silc_idcache_find_by_id_one(id_list->clients, (void *)id, 
410                                    SILC_ID_CLIENT, &id_cache))
411     return NULL;
412
413   client = (SilcClientEntry)id_cache->context;
414
415   if (ret_entry)
416     *ret_entry = id_cache;
417
418   return client;
419 }
420
421 /* Replaces old Client ID with new one */
422
423 SilcClientEntry
424 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
425                               SilcClientID *new_id)
426 {
427   SilcIDCacheEntry id_cache = NULL;
428   SilcClientEntry client;
429
430   if (!old_id || !new_id)
431     return NULL;
432
433   SILC_LOG_DEBUG(("Replacing Client ID"));
434
435   if (!silc_idcache_find_by_id_one(id_list->clients, (void *)old_id, 
436                                    SILC_ID_CLIENT, &id_cache))
437     return NULL;
438
439   client = (SilcClientEntry)id_cache->context;
440   silc_free(client->id);
441   client->id = new_id;
442   id_cache->id = (void *)new_id;
443
444   /* If the old ID Cache data was the hash value of the old Client ID
445      replace it with the hash of new Client ID */
446   if (id_cache->data && !SILC_ID_COMPARE_HASH(old_id, id_cache->data)) {
447     silc_free(id_cache->data);
448     id_cache->data = silc_calloc(sizeof(new_id->hash), sizeof(unsigned char));
449     memcpy(id_cache->data, new_id->hash, sizeof(new_id->hash));
450     silc_idcache_sort_by_data(id_list->clients);
451   }
452
453   return client;
454 }
455
456
457 /******************************************************************************
458
459                           Channel entry functions
460
461 ******************************************************************************/
462
463 /* Add new channel entry. This add the new channel entry to the ID cache
464    system and returns the allocated entry or NULL on error. */
465
466 SilcChannelEntry
467 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
468                         SilcChannelID *id, SilcServerEntry router,
469                         SilcCipher channel_key)
470 {
471   SilcChannelEntry channel;
472
473   channel = silc_calloc(1, sizeof(*channel));
474   channel->channel_name = channel_name;
475   channel->mode = mode;
476   channel->id = id;
477   channel->router = router;
478   channel->channel_key = channel_key;
479   silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct, 
480                  channel_list);
481
482   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
483                         SILC_ID_CHANNEL, (void *)channel->id, 
484                         (void *)channel, TRUE)) {
485     silc_free(channel);
486     return NULL;
487   }
488
489   return channel;
490 }
491
492 /* Free channel entry.  This free's everything. */
493
494 void silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
495 {
496   if (entry) {
497     SilcChannelClientEntry chl;
498
499     /* Remove from cache */
500     if (entry->id)
501       silc_idcache_del_by_id(id_list->channels, SILC_ID_CHANNEL, 
502                              (void *)entry->id);
503
504     /* Free data */
505     if (entry->channel_name)
506       silc_free(entry->channel_name);
507     if (entry->id)
508       silc_free(entry->id);
509     if (entry->topic)
510       silc_free(entry->topic);
511     if (entry->channel_key)
512       silc_cipher_free(entry->channel_key);
513     if (entry->key) {
514       memset(entry->key, 0, entry->key_len / 8);
515       silc_free(entry->key);
516     }
517     memset(entry->iv, 0, sizeof(entry->iv));
518     
519     silc_list_start(entry->user_list);
520     while ((chl = silc_list_get(entry->user_list)) != SILC_LIST_END) {
521       silc_list_del(entry->user_list, chl);
522       silc_free(chl);
523     }
524   }
525 }
526
527 /* Finds channel by channel name. Channel names are unique and they
528    are not case-sensitive. */
529
530 SilcChannelEntry
531 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
532                                  SilcIDCacheEntry *ret_entry)
533 {
534   SilcIDCacheList list = NULL;
535   SilcIDCacheEntry id_cache = NULL;
536   SilcChannelEntry channel;
537
538   SILC_LOG_DEBUG(("Finding channel by name"));
539
540   if (!silc_idcache_find_by_data_loose(id_list->channels, name, &list))
541     return NULL;
542   
543   if (!silc_idcache_list_first(list, &id_cache)) {
544     silc_idcache_list_free(list);
545     return NULL;
546   }
547
548   channel = (SilcChannelEntry)id_cache->context;
549
550   if (ret_entry)
551     *ret_entry = id_cache;
552
553   silc_idcache_list_free(list);
554
555   return channel;
556 }
557
558 /* Finds channel by Channel ID. */
559
560 SilcChannelEntry
561 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
562                                SilcIDCacheEntry *ret_entry)
563 {
564   SilcIDCacheEntry id_cache = NULL;
565   SilcChannelEntry channel;
566
567   if (!id)
568     return NULL;
569
570   SILC_LOG_DEBUG(("Finding channel by ID"));
571
572   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, 
573                                    SILC_ID_CHANNEL, &id_cache))
574     return NULL;
575
576   channel = (SilcChannelEntry)id_cache->context;
577
578   if (ret_entry)
579     *ret_entry = id_cache;
580
581   return channel;
582 }