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