updates.
[silc.git] / apps / silcd / server.c
index 26b214fd3aef7adf1900caa2cd8f0204d25d7be6..86b0e6f57d23b86361624054d72dc1fc71efc058 100644 (file)
@@ -349,6 +349,110 @@ int silc_server_init(SilcServer server)
   return FALSE;
 }
 
+/* Fork server to background and set gid+uid to non-root.
+   Silcd will not run as root, so trying to set either user or group to
+   root will cause silcd to exit. */
+
+void silc_server_daemonise(SilcServer server)
+{
+  /* Are we executing silcd as root or a regular user? */
+  if (geteuid()==0) {
+    
+    struct passwd *pw;
+    struct group *gr;
+    char *user, *group;
+    
+    if (!server->config->identity || !server->config->identity->user || 
+       !server->config->identity->group) {
+      fprintf(stderr, "Error:"
+       "\tSILC server must not be run as root.  For the security of your\n"
+       "\tsystem it is strongly suggested that you run SILC under dedicated\n"
+       "\tuser account.  Modify the [Identity] configuration section to run\n"
+       "\tthe server as non-root user.\n");
+      exit(1);
+    }
+    
+    /* Get the values given for user and group in configuration file */
+    user=server->config->identity->user;
+    group=server->config->identity->group;
+    
+    /* Check whether the user/group information is text */ 
+    if (atoi(user)!=0 || atoi(group)!=0) {
+      SILC_LOG_DEBUG(("Invalid user and/or group information"));
+      SILC_LOG_DEBUG(("User and/or group given as number"));
+      fprintf(stderr, "Invalid user and/or group information\n");
+      fprintf(stderr, "Please assign them as names, not numbers\n");
+      exit(1);
+    }
+    
+    /* Catch the nasty incident of string "0" returning 0 from atoi */
+    if (strcmp("0", user)==0 || strcmp("0", group)==0) {
+      SILC_LOG_DEBUG(("User and/or group configured to 0. Unacceptable"));
+      fprintf(stderr, "User and/or group configured to 0. Exiting\n");
+      exit(1);
+    }
+    
+    pw=getpwnam(user);
+    gr=getgrnam(group);
+
+    if (!pw) {
+      fprintf(stderr, "No such user %s found\n", user);
+      exit(1);
+    }
+
+    if (!gr) {
+      fprintf(stderr, "No such group %s found\n", group);
+      exit(1);
+    }
+    
+    /* Check whether user and/or group is set to root. If yes, exit
+       immediately. Otherwise, setgid and setuid server to user.group */
+    if (gr->gr_gid==0 || pw->pw_uid==0) {
+      fprintf(stderr, "Error:"
+       "\tSILC server must not be run as root.  For the security of your\n"
+       "\tsystem it is strongly suggested that you run SILC under dedicated\n"
+       "\tuser account.  Modify the [Identity] configuration section to run\n"
+       "\tthe server as non-root user.\n");
+      exit(1);
+    } else {
+      /* Fork server to background, making it a daemon */
+      if (fork()) {
+        SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
+        SILC_LOG_DEBUG(("Forking SILC server to background"));
+        exit(0);
+      } 
+      setsid();
+      
+      SILC_LOG_DEBUG(("Changing to group %s", group));
+      if(setgid(gr->gr_gid)==0) {
+        SILC_LOG_DEBUG(("Setgid to %s", group));
+      } else {
+        SILC_LOG_DEBUG(("Setgid to %s failed", group));
+        fprintf(stderr, "Tried to setgid %s but no such group. Exiting\n",
+                group);
+        exit(1);
+      }
+      SILC_LOG_DEBUG(("Changing to user nobody"));
+      if(setuid(pw->pw_uid)==0) {
+        SILC_LOG_DEBUG(("Setuid to %s", user));
+      } else {
+        SILC_LOG_DEBUG(("Setuid to %s failed", user));
+        fprintf(stderr, "Tried to setuid %s but no such user. Exiting\n",
+                user);
+        exit(1);
+      }
+    }
+  } else {
+    /* Fork server to background, making it a daemon */
+    if (fork()) {
+      SILC_LOG_DEBUG(("Server started as user")); 
+      SILC_LOG_DEBUG(("Forking SILC server to background"));
+      exit(0);
+    }
+    setsid();
+  }
+}
+
 /* Stops the SILC server. This function is used to shutdown the server. 
    This is usually called after the scheduler has returned. After stopping 
    the server one should call silc_server_free. */
