updates.
[silc.git] / apps / silcd / server_util.c
1 /*
2
3   server_util.c 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
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; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "serverincludes.h"
22 #include "server_internal.h"
23
24 /* Removes the client from channels and possibly removes the channels
25    as well.  After removing those channels that exist, their channel
26    keys are regnerated. This is called only by the function
27    silc_server_remove_clients_by_server. */
28
29 static void silc_server_remove_clients_channels(SilcServer server, 
30                                                 SilcSocketConnection sock,
31                                                 SilcClientEntry client,
32                                                 SilcHashTable channels)
33 {
34   SilcChannelEntry channel;
35   SilcChannelClientEntry chl;
36   SilcHashTableList htl;
37   SilcBuffer clidp;
38
39   SILC_LOG_DEBUG(("Start"));
40
41   if (!client || !client->id)
42     return;
43
44   clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
45
46   /* Remove the client from all channels. The client is removed from
47      the channels' user list. */
48   silc_hash_table_list(client->channels, &htl);
49   while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
50     channel = chl->channel;
51
52     /* Remove channel from client's channel list */
53     silc_hash_table_del(client->channels, channel);
54
55     /* Remove channel if there is no users anymore */
56     if (server->server_type == SILC_ROUTER &&
57         silc_hash_table_count(channel->user_list) < 2) {
58
59       if (silc_hash_table_find(channels, channel, NULL, NULL))
60         silc_hash_table_del(channels, channel);
61
62       if (channel->rekey)
63         silc_schedule_task_del_by_context(server->schedule, channel->rekey);
64
65       if (!silc_idlist_del_channel(server->local_list, channel))
66         silc_idlist_del_channel(server->global_list, channel);
67       server->stat.my_channels--;
68       continue;
69     }
70
71     /* Remove client from channel's client list */
72     silc_hash_table_del(channel->user_list, chl->client);
73
74     /* If there is no global users on the channel anymore mark the channel
75        as local channel. Do not check if the removed client is local client. */
76     if (server->server_type != SILC_ROUTER && channel->global_users && 
77         chl->client->router && !silc_server_channel_has_global(channel))
78       channel->global_users = FALSE;
79
80     silc_free(chl);
81     server->stat.my_chanclients--;
82
83     /* If there is not at least one local user on the channel then we don't
84        need the channel entry anymore, we can remove it safely. */
85     if (server->server_type != SILC_ROUTER &&
86         !silc_server_channel_has_local(channel)) {
87
88       if (silc_hash_table_find(channels, channel, NULL, NULL))
89         silc_hash_table_del(channels, channel);
90
91       if (channel->rekey)
92         silc_schedule_task_del_by_context(server->schedule, channel->rekey);
93
94       if (channel->founder_key) {
95         /* The founder auth data exists, do not remove the channel entry */
96         SilcChannelClientEntry chl2;
97         SilcHashTableList htl2;
98
99         channel->id = NULL;
100
101         silc_hash_table_list(channel->user_list, &htl2);
102         while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
103           silc_hash_table_del(chl2->client->channels, channel);
104           silc_hash_table_del(channel->user_list, chl2->client);
105           silc_free(chl2);
106         }
107         continue;
108       }
109
110       /* Remove the channel entry */
111       if (!silc_idlist_del_channel(server->local_list, channel))
112         silc_idlist_del_channel(server->global_list, channel);
113       server->stat.my_channels--;
114       continue;
115     }
116
117     /* Add the channel to the the channels list to regenerate the 
118        channel key */
119     if (!silc_hash_table_find(channels, channel, NULL, NULL))
120       silc_hash_table_add(channels, channel, channel);
121   }
122
123   silc_buffer_free(clidp);
124 }
125
126 /* This function is used to remove all client entries by the server `entry'.
127    This is called when the connection is lost to the server. In this case
128    we must invalidate all the client entries owned by the server `entry'. 
129    If the `server_signoff' is TRUE then the SERVER_SIGNOFF notify is
130    distributed to our local clients. */
131
132 bool silc_server_remove_clients_by_server(SilcServer server, 
133                                           SilcServerEntry entry,
134                                           bool server_signoff)
135 {
136   SilcIDCacheList list = NULL;
137   SilcIDCacheEntry id_cache = NULL;
138   SilcClientEntry client = NULL;
139   SilcBuffer idp;
140   SilcClientEntry *clients = NULL;
141   uint32 clients_c = 0;
142   unsigned char **argv = NULL;
143   uint32 *argv_lens = NULL, *argv_types = NULL, argc = 0;
144   SilcHashTableList htl;
145   SilcChannelEntry channel;
146   SilcHashTable channels;
147   int i;
148
149   SILC_LOG_DEBUG(("Start"));
150
151   /* Allocate the hash table that holds the channels that require
152      channel key re-generation after we've removed this server's clients
153      from the channels. */
154   channels = silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL, NULL,
155                                    NULL, NULL, TRUE);
156
157   if (server_signoff) {
158     idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
159     argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
160     argv_lens = silc_realloc(argv_lens,  sizeof(*argv_lens) * (argc + 1));
161     argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
162     argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
163     memcpy(argv[argc], idp->data, idp->len);
164     argv_lens[argc] = idp->len;
165     argv_types[argc] = argc + 1;
166     argc++;
167     silc_buffer_free(idp);
168   }
169
170   if (silc_idcache_get_all(server->local_list->clients, &list)) {
171
172     if (silc_idcache_list_first(list, &id_cache)) {
173       while (id_cache) {
174         client = (SilcClientEntry)id_cache->context;
175         if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
176           if (!silc_idcache_list_next(list, &id_cache))
177             break;
178           else
179             continue;
180         }
181
182         if (client->router != entry) {
183           if (server_signoff && client->connection) {
184             clients = silc_realloc(clients, 
185                                    sizeof(*clients) * (clients_c + 1));
186             clients[clients_c] = client;
187             clients_c++;
188           }
189
190           if (!silc_idcache_list_next(list, &id_cache))
191             break;
192           else
193             continue;
194         }
195
196         if (server_signoff) {
197           idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
198           argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
199           argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
200                                    (argc + 1));
201           argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
202                                     (argc + 1));
203           argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
204           memcpy(argv[argc], idp->data, idp->len);
205           argv_lens[argc] = idp->len;
206           argv_types[argc] = argc + 1;
207           argc++;
208           silc_buffer_free(idp);
209         }
210
211         /* Remove the client entry */
212         silc_server_remove_clients_channels(server, NULL, client, channels);
213         silc_idlist_del_client(server->local_list, client);
214
215         if (!silc_idcache_list_next(list, &id_cache))
216           break;
217       }
218     }
219     silc_idcache_list_free(list);
220   }
221   
222   if (silc_idcache_get_all(server->global_list->clients, &list)) {
223
224     if (silc_idcache_list_first(list, &id_cache)) {
225       while (id_cache) {
226         client = (SilcClientEntry)id_cache->context;
227         if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
228           if (!silc_idcache_list_next(list, &id_cache))
229             break;
230           else
231             continue;
232         }
233         
234         if (client->router != entry) {
235           if (server_signoff && client->connection) {
236             clients = silc_realloc(clients, 
237                                    sizeof(*clients) * (clients_c + 1));
238             clients[clients_c] = client;
239             clients_c++;
240           }
241
242           if (!silc_idcache_list_next(list, &id_cache))
243             break;
244           else
245             continue;
246         }
247
248         if (server_signoff) {
249           idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
250           argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
251           argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
252                                    (argc + 1));
253           argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
254                                     (argc + 1));
255           argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
256           memcpy(argv[argc], idp->data, idp->len);
257           argv_lens[argc] = idp->len;
258           argv_types[argc] = argc + 1;
259           argc++;
260           silc_buffer_free(idp);
261         }
262
263         /* Remove the client entry */
264         silc_server_remove_clients_channels(server, NULL, client, channels);
265         silc_idlist_del_client(server->global_list, client);
266
267         if (!silc_idcache_list_next(list, &id_cache))
268           break;
269       }
270     }
271     silc_idcache_list_free(list);
272   }
273
274   /* Send the SERVER_SIGNOFF notify */
275   if (server_signoff) {
276     SilcBuffer args;
277
278     /* Send SERVER_SIGNOFF notify to our primary router */
279     if (!server->standalone && server->router &&
280         server->router != entry) {
281       args = silc_argument_payload_encode(1, argv, argv_lens,
282                                           argv_types);
283       silc_server_send_notify_args(server, 
284                                    server->router->connection,
285                                    server->server_type == SILC_SERVER ? 
286                                    FALSE : TRUE, 
287                                    SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
288                                    argc, args);
289       silc_buffer_free(args);
290     }
291
292     args = silc_argument_payload_encode(argc, argv, argv_lens,
293                                         argv_types);
294     /* Send to local clients */
295     for (i = 0; i < clients_c; i++) {
296       silc_server_send_notify_args(server, clients[i]->connection,
297                                    FALSE, SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
298                                    argc, args);
299     }
300
301     silc_free(clients);
302     silc_buffer_free(args);
303     for (i = 0; i < argc; i++)
304       silc_free(argv[i]);
305     silc_free(argv);
306     silc_free(argv_lens);
307     silc_free(argv_types);
308   }
309
310   /* We must now re-generate the channel key for all channels that had
311      this server's client(s) on the channel. As they left the channel we
312      must re-generate the channel key. */
313   silc_hash_table_list(channels, &htl);
314   while (silc_hash_table_get(&htl, NULL, (void *)&channel)) {
315     if (!silc_server_create_channel_key(server, channel, 0))
316       return FALSE;
317
318     /* Do not send the channel key if private channel key mode is set */
319     if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY)
320       continue;
321
322     silc_server_send_channel_key(server, NULL, channel, 
323                                  server->server_type == SILC_ROUTER ? 
324                                  FALSE : !server->standalone);
325   }
326   silc_hash_table_free(channels);
327
328   return TRUE;
329 }
330
331 static SilcServerEntry
332 silc_server_update_clients_by_real_server(SilcServer server,
333                                           SilcServerEntry from,
334                                           SilcClientEntry client,
335                                           bool local,
336                                           SilcIDCacheEntry client_cache)
337 {
338   SilcServerEntry server_entry;
339   SilcIDCacheEntry id_cache = NULL;
340   SilcIDCacheList list;
341
342   if (!silc_idcache_get_all(server->local_list->servers, &list))
343     return NULL;
344
345   if (silc_idcache_list_first(list, &id_cache)) {
346     while (id_cache) {
347       server_entry = (SilcServerEntry)id_cache->context;
348       if (server_entry != from &&
349           SILC_ID_COMPARE(server_entry->id, client->id, 
350                           client->id->ip.data_len)) {
351         SILC_LOG_DEBUG(("Found (local) %s",
352                         silc_id_render(server_entry->id, SILC_ID_SERVER)));
353
354         /* If the client is not marked as local then move it to local list
355            since the server is local. */
356         if (server_entry->server_type != SILC_BACKUP_ROUTER && !local) {
357           SILC_LOG_DEBUG(("Moving client to local list"));
358           silc_idcache_add(server->local_list->clients, client_cache->name,
359                            client_cache->id, client_cache->context,
360                            client_cache->expire);
361           silc_idcache_del_by_context(server->global_list->clients, client);
362         }
363
364         silc_idcache_list_free(list);
365         return server_entry;
366       }
367
368       if (!silc_idcache_list_next(list, &id_cache))
369         break;
370     }
371   }
372
373   silc_idcache_list_free(list);
374
375   if (!silc_idcache_get_all(server->global_list->servers, &list))
376     return NULL;
377
378   if (silc_idcache_list_first(list, &id_cache)) {
379     while (id_cache) {
380       server_entry = (SilcServerEntry)id_cache->context;
381       if (server_entry != from &&
382           SILC_ID_COMPARE(server_entry->id, client->id, 
383                           client->id->ip.data_len)) {
384         SILC_LOG_DEBUG(("Found (global) %s",
385                         silc_id_render(server_entry->id, SILC_ID_SERVER)));
386
387         /* If the client is marked as local then move it to global list
388            since the server is global. */
389         if (server_entry->server_type != SILC_BACKUP_ROUTER && local) {
390           SILC_LOG_DEBUG(("Moving client to global list"));
391           silc_idcache_add(server->global_list->clients, client_cache->name,
392                            client_cache->id, client_cache->context,
393                            client_cache->expire);
394           silc_idcache_del_by_context(server->local_list->clients, client);
395         }
396
397         silc_idcache_list_free(list);
398         return server_entry;
399       }
400
401       if (!silc_idcache_list_next(list, &id_cache))
402         break;
403     }
404   }
405
406   silc_idcache_list_free(list);
407
408   return NULL;
409 }
410
411 /* Updates the clients that are originated from the `from' to be originated
412    from the `to'. If the `resolve_real_server' is TRUE then this will
413    attempt to figure out which clients really are originated from the
414    `from' and which are originated from a server that we have connection
415    to, when we've acting as backup router. If it is FALSE the `to' will
416    be the new source. This function also removes the clients that are
417    *really* originated from `from' if `remove_from' is TRUE. These are
418    clients that the `from' owns, and not just clients that are behind
419    the `from'. */
420
421 void silc_server_update_clients_by_server(SilcServer server, 
422                                           SilcServerEntry from,
423                                           SilcServerEntry to,
424                                           bool resolve_real_server,
425                                           bool remove_from)
426 {
427   SilcIDCacheList list = NULL;
428   SilcIDCacheEntry id_cache = NULL;
429   SilcClientEntry client = NULL;
430   bool local;
431
432   SILC_LOG_DEBUG(("Start"));
433
434   SILC_LOG_DEBUG(("Updating %s", silc_id_render(from->id,
435                                                 SILC_ID_SERVER)));
436   SILC_LOG_DEBUG(("to %s", silc_id_render(to->id,
437                                           SILC_ID_SERVER)));
438
439
440   local = TRUE;
441   if (silc_idcache_get_all(server->local_list->clients, &list)) {
442     if (silc_idcache_list_first(list, &id_cache)) {
443       while (id_cache) {
444         client = (SilcClientEntry)id_cache->context;
445
446         if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
447           if (!silc_idcache_list_next(list, &id_cache))
448             break;
449           else
450             continue;
451         }
452
453         SILC_LOG_DEBUG(("Client (local) %s", 
454                         silc_id_render(client->id, SILC_ID_CLIENT)));
455
456         if (client->router == from) {
457           /* Skip clients that are *really* owned by the `from' */
458           if (remove_from && SILC_ID_COMPARE(from->id, client->id, 
459                                              client->id->ip.data_len)) {
460             SILC_LOG_DEBUG(("Found really owned client, skip it"));
461             if (!silc_idcache_list_next(list, &id_cache))
462               break;
463             else
464               continue;
465           }
466
467           if (resolve_real_server) {
468             client->router = 
469               silc_server_update_clients_by_real_server(server, from, client,
470                                                         local, id_cache);
471             if (!client->router)
472               client->router = to;
473           } else {
474             client->router = to;
475           }
476         }
477
478         if (!silc_idcache_list_next(list, &id_cache))
479           break;
480       }
481     }
482     silc_idcache_list_free(list);
483   }
484
485   local = FALSE;
486   if (silc_idcache_get_all(server->global_list->clients, &list)) {
487     if (silc_idcache_list_first(list, &id_cache)) {
488       while (id_cache) {
489         client = (SilcClientEntry)id_cache->context;
490         
491
492         if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
493           if (!silc_idcache_list_next(list, &id_cache))
494             break;
495           else
496             continue;
497         }
498
499         SILC_LOG_DEBUG(("Client (global) %s", 
500                         silc_id_render(client->id, SILC_ID_CLIENT)));
501
502         if (client->router == from) {
503           /* Skip clients that are *really* owned by the `from' */
504           if (remove_from && SILC_ID_COMPARE(from->id, client->id, 
505                                              client->id->ip.data_len)) {
506             SILC_LOG_DEBUG(("Found really owned client, skip it"));
507             if (!silc_idcache_list_next(list, &id_cache))
508               break;
509             else
510               continue;
511           }
512
513           if (resolve_real_server) {
514             client->router = 
515               silc_server_update_clients_by_real_server(server, from, client,
516                                                         local, id_cache);
517             if (!client->router)
518               client->router = to;
519           } else {
520             client->router = to;
521           }
522         }
523
524         if (!silc_idcache_list_next(list, &id_cache))
525           break;
526       }
527     }
528     silc_idcache_list_free(list);
529   }
530
531   if (remove_from)
532     /* Now remove the clients that are still marked as orignated from the
533        `from'. These are the clients that really was owned by the `from' and
534        not just exist behind the `from'. */
535     silc_server_remove_clients_by_server(server, from, TRUE);
536 }
537
538 /* Checks whether given channel has global users.  If it does this returns
539    TRUE and FALSE if there is only locally connected clients on the channel. */
540
541 bool silc_server_channel_has_global(SilcChannelEntry channel)
542 {
543   SilcChannelClientEntry chl;
544   SilcHashTableList htl;
545
546   silc_hash_table_list(channel->user_list, &htl);
547   while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
548     if (chl->client->router)
549       return TRUE;
550   }
551
552   return FALSE;
553 }
554
555 /* Checks whether given channel has locally connected users.  If it does this
556    returns TRUE and FALSE if there is not one locally connected client. */
557
558 bool silc_server_channel_has_local(SilcChannelEntry channel)
559 {
560   SilcChannelClientEntry chl;
561   SilcHashTableList htl;
562
563   silc_hash_table_list(channel->user_list, &htl);
564   while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
565     if (!chl->client->router)
566       return TRUE;
567   }
568
569   return FALSE;
570 }
571
572 /* Returns TRUE if the given client is on the channel.  FALSE if not. 
573    This works because we assure that the user list on the channel is
574    always in up to date thus we can only check the channel list from 
575    `client' which is faster than checking the user list from `channel'. */
576
577 bool silc_server_client_on_channel(SilcClientEntry client,
578                                    SilcChannelEntry channel)
579 {
580   if (!client || !channel)
581     return FALSE;
582
583   if (silc_hash_table_find(client->channels, channel, NULL, NULL))
584     return TRUE;
585
586   return FALSE;
587 }