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