Added WHOIS to send multiple replies if multiple nicknames are
[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 /*
21  * $Id$
22  * $Log$
23  * Revision 1.8  2000/10/06 08:10:23  priikone
24  *      Added WHOIS to send multiple replies if multiple nicknames are
25  *      found.
26  *      Added MOTD command and [motd] config section and server also sends
27  *      motd to client on connection now.
28  *      Fixed TOPIC command some more.
29  *
30  * Revision 1.7  2000/07/26 07:04:01  priikone
31  *      Added server_find_by_id, replace_[server/client]_id.
32  *
33  * Revision 1.6  2000/07/17 11:47:30  priikone
34  *      Added command lagging support. Added idle counting support.
35  *
36  * Revision 1.5  2000/07/12 05:59:41  priikone
37  *      Major rewrite of ID Cache system. Support added for the new
38  *      ID cache system. Major rewrite of ID List stuff on server.  All
39  *      SilcXXXList's are now called SilcXXXEntry's and they are pointers
40  *      by default. A lot rewritten ID list functions.
41  *
42  * Revision 1.4  2000/07/06 07:16:13  priikone
43  *      Added SilcPublicKey's
44  *
45  * Revision 1.3  2000/07/05 06:14:01  priikone
46  *      Global costemic changes.
47  *
48  * Revision 1.2  2000/07/03 05:52:11  priikone
49  *      Fixed typo and a bug.
50  *
51  * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
52  *      Imported from internal CVS/Added Log headers.
53  *
54  *
55  */
56
57 #include "serverincludes.h"
58 #include "idlist.h"
59
60 /******************************************************************************
61
62                           Server entry functions
63
64 ******************************************************************************/
65
66 /* Add new server entry. This adds the new server entry to ID cache and
67    returns the allocated entry object or NULL on error. This is called
68    when new server connects to us. We also add ourselves to cache with
69    this function. */
70
71 SilcServerEntry 
72 silc_idlist_add_server(SilcIDList id_list, 
73                        char *server_name, int server_type,
74                        SilcServerID *id, SilcServerEntry router,
75                        SilcCipher send_key, SilcCipher receive_key,
76                        SilcPKCS pkcs, SilcHmac hmac, 
77                        SilcPublicKey public_key, void *connection)
78 {
79   SilcServerEntry server;
80
81   SILC_LOG_DEBUG(("Adding new server entry"));
82
83   server = silc_calloc(1, sizeof(*server));
84   server->server_name = server_name;
85   server->server_type = server_type;
86   server->id = id;
87   server->router = router;
88   server->send_key = send_key;
89   server->receive_key = receive_key;
90   server->pkcs = pkcs;
91   server->hmac = hmac;
92   server->public_key = public_key;
93   server->connection = connection;
94
95   if (!silc_idcache_add(id_list->servers, server->server_name, SILC_ID_SERVER,
96                         (void *)server->id, (void *)server, TRUE)) {
97     silc_free(server);
98     return NULL;
99   }
100
101   return server;
102 }
103
104 /* Finds server by Server ID */
105
106 SilcServerEntry
107 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id)
108 {
109   SilcIDCacheEntry id_cache = NULL;
110   SilcServerEntry server;
111
112   if (!id)
113     return NULL;
114
115   SILC_LOG_DEBUG(("Finding server by ID"));
116
117   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id, 
118                                    SILC_ID_SERVER, &id_cache))
119     return NULL;
120
121   server = (SilcServerEntry)id_cache->context;
122
123   return server;
124 }
125
126 /* Replaces old Server ID with new one */ 
127
128 SilcServerEntry
129 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
130                               SilcServerID *new_id)
131 {
132   SilcIDCacheEntry id_cache = NULL;
133   SilcServerEntry server;
134
135   if (!old_id || !new_id)
136     return NULL;
137
138   SILC_LOG_DEBUG(("Replacing Server ID"));
139
140   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id, 
141                                    SILC_ID_SERVER, &id_cache))
142     return NULL;
143
144   server = (SilcServerEntry)id_cache->context;
145   silc_free(server->id);
146   server->id = new_id;
147   id_cache->id = (void *)new_id;
148
149   return server;
150 }
151
152 /******************************************************************************
153
154                           Client entry functions
155
156 ******************************************************************************/
157
158 /* Add new client entry. This adds the client entry to ID cache system
159    and returns the allocated client entry or NULL on error.  This is
160    called when new client connection is accepted to the server. */
161
162 SilcClientEntry
163 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username,
164                        char *userinfo, SilcClientID *id, 
165                        SilcServerEntry router,
166                        SilcCipher send_key, SilcCipher receive_key,
167                        SilcPKCS pkcs, SilcHmac hmac, 
168                        SilcPublicKey public_key, void *connection)
169 {
170   SilcClientEntry client;
171
172   SILC_LOG_DEBUG(("Adding new client entry"));
173
174   client = silc_calloc(1, sizeof(*client));
175   client->nickname = nickname;
176   client->username = username;
177   client->userinfo = userinfo;
178   client->id = id;
179   client->router = router;
180   client->send_key = send_key;
181   client->receive_key = receive_key;
182   client->pkcs = pkcs;
183   client->hmac = hmac;
184   client->public_key = public_key;
185   client->connection = connection;
186
187   if (!silc_idcache_add(id_list->clients, client->nickname, SILC_ID_CLIENT,
188                         (void *)client->id, (void *)client, TRUE)) {
189     silc_free(client);
190     return NULL;
191   }
192
193   return client;
194 }
195
196 /* Free client entry. This free's everything and removes the entry
197    from ID cache. */
198
199 void silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
200 {
201   if (entry) {
202     /* Remove from cache */
203     if (entry->id)
204       silc_idcache_del_by_id(id_list->clients, SILC_ID_CLIENT, 
205                              (void *)entry->id);
206
207     /* Free data */
208     if (entry->nickname)
209       silc_free(entry->nickname);
210     if (entry->username)
211       silc_free(entry->username);
212     if (entry->userinfo)
213       silc_free(entry->userinfo);
214     if (entry->id)
215       silc_free(entry->id);
216     if (entry->send_key)
217       silc_cipher_free(entry->send_key);
218     if (entry->receive_key)
219       silc_cipher_free(entry->receive_key);
220     if (entry->pkcs)
221       silc_pkcs_free(entry->pkcs);
222     if (entry->public_key)
223       silc_pkcs_public_key_free(entry->public_key);
224     if (entry->hmac)
225       silc_hmac_free(entry->hmac);
226   }
227 }
228
229 /* Returns all clients matching requested nickname. Number of clients is
230    returned to `clients_count'. Caller must free the returned table. */
231
232 SilcClientEntry *
233 silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
234                                     char *server, unsigned int *clients_count)
235 {
236   SilcIDCacheList list = NULL;
237   SilcIDCacheEntry id_cache = NULL;
238   SilcClientEntry *clients;
239   int i;
240
241   if (!silc_idcache_find_by_data(id_list->clients, nickname, &list))
242     return NULL;
243
244   clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
245
246   i = 0;
247   silc_idcache_list_first(list, &id_cache);
248   clients[i++] = (SilcClientEntry)id_cache->context;
249
250   while (silc_idcache_list_next(list, &id_cache))
251     clients[i++] = (SilcClientEntry)id_cache->context;
252   
253   silc_idcache_list_free(list);
254   
255   if (clients_count)
256     *clients_count = i;
257
258   return clients;
259 }
260
261 /* Finds client entry by nickname. */
262
263 SilcClientEntry
264 silc_idlist_find_client_by_nickname(SilcIDList id_list, char *nickname,
265                                     char *server)
266 {
267   SilcIDCacheList list = NULL;
268   SilcIDCacheEntry id_cache = NULL;
269   SilcClientEntry client = NULL;
270
271   SILC_LOG_DEBUG(("Finding client by nickname"));
272
273   if (server) {
274     if (!silc_idcache_find_by_data(id_list->clients, nickname, &list))
275       return NULL;
276
277 #if 0
278     while (silc_idcache_list_next(list, &id_cache)) {
279       client = (SilcClientEntry)id_cache->context;
280
281       if (!strcmp(server, XXX, strlen(server)))
282         break;
283
284       client = NULL;
285     }
286 #endif
287
288    silc_idcache_list_free(list);
289
290    if (!client)
291      return NULL;
292   } else {
293     if (!silc_idcache_find_by_data_one(id_list->clients, nickname, &id_cache))
294       return NULL;
295
296     client = (SilcClientEntry)id_cache->context;
297   }
298
299   return client;
300 }
301
302 /* Finds client by nickname hash. */
303
304 SilcClientEntry
305 silc_idlist_find_client_by_hash(SilcIDList id_list, char *nickname,
306                                 SilcHash md5hash)
307 {
308   SilcIDCacheList list = NULL;
309   SilcIDCacheEntry id_cache = NULL;
310   SilcClientEntry client = NULL;
311   unsigned char hash[32];
312
313   SILC_LOG_DEBUG(("Finding client by hash"));
314
315   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
316
317   if (!silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY, 
318                                SILC_ID_CLIENT, &list))
319     return NULL;
320
321   if (!silc_idcache_list_first(list, &id_cache)) {
322     silc_idcache_list_free(list);
323     return NULL;
324   }
325
326   while (id_cache) {
327     client = (SilcClientEntry)id_cache->context;
328     
329     if (client && !SILC_ID_COMPARE_HASH(client->id, hash))
330       break;
331
332     id_cache = NULL;
333     client = NULL;
334
335     if (!silc_idcache_list_next(list, &id_cache))
336       break;
337   }
338   
339   silc_idcache_list_free(list);
340
341   return client;
342 }
343
344 /* Finds client by Client ID */
345
346 SilcClientEntry
347 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id)
348 {
349   SilcIDCacheEntry id_cache = NULL;
350   SilcClientEntry client;
351
352   if (!id)
353     return NULL;
354
355   SILC_LOG_DEBUG(("Finding client by ID"));
356
357   if (!silc_idcache_find_by_id_one(id_list->clients, (void *)id, 
358                                    SILC_ID_CLIENT, &id_cache))
359     return NULL;
360
361   client = (SilcClientEntry)id_cache->context;
362
363   return client;
364 }
365
366 /* Replaces old Client ID with new one */
367
368 SilcClientEntry
369 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
370                               SilcClientID *new_id)
371 {
372   SilcIDCacheEntry id_cache = NULL;
373   SilcClientEntry client;
374
375   if (!old_id || !new_id)
376     return NULL;
377
378   SILC_LOG_DEBUG(("Replacing Client ID"));
379
380   if (!silc_idcache_find_by_id_one(id_list->clients, (void *)old_id, 
381                                    SILC_ID_CLIENT, &id_cache))
382     return NULL;
383
384   client = (SilcClientEntry)id_cache->context;
385   silc_free(client->id);
386   client->id = new_id;
387   id_cache->id = (void *)new_id;
388
389   return client;
390 }
391
392
393 /******************************************************************************
394
395                           Channel entry functions
396
397 ******************************************************************************/
398
399 /* Add new channel entry. This add the new channel entry to the ID cache
400    system and returns the allocated entry or NULL on error. */
401
402 SilcChannelEntry
403 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
404                         SilcChannelID *id, SilcServerEntry router,
405                         SilcCipher channel_key)
406 {
407   SilcChannelEntry channel;
408
409   channel = silc_calloc(1, sizeof(*channel));
410   channel->channel_name = channel_name;
411   channel->mode = mode;
412   channel->id = id;
413   channel->router = router;
414   channel->channel_key = channel_key;
415
416   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
417                         SILC_ID_CHANNEL, (void *)channel->id, 
418                         (void *)channel, TRUE)) {
419     silc_free(channel);
420     return NULL;
421   }
422
423   return channel;
424 }
425
426 /* Free channel entry.  This free's everything. */
427
428 void silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
429 {
430   if (entry) {
431     /* Remove from cache */
432     if (entry->id)
433       silc_idcache_del_by_id(id_list->channels, SILC_ID_CHANNEL, 
434                              (void *)entry->id);
435
436     /* Free data */
437     if (entry->channel_name)
438       silc_free(entry->channel_name);
439     if (entry->id)
440       silc_free(entry->id);
441     if (entry->topic)
442       silc_free(entry->topic);
443     if (entry->channel_key)
444       silc_cipher_free(entry->channel_key);
445     if (entry->key) {
446       memset(entry->key, 0, entry->key_len / 8);
447       silc_free(entry->key);
448     }
449     memset(entry->iv, 0, sizeof(entry->iv));
450
451     if (entry->user_list_count)
452       silc_free(entry->user_list);
453   }
454 }
455
456 /* Finds channel by channel name. Channel names are unique and they
457    are not case-sensitive. */
458
459 SilcChannelEntry
460 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name)
461 {
462   SilcIDCacheList list = NULL;
463   SilcIDCacheEntry id_cache = NULL;
464   SilcChannelEntry channel;
465
466   SILC_LOG_DEBUG(("Finding channel by name"));
467
468   if (!silc_idcache_find_by_data_loose(id_list->channels, name, &list))
469     return NULL;
470   
471   if (!silc_idcache_list_first(list, &id_cache)) {
472     silc_idcache_list_free(list);
473     return NULL;
474   }
475
476   channel = (SilcChannelEntry)id_cache->context;
477
478   silc_idcache_list_free(list);
479
480   return channel;
481 }
482
483 /* Finds channel by Channel ID. */
484
485 SilcChannelEntry
486 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id)
487 {
488   SilcIDCacheEntry id_cache = NULL;
489   SilcChannelEntry channel;
490
491   if (!id)
492     return NULL;
493
494   SILC_LOG_DEBUG(("Finding channel by ID"));
495
496   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, 
497                                    SILC_ID_CHANNEL, &id_cache))
498     return NULL;
499
500   channel = (SilcChannelEntry)id_cache->context;
501
502   return channel;
503 }