updates.
[silc.git] / apps / silcd / server_util.c
index 7c6c1b3d39d0e2670c4909c95df1913c9e779e89..a29c2fd3757685017be70b928ae0368b528ea117 100644 (file)
@@ -62,9 +62,10 @@ static void silc_server_remove_clients_channels(SilcServer server,
       if (channel->rekey)
        silc_schedule_task_del_by_context(server->schedule, channel->rekey);
 
-      if (!silc_idlist_del_channel(server->local_list, channel))
-       silc_idlist_del_channel(server->global_list, channel);
-      server->stat.my_channels--;
+      if (silc_idlist_del_channel(server->local_list, channel))
+       server->stat.my_channels--;
+      else 
+        silc_idlist_del_channel(server->global_list, channel);
       continue;
     }
 
@@ -96,7 +97,7 @@ static void silc_server_remove_clients_channels(SilcServer server,
        SilcChannelClientEntry chl2;
        SilcHashTableList htl2;
 
-       channel->id = NULL;
+       channel->disabled = TRUE;
 
        silc_hash_table_list(channel->user_list, &htl2);
        while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
@@ -108,9 +109,10 @@ static void silc_server_remove_clients_channels(SilcServer server,
       }
 
       /* Remove the channel entry */
-      if (!silc_idlist_del_channel(server->local_list, channel))
-       silc_idlist_del_channel(server->global_list, channel);
-      server->stat.my_channels--;
+      if (silc_idlist_del_channel(server->local_list, channel))
+       server->stat.my_channels--;
+      else 
+        silc_idlist_del_channel(server->global_list, channel);
       continue;
     }
 
@@ -180,7 +182,7 @@ bool silc_server_remove_clients_by_server(SilcServer server,
        }
 
        if (client->router != entry) {
-         if (server_signoff && client->connection) {
+         if (server_signoff) {
            clients = silc_realloc(clients, 
                                   sizeof(*clients) * (clients_c + 1));
            clients[clients_c] = client;
@@ -210,7 +212,11 @@ bool silc_server_remove_clients_by_server(SilcServer server,
 
        /* Remove the client entry */
        silc_server_remove_clients_channels(server, NULL, client, channels);
-       silc_idlist_del_client(server->local_list, client);
+       client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+       id_cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+       server->stat.clients--;
+       if (server->server_type == SILC_ROUTER)
+         server->stat.cell_clients--;
 
        if (!silc_idcache_list_next(list, &id_cache))
          break;
@@ -262,7 +268,11 @@ bool silc_server_remove_clients_by_server(SilcServer server,
 
        /* Remove the client entry */
        silc_server_remove_clients_channels(server, NULL, client, channels);
-       silc_idlist_del_client(server->global_list, client);
+       client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+       id_cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+       server->stat.clients--;
+       if (server->server_type == SILC_ROUTER)
+         server->stat.cell_clients--;
 
        if (!silc_idcache_list_next(list, &id_cache))
          break;
@@ -273,7 +283,7 @@ bool silc_server_remove_clients_by_server(SilcServer server,
 
   /* Send the SERVER_SIGNOFF notify */
   if (server_signoff) {
-    SilcBuffer args;
+    SilcBuffer args, not;
 
     /* Send SERVER_SIGNOFF notify to our primary router */
     if (!server->standalone && server->router &&
@@ -289,17 +299,19 @@ bool silc_server_remove_clients_by_server(SilcServer server,
       silc_buffer_free(args);
     }
 
+    /* Send to local clients. We also send the list of client ID's that
+       is to be removed for those servers that would like to use that list. */
     args = silc_argument_payload_encode(argc, argv, argv_lens,
                                        argv_types);
-    /* Send to local clients */
-    for (i = 0; i < clients_c; i++) {
-      silc_server_send_notify_args(server, clients[i]->connection,
-                                  FALSE, SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
-                                  argc, args);
-    }
+    not = silc_notify_payload_encode_args(SILC_NOTIFY_TYPE_SERVER_SIGNOFF, 
+                                         argc, args);
+    silc_server_packet_send_clients(server, clients, clients_c,
+                                   SILC_PACKET_NOTIFY, 0, FALSE,
+                                   not->data, not->len, FALSE);
 
     silc_free(clients);
     silc_buffer_free(args);
+    silc_buffer_free(not);
     for (i = 0; i < argc; i++)
       silc_free(argv[i]);
     silc_free(argv);
@@ -330,7 +342,10 @@ bool silc_server_remove_clients_by_server(SilcServer server,
 
 static SilcServerEntry
 silc_server_update_clients_by_real_server(SilcServer server,
-                                         SilcClientEntry client)
+                                         SilcServerEntry from,
+                                         SilcClientEntry client,
+                                         bool local,
+                                         SilcIDCacheEntry client_cache)
 {
   SilcServerEntry server_entry;
   SilcIDCacheEntry id_cache = NULL;
@@ -342,9 +357,83 @@ silc_server_update_clients_by_real_server(SilcServer server,
   if (silc_idcache_list_first(list, &id_cache)) {
     while (id_cache) {
       server_entry = (SilcServerEntry)id_cache->context;
-      if (SILC_ID_COMPARE(server_entry->id, client->id, 
+      if (server_entry != from &&
+         SILC_ID_COMPARE(server_entry->id, client->id, 
+                         client->id->ip.data_len)) {
+       SILC_LOG_DEBUG(("Found (local) %s",
+                       silc_id_render(server_entry->id, SILC_ID_SERVER)));
+
+       if (!server_entry->data.send_key && server_entry->router) {
+         SILC_LOG_DEBUG(("Server not locally connected, use its router"));
+         /* If the client is not marked as local then move it to local list
+            since the server is local. */
+         if (!local) {
+           SILC_LOG_DEBUG(("Moving client to local list"));
+           silc_idcache_add(server->local_list->clients, client_cache->name,
+                            client_cache->id, client_cache->context,
+                            client_cache->expire);
+           silc_idcache_del_by_context(server->global_list->clients, client);
+         }
+         server_entry = server_entry->router;
+       } else {
+         /* If the client is not marked as local then move it to local list
+            since the server is local. */
+         if (server_entry->server_type != SILC_BACKUP_ROUTER && !local) {
+           SILC_LOG_DEBUG(("Moving client to local list"));
+           silc_idcache_add(server->local_list->clients, client_cache->name,
+                            client_cache->id, client_cache->context,
+                            client_cache->expire);
+           silc_idcache_del_by_context(server->global_list->clients, client);
+         }
+       }
+
+       silc_idcache_list_free(list);
+       return server_entry;
+      }
+
+      if (!silc_idcache_list_next(list, &id_cache))
+       break;
+    }
+  }
+
+  silc_idcache_list_free(list);
+
+  if (!silc_idcache_get_all(server->global_list->servers, &list))
+    return NULL;
+
+  if (silc_idcache_list_first(list, &id_cache)) {
+    while (id_cache) {
+      server_entry = (SilcServerEntry)id_cache->context;
+      if (server_entry != from &&
+         SILC_ID_COMPARE(server_entry->id, client->id, 
                          client->id->ip.data_len)) {
-       SILC_LOG_DEBUG(("Found"));
+       SILC_LOG_DEBUG(("Found (global) %s",
+                       silc_id_render(server_entry->id, SILC_ID_SERVER)));
+
+       if (!server_entry->data.send_key && server_entry->router) {
+         SILC_LOG_DEBUG(("Server not locally connected, use its router"));
+         /* If the client is marked as local then move it to global list
+            since the server is global. */
+         if (local) {
+           SILC_LOG_DEBUG(("Moving client to global list"));
+           silc_idcache_add(server->global_list->clients, client_cache->name,
+                            client_cache->id, client_cache->context,
+                            client_cache->expire);
+           silc_idcache_del_by_context(server->local_list->clients, client);
+         }
+         server_entry = server_entry->router;
+       } else {
+         /* If the client is marked as local then move it to global list
+            since the server is global. */
+         if (server_entry->server_type != SILC_BACKUP_ROUTER && local) {
+           SILC_LOG_DEBUG(("Moving client to global list"));
+           silc_idcache_add(server->global_list->clients, client_cache->name,
+                            client_cache->id, client_cache->context,
+                            client_cache->expire);
+           silc_idcache_del_by_context(server->local_list->clients, client);
+         }
+       }
+
        silc_idcache_list_free(list);
        return server_entry;
       }
@@ -378,6 +467,7 @@ void silc_server_update_clients_by_server(SilcServer server,
   SilcIDCacheList list = NULL;
   SilcIDCacheEntry id_cache = NULL;
   SilcClientEntry client = NULL;
+  bool local;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -386,7 +476,9 @@ void silc_server_update_clients_by_server(SilcServer server,
   SILC_LOG_DEBUG(("to %s", silc_id_render(to->id,
                                          SILC_ID_SERVER)));
 
-  if (silc_idcache_get_all(server->local_list->clients, &list)) {
+
+  local = FALSE;
+  if (silc_idcache_get_all(server->global_list->clients, &list)) {
     if (silc_idcache_list_first(list, &id_cache)) {
       while (id_cache) {
        client = (SilcClientEntry)id_cache->context;
@@ -397,6 +489,12 @@ void silc_server_update_clients_by_server(SilcServer server,
            continue;
        }
 
+       SILC_LOG_DEBUG(("Client (global) %s", 
+                       silc_id_render(client->id, SILC_ID_CLIENT)));
+       if (client->router)
+         SILC_LOG_DEBUG(("Client->router (global) %s", 
+                         silc_id_render(client->router->id, SILC_ID_SERVER)));
+
        if (client->router == from) {
          /* Skip clients that are *really* owned by the `from' */
          if (remove_from && SILC_ID_COMPARE(from->id, client->id, 
@@ -410,7 +508,8 @@ void silc_server_update_clients_by_server(SilcServer server,
 
          if (resolve_real_server) {
            client->router = 
-             silc_server_update_clients_by_real_server(server, client);
+             silc_server_update_clients_by_real_server(server, from, client,
+                                                       local, id_cache);
            if (!client->router)
              client->router = to;
          } else {
@@ -425,7 +524,8 @@ void silc_server_update_clients_by_server(SilcServer server,
     silc_idcache_list_free(list);
   }
 
-  if (silc_idcache_get_all(server->global_list->clients, &list)) {
+  local = TRUE;
+  if (silc_idcache_get_all(server->local_list->clients, &list)) {
     if (silc_idcache_list_first(list, &id_cache)) {
       while (id_cache) {
        client = (SilcClientEntry)id_cache->context;
@@ -436,6 +536,12 @@ void silc_server_update_clients_by_server(SilcServer server,
            continue;
        }
 
+       SILC_LOG_DEBUG(("Client (local) %s", 
+                       silc_id_render(client->id, SILC_ID_CLIENT)));
+       if (client->router)
+         SILC_LOG_DEBUG(("Client->router (local) %s", 
+                         silc_id_render(client->router->id, SILC_ID_SERVER)));
+
        if (client->router == from) {
          /* Skip clients that are *really* owned by the `from' */
          if (remove_from && SILC_ID_COMPARE(from->id, client->id, 
@@ -449,9 +555,10 @@ void silc_server_update_clients_by_server(SilcServer server,
 
          if (resolve_real_server) {
            client->router = 
-             silc_server_update_clients_by_real_server(server, client);
+             silc_server_update_clients_by_real_server(server, from, client,
+                                                       local, id_cache);
            if (!client->router)
-             client->router = to;
+             client->router = from; /* on local list put old from */
          } else {
            client->router = to;
          }
@@ -471,6 +578,101 @@ void silc_server_update_clients_by_server(SilcServer server,
     silc_server_remove_clients_by_server(server, from, TRUE);
 }
 
+/* Updates servers that are from `from' to be originated from `to'.  This
+   will also update the server's connection to `to's connection. */
+
+void silc_server_update_servers_by_server(SilcServer server, 
+                                         SilcServerEntry from,
+                                         SilcServerEntry to)
+{
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcServerEntry server_entry = NULL;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (silc_idcache_get_all(server->local_list->servers, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       server_entry = (SilcServerEntry)id_cache->context;
+       if (server_entry->router == from) {
+         server_entry->router = to;
+         server_entry->connection = to->connection;
+       }
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+
+  if (silc_idcache_get_all(server->global_list->servers, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       server_entry = (SilcServerEntry)id_cache->context;
+       if (server_entry->router == from) {
+         server_entry->router = to;
+         server_entry->connection = to->connection;
+       }
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+}
+
+/* Removes channels that are from `from. */
+
+void silc_server_remove_channels_by_server(SilcServer server, 
+                                          SilcServerEntry from)
+{
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcChannelEntry channel = NULL;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (silc_idcache_get_all(server->global_list->channels, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       channel = (SilcChannelEntry)id_cache->context;
+       if (channel->router == from)
+         silc_idlist_del_channel(server->global_list, channel);
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+}
+
+/* Updates channels that are from `from' to be originated from `to'.  */
+
+void silc_server_update_channels_by_server(SilcServer server, 
+                                          SilcServerEntry from,
+                                          SilcServerEntry to)
+{
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcChannelEntry channel = NULL;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (silc_idcache_get_all(server->global_list->channels, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       channel = (SilcChannelEntry)id_cache->context;
+       if (channel->router == from)
+         channel->router = to;
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+}
+
 /* Checks whether given channel has global users.  If it does this returns
    TRUE and FALSE if there is only locally connected clients on the channel. */