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 
68         silc_idlist_del_channel(server->global_list, channel);
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->disabled = TRUE;
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 
115         silc_idlist_del_channel(server->global_list, channel);
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) {
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         client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
216         id_cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
217         server->stat.clients--;
218         if (server->server_type == SILC_ROUTER)
219           server->stat.cell_clients--;
220
221         if (!silc_idcache_list_next(list, &id_cache))
222           break;
223       }
224     }
225     silc_idcache_list_free(list);
226   }
227   
228   if (silc_idcache_get_all(server->global_list->clients, &list)) {
229
230     if (silc_idcache_list_first(list, &id_cache)) {
231       while (id_cache) {
232         client = (SilcClientEntry)id_cache->context;
233         if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
234           if (!silc_idcache_list_next(list, &id_cache))
235             break;
236           else
237             continue;
238         }
239         
240         if (client->router != entry) {
241           if (server_signoff && client->connection) {
242             clients = silc_realloc(clients, 
243                                    sizeof(*clients) * (clients_c + 1));
244             clients[clients_c] = client;
245             clients_c++;
246           }
247
248           if (!silc_idcache_list_next(list, &id_cache))
249             break;
250           else
251             continue;
252         }
253
254         if (server_signoff) {
255           idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
256           argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
257           argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
258                                    (argc + 1));
259           argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
260                                     (argc + 1));
261           argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
262           memcpy(argv[argc], idp->data, idp->len);
263           argv_lens[argc] = idp->len;
264           argv_types[argc] = argc + 1;
265           argc++;
266           silc_buffer_free(idp);
267         }
268
269         /* Remove the client entry */
270         silc_server_remove_clients_channels(server, NULL, client, channels);
271         client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
272         id_cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
273         server->stat.clients--;
274         if (server->server_type == SILC_ROUTER)
275           server->stat.cell_clients--;
276
277         if (!silc_idcache_list_next(list, &id_cache))
278           break;
279       }
280     }
281     silc_idcache_list_free(list);
282   }
283
284   /* Send the SERVER_SIGNOFF notify */
285   if (server_signoff) {
286     SilcBuffer args, not;
287
288     /* Send SERVER_SIGNOFF notify to our primary router */
289     if (!server->standalone && server->router &&
290         server->router != entry) {
291       args = silc_argument_payload_encode(1, argv, argv_lens,
292                                           argv_types);
293       silc_server_send_notify_args(server, 
294                                    server->router->connection,
295                                    server->server_type == SILC_SERVER ? 
296                                    FALSE : TRUE, 
297                                    SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
298                                    argc, args);
299       silc_buffer_free(args);
300     }
301
302     /* Send to local clients. We also send the list of client ID's that
303        is to be removed for those servers that would like to use that list. */
304     args = silc_argument_payload_encode(argc, argv, argv_lens,
305                                         argv_types);
306     not = silc_notify_payload_encode_args(SILC_NOTIFY_TYPE_SERVER_SIGNOFF, 
307                                           argc, args);
308     silc_server_packet_send_clients(server, clients, clients_c,
309                                     SILC_PACKET_NOTIFY, 0, FALSE,
310                                     not->data, not->len, FALSE);
311
312     silc_free(clients);
313     silc_buffer_free(args);
314     silc_buffer_free(not);
315     for (i = 0; i < argc; i++)
316       silc_free(argv[i]);
317     silc_free(argv);
318     silc_free(argv_lens);
319     silc_free(argv_types);
320   }
321
322   /* We must now re-generate the channel key for all channels that had
323      this server's client(s) on the channel. As they left the channel we
324      must re-generate the channel key. */
325   silc_hash_table_list(channels, &htl);
326   while (silc_hash_table_get(&htl, NULL, (void *)&channel)) {
327     if (!silc_server_create_channel_key(server, channel, 0))
328       return FALSE;
329
330     /* Do not send the channel key if private channel key mode is set */
331     if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY)
332       continue;
333
334     silc_server_send_channel_key(server, NULL, channel, 
335                                  server->server_type == SILC_ROUTER ? 
336                                  FALSE : !server->standalone);
337   }
338   silc_hash_table_free(channels);
339
340   return TRUE;
341 }
342
343 static SilcServerEntry
344 silc_server_update_clients_by_real_server(SilcServer server,
345                                           SilcServerEntry from,
346                                           SilcClientEntry client,
347                                           bool local,
348                                           SilcIDCacheEntry client_cache)
349 {
350   SilcServerEntry server_entry;
351   SilcIDCacheEntry id_cache = NULL;
352   SilcIDCacheList list;
353
354   if (!silc_idcache_get_all(server->local_list->servers, &list))
355     return NULL;
356
357   if (silc_idcache_list_first(list, &id_cache)) {
358     while (id_cache) {
359       server_entry = (SilcServerEntry)id_cache->context;
360       if (server_entry != from &&
361           SILC_ID_COMPARE(server_entry->id, client->id, 
362                           client->id->ip.data_len)) {
363         SILC_LOG_DEBUG(("Found (local) %s",
364                         silc_id_render(server_entry->id, SILC_ID_SERVER)));
365
366         if (!server_entry->data.send_key && server_entry->router) {
367           SILC_LOG_DEBUG(("Server not locally connected, use its router"));
368           /* If the client is not marked as local then move it to local list
369              since the server is local. */
370           if (!local) {
371             SILC_LOG_DEBUG(("Moving client to local list"));
372             silc_idcache_add(server->local_list->clients, client_cache->name,
373                              client_cache->id, client_cache->context,
374                              client_cache->expire);
375             silc_idcache_del_by_context(server->global_list->clients, client);
376           }
377           server_entry = server_entry->router;
378         } else {
379           /* If the client is not marked as local then move it to local list
380              since the server is local. */
381           if (server_entry->server_type != SILC_BACKUP_ROUTER && !local) {
382             SILC_LOG_DEBUG(("Moving client to local list"));
383             silc_idcache_add(server->local_list->clients, client_cache->name,
384                              client_cache->id, client_cache->context,
385                              client_cache->expire);
386             silc_idcache_del_by_context(server->global_list->clients, client);
387           }
388         }
389
390         silc_idcache_list_free(list);
391         return server_entry;
392       }
393
394       if (!silc_idcache_list_next(list, &id_cache))
395         break;
396     }
397   }
398
399   silc_idcache_list_free(list);
400
401   if (!silc_idcache_get_all(server->global_list->servers, &list))
402     return NULL;
403
404   if (silc_idcache_list_first(list, &id_cache)) {
405     while (id_cache) {
406       server_entry = (SilcServerEntry)id_cache->context;
407       if (server_entry != from &&
408           SILC_ID_COMPARE(server_entry->id, client->id, 
409                           client->id->ip.data_len)) {
410         SILC_LOG_DEBUG(("Found (global) %s",
411                         silc_id_render(server_entry->id, SILC_ID_SERVER)));
412
413         if (!server_entry->data.send_key && server_entry->router) {
414           SILC_LOG_DEBUG(("Server not locally connected, use its router"));
415           /* If the client is marked as local then move it to global list
416              since the server is global. */
417           if (local) {
418             SILC_LOG_DEBUG(("Moving client to global list"));
419             silc_idcache_add(server->global_list->clients, client_cache->name,
420                              client_cache->id, client_cache->context,
421                              client_cache->expire);
422             silc_idcache_del_by_context(server->local_list->clients, client);
423           }
424           server_entry = server_entry->router;
425         } else {
426           /* If the client is marked as local then move it to global list
427              since the server is global. */
428           if (server_entry->server_type != SILC_BACKUP_ROUTER && local) {
429             SILC_LOG_DEBUG(("Moving client to global list"));
430             silc_idcache_add(server->global_list->clients, client_cache->name,
431                              client_cache->id, client_cache->context,
432                              client_cache->expire);
433             silc_idcache_del_by_context(server->local_list->clients, client);
434           }
435         }
436
437         silc_idcache_list_free(list);
438         return server_entry;
439       }
440
441       if (!silc_idcache_list_next(list, &id_cache))
442         break;
443     }
444   }
445
446   silc_idcache_list_free(list);
447
448   return NULL;
449 }
450
451 /* Updates the clients that are originated from the `from' to be originated
452    from the `to'. If the `resolve_real_server' is TRUE then this will
453    attempt to figure out which clients really are originated from the
454    `from' and which are originated from a server that we have connection
455    to, when we've acting as backup router. If it is FALSE the `to' will
456    be the new source. This function also removes the clients that are
457    *really* originated from `from' if `remove_from' is TRUE. These are
458    clients that the `from' owns, and not just clients that are behind
459    the `from'. */
460
461 void silc_server_update_clients_by_server(SilcServer server, 
462                                           SilcServerEntry from,
463                                           SilcServerEntry to,
464                                           bool resolve_real_server,
465                                           bool remove_from)
466 {
467   SilcIDCacheList list = NULL;
468   SilcIDCacheEntry id_cache = NULL;
469   SilcClientEntry client = NULL;
470   bool local;
471
472   SILC_LOG_DEBUG(("Start"));
473
474   SILC_LOG_DEBUG(("Updating %s", silc_id_render(from->id,
475                                                 SILC_ID_SERVER)));
476   SILC_LOG_DEBUG(("to %s", silc_id_render(to->id,
477                                           SILC_ID_SERVER)));
478
479
480   local = FALSE;
481   if (silc_idcache_get_all(server->global_list->clients, &list)) {
482     if (silc_idcache_list_first(list, &id_cache)) {
483       while (id_cache) {
484         client = (SilcClientEntry)id_cache->context;
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         if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
533           if (!silc_idcache_list_next(list, &id_cache))
534             break;
535           else
536             continue;
537         }
538
539         SILC_LOG_DEBUG(("Client (local) %s", 
540                         silc_id_render(client->id, SILC_ID_CLIENT)));
541         if (client->router)
542           SILC_LOG_DEBUG(("Client->router (local) %s", 
543                           silc_id_render(client->router->id, SILC_ID_SERVER)));
544
545         if (client->router == from) {
546           /* Skip clients that are *really* owned by the `from' */
547           if (remove_from && SILC_ID_COMPARE(from->id, client->id, 
548                                              client->id->ip.data_len)) {
549             SILC_LOG_DEBUG(("Found really owned client, skip it"));
550             if (!silc_idcache_list_next(list, &id_cache))
551               break;
552             else
553               continue;
554           }
555
556           if (resolve_real_server) {
557             client->router = 
558               silc_server_update_clients_by_real_server(server, from, client,
559                                                         local, id_cache);
560             if (!client->router)
561               client->router = from; /* on local list put old from */
562           } else {
563             client->router = to;
564           }
565         }
566
567         if (!silc_idcache_list_next(list, &id_cache))
568           break;
569       }
570     }
571     silc_idcache_list_free(list);
572   }
573
574   if (remove_from)
575     /* Now remove the clients that are still marked as orignated from the
576        `from'. These are the clients that really was owned by the `from' and
577        not just exist behind the `from'. */
578     silc_server_remove_clients_by_server(server, from, TRUE);
579 }
580
581 /* Updates servers that are from `from' to be originated from `to'.  This
582    will also update the server's connection to `to's connection. */
583
584 void silc_server_update_servers_by_server(SilcServer server, 
585                                           SilcServerEntry from,
586                                           SilcServerEntry to)
587 {
588   SilcIDCacheList list = NULL;
589   SilcIDCacheEntry id_cache = NULL;
590   SilcServerEntry server_entry = NULL;
591
592   SILC_LOG_DEBUG(("Start"));
593
594   if (silc_idcache_get_all(server->local_list->servers, &list)) {
595     if (silc_idcache_list_first(list, &id_cache)) {
596       while (id_cache) {
597         server_entry = (SilcServerEntry)id_cache->context;
598         if (server_entry->router == from) {
599           server_entry->router = to;
600           server_entry->connection = to->connection;
601         }
602         if (!silc_idcache_list_next(list, &id_cache))
603           break;
604       }
605     }
606     silc_idcache_list_free(list);
607   }
608
609   if (silc_idcache_get_all(server->global_list->servers, &list)) {
610     if (silc_idcache_list_first(list, &id_cache)) {
611       while (id_cache) {
612         server_entry = (SilcServerEntry)id_cache->context;
613         if (server_entry->router == from) {
614           server_entry->router = to;
615           server_entry->connection = to->connection;
616         }
617         if (!silc_idcache_list_next(list, &id_cache))
618           break;
619       }
620     }
621     silc_idcache_list_free(list);
622   }
623 }
624
625 /* Checks whether given channel has global users.  If it does this returns
626    TRUE and FALSE if there is only locally connected clients on the channel. */
627
628 bool silc_server_channel_has_global(SilcChannelEntry channel)
629 {
630   SilcChannelClientEntry chl;
631   SilcHashTableList htl;
632
633   silc_hash_table_list(channel->user_list, &htl);
634   while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
635     if (chl->client->router)
636       return TRUE;
637   }
638
639   return FALSE;
640 }
641
642 /* Checks whether given channel has locally connected users.  If it does this
643    returns TRUE and FALSE if there is not one locally connected client. */
644
645 bool silc_server_channel_has_local(SilcChannelEntry channel)
646 {
647   SilcChannelClientEntry chl;
648   SilcHashTableList htl;
649
650   silc_hash_table_list(channel->user_list, &htl);
651   while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
652     if (!chl->client->router)
653       return TRUE;
654   }
655
656   return FALSE;
657 }
658
659 /* Returns TRUE if the given client is on the channel.  FALSE if not. 
660    This works because we assure that the user list on the channel is
661    always in up to date thus we can only check the channel list from 
662    `client' which is faster than checking the user list from `channel'. */
663
664 bool silc_server_client_on_channel(SilcClientEntry client,
665                                    SilcChannelEntry channel)
666 {
667   if (!client || !channel)
668     return FALSE;
669
670   if (silc_hash_table_find(client->channels, channel, NULL, NULL))
671     return TRUE;
672
673   return FALSE;
674 }