@@ -453,6 +557,7 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
   silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
   server->sockets[sock] = newsocket;
   newsocket->hostname = strdup(sconn->remote_host);
+  newsocket->ip = strdup(sconn->remote_host);
   newsocket->port = sconn->remote_port;
   sconn->sock = newsocket;
 
@@ -679,6 +784,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   SilcSocketConnection sock = ctx->sock;
   SilcServerEntry id_entry;
   SilcBuffer packet;
+  SilcServerHBContext hb_context;
   unsigned char *id_string;
 
   SILC_LOG_DEBUG(("Start"));
@@ -745,6 +851,23 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   server->router = id_entry;
   server->router->data.registered = TRUE;
 
+  /* Perform keepalive. The `hb_context' will be freed automatically
+     when finally calling the silc_socket_free function. XXX hardcoded 
+     timeout!! */
+  hb_context = silc_calloc(1, sizeof(*hb_context));
+  hb_context->server = server;
+  silc_socket_set_heartbeat(sock, 600, hb_context,
+                           silc_server_perform_heartbeat,
+                           server->timeout_queue);
+
+  /* If we are router then announce our possible servers. */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_announce_servers(server);
+
+  /* Announce our clients and channels to the router */
+  silc_server_announce_clients(server);
+  silc_server_announce_channels(server);
+
  out:
   /* Free the temporary connection data context */
   if (sconn)
