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