Router environment. WHOIS/IDENTIFY/JOIN/channel message sending
[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   SILC_LOG_DEBUG(("Found"));
348
349   return client;
350 }
351
352 /* Finds client by nickname hash. */
353
354 SilcClientEntry
355 silc_idlist_find_client_by_hash(SilcIDList id_list, char *nickname,
356                                 SilcHash md5hash, SilcIDCacheEntry *ret_entry)
357 {
358   SilcIDCacheList list = NULL;
359   SilcIDCacheEntry id_cache = NULL;
360   SilcClientEntry client = NULL;
361   unsigned char hash[32];
362
363   SILC_LOG_DEBUG(("Finding client by hash"));
364
365   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
366
367   if (!silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY, 
368                                SILC_ID_CLIENT, &list))
369     return NULL;
370
371   if (!silc_idcache_list_first(list, &id_cache)) {
372     silc_idcache_list_free(list);
373     return NULL;
374   }
375
376   while (id_cache) {
377     client = (SilcClientEntry)id_cache->context;
378     
379     if (client && !SILC_ID_COMPARE_HASH(client->id, hash))
380       break;
381
382     id_cache = NULL;
383     client = NULL;
384
385     if (!silc_idcache_list_next(list, &id_cache))
386       break;
387   }
388   
389   silc_idcache_list_free(list);
390
391   if (ret_entry)
392     *ret_entry = id_cache;
393
394   SILC_LOG_DEBUG(("Found"));
395
396   return client;
397 }
398
399 /* Finds client by Client ID */
400
401 SilcClientEntry
402 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
403                               SilcIDCacheEntry *ret_entry)
404 {
405   SilcIDCacheEntry id_cache = NULL;
406   SilcClientEntry client;
407
408   if (!id)
409     return NULL;
410
411   SILC_LOG_DEBUG(("Finding client by ID"));
412
413   if (!silc_idcache_find_by_id_one(id_list->clients, (void *)id, 
414                                    SILC_ID_CLIENT, &id_cache))
415     return NULL;
416
417   client = (SilcClientEntry)id_cache->context;
418
419   if (ret_entry)
420     *ret_entry = id_cache;
421
422   SILC_LOG_DEBUG(("Found"));
423
424   return client;
425 }
426
427 /* Replaces old Client ID with new one */
428
429 SilcClientEntry
430 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
431                               SilcClientID *new_id)
432 {
433   SilcIDCacheEntry id_cache = NULL;
434   SilcClientEntry client;
435
436   if (!old_id || !new_id)
437     return NULL;
438
439   SILC_LOG_DEBUG(("Replacing Client ID"));
440
441   if (!silc_idcache_find_by_id_one(id_list->clients, (void *)old_id, 
442                                    SILC_ID_CLIENT, &id_cache))
443     return NULL;
444
445   client = (SilcClientEntry)id_cache->context;
446   silc_free(client->id);
447   client->id = new_id;
448   id_cache->id = (void *)new_id;
449
450   /* If the old ID Cache data was the hash value of the old Client ID
451      replace it with the hash of new Client ID */
452   if (id_cache->data && !SILC_ID_COMPARE_HASH(old_id, id_cache->data)) {
453     silc_free(id_cache->data);
454     id_cache->data = silc_calloc(sizeof(new_id->hash), sizeof(unsigned char));
455     memcpy(id_cache->data, new_id->hash, sizeof(new_id->hash));
456     silc_idcache_sort_by_data(id_list->clients);
457   }
458
459   return client;
460 }
461
462
463 /******************************************************************************
464
465                           Channel entry functions
466
467 ******************************************************************************/
468
469 /* Add new channel entry. This add the new channel entry to the ID cache
470    system and returns the allocated entry or NULL on error. */
471
472 SilcChannelEntry
473 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
474                         SilcChannelID *id, SilcServerEntry router,
475                         SilcCipher channel_key)
476 {
477   SilcChannelEntry channel;
478
479   channel = silc_calloc(1, sizeof(*channel));
480   channel->channel_name = channel_name;
481   channel->mode = mode;
482   channel->id = id;
483   channel->router = router;
484   channel->channel_key = channel_key;
485   silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct, 
486                  channel_list);
487
488   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
489                         SILC_ID_CHANNEL, (void *)channel->id, 
490                         (void *)channel, TRUE)) {
491     silc_free(channel);
492     return NULL;
493   }
494
495   return channel;
496 }
497
498 /* Free channel entry.  This free's everything. */
499
500 void silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
501 {
502   if (entry) {
503     SilcChannelClientEntry chl;
504
505     /* Remove from cache */
506     if (entry->id)
507       silc_idcache_del_by_id(id_list->channels, SILC_ID_CHANNEL, 
508                              (void *)entry->id);
509
510     /* Free data */
511     if (entry->channel_name)
512       silc_free(entry->channel_name);
513     if (entry->id)
514       silc_free(entry->id);
515     if (entry->topic)
516       silc_free(entry->topic);
517     if (entry->channel_key)
518       silc_cipher_free(entry->channel_key);
519     if (entry->key) {
520       memset(entry->key, 0, entry->key_len / 8);
521       silc_free(entry->key);
522     }
523     memset(entry->iv, 0, sizeof(entry->iv));
524     
525     silc_list_start(entry->user_list);
526     while ((chl = silc_list_get(entry->user_list)) != SILC_LIST_END) {
527       silc_list_del(entry->user_list, chl);
528       silc_free(chl);
529     }
530   }
531 }
532
533 /* Finds channel by channel name. Channel names are unique and they
534    are not case-sensitive. */
535
536 SilcChannelEntry
537 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
538                                  SilcIDCacheEntry *ret_entry)
539 {
540   SilcIDCacheList list = NULL;
541   SilcIDCacheEntry id_cache = NULL;
542   SilcChannelEntry channel;
543
544   SILC_LOG_DEBUG(("Finding channel by name"));
545
546   if (!silc_idcache_find_by_data_loose(id_list->channels, name, &list))
547     return NULL;
548   
549   if (!silc_idcache_list_first(list, &id_cache)) {
550     silc_idcache_list_free(list);
551     return NULL;
552   }
553
554   channel = (SilcChannelEntry)id_cache->context;
555
556   if (ret_entry)
557     *ret_entry = id_cache;
558
559   silc_idcache_list_free(list);
560
561   SILC_LOG_DEBUG(("Found"));
562
563   return channel;
564 }
565
566 /* Finds channel by Channel ID. */
567
568 SilcChannelEntry
569 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
570                                SilcIDCacheEntry *ret_entry)
571 {
572   SilcIDCacheEntry id_cache = NULL;
573   SilcChannelEntry channel;
574
575   if (!id)
576     return NULL;
577
578   SILC_LOG_DEBUG(("Finding channel by ID"));
579
580   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, 
581                                    SILC_ID_CHANNEL, &id_cache))
582     return NULL;
583
584   channel = (SilcChannelEntry)id_cache->context;
585
586   if (ret_entry)
587     *ret_entry = id_cache;
588
589   SILC_LOG_DEBUG(("Found"));
590
591   return channel;
592 }