@@ -938,6 +1061,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
     (SilcServerConnAuthInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
   SilcSocketConnection sock = ctx->sock;
+  SilcServerHBContext hb_context;
   void *id_entry = NULL;
 
   SILC_LOG_DEBUG(("Start"));
@@ -1056,6 +1180,15 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   /* Connection has been fully established now. Everything is ok. */
   SILC_LOG_DEBUG(("New connection authenticated"));
 
+  /* Perform keepalive. The `hb_context' will be freed automatically
+     when finally calling the silc_socket_free function. XXX hardcoded 
+     timeout!! */
+  hb_context = silc_calloc(1, sizeof(*hb_context));
+  hb_context->server = server;
+  silc_socket_set_heartbeat(sock, 600, hb_context,
+                           silc_server_perform_heartbeat,
+                           server->timeout_queue);
+
   silc_protocol_free(protocol);
   if (ctx->packet)
     silc_packet_context_free(ctx->packet);
@@ -1521,6 +1654,15 @@ void silc_server_packet_parse_type(SilcServer server,
     silc_server_new_id(server, sock, packet);
     break;
 
+  case SILC_PACKET_NEW_ID_LIST:
+    /*
+     * Received list of ID's. This packet is used by servers and routers
+     * to notify their primary router about clients and servers they have.
+     */
+    SILC_LOG_DEBUG(("New ID List packet"));
+    silc_server_new_id_list(server, sock, packet);
+    break;
+
   case SILC_PACKET_NEW_CLIENT:
     /*
      * Received new client packet. This includes client information that
@@ -1568,6 +1710,8 @@ void silc_server_packet_parse_type(SilcServer server,
      * existing server or router connects to us and distributes information
      * of all channels it has.
      */
+    SILC_LOG_DEBUG(("New Channel List packet"));
+    silc_server_new_channel_list(server, sock, packet);
     break;
 
   case SILC_PACKET_NEW_CHANNEL_USER_LIST:
@@ -1576,6 +1720,8 @@ void silc_server_packet_parse_type(SilcServer server,
      * when existing server or router connects to us and distributes 
      * information of all channel users it has.
      */
+    SILC_LOG_DEBUG(("New Channel User List packet"));
+    silc_server_new_channel_user_list(server, sock, packet);
     break;
 
   case SILC_PACKET_REPLACE_ID:
@@ -1613,6 +1759,13 @@ void silc_server_packet_parse_type(SilcServer server,
     silc_server_set_mode(server, sock, packet);
     break;
 
+  case SILC_PACKET_HEARTBEAT:
+    /*
+     * Received heartbeat.
+     */
+    SILC_LOG_DEBUG(("Heartbeat packet"));
+    break;
+
   default:
     SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
     break;
@@ -1717,6 +1870,18 @@ void silc_server_free_sock_user_data(SilcServer server,
                                   FALSE : TRUE, user_data->id, 
                                   SILC_ID_CLIENT_LEN, SILC_ID_CLIENT);
 
+      /* Then also free all client entries that this server owns as
+        they will become invalid now as well. */
+      silc_server_remove_clients_by_server(server, user_data);
+
+      /* If this was our primary router connection then we're lost to
+        the outside world. */
+      if (server->server_type == SILC_SERVER && server->router == user_data) {
+       server->id_entry->router = NULL;
+       server->router = NULL;
+       server->standalone = TRUE;
+      }
+
       /* Free the server entry */
       silc_idlist_del_data(user_data);
       silc_idlist_del_server(server->local_list, user_data);
@@ -1739,6 +1904,70 @@ void silc_server_free_sock_user_data(SilcServer server,
   sock->user_data = NULL;
 }
 
+/* This function is used to remove all client entries by the server `entry'.
+   This is called when the connection is lost to the server. In this case
+   we must invalidate all the client entries owned by the server `entry'. */
+
+int silc_server_remove_clients_by_server(SilcServer server, 
+                                        SilcServerEntry entry)
+{
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcClientEntry client = NULL;
+
+  if (silc_idcache_find_by_id(server->local_list->clients, 
+                             SILC_ID_CACHE_ANY, SILC_ID_CLIENT, &list)) {
+
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       client = (SilcClientEntry)id_cache->context;
+       
+       if (client->router != entry) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
+
+       /* Remove the client entry */
+       silc_server_remove_from_channels(server, NULL, client);
+       silc_idlist_del_client(server->local_list, client);
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+  
+  if (silc_idcache_find_by_id(server->global_list->clients, 
+                             SILC_ID_CACHE_ANY, SILC_ID_CLIENT, &list)) {
+
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       client = (SilcClientEntry)id_cache->context;
+       
+       if (client->router != entry) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
+
+       /* Remove the client entry */
+       silc_server_remove_from_channels(server, NULL, client);
+       silc_idlist_del_client(server->global_list, client);
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+  
+  return TRUE;
+}
+
 /* 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. */
 
@@ -1781,7 +2010,7 @@ void silc_server_remove_from_channels(SilcServer server,
 {
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
-  SilcBuffer chidp, clidp;
+  SilcBuffer clidp;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -1795,40 +2024,45 @@ void silc_server_remove_from_channels(SilcServer server,
   silc_list_start(client->channels);
   while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
     channel = chl->channel;
-    chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
 
-    /* Remove from list */
+    /* Remove channel from client's channel list */
     silc_list_del(client->channels, chl);
 
-    /* If this client is last one on the channel the channel
-       is removed all together. */
-    if (silc_list_count(channel->user_list) < 2) {
+    /* Remove channel if there is no users anymore */
+    if (server->server_type == SILC_ROUTER &&
+       silc_list_count(channel->user_list) < 2) {
+      if (!silc_idlist_del_channel(server->local_list, channel))
+       silc_idlist_del_channel(server->global_list, channel);
+      server->stat.my_channels--;
+      continue;
+    }
+
+    /* Remove client from channel's client list */
+    silc_list_del(channel->user_list, chl);
+    silc_free(chl);
+    server->stat.my_chanclients--;
 
-      /* However, if the channel has marked global users then the 
-        channel is not created locally, and this does not remove the
-        channel globally from SILC network, in this case we will
-        notify that this client has left the channel. */
+    /* If there is not at least one local user on the channel then we don't
+       need the channel entry anymore, we can remove it safely. */
+    if (server->server_type == SILC_SERVER &&
+       !silc_server_channel_has_local(channel)) {
+      /* Notify about leaving client if this channel has global users. */
       if (channel->global_users)
        silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
                                           SILC_NOTIFY_TYPE_SIGNOFF, 1,
                                           clidp->data, clidp->len);
-      
-      silc_idlist_del_channel(server->local_list, channel);
+
+      if (!silc_idlist_del_channel(server->local_list, channel))
+       silc_idlist_del_channel(server->global_list, channel);
       server->stat.my_channels--;
       continue;
     }
 
-    /* Remove from list */
-    silc_list_del(channel->user_list, chl);
-    silc_free(chl);
-    server->stat.my_chanclients--;
-
     /* Send notify to channel about client leaving SILC and thus
        the entire channel. */
     silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
                                       SILC_NOTIFY_TYPE_SIGNOFF, 1,
                                       clidp->data, clidp->len);
-    silc_buffer_free(chidp);
   }
 
   silc_buffer_free(clidp);
@@ -1863,25 +2097,20 @@ int silc_server_remove_from_one_channel(SilcServer server,
 
     ch = chl->channel;
 
-    /* Remove from list */
+    /* Remove channel from client's channel list */
     silc_list_del(client->channels, chl);
 
-    /* If this client is last one on the channel the channel
-       is removed all together. */
-    if (silc_list_count(channel->user_list) < 2) {
-      /* Notify about leaving client if this channel has global users. */
-      if (notify && channel->global_users)
-       silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
-                                          SILC_NOTIFY_TYPE_LEAVE, 1,
-                                          clidp->data, clidp->len);
-      
-      silc_idlist_del_channel(server->local_list, channel);
+    /* Remove channel if there is no users anymore */
+    if (server->server_type == SILC_ROUTER &&
+       silc_list_count(channel->user_list) < 2) {
+      if (!silc_idlist_del_channel(server->local_list, channel))
+       silc_idlist_del_channel(server->global_list, channel);
       silc_buffer_free(clidp);
       server->stat.my_channels--;
       return FALSE;
     }
 
-    /* Remove from list */
+    /* Remove client from channel's client list */
     silc_list_del(channel->user_list, chl);
     silc_free(chl);
     server->stat.my_chanclients--;
@@ -1892,11 +2121,18 @@ int silc_server_remove_from_one_channel(SilcServer server,
        !silc_server_channel_has_global(channel))
       channel->global_users = FALSE;
 
-    /* If tehre is not at least one local user on the channel then we don't
+    /* If there is not at least one local user on the channel then we don't
        need the channel entry anymore, we can remove it safely. */
     if (server->server_type == SILC_SERVER &&
        !silc_server_channel_has_local(channel)) {
-      silc_idlist_del_channel(server->local_list, channel);
+      /* Notify about leaving client if this channel has global users. */
+      if (notify && channel->global_users)
+       silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                          SILC_NOTIFY_TYPE_LEAVE, 1,
+                                          clidp->data, clidp->len);
+
+      if (!silc_idlist_del_channel(server->local_list, channel))
+       silc_idlist_del_channel(server->global_list, channel);
       silc_buffer_free(clidp);
       server->stat.my_channels--;
       return FALSE;
@@ -2004,6 +2240,51 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
   return entry;
 }
 
+/* Same as above but creates the channel with Channel ID `channel_id. */
+
+SilcChannelEntry 
+silc_server_create_new_channel_with_id(SilcServer server, 
+                                      char *cipher, 
+                                      char *channel_name,
+                                      SilcChannelID *channel_id)
+{
+  SilcChannelEntry entry;
+  SilcCipher key;
+
+  SILC_LOG_DEBUG(("Creating new channel"));
+
+  if (!cipher)
+    cipher = "twofish";
+
+  /* Allocate cipher */
+  silc_cipher_alloc(cipher, &key);
+
+  channel_name = strdup(channel_name);
+
+  /* Create the channel */
+  entry = silc_idlist_add_channel(server->local_list, channel_name, 
+                                 SILC_CHANNEL_MODE_NONE, channel_id, 
+                                 NULL, key);
+  if (!entry) {
+    silc_free(channel_name);
+    return NULL;
+  }
+
+  /* Now create the actual key material */
+  silc_server_create_channel_key(server, entry, 16);
+
+  /* Notify other routers about the new channel. We send the packet
+     to our primary route. */
+  if (server->standalone == FALSE) {
+    silc_server_send_new_channel(server, server->router->connection, TRUE, 
+                                channel_name, entry->id, SILC_ID_CHANNEL_LEN);
+  }
+
+  server->stat.my_channels++;
+
+  return entry;
+}
+
 /* Generates new channel key. This is used to create the initial channel key
    but also to re-generate new key for channel. If `key_len' is provided
    it is the bytes of the key length. */
@@ -2081,8 +2362,11 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
 
     channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
     if (!channel) {
-      SILC_LOG_ERROR(("Received key for non-existent channel"));
-      goto out;
+      channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+      if (!channel) {
+       SILC_LOG_ERROR(("Received key for non-existent channel"));
+       goto out;
+      }
     }
   }
 
@@ -2126,3 +2410,280 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
 
   return channel;
 }
+
+/* Heartbeat callback. This function is set as argument for the
+   silc_socket_set_heartbeat function. The library will call this function
+   at the set time interval. */
+
+void silc_server_perform_heartbeat(SilcSocketConnection sock,
+                                  void *hb_context)
+{
+  SilcServerHBContext hb = (SilcServerHBContext)hb_context;
+
+  SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname,
+                 sock->ip));
+
+  /* Send the heartbeat */
+  silc_server_send_heartbeat(hb->server, sock);
+}
+
+/* Returns assembled of all servers in the given ID list. The packet's
+   form is dictated by the New ID payload. */
+
+static void silc_server_announce_get_servers(SilcServer server,
+                                            SilcIDList id_list,
+                                            SilcBuffer *servers)
+{
+  SilcIDCacheList list;
+  SilcIDCacheEntry id_cache;
+  SilcServerEntry entry;
+  SilcBuffer idp;
+
+  /* Go through all clients in the list */
+  if (silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY, 
+                             SILC_ID_SERVER, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       entry = (SilcServerEntry)id_cache->context;
+
+       idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
+
+       *servers = silc_buffer_realloc(*servers, 
+                                      (*servers ? 
+                                       (*servers)->truelen + idp->len : 
+                                       idp->len));
+       silc_buffer_pull_tail(*servers, ((*servers)->end - (*servers)->data));
+       silc_buffer_put(*servers, idp->data, idp->len);
+       silc_buffer_pull(*servers, idp->len);
+       silc_buffer_free(idp);
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+
+    silc_idcache_list_free(list);
+  }
+}
+
+/* This function is used by router to announce existing servers to our
+   primary router when we've connected to it. */
+
+void silc_server_announce_servers(SilcServer server)
+{
+  SilcBuffer servers = NULL;
+
+  SILC_LOG_DEBUG(("Announcing servers"));
+
+  /* Get servers in local list */
+  silc_server_announce_get_servers(server, server->local_list, &servers);
+
+  /* Get servers in global list */
+  silc_server_announce_get_servers(server, server->global_list, &servers);
+
+  if (servers) {
+    silc_buffer_push(servers, servers->data - servers->head);
+    SILC_LOG_HEXDUMP(("servers"), servers->data, servers->len);
+
+    /* Send the packet */
+    silc_server_packet_send(server, server->router->connection,
+                           SILC_PACKET_NEW_ID_LIST, 0,
+                           servers->data, servers->len, TRUE);
+
+    silc_buffer_free(servers);
+  }
+}
+
+/* Returns assembled packet of all clients in the given ID list. The
+   packet's form is dictated by the New ID Payload. */
+
+static void silc_server_announce_get_clients(SilcServer server,
+                                            SilcIDList id_list,
+                                            SilcBuffer *clients)
+{
+  SilcIDCacheList list;
+  SilcIDCacheEntry id_cache;
+  SilcClientEntry client;
+  SilcBuffer idp;
+
+  /* Go through all clients in the list */
+  if (silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY, 
+                             SILC_ID_CLIENT, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       client = (SilcClientEntry)id_cache->context;
+
+       idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+       *clients = silc_buffer_realloc(*clients, 
+                                      (*clients ? 
+                                       (*clients)->truelen + idp->len : 
+                                       idp->len));
+       silc_buffer_pull_tail(*clients, ((*clients)->end - (*clients)->data));
+       silc_buffer_put(*clients, idp->data, idp->len);
+       silc_buffer_pull(*clients, idp->len);
+       silc_buffer_free(idp);
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+
+    silc_idcache_list_free(list);
+  }
+}
+
+/* This function is used to announce our existing clients to our router
+   when we've connected to it. */
+
+void silc_server_announce_clients(SilcServer server)
+{
+  SilcBuffer clients = NULL;
+
+  SILC_LOG_DEBUG(("Announcing clients"));
+
+  /* Get clients in local list */
+  silc_server_announce_get_clients(server, server->local_list,
+                                  &clients);
+
+  /* As router we announce our global list as well */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_announce_get_clients(server, server->global_list,
+                                    &clients);
+
+  if (clients) {
+    silc_buffer_push(clients, clients->data - clients->head);
+    SILC_LOG_HEXDUMP(("clients"), clients->data, clients->len);
+
+    /* Send the packet */
+    silc_server_packet_send(server, server->router->connection,
+                           SILC_PACKET_NEW_ID_LIST, 0,
+                           clients->data, clients->len, TRUE);
+
+    silc_buffer_free(clients);
+  }
+}
+
+/* Returns assembled packets for all channels and users on those channels
+   from the given ID List. The packets are in the form dictated by the
+   New Channel and New Channel User payloads. */
+
+static void silc_server_announce_get_channels(SilcServer server,
+                                             SilcIDList id_list,
+                                             SilcBuffer *channels,
+                                             SilcBuffer *channel_users)
+{
+  SilcIDCacheList list;
+  SilcIDCacheEntry id_cache;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  unsigned char *cid;
+  unsigned short name_len;
+  int len;
+
+  /* Go through all channels in the list */
+  if (silc_idcache_find_by_id(id_list->channels, SILC_ID_CACHE_ANY, 
+                             SILC_ID_CHANNEL, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       channel = (SilcChannelEntry)id_cache->context;
+       
+       cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+       name_len = strlen(channel->channel_name);
+
+       len = 4 + name_len + SILC_ID_CHANNEL_LEN;
+       *channels = 
+         silc_buffer_realloc(*channels, 
+                             (*channels ? (*channels)->truelen + len : len));
+       silc_buffer_pull_tail(*channels, 
+                             ((*channels)->end - (*channels)->data));
+       silc_buffer_format(*channels,
+                          SILC_STR_UI_SHORT(name_len),
+                          SILC_STR_UI_XNSTRING(channel->channel_name, 
+                                               name_len),
+                          SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
+                          SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
+                          SILC_STR_END);
+       silc_buffer_pull(*channels, len);
+
+       /* Now find all users on the channel */
+       silc_list_start(channel->user_list);
+       while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+         unsigned char *clid;
+
+         clid = silc_id_id2str(chl->client->id, SILC_ID_CLIENT);
+         
+         len = 4 + SILC_ID_CHANNEL_LEN + SILC_ID_CLIENT_LEN;
+         *channel_users = 
+           silc_buffer_realloc(*channel_users, 
+                               (*channel_users ? 
+                                (*channel_users)->truelen + len : len));
+         silc_buffer_pull_tail(*channel_users, 
+                               ((*channel_users)->end - 
+                                (*channel_users)->data));
+         silc_buffer_format(*channel_users,
+                            SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
+                            SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
+                            SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN),
+                            SILC_STR_UI_XNSTRING(clid, SILC_ID_CLIENT_LEN),
+                            SILC_STR_END);
+         silc_buffer_pull(*channel_users, len);
+         silc_free(clid);
+       }
+
+       silc_free(cid);
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+
+    silc_idcache_list_free(list);
+  }
+}
+
+/* This function is used to announce our existing channels to our router
+   when we've connected to it. This also announces the users on the
+   channels to the router. */
+
+void silc_server_announce_channels(SilcServer server)
+{
+  SilcBuffer channels = NULL, channel_users = NULL;
+
+  SILC_LOG_DEBUG(("Announcing channels and channel users"));
+
+  /* Get channels and channel users in local list */
+  silc_server_announce_get_channels(server, server->local_list,
+                                   &channels, &channel_users);
+
+  /* Get channels and channel users in global list */
+  silc_server_announce_get_channels(server, server->global_list,
+                                   &channels, &channel_users);
+
+  if (channels) {
+    silc_buffer_push(channels, channels->data - channels->head);
+    SILC_LOG_HEXDUMP(("channels"), channels->data, channels->len);
+
+    /* Send the packet */
+    silc_server_packet_send(server, server->router->connection,
+                           SILC_PACKET_NEW_CHANNEL_LIST, 0,
+                           channels->data, channels->len,
+                           FALSE);
+
+    silc_buffer_free(channels);
+  }
+
+  if (channel_users) {
+    silc_buffer_push(channel_users, channel_users->data - channel_users->head);
+    SILC_LOG_HEXDUMP(("channel users"), channel_users->data, 
+                    channel_users->len);
+
+    /* Send the packet */
+    silc_server_packet_send(server, server->router->connection,
+                           SILC_PACKET_NEW_CHANNEL_USER_LIST, 0,
+                           channel_users->data, channel_users->len,
+                           FALSE);
+
+    silc_buffer_free(channel_users);
+  }
+}