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(("Server ID (%s)",
120                   silc_id_render(id, SILC_ID_SERVER)));
121
122   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id, 
123                                    SILC_ID_SERVER, &id_cache))
124     return NULL;
125
126   server = (SilcServerEntry)id_cache->context;
127
128   if (ret_entry)
129     *ret_entry = id_cache;
130
131   return server;
132 }
133
134 /* Replaces old Server ID with new one */ 
135
136 SilcServerEntry
137 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
138                               SilcServerID *new_id)
139 {
140   SilcIDCacheEntry id_cache = NULL;
141   SilcServerEntry server;
142
143   if (!old_id || !new_id)
144     return NULL;
145
146   SILC_LOG_DEBUG(("Replacing Server ID"));
147
148   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id, 
149                                    SILC_ID_SERVER, &id_cache))
150     return NULL;
151
152   server = (SilcServerEntry)id_cache->context;
153   silc_free(server->id);
154   server->id = new_id;
155   id_cache->id = (void *)new_id;
156
157   return server;
158 }
159
160 /* Removes and free's server entry from ID list */
161
162 void silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
163 {
164   if (entry) {
165     /* Remove from cache */
166     if (entry->id)
167       silc_idcache_del_by_id(id_list->servers, SILC_ID_SERVER, 
168                              (void *)entry->id);
169
170     /* Free data */
171     if (entry->server_name)
172       silc_free(entry->server_name);
173     if (entry->id)
174       silc_free(entry->id);
175   }
176 }
177
178 /******************************************************************************
179
180                           Client entry functions
181
182 ******************************************************************************/
183
184 /* Add new client entry. This adds the client entry to ID cache system
185    and returns the allocated client entry or NULL on error.  This is
186    called when new client connection is accepted to the server. */
187
188 SilcClientEntry
189 silc_idlist_add_client(SilcIDList id_list, unsigned char *nickname, 
190                        char *username, char *userinfo, SilcClientID *id, 
191                        SilcServerEntry router, void *connection)
192 {
193   SilcClientEntry client;
194
195   SILC_LOG_DEBUG(("Adding new client entry"));
196
197   client = silc_calloc(1, sizeof(*client));
198   client->nickname = nickname;
199   client->username = username;
200   client->userinfo = userinfo;
201   client->id = id;
202   client->router = router;
203   client->connection = connection;
204   silc_list_init(client->channels, struct SilcChannelClientEntryStruct, 
205                  client_list);
206
207   if (!silc_idcache_add(id_list->clients, nickname, SILC_ID_CLIENT,
208                         (void *)client->id, (void *)client, TRUE)) {
209     silc_free(client);
210     return NULL;
211   }
212
213   return client;
214 }
215
216 /* Free client entry. This free's everything and removes the entry
217    from ID cache. Call silc_idlist_del_data before calling this one. */
218
219 void silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
220 {
221   if (entry) {
222     /* Remove from cache */
223     if (entry->id)
224       silc_idcache_del_by_id(id_list->clients, SILC_ID_CLIENT, 
225                              (void *)entry->id);
226
227     /* Free data */
228     if (entry->nickname)
229       silc_free(entry->nickname);
230     if (entry->username)
231       silc_free(entry->username);
232     if (entry->userinfo)
233       silc_free(entry->userinfo);
234     if (entry->id)
235       silc_free(entry->id);
236   }
237 }
238
239 /* Returns all clients matching requested nickname. Number of clients is
240    returned to `clients_count'. Caller must free the returned table. */
241
242 SilcClientEntry *
243 silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
244                                     char *server, unsigned int *clients_count)
245 {
246   SilcIDCacheList list = NULL;
247   SilcIDCacheEntry id_cache = NULL;
248   SilcClientEntry *clients;
249   int i;
250
251   if (!silc_idcache_find_by_data(id_list->clients, nickname, &list))
252     return NULL;
253
254   clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
255
256   i = 0;
257   silc_idcache_list_first(list, &id_cache);
258   clients[i++] = (SilcClientEntry)id_cache->context;
259
260   while (silc_idcache_list_next(list, &id_cache))
261     clients[i++] = (SilcClientEntry)id_cache->context;
262   
263   silc_idcache_list_free(list);
264   
265   if (clients_count)
266     *clients_count = i;
267
268   return clients;
269 }
270
271 /* Returns all clients matching requested nickname. Number of clients is
272    returned to `clients_count'. Caller must free the returned table. */
273
274 SilcClientEntry *
275 silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
276                                 SilcHash md5hash,
277                                 unsigned int *clients_count)
278 {
279   SilcIDCacheList list = NULL;
280   SilcIDCacheEntry id_cache = NULL;
281   SilcClientEntry *clients;
282   unsigned char hash[32];
283   int i;
284
285   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
286
287   if (!silc_idcache_find_by_data(id_list->clients, hash, &list))
288     return NULL;
289
290   clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
291
292   i = 0;
293   silc_idcache_list_first(list, &id_cache);
294   clients[i++] = (SilcClientEntry)id_cache->context;
295
296   while (silc_idcache_list_next(list, &id_cache))
297     clients[i++] = (SilcClientEntry)id_cache->context;
298   
299   silc_idcache_list_free(list);
300   
301   if (clients_count)
302     *clients_count = i;
303
304   return clients;
305 }
306
307 /* Finds client entry by nickname. */
308
309 SilcClientEntry
310 silc_idlist_find_client_by_nickname(SilcIDList id_list, char *nickname,
311                                     char *server, SilcIDCacheEntry *ret_entry)
312 {
313   SilcIDCacheList list = NULL;
314   SilcIDCacheEntry id_cache = NULL;
315   SilcClientEntry client = NULL;
316
317   SILC_LOG_DEBUG(("Client by nickname"));
318
319   if (server) {
320     if (!silc_idcache_find_by_data(id_list->clients, nickname, &list))
321       return NULL;
322
323 #if 0
324     while (silc_idcache_list_next(list, &id_cache)) {
325       client = (SilcClientEntry)id_cache->context;
326
327       if (!strcmp(server, XXX, strlen(server)))
328         break;
329
330       client = NULL;
331     }
332 #endif
333
334    silc_idcache_list_free(list);
335
336    if (!client)
337      return NULL;
338   } else {
339     if (!silc_idcache_find_by_data_one(id_list->clients, nickname, &id_cache))
340       return NULL;
341
342     client = (SilcClientEntry)id_cache->context;
343
344     if (ret_entry)
345       *ret_entry = id_cache;
346   }
347
348   SILC_LOG_DEBUG(("Found"));
349
350   return client;
351 }
352
353 /* Finds client by nickname hash. */
354
355 SilcClientEntry
356 silc_idlist_find_client_by_hash(SilcIDList id_list, char *nickname,
357                                 SilcHash md5hash, SilcIDCacheEntry *ret_entry)
358 {
359   SilcIDCacheList list = NULL;
360   SilcIDCacheEntry id_cache = NULL;
361   SilcClientEntry client = NULL;
362   unsigned char hash[32];
363
364   SILC_LOG_DEBUG(("Client by hash"));
365
366   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
367
368   if (!silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY, 
369                                SILC_ID_CLIENT, &list))
370     return NULL;
371
372   if (!silc_idcache_list_first(list, &id_cache)) {
373     silc_idcache_list_free(list);
374     return NULL;
375   }
376
377   while (id_cache) {
378     client = (SilcClientEntry)id_cache->context;
379     
380     if (client && !SILC_ID_COMPARE_HASH(client->id, hash))
381       break;
382
383     id_cache = NULL;
384     client = NULL;
385
386     if (!silc_idcache_list_next(list, &id_cache))
387       break;
388   }
389   
390   silc_idcache_list_free(list);
391
392   if (ret_entry)
393     *ret_entry = id_cache;
394
395   SILC_LOG_DEBUG(("Found"));
396
397   return client;
398 }
399
400 /* Finds client by Client ID */
401
402 SilcClientEntry
403 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
404                               SilcIDCacheEntry *ret_entry)
405 {
406   SilcIDCacheEntry id_cache = NULL;
407   SilcClientEntry client;
408
409   if (!id)
410     return NULL;
411
412   SILC_LOG_DEBUG(("Client ID (%s)", 
413                   silc_id_render(id, SILC_ID_CLIENT)));
414
415   if (!silc_idcache_find_by_id_one(id_list->clients, (void *)id, 
416                                    SILC_ID_CLIENT, &id_cache))
417     return NULL;
418
419   client = (SilcClientEntry)id_cache->context;
420
421   if (ret_entry)
422     *ret_entry = id_cache;
423
424   SILC_LOG_DEBUG(("Found"));
425
426   return client;
427 }
428
429 /* Replaces old Client ID with new one */
430
431 SilcClientEntry
432 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
433                               SilcClientID *new_id)
434 {
435   SilcIDCacheEntry id_cache = NULL;
436   SilcClientEntry client;
437
438   if (!old_id || !new_id)
439     return NULL;
440
441   SILC_LOG_DEBUG(("Replacing Client ID"));
442
443   if (!silc_idcache_find_by_id_one(id_list->clients, (void *)old_id, 
444                                    SILC_ID_CLIENT, &id_cache))
445     return NULL;
446
447   client = (SilcClientEntry)id_cache->context;
448   silc_free(client->id);
449   client->id = new_id;
450   id_cache->id = (void *)new_id;
451
452   /* If the old ID Cache data was the hash value of the old Client ID
453      replace it with the hash of new Client ID */
454   if (id_cache->data && !SILC_ID_COMPARE_HASH(old_id, id_cache->data)) {
455     silc_free(id_cache->data);
456     id_cache->data = silc_calloc(sizeof(new_id->hash), sizeof(unsigned char));
457     memcpy(id_cache->data, new_id->hash, sizeof(new_id->hash));
458     silc_idcache_sort_by_data(id_list->clients);
459   }
460
461   return client;
462 }
463
464
465 /******************************************************************************
466
467                           Channel entry functions
468
469 ******************************************************************************/
470
471 /* Add new channel entry. This add the new channel entry to the ID cache
472    system and returns the allocated entry or NULL on error. */
473
474 SilcChannelEntry
475 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
476                         SilcChannelID *id, SilcServerEntry router,
477                         SilcCipher channel_key)
478 {
479   SilcChannelEntry channel;
480
481   channel = silc_calloc(1, sizeof(*channel));
482   channel->channel_name = channel_name;
483   channel->mode = mode;
484   channel->id = id;
485   channel->router = router;
486   channel->channel_key = channel_key;
487   silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct, 
488                  channel_list);
489
490   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
491                         SILC_ID_CHANNEL, (void *)channel->id, 
492                         (void *)channel, TRUE)) {
493     silc_free(channel);
494     return NULL;
495   }
496
497   return channel;
498 }
499
500 /* Free channel entry.  This free's everything. */
501
502 void silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
503 {
504   if (entry) {
505     SilcChannelClientEntry chl;
506
507     /* Remove from cache */
508     if (entry->id)
509       silc_idcache_del_by_id(id_list->channels, SILC_ID_CHANNEL, 
510                              (void *)entry->id);
511
512     /* Free data */
513     if (entry->channel_name)
514       silc_free(entry->channel_name);
515     if (entry->id)
516       silc_free(entry->id);
517     if (entry->topic)
518       silc_free(entry->topic);
519     if (entry->channel_key)
520       silc_cipher_free(entry->channel_key);
521     if (entry->key) {
522       memset(entry->key, 0, entry->key_len / 8);
523       silc_free(entry->key);
524     }
525     memset(entry->iv, 0, sizeof(entry->iv));
526     
527     silc_list_start(entry->user_list);
528     while ((chl = silc_list_get(entry->user_list)) != SILC_LIST_END) {
529       silc_list_del(entry->user_list, chl);
530       silc_free(chl);
531     }
532   }
533 }
534
535 /* Finds channel by channel name. Channel names are unique and they
536    are not case-sensitive. */
537
538 SilcChannelEntry
539 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
540                                  SilcIDCacheEntry *ret_entry)
541 {
542   SilcIDCacheList list = NULL;
543   SilcIDCacheEntry id_cache = NULL;
544   SilcChannelEntry channel;
545
546   SILC_LOG_DEBUG(("Channel by name"));
547
548   if (!silc_idcache_find_by_data_loose(id_list->channels, name, &list))
549     return NULL;
550   
551   if (!silc_idcache_list_first(list, &id_cache)) {
552     silc_idcache_list_free(list);
553     return NULL;
554   }
555
556   channel = (SilcChannelEntry)id_cache->context;
557
558   if (ret_entry)
559     *ret_entry = id_cache;
560
561   silc_idcache_list_free(list);
562
563   SILC_LOG_DEBUG(("Found"));
564
565   return channel;
566 }
567
568 /* Finds channel by Channel ID. */
569
570 SilcChannelEntry
571 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
572                                SilcIDCacheEntry *ret_entry)
573 {
574   SilcIDCacheEntry id_cache = NULL;
575   SilcChannelEntry channel;
576
577   if (!id)
578     return NULL;
579
580   SILC_LOG_DEBUG(("Channel ID (%s)",
581                   silc_id_render(id, SILC_ID_CHANNEL)));
582
583   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, 
584                                    SILC_ID_CHANNEL, &id_cache))
585     return NULL;
586
587   channel = (SilcChannelEntry)id_cache->context;
588
589   if (ret_entry)
590     *ret_entry = id_cache;
591
592   SILC_LOG_DEBUG(("Found"));
593
594   return channel;
595 }