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