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