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                                           SilcClientEntry client)
334 {
335   SilcServerEntry server_entry;
336   SilcIDCacheEntry id_cache = NULL;
337   SilcIDCacheList list;
338
339   if (!silc_idcache_get_all(server->local_list->servers, &list))
340     return NULL;
341
342   if (silc_idcache_list_first(list, &id_cache)) {
343     while (id_cache) {
344       server_entry = (SilcServerEntry)id_cache->context;
345       if (SILC_ID_COMPARE(server_entry->id, client->id, 
346                           client->id->ip.data_len)) {
347         SILC_LOG_DEBUG(("Found"));
348         silc_idcache_list_free(list);
349         return server_entry;
350       }
351
352       if (!silc_idcache_list_next(list, &id_cache))
353         break;
354     }
355   }
356
357   silc_idcache_list_free(list);
358
359   return NULL;
360 }
361
362 /* Updates the clients that are originated from the `from' to be originated
363    from the `to'. If the `resolve_real_server' is TRUE then this will
364    attempt to figure out which clients really are originated from the
365    `from' and which are originated from a server that we have connection
366    to, when we've acting as backup router. If it is FALSE the `to' will
367    be the new source. This function also removes the clients that are
368    *really* originated from `from'. These are clients that the `from'
369    owns, and not just clients that are behind the `from'. */
370
371 void silc_server_update_clients_by_server(SilcServer server, 
372                                           SilcServerEntry from,
373                                           SilcServerEntry to,
374                                           bool resolve_real_server)
375 {
376   SilcIDCacheList list = NULL;
377   SilcIDCacheEntry id_cache = NULL;
378   SilcClientEntry client = NULL;
379
380   SILC_LOG_DEBUG(("Start"));
381
382   if (silc_idcache_get_all(server->local_list->clients, &list)) {
383     if (silc_idcache_list_first(list, &id_cache)) {
384       while (id_cache) {
385         client = (SilcClientEntry)id_cache->context;
386         if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
387           if (!silc_idcache_list_next(list, &id_cache))
388             break;
389           else
390             continue;
391         }
392
393         if (client->router == from) {
394           /* Skip clients that are *really* owned by the `from' */
395           if (SILC_ID_COMPARE(from->id, client->id, 
396                               client->id->ip.data_len)) {
397             SILC_LOG_DEBUG(("Found really owned client, will remove it"));
398             if (!silc_idcache_list_next(list, &id_cache))
399               break;
400             else
401               continue;
402           }
403
404           if (resolve_real_server) {
405             client->router = 
406               silc_server_update_clients_by_real_server(server, client);
407             if (!client->router)
408               client->router = to;
409           } else {
410             client->router = to;
411           }
412         }
413
414         if (!silc_idcache_list_next(list, &id_cache))
415           break;
416       }
417     }
418     silc_idcache_list_free(list);
419   }
420
421   if (silc_idcache_get_all(server->global_list->clients, &list)) {
422     if (silc_idcache_list_first(list, &id_cache)) {
423       while (id_cache) {
424         client = (SilcClientEntry)id_cache->context;
425         if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
426           if (!silc_idcache_list_next(list, &id_cache))
427             break;
428           else
429             continue;
430         }
431
432         if (client->router == from) {
433           /* Skip clients that are *really* owned by the `from' */
434           if (SILC_ID_COMPARE(from->id, client->id, 
435                               client->id->ip.data_len)) {
436             SILC_LOG_DEBUG(("Found really owned client, will remove it"));
437             if (!silc_idcache_list_next(list, &id_cache))
438               break;
439             else
440               continue;
441           }
442
443           if (resolve_real_server) {
444             client->router = 
445               silc_server_update_clients_by_real_server(server, client);
446             if (!client->router)
447               client->router = to;
448           } else {
449             client->router = to;
450           }
451         }
452
453         if (!silc_idcache_list_next(list, &id_cache))
454           break;
455       }
456     }
457     silc_idcache_list_free(list);
458   }
459
460   /* Now remove the clients that are still marked as orignated from the
461      `from'. These are the clients that really was owned by the `from' and
462      not just exist behind the `from'. */
463   silc_server_remove_clients_by_server(server, from, TRUE);
464 }
465
466 /* Checks whether given channel has global users.  If it does this returns
467    TRUE and FALSE if there is only locally connected clients on the channel. */
468
469 bool silc_server_channel_has_global(SilcChannelEntry channel)
470 {
471   SilcChannelClientEntry chl;
472   SilcHashTableList htl;
473
474   silc_hash_table_list(channel->user_list, &htl);
475   while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
476     if (chl->client->router)
477       return TRUE;
478   }
479
480   return FALSE;
481 }
482
483 /* Checks whether given channel has locally connected users.  If it does this
484    returns TRUE and FALSE if there is not one locally connected client. */
485
486 bool silc_server_channel_has_local(SilcChannelEntry channel)
487 {
488   SilcChannelClientEntry chl;
489   SilcHashTableList htl;
490
491   silc_hash_table_list(channel->user_list, &htl);
492   while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
493     if (!chl->client->router)
494       return TRUE;
495   }
496
497   return FALSE;
498 }
499
500 /* Returns TRUE if the given client is on the channel.  FALSE if not. 
501    This works because we assure that the user list on the channel is
502    always in up to date thus we can only check the channel list from 
503    `client' which is faster than checking the user list from `channel'. */
504
505 bool silc_server_client_on_channel(SilcClientEntry client,
506                                    SilcChannelEntry channel)
507 {
508   if (!client || !channel)
509     return FALSE;
510
511   if (silc_hash_table_find(client->channels, channel, NULL, NULL))
512     return TRUE;
513
514   return FALSE;
515 }