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