Added SILC Server library.
[silc.git] / lib / silcserver / server_entry.c
1 /*
2
3   server_entry.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2005 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 "silc.h"
23 #include "silcserver.h"
24 #include "server_internal.h"
25
26 /************************ Static utility functions **************************/
27
28 /* Foreach callbcak to free all users from the channel when deleting a
29    channel entry. */
30
31 static void silc_server_del_channel_foreach(void *key, void *context,
32                                             void *user_context)
33 {
34   SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
35
36   SILC_LOG_DEBUG(("Removing client %s from channel %s",
37                   chl->client->nickname ? chl->client->nickname :
38                   (unsigned char *)"", chl->channel->channel_name));
39
40   /* Remove the context from the client's channel hash table as that
41      table and channel's user_list hash table share this same context. */
42   silc_hash_table_del(chl->client->channels, chl->channel);
43   silc_free(chl);
44 }
45
46
47 /****************************** Server entry ********************************/
48
49 void silc_server_destructor_server(SilcIDCache cache,
50                                    const SilcIDCacheEntry entry,
51                                    void *destructor_context,
52                                    void *app_context)
53 {
54
55 }
56
57 /* Adds new server entry to server */
58
59 SilcServerEntry silc_server_add_server(SilcServer server,
60                                        const char *server_name,
61                                        SilcServerType server_type,
62                                        SilcServerID *id,
63                                        SilcPacketStream origin)
64 {
65   SilcServerEntry entry;
66   char *server_namec = NULL;
67
68   if (!id || !origin)
69     return NULL;
70
71   entry = silc_calloc(1, sizeof(*entry));
72   if (!entry)
73     return NULL;
74
75   SILC_LOG_DEBUG(("Adding server entry %p %s", entry,
76                   silc_id_render(id, SILC_ID_SERVER)));
77
78   /* Normalize name.  This is cached, original is in server context.  */
79   if (server_name) {
80     server_namec = silc_identifier_check(server_name, strlen(server_name),
81                                          SILC_STRING_UTF8, 256, NULL);
82     if (!server_namec) {
83       silc_free(entry);
84       return NULL;
85     }
86
87     entry->server_name = strdup(server_name);
88     if (!server->server_name) {
89       silc_free(server_namec);
90       silc_free(entry);
91       return NULL;
92     }
93   }
94
95   entry->server_type = server_type;
96   entry->id = *id;
97   entry->stream = origin;
98
99   /* Add to cache */
100   if (!silc_idcache_add(server->servers, server_namec, &entry->id,
101                         entry)) {
102     silc_free(server_namec);
103     silc_free(entry->server_name);
104     silc_free(entry);
105     return NULL;
106   }
107
108   /* Take reference of the packet stream */
109   silc_packet_stream_ref(origin);
110
111   return entry;
112 }
113
114 /* Delete server entry */
115
116 SilcBool silc_server_del_server(SilcServer server, SilcServerEntry entry)
117 {
118   SILC_LOG_DEBUG(("Deleting server %s id %s", entry->server_name ?
119                   entry->server_name : "??", &entry->id ?
120                   silc_id_render(&entry->id, SILC_ID_SERVER) : "??"));
121
122   /* Delete */
123   if (!silc_idcache_del_by_context(server->servers, entry, NULL)) {
124     SILC_LOG_ERROR(("Unknown server %s, could not delete from cache",
125                     &entry->id ? silc_id_render(&entry->id, SILC_ID_SERVER) :
126                     "??"));
127     return FALSE;
128   }
129
130   return TRUE;
131 }
132
133 /* Find server by Server ID */
134
135 SilcServerEntry
136 silc_server_find_server_by_id(SilcServer server,
137                               SilcServerID *id,
138                               SilcBool registered,
139                               SilcIDCacheEntry *ret_entry)
140 {
141   SilcIDCacheEntry id_cache = NULL;
142   SilcServerEntry entry;
143
144   if (!id)
145     return NULL;
146
147   SILC_LOG_DEBUG(("Find Server ID (%s)",
148                   silc_id_render(id, SILC_ID_SERVER)));
149
150   if (!silc_idcache_find_by_id_one(server->servers, (void *)id,
151                                    &id_cache))
152     return NULL;
153
154   entry = id_cache->context;
155
156   if (entry && registered && !(entry->data.registered))
157     return NULL;
158
159   if (ret_entry)
160     *ret_entry = id_cache;
161
162   SILC_LOG_DEBUG(("Found"));
163
164   return entry;
165 }
166
167 /* Find server by name.  The 'name' must be normalized already. */
168
169 SilcServerEntry
170 silc_server_find_server_by_name(SilcServer server, char *name,
171                                 SilcBool registered,
172                                 SilcIDCacheEntry *ret_entry)
173 {
174   SilcIDCacheEntry id_cache = NULL;
175   SilcServerEntry entry;
176
177   SILC_LOG_DEBUG(("Find server by name `%s'", name));
178
179   if (!silc_idcache_find_by_name_one(server->servers, name, &id_cache))
180     return NULL;
181
182   entry = id_cache->context;
183
184   if (entry && registered && !(entry->data.registered))
185     return NULL;
186
187   if (ret_entry)
188     *ret_entry = id_cache;
189
190   SILC_LOG_DEBUG(("Found"));
191
192   return entry;
193 }
194
195 /* Find server by connection parameters, hostname and port */
196
197 SilcServerEntry
198 silc_server_find_server_by_conn(SilcServer server, char *hostname,
199                                 int port, SilcBool registered,
200                                 SilcIDCacheEntry *ret_entry)
201 {
202   SilcIDCacheEntry id_cache;
203   SilcServerEntry entry;
204   SilcStream stream;
205   SilcList list;
206   const char *h;
207   SilcUInt16 p;
208
209   SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
210
211   if (!silc_idcache_get_all(server->servers, &list))
212     return NULL;
213
214   while ((id_cache = silc_list_get(list)) != SILC_LIST_END) {
215     entry = id_cache->context;
216     stream = silc_packet_stream_get_stream(entry->stream);
217
218     if (entry && registered && !(entry->data.registered))
219       continue;
220
221     if (silc_socket_stream_get_info(stream, NULL, &h, NULL, &p)) {
222       if (silc_utf8_strcasecmp(hostname, h) && p == port)
223         break;
224     }
225   }
226   if (!id_cache)
227     entry = NULL;
228
229   if (ret_entry)
230     *ret_entry = id_cache;
231
232   SILC_LOG_DEBUG(("Found"));
233
234   return entry;
235 }
236
237 /* Replaces old Server ID with new one */
238
239 SilcServerEntry
240 silc_server_replace_server_id(SilcServer server, SilcServerID *old_id,
241                               SilcServerID *new_id)
242 {
243   SilcIDCacheEntry id_cache = NULL;
244   SilcServerEntry entry;
245
246   if (!old_id || !new_id)
247     return NULL;
248
249   SILC_LOG_DEBUG(("Replacing Server ID %s",
250                   silc_id_render(old_id, SILC_ID_SERVER)));
251   SILC_LOG_DEBUG(("New Server ID %s",
252                   silc_id_render(new_id, SILC_ID_SERVER)));
253
254   if (!silc_idcache_find_by_id_one(server->servers, (void *)old_id,
255                                    &id_cache))
256     return NULL;
257
258   entry = id_cache->context;
259   entry->id = *new_id;
260
261   if (!silc_idcache_update(server->servers, id_cache, old_id, &entry->id,
262                            NULL, NULL)) {
263     SILC_LOG_ERROR(("Error updating Server ID"));
264     return NULL;
265   }
266
267   SILC_LOG_DEBUG(("Replaced"));
268
269   return entry;
270 }
271
272
273 /****************************** Client entry ********************************/
274
275 void silc_server_destructor_client(SilcIDCache cache,
276                                    const SilcIDCacheEntry entry,
277                                    void *destructor_context,
278                                    void *app_context)
279 {
280
281 }
282
283 /* Adds new client to server */
284
285 SilcClientEntry silc_server_add_client(SilcServer server,
286                                        const char *nickname,
287                                        const char *username,
288                                        const char *userinfo,
289                                        SilcClientID *id,
290                                        SilcUInt32 mode,
291                                        SilcPacketStream origin)
292 {
293   SilcClientEntry client;
294   char *nicknamec = NULL;
295   char u[128], h[256];
296   int ret;
297
298   if (!id || !origin || !nickname || !username)
299     return NULL;
300
301   client = silc_calloc(1, sizeof(*client));
302   if (!client)
303     return NULL;
304
305   SILC_LOG_DEBUG(("Adding client entry %p", client));
306
307   /* Normalize name.  This is cached, original is in client context.  */
308   nicknamec = silc_identifier_check(nickname, strlen(nickname),
309                                     SILC_STRING_UTF8, 128, NULL);
310   if (!nicknamec)
311     goto err;
312
313   /* Check username */
314   ret = silc_parse_userfqdn(username, u, sizeof(u), h, sizeof(h));
315   if (!ret)
316     goto err;
317   if (!silc_identifier_verify(u, strlen(u), SILC_STRING_UTF8, 128))
318     goto err;
319   if (ret == 2 &&
320       !silc_identifier_verify(h, strlen(h), SILC_STRING_UTF8, 256))
321     goto err;
322
323   client->nickname = strdup(nickname);
324   if (!client->nickname)
325     goto err;
326
327   client->username = strdup(username);
328   if (!client->username)
329     goto err;
330
331   client->userinfo = userinfo ? strdup(userinfo) : NULL;
332   if (!client->userinfo)
333     goto err;
334
335   client->id = *id;
336   client->mode = mode;
337   client->stream = origin;
338
339   client->channels = silc_hash_table_alloc(0, silc_hash_ptr, NULL,
340                                            NULL, NULL, NULL, NULL, TRUE);
341   if (!client->channels)
342     goto err;
343
344   if (!silc_idcache_add(server->clients, nicknamec, (void *)&client->id,
345                         (void *)client))
346     goto err;
347
348   /* Take reference of the packet stream */
349   silc_packet_stream_ref(origin);
350
351   return client;
352
353  err:
354   if (client->channels)
355     silc_hash_table_free(client->channels);
356   silc_free(client->nickname);
357   silc_free(client->username);
358   silc_free(client->userinfo);
359   silc_free(client);
360   silc_free(nicknamec);
361   return NULL;
362 }
363
364 /* Delete client entry */
365
366 SilcBool silc_server_del_client(SilcServer server, SilcClientEntry entry)
367 {
368   SILC_LOG_DEBUG(("Deleting client %s id %s", entry->nickname ?
369                   entry->nickname : (unsigned char *)"??", &entry->id ?
370                   silc_id_render(&entry->id, SILC_ID_CLIENT) : "??"));
371
372   /* Delete */
373   if (!silc_idcache_del_by_context(server->clients, entry, NULL)) {
374     SILC_LOG_ERROR(("Unknown client %s, could not delete from cache",
375                     &entry->id ? silc_id_render(&entry->id, SILC_ID_CLIENT) :
376                     "??"));
377     return FALSE;
378   }
379
380   return TRUE;
381 }
382
383 /* Finds all clients matching the nickanem `nickname'.  Returns list of
384    SilcIDCacheEntry entries.  The `nickname' must be normalized. */
385
386 SilcBool silc_server_find_clients(SilcServer server, char *nickname,
387                                   SilcList *list)
388 {
389   SilcClientID client_id;
390   unsigned char hash[16];
391
392   SILC_LOG_DEBUG(("Find clients named %s", nickname));
393
394   /* Find using Client ID hash, as Client ID is based on the nickname,
395      we can find clients quickly using the hash of the nickname. */
396   memset(&client_id, 0, sizeof(client_id));
397   silc_hash_make(server->md5hash, nickname, strlen(nickname), hash);
398   memcpy(client_id.hash, hash, CLIENTID_HASH_LEN);
399
400   if (!silc_idcache_find_by_id(server->clients, &client_id, list))
401     return FALSE;
402
403   SILC_LOG_DEBUG(("Found %d clients", silc_list_count(*list)));
404
405   return TRUE;
406 }
407
408 /* Finds client by Client ID */
409
410 SilcClientEntry silc_server_find_client_by_id(SilcServer server,
411                                               SilcClientID *id,
412                                               SilcBool registered,
413                                               SilcIDCacheEntry *ret_entry)
414 {
415   SilcIDCacheEntry id_cache = NULL;
416   SilcClientEntry client;
417
418   if (!id)
419     return NULL;
420
421   SILC_LOG_DEBUG(("Client ID (%s)", silc_id_render(id, SILC_ID_CLIENT)));
422
423   if (!silc_idcache_find_by_id_one(server->clients, (void *)id, &id_cache))
424     return NULL;
425
426   client = id_cache->context;
427
428   if (client && registered && !(client->data.registered))
429     return NULL;
430
431   if (ret_entry)
432     *ret_entry = id_cache;
433
434   SILC_LOG_DEBUG(("Found"));
435
436   return client;
437 }
438
439 /* Replaces old Client ID with new one */
440
441 SilcClientEntry
442 silc_server_replace_client_id(SilcServer server, SilcClientID *old_id,
443                               SilcClientID *new_id, const char *nickname)
444 {
445   SilcIDCacheEntry id_cache = NULL;
446   SilcClientEntry entry;
447   char *name, *nicknamec = NULL;
448
449   if (!old_id || !new_id)
450     return NULL;
451
452   SILC_LOG_DEBUG(("Replacing Client ID %s",
453                   silc_id_render(old_id, SILC_ID_SERVER)));
454   SILC_LOG_DEBUG(("New Client ID %s",
455                   silc_id_render(new_id, SILC_ID_SERVER)));
456
457   /* Normalize name. This is cached, original is in client context.  */
458   if (nickname) {
459     nicknamec = silc_identifier_check(nickname, strlen(nickname),
460                                       SILC_STRING_UTF8, 128, NULL);
461     if (!nicknamec)
462       return NULL;
463   }
464
465   if (!silc_idcache_find_by_id_one(server->clients, (void *)old_id,
466                                    &id_cache))
467     return NULL;
468
469   entry = id_cache->context;
470   entry->id = *new_id;
471
472   name = id_cache->name;
473   if (!silc_idcache_update(server->clients, id_cache, old_id, &entry->id,
474                            name, nicknamec)) {
475     SILC_LOG_ERROR(("Error updating Client ID"));
476     return NULL;
477   }
478   if (nicknamec)
479     silc_free(name);
480
481   /* Check if anyone is watching old nickname */
482   if (server->server_type == SILC_ROUTER)
483     silc_server_check_watcher_list(server, entry, nickname,
484                                    SILC_NOTIFY_TYPE_NICK_CHANGE);
485
486   silc_free(entry->nickname);
487   entry->nickname = nickname ? strdup(nickname) : NULL;
488
489   /* Check if anyone is watching new nickname */
490   if (server->server_type == SILC_ROUTER)
491     silc_server_check_watcher_list(server, entry, nickname,
492                                    SILC_NOTIFY_TYPE_NICK_CHANGE);
493
494   SILC_LOG_DEBUG(("Replaced"));
495
496   return entry;
497 }
498
499
500 /****************************** Channel entry *******************************/
501
502 void silc_server_destructor_channel(SilcIDCache cache,
503                                     const SilcIDCacheEntry entry,
504                                     void *destructor_context,
505                                     void *app_context)
506 {
507
508 }
509
510 /* Add new channel */
511
512 SilcChannelEntry silc_server_add_channel(SilcServer server,
513                                          const char *channel_name,
514                                          SilcUInt32 mode,
515                                          SilcChannelID *id,
516                                          SilcPacketStream origin,
517                                          SilcCipher channel_key,
518                                          SilcHmac hmac)
519 {
520   SilcChannelEntry channel;
521   char *channel_namec = NULL;
522
523   if (!id || !channel_key || !hmac)
524     return NULL;
525
526   channel = silc_calloc(1, sizeof(*channel));
527   if (!channel)
528     return NULL;
529
530   SILC_LOG_DEBUG(("Adding new channel %s %p", channel_name, channel));
531
532   /* Normalize name.  This is cached, original is in client context.  */
533   if (channel_name) {
534     channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
535                                             SILC_STRING_UTF8, 256, NULL);
536     if (!channel_namec) {
537       silc_free(channel);
538       return NULL;
539     }
540   }
541
542   channel->channel_name = channel_name ? strdup(channel_name) : NULL;
543   if (!channel) {
544       silc_free(channel);
545       silc_free(channel_namec);
546       return NULL;
547   }
548
549   channel->mode = mode;
550   channel->id = *id;
551   channel->channel_key = channel_key;
552   channel->hmac = hmac;
553   channel->router = origin;
554
555   channel->user_list = silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL,
556                                              NULL, NULL, NULL, TRUE);
557   if (!channel->user_list) {
558     silc_cipher_free(channel->channel_key);
559     silc_hmac_free(channel->hmac);
560     silc_free(channel->channel_name);
561     silc_free(channel);
562     silc_free(channel_namec);
563     return NULL;
564   }
565
566   if (!silc_idcache_add(server->channels, channel_namec,
567                         (void *)&channel->id, (void *)channel)) {
568     silc_cipher_free(channel->channel_key);
569     silc_hmac_free(channel->hmac);
570     silc_free(channel->channel_name);
571     silc_hash_table_free(channel->user_list);
572     silc_free(channel);
573     silc_free(channel_namec);
574     return NULL;
575   }
576
577   /* Take reference of the packet stream */
578   silc_packet_stream_ref(origin);
579
580   return channel;
581 }
582
583 /* Free channel entry.  This free's everything. */
584
585 SilcBool silc_server_del_channel(SilcServer server, SilcChannelEntry entry)
586 {
587   SILC_LOG_DEBUG(("Deleting channel %s", entry->channel_name));
588
589   /* Remove from cache */
590   if (!silc_idcache_del_by_context(server->channels, entry, NULL)) {
591     SILC_LOG_DEBUG(("Unknown channel %s, did not delete",
592                     entry->channel_name));
593     return FALSE;
594   }
595
596   return TRUE;
597 }
598
599 /* Finds channel by channel name.  Channel names are unique and they
600    are not case-sensitive.  The 'name' must be normalized already. */
601
602 SilcChannelEntry silc_server_find_channel_by_name(SilcServer server,
603                                                   const char *name,
604                                                   SilcIDCacheEntry *ret_entry)
605 {
606   SilcIDCacheEntry id_cache = NULL;
607
608   SILC_LOG_DEBUG(("Channel by name %s", name));
609
610   if (!silc_idcache_find_by_name_one(server->channels, (char *)name,
611                                      &id_cache))
612     return NULL;
613
614   if (ret_entry)
615     *ret_entry = id_cache;
616
617   SILC_LOG_DEBUG(("Found"));
618
619   return id_cache->context;
620 }
621
622 /* Finds channel by Channel ID. */
623
624 SilcChannelEntry silc_server_find_channel_by_id(SilcServer server,
625                                                 SilcChannelID *id,
626                                                 SilcIDCacheEntry *ret_entry)
627 {
628   SilcIDCacheEntry id_cache = NULL;
629
630   if (!id)
631     return NULL;
632
633   SILC_LOG_DEBUG(("Channel ID (%s)", silc_id_render(id, SILC_ID_CHANNEL)));
634
635   if (!silc_idcache_find_by_id_one(server->channels, (void *)id, &id_cache))
636     return NULL;
637
638   if (ret_entry)
639     *ret_entry = id_cache;
640
641   SILC_LOG_DEBUG(("Found"));
642
643   return id_cache->context;
644 }
645
646 /* Replaces old Channel ID with new one. This is done when router forces
647    normal server to change Channel ID. */
648
649 SilcChannelEntry silc_server_replace_channel_id(SilcServer server,
650                                                 SilcChannelID *old_id,
651                                                 SilcChannelID *new_id)
652 {
653   SilcIDCacheEntry id_cache = NULL;
654   SilcChannelEntry entry;
655
656   if (!old_id || !new_id)
657     return NULL;
658
659   SILC_LOG_DEBUG(("Replacing Channel ID %s",
660                   silc_id_render(old_id, SILC_ID_CHANNEL)));
661   SILC_LOG_DEBUG(("New Channel ID %s",
662                   silc_id_render(new_id, SILC_ID_CHANNEL)));
663
664   if (!silc_idcache_find_by_id_one(server->channels, (void *)old_id,
665                                    &id_cache))
666     return NULL;
667
668   entry = id_cache->context;
669   entry->id = *new_id;
670
671   if (!silc_idcache_update(server->channels, id_cache, old_id, &entry->id,
672                            NULL, NULL)) {
673     SILC_LOG_ERROR(("Error updating Channel ID"));
674     return NULL;
675   }
676
677   SILC_LOG_DEBUG(("Replaced"));
678
679   return entry;
680 }
681
682 /* Returns channels from the ID list.  If the `channel_id' is NULL then
683    all channels are returned.  Returns list of SilcIDCacheEntry entries. */
684
685 SilcBool silc_server_get_channels(SilcServer server,
686                                   SilcChannelID *channel_id,
687                                   SilcList *list)
688 {
689   SILC_LOG_DEBUG(("Start"));
690
691   if (!channel_id) {
692     if (!silc_idcache_get_all(server->channels, list))
693       return FALSE;
694   } else {
695     if (!silc_idcache_find_by_id(server->channels, channel_id, list))
696       return FALSE;
697   }
698
699   return TRUE;
700 }