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 0
357         if (!server_entry->data.send_key && server_entry->router) {
358           SILC_LOG_DEBUG(("Server not locally connected, use its router"));
359           /* If the client is not marked as local then move it to local list
360              since the server is local. */
361           if (!local) {
362             SILC_LOG_DEBUG(("Moving client to local list"));
363             silc_idcache_add(server->local_list->clients, client_cache->name,
364                              client_cache->id, client_cache->context,
365                              client_cache->expire);
366             silc_idcache_del_by_context(server->global_list->clients, client);
367           }
368           server_entry = server_entry->router;
369         } else {
370 #endif
371           /* If the client is not marked as local then move it to local list
372              since the server is local. */
373           if (server_entry->server_type != SILC_BACKUP_ROUTER && !local) {
374             SILC_LOG_DEBUG(("Moving client to local list"));
375             silc_idcache_add(server->local_list->clients, client_cache->name,
376                              client_cache->id, client_cache->context,
377                              client_cache->expire);
378             silc_idcache_del_by_context(server->global_list->clients, client);
379           }
380 #if 0
381         }
382 #endif
383
384         silc_idcache_list_free(list);
385         return server_entry;
386       }
387
388       if (!silc_idcache_list_next(list, &id_cache))
389         break;
390     }
391   }
392
393   silc_idcache_list_free(list);
394
395   if (!silc_idcache_get_all(server->global_list->servers, &list))
396     return NULL;
397
398   if (silc_idcache_list_first(list, &id_cache)) {
399     while (id_cache) {
400       server_entry = (SilcServerEntry)id_cache->context;
401       if (server_entry != from &&
402           SILC_ID_COMPARE(server_entry->id, client->id, 
403                           client->id->ip.data_len)) {
404         SILC_LOG_DEBUG(("Found (global) %s",
405                         silc_id_render(server_entry->id, SILC_ID_SERVER)));
406
407 #if 0
408         if (!server_entry->data.send_key && server_entry->router) {
409           SILC_LOG_DEBUG(("Server not locally connected, use its router"));
410           /* If the client is marked as local then move it to global list
411              since the server is global. */
412           if (local) {
413             SILC_LOG_DEBUG(("Moving client to global list"));
414             silc_idcache_add(server->global_list->clients, client_cache->name,
415                              client_cache->id, client_cache->context,
416                              client_cache->expire);
417             silc_idcache_del_by_context(server->local_list->clients, client);
418           }
419           server_entry = server_entry->router;
420         } else {
421 #endif
422           /* If the client is marked as local then move it to global list
423              since the server is global. */
424           if (server_entry->server_type != SILC_BACKUP_ROUTER && local) {
425             SILC_LOG_DEBUG(("Moving client to global list"));
426             silc_idcache_add(server->global_list->clients, client_cache->name,
427                              client_cache->id, client_cache->context,
428                              client_cache->expire);
429             silc_idcache_del_by_context(server->local_list->clients, client);
430           }
431 #if 0
432         }
433 #endif
434
435         silc_idcache_list_free(list);
436         return server_entry;
437       }
438
439       if (!silc_idcache_list_next(list, &id_cache))
440         break;
441     }
442   }
443
444   silc_idcache_list_free(list);
445
446   return NULL;
447 }
448
449 /* Updates the clients that are originated from the `from' to be originated
450    from the `to'. If the `resolve_real_server' is TRUE then this will
451    attempt to figure out which clients really are originated from the
452    `from' and which are originated from a server that we have connection
453    to, when we've acting as backup router. If it is FALSE the `to' will
454    be the new source. This function also removes the clients that are
455    *really* originated from `from' if `remove_from' is TRUE. These are
456    clients that the `from' owns, and not just clients that are behind
457    the `from'. */
458
459 void silc_server_update_clients_by_server(SilcServer server, 
460                                           SilcServerEntry from,
461                                           SilcServerEntry to,
462                                           bool resolve_real_server,
463                                           bool remove_from)
464 {
465   SilcIDCacheList list = NULL;
466   SilcIDCacheEntry id_cache = NULL;
467   SilcClientEntry client = NULL;
468   bool local;
469
470   SILC_LOG_DEBUG(("Start"));
471
472   SILC_LOG_DEBUG(("Updating %s", silc_id_render(from->id,
473                                                 SILC_ID_SERVER)));
474   SILC_LOG_DEBUG(("to %s", silc_id_render(to->id,
475                                           SILC_ID_SERVER)));
476
477
478   local = FALSE;
479   if (silc_idcache_get_all(server->global_list->clients, &list)) {
480     if (silc_idcache_list_first(list, &id_cache)) {
481       while (id_cache) {
482         client = (SilcClientEntry)id_cache->context;
483         
484
485         if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
486           if (!silc_idcache_list_next(list, &id_cache))
487             break;
488           else
489             continue;
490         }
491
492         SILC_LOG_DEBUG(("Client (global) %s", 
493                         silc_id_render(client->id, SILC_ID_CLIENT)));
494         if (client->router)
495           SILC_LOG_DEBUG(("Client->router (global) %s", 
496                           silc_id_render(client->router->id, SILC_ID_SERVER)));
497
498         if (client->router == from) {
499           /* Skip clients that are *really* owned by the `from' */
500           if (remove_from && SILC_ID_COMPARE(from->id, client->id, 
501                                              client->id->ip.data_len)) {
502             SILC_LOG_DEBUG(("Found really owned client, skip it"));
503             if (!silc_idcache_list_next(list, &id_cache))
504               break;
505             else
506               continue;
507           }
508
509           if (resolve_real_server) {
510             client->router = 
511               silc_server_update_clients_by_real_server(server, from, client,
512                                                         local, id_cache);
513             if (!client->router)
514               client->router = to;
515           } else {
516             client->router = to;
517           }
518         }
519
520         if (!silc_idcache_list_next(list, &id_cache))
521           break;
522       }
523     }
524     silc_idcache_list_free(list);
525   }
526
527   local = TRUE;
528   if (silc_idcache_get_all(server->local_list->clients, &list)) {
529     if (silc_idcache_list_first(list, &id_cache)) {
530       while (id_cache) {
531         client = (SilcClientEntry)id_cache->context;
532
533         if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
534           if (!silc_idcache_list_next(list, &id_cache))
535             break;
536           else
537             continue;
538         }
539
540         SILC_LOG_DEBUG(("Client (local) %s", 
541                         silc_id_render(client->id, SILC_ID_CLIENT)));
542         if (client->router)
543           SILC_LOG_DEBUG(("Client->router (local) %s", 
544                           silc_id_render(client->router->id, SILC_ID_SERVER)));
545
546         if (client->router == from) {
547           /* Skip clients that are *really* owned by the `from' */
548           if (remove_from && SILC_ID_COMPARE(from->id, client->id, 
549                                              client->id->ip.data_len)) {
550             SILC_LOG_DEBUG(("Found really owned client, skip it"));
551             if (!silc_idcache_list_next(list, &id_cache))
552               break;
553             else
554               continue;
555           }
556
557           if (resolve_real_server) {
558             client->router = 
559               silc_server_update_clients_by_real_server(server, from, client,
560                                                         local, id_cache);
561             if (!client->router)
562               client->router = from; /* on local list put old from */
563           } else {
564             client->router = to;
565           }
566         }
567
568         if (!silc_idcache_list_next(list, &id_cache))
569           break;
570       }
571     }
572     silc_idcache_list_free(list);
573   }
574
575   if (remove_from)
576     /* Now remove the clients that are still marked as orignated from the
577        `from'. These are the clients that really was owned by the `from' and
578        not just exist behind the `from'. */
579     silc_server_remove_clients_by_server(server, from, TRUE);
580 }
581
582 /* Updates servers that are from `from' to be originated from `to'.  This
583    will also update the server's connection to `to's connection. */
584
585 void silc_server_update_servers_by_server(SilcServer server, 
586                                           SilcServerEntry from,
587                                           SilcServerEntry to)
588 {
589   SilcIDCacheList list = NULL;
590   SilcIDCacheEntry id_cache = NULL;
591   SilcServerEntry server_entry = NULL;
592
593   SILC_LOG_DEBUG(("Start"));
594
595   if (silc_idcache_get_all(server->local_list->servers, &list)) {
596     if (silc_idcache_list_first(list, &id_cache)) {
597       while (id_cache) {
598         server_entry = (SilcServerEntry)id_cache->context;
599         if (server_entry->router == from) {
600           server_entry->router = to;
601           server_entry->connection = to->connection;
602         }
603         if (!silc_idcache_list_next(list, &id_cache))
604           break;
605       }
606     }
607     silc_idcache_list_free(list);
608   }
609
610   if (silc_idcache_get_all(server->global_list->servers, &list)) {
611     if (silc_idcache_list_first(list, &id_cache)) {
612       while (id_cache) {
613         server_entry = (SilcServerEntry)id_cache->context;
614         if (server_entry->router == from) {
615           server_entry->router = to;
616           server_entry->connection = to->connection;
617         }
618         if (!silc_idcache_list_next(list, &id_cache))
619           break;
620       }
621     }
622     silc_idcache_list_free(list);
623   }
624 }
625
626 /* Checks whether given channel has global users.  If it does this returns
627    TRUE and FALSE if there is only locally connected clients on the channel. */
628
629 bool silc_server_channel_has_global(SilcChannelEntry channel)
630 {
631   SilcChannelClientEntry chl;
632   SilcHashTableList htl;
633
634   silc_hash_table_list(channel->user_list, &htl);
635   while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
636     if (chl->client->router)
637       return TRUE;
638   }
639
640   return FALSE;
641 }
642
643 /* Checks whether given channel has locally connected users.  If it does this
644    returns TRUE and FALSE if there is not one locally connected client. */
645
646 bool silc_server_channel_has_local(SilcChannelEntry channel)
647 {
648   SilcChannelClientEntry chl;
649   SilcHashTableList htl;
650
651   silc_hash_table_list(channel->user_list, &htl);
652   while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
653     if (!chl->client->router)
654       return TRUE;
655   }
656
657   return FALSE;
658 }
659
660 /* Returns TRUE if the given client is on the channel.  FALSE if not. 
661    This works because we assure that the user list on the channel is
662    always in up to date thus we can only check the channel list from 
663    `client' which is faster than checking the user list from `channel'. */
664
665 bool silc_server_client_on_channel(SilcClientEntry client,
666                                    SilcChannelEntry channel)
667 {
668   if (!client || !channel)
669     return FALSE;
670
671   if (silc_hash_table_find(client->channels, channel, NULL, NULL))
672     return TRUE;
673
674   return FALSE;
675 }