updates.
[silc.git] / apps / silcd / server_util.c
index 26c33fc6db6a616d6fb9aa15392ade6f0c738423..5c06bcd17f11abd582c0b083772f33e68f751a17 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)) {
@@ -104,13 +105,15 @@ static void silc_server_remove_clients_channels(SilcServer server,
          silc_hash_table_del(channel->user_list, chl2->client);
          silc_free(chl2);
        }
+       silc_hash_table_list_reset(&htl2);
        continue;
       }
 
       /* 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;
     }
 
@@ -119,6 +122,7 @@ static void silc_server_remove_clients_channels(SilcServer server,
     if (!silc_hash_table_find(channels, channel, NULL, NULL))
       silc_hash_table_add(channels, channel, channel);
   }
+  silc_hash_table_list_reset(&htl);
 
   silc_buffer_free(clidp);
 }
@@ -180,7 +184,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;
@@ -208,9 +212,21 @@ bool silc_server_remove_clients_by_server(SilcServer server,
          silc_buffer_free(idp);
        }
 
+       /* Update statistics */
+       server->stat.clients--;
+       if (server->server_type == SILC_ROUTER)
+         server->stat.cell_clients--;
+       SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+       SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+
        /* Remove the client entry */
        silc_server_remove_clients_channels(server, NULL, client, channels);
-       silc_idlist_del_client(server->local_list, client);
+       if (!server_signoff) {
+         client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+         id_cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+       } else {
+         silc_idlist_del_client(server->local_list, client);
+       }
 
        if (!silc_idcache_list_next(list, &id_cache))
          break;
@@ -260,9 +276,21 @@ bool silc_server_remove_clients_by_server(SilcServer server,
          silc_buffer_free(idp);
        }
 
+       /* Update statistics */
+       server->stat.clients--;
+       if (server->server_type == SILC_ROUTER)
+         server->stat.cell_clients--;
+       SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+       SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+
        /* Remove the client entry */
        silc_server_remove_clients_channels(server, NULL, client, channels);
-       silc_idlist_del_client(server->global_list, client);
+       if (!server_signoff) {
+         client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+         id_cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+       } else {
+         silc_idlist_del_client(server->global_list, client);
+       }
 
        if (!silc_idcache_list_next(list, &id_cache))
          break;
@@ -273,7 +301,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 +317,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);
@@ -312,8 +342,11 @@ bool silc_server_remove_clients_by_server(SilcServer server,
      must re-generate the channel key. */
   silc_hash_table_list(channels, &htl);
   while (silc_hash_table_get(&htl, NULL, (void *)&channel)) {
-    if (!silc_server_create_channel_key(server, channel, 0))
+    if (!silc_server_create_channel_key(server, channel, 0)) {
+      silc_hash_table_list_reset(&htl);
+      silc_hash_table_free(channels);
       return FALSE;
+    }
 
     /* Do not send the channel key if private channel key mode is set */
     if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY)
@@ -323,6 +356,7 @@ bool silc_server_remove_clients_by_server(SilcServer server,
                                 server->server_type == SILC_ROUTER ? 
                                 FALSE : !server->standalone);
   }
+  silc_hash_table_list_reset(&htl);
   silc_hash_table_free(channels);
 
   return TRUE;
@@ -351,14 +385,28 @@ silc_server_update_clients_by_real_server(SilcServer server,
        SILC_LOG_DEBUG(("Found (local) %s",
                        silc_id_render(server_entry->id, SILC_ID_SERVER)));
 
-       /* 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);
+       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, NULL);
+           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, NULL);
+           silc_idcache_del_by_context(server->global_list->clients, client);
+         }
        }
 
        silc_idcache_list_free(list);
@@ -384,14 +432,28 @@ silc_server_update_clients_by_real_server(SilcServer server,
        SILC_LOG_DEBUG(("Found (global) %s",
                        silc_id_render(server_entry->id, SILC_ID_SERVER)));
 
-       /* 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);
+       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, NULL);
+           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, NULL);
+           silc_idcache_del_by_context(server->local_list->clients, client);
+         }
        }
 
        silc_idcache_list_free(list);
@@ -437,12 +499,11 @@ void silc_server_update_clients_by_server(SilcServer server,
                                          SILC_ID_SERVER)));
 
 
-  local = TRUE;
-  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;
-
        if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
          if (!silc_idcache_list_next(list, &id_cache))
            break;
@@ -450,8 +511,11 @@ void silc_server_update_clients_by_server(SilcServer server,
            continue;
        }
 
-       SILC_LOG_DEBUG(("Client (local) %s", 
+       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' */
@@ -482,13 +546,11 @@ void silc_server_update_clients_by_server(SilcServer server,
     silc_idcache_list_free(list);
   }
 
-  local = FALSE;
-  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;
-       
-
        if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
          if (!silc_idcache_list_next(list, &id_cache))
            break;
@@ -496,8 +558,11 @@ void silc_server_update_clients_by_server(SilcServer server,
            continue;
        }
 
-       SILC_LOG_DEBUG(("Client (global) %s", 
+       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' */
@@ -515,7 +580,7 @@ void silc_server_update_clients_by_server(SilcServer server,
              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;
          }
@@ -535,6 +600,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. */
 
@@ -545,9 +705,12 @@ bool silc_server_channel_has_global(SilcChannelEntry channel)
 
   silc_hash_table_list(channel->user_list, &htl);
   while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
-    if (chl->client->router)
+    if (chl->client->router) {
+      silc_hash_table_list_reset(&htl);
       return TRUE;
+    }
   }
+  silc_hash_table_list_reset(&htl);
 
   return FALSE;
 }
@@ -562,9 +725,12 @@ bool silc_server_channel_has_local(SilcChannelEntry channel)
 
   silc_hash_table_list(channel->user_list, &htl);
   while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
-    if (!chl->client->router)
+    if (!chl->client->router) {
+      silc_hash_table_list_reset(&htl);
       return TRUE;
+    }
   }
+  silc_hash_table_list_reset(&htl);
 
   return FALSE;
 }
@@ -580,8 +746,44 @@ bool silc_server_client_on_channel(SilcClientEntry client,
   if (!client || !channel)
     return FALSE;
 
-  if (silc_hash_table_find(client->channels, channel, NULL, NULL))
-    return TRUE;
+  return silc_hash_table_find(client->channels, channel, NULL, NULL);
+}
+
+/* Checks string for bad characters and returns TRUE if they are found. */
+
+bool silc_server_name_bad_chars(const char *name, uint32 name_len)
+{
+  int i;
+
+  for (i = 0; i < name_len; i++) {
+    if (!isascii(name[i]))
+      return TRUE;
+    if (name[i] <= 32) return TRUE;
+    if (name[i] == ' ') return TRUE;
+    if (name[i] == '*') return TRUE;
+    if (name[i] == '?') return TRUE;
+    if (name[i] == ',') return TRUE;
+  }
 
   return FALSE;
 }
+
+/* Modifies the `name' if it includes bad characters and returns new
+   allocated name that does not include bad characters. */
+
+char *silc_server_name_modify_bad(const char *name, uint32 name_len)
+{
+  int i;
+  char *newname = strdup(name);
+
+  for (i = 0; i < name_len; i++) {
+    if (!isascii(newname[i])) newname[i] = '_';
+    if (newname[i] <= 32) newname[i] = '_';
+    if (newname[i] == ' ') newname[i] = '_';
+    if (newname[i] == '*') newname[i] = '_';
+    if (newname[i] == '?') newname[i] = '_';
+    if (newname[i] == ',') newname[i] = '_';
+  }
+
+  return newname;
+}