updates.
[silc.git] / apps / silcd / server.c
index 849fc10bbeaeb085cb4f008d4467ce1ff9b0aedc..64e70fd16d7597710460572b33e2a9f8f6a445fb 100644 (file)
@@ -113,6 +113,7 @@ int silc_server_init(SilcServer server)
   int *sock = NULL, sock_count = 0, i;
   SilcServerID *id;
   SilcServerEntry id_entry;
+  SilcIDListPurge purge;
 
   SILC_LOG_DEBUG(("Initializing server"));
   assert(server);
@@ -335,6 +336,27 @@ int silc_server_init(SilcServer server)
   if (server->config->servers)
     server->server_type = SILC_ROUTER;
 
+  /* Register the ID Cache purge task. This periodically purges the ID cache
+     and removes the expired cache entries. */
+
+  /* Clients local list */
+  purge = silc_calloc(1, sizeof(*purge));
+  purge->cache = server->local_list->clients;
+  purge->timeout_queue = server->timeout_queue;
+  silc_task_register(purge->timeout_queue, 0, 
+                    silc_idlist_purge,
+                    (void *)purge, 600, 0,
+                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+
+  /* Clients global list */
+  purge = silc_calloc(1, sizeof(*purge));
+  purge->cache = server->global_list->clients;
+  purge->timeout_queue = server->timeout_queue;
+  silc_task_register(purge->timeout_queue, 0, 
+                    silc_idlist_purge,
+                    (void *)purge, 300, 0,
+                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+
   SILC_LOG_DEBUG(("Server initialized"));
 
   /* We are done here, return succesfully */
@@ -689,10 +711,12 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
   SilcServerConnection sconn = (SilcServerConnection)ctx->context;
   SilcSocketConnection sock = NULL;
   SilcServerConnAuthInternalContext *proto_ctx;
+  SilcServerConfigSectionServerConnection *conn = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
     silc_ske_free_key_material(ctx->keymat);
@@ -746,28 +770,34 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
   proto_ctx->dest_id_type = ctx->dest_id_type;
   proto_ctx->dest_id = ctx->dest_id;
 
-  /* Resolve the authentication method used in this connection */
-  proto_ctx->auth_meth = SILC_AUTH_PASSWORD;
-  if (server->config->routers) {
-    SilcServerConfigSectionServerConnection *conn = NULL;
-
-    /* Check if we find a match from user configured connections */
-    conn = silc_server_config_find_router_conn(server->config,
-                                              sock->hostname,
-                                              sock->port);
-    if (conn) {
-      /* Match found. Use the configured authentication method */
-      proto_ctx->auth_meth = conn->auth_meth;
-      if (conn->auth_data) {
-       proto_ctx->auth_data = strdup(conn->auth_data);
-       proto_ctx->auth_data_len = strlen(conn->auth_data);
-      }
-    } else {
-      /* No match found. */
-      /* XXX */
+  /* Resolve the authentication method used in this connection. Check if 
+     we find a match from user configured connections */
+  conn = silc_server_config_find_router_conn(server->config,
+                                            sock->hostname,
+                                            sock->port);
+  if (conn) {
+    /* Match found. Use the configured authentication method */
+    proto_ctx->auth_meth = conn->auth_meth;
+    if (conn->auth_data) {
+      proto_ctx->auth_data = strdup(conn->auth_data);
+      proto_ctx->auth_data_len = strlen(conn->auth_data);
     }
   } else {
-    /* XXX */
+    SILC_LOG_ERROR(("Could not find connection data for %s (%s) on port",
+                   sock->hostname, sock->ip, sock->port));
+    silc_protocol_free(protocol);
+    if (ctx->packet)
+      silc_packet_context_free(ctx->packet);
+    if (ctx->ske)
+      silc_ske_free(ctx->ske);
+    if (ctx->dest_id)
+      silc_free(ctx->dest_id);
+    silc_free(ctx);
+    silc_task_unregister_by_callback(server->timeout_queue,
+                                    silc_server_failure_callback);
+    silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                                 "Key exchange failed");
+    return;
   }
 
   /* Free old protocol as it is finished now */
@@ -818,7 +848,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     if (ctx->dest_id)
       silc_free(ctx->dest_id);
@@ -937,13 +968,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
 
   /* Check max connections */
   if (sock > SILC_SERVER_MAX_CONNECTIONS) {
-    if (server->config->redirect) {
-      /* XXX Redirecting connection to somewhere else now?? */
-      /*silc_server_send_notify("Server is full, trying to redirect..."); */
-    } else {
-      SILC_LOG_ERROR(("Refusing connection, server is full"));
-      server->stat.conn_failures++;
-    }
+    SILC_LOG_ERROR(("Refusing connection, server is full"));
+    server->stat.conn_failures++;
     return;
   }
 
@@ -1028,7 +1054,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
     silc_ske_free_key_material(ctx->keymat);
@@ -1126,7 +1153,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
     if (ctx->packet)
@@ -1160,7 +1188,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
         and other information is created after we have received NEW_CLIENT
         packet from client. */
       client = silc_idlist_add_client(server->local_list, 
-                                     NULL, NULL, NULL, NULL, NULL, sock);
+                                     NULL, 0, NULL, NULL, NULL, NULL, sock);
       if (!client) {
        SILC_LOG_ERROR(("Could not add new client to cache"));
        silc_free(sock->user_data);
@@ -1291,7 +1319,7 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     server->stat.packets_sent++;
 
     if (sock->outbuf->data - sock->outbuf->head)
-      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+     silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
 
     ret = silc_server_packet_send_real(server, sock, TRUE);
 
@@ -1396,9 +1424,12 @@ static int silc_server_packet_decrypt_check(SilcPacketType packet_type,
 
      all other packets are special packets 
   */
-  if ((packet_type == SILC_PACKET_PRIVATE_MESSAGE &&
-       !(buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY)) ||
-      packet_type != SILC_PACKET_CHANNEL_MESSAGE || 
+
+  if (packet_type == SILC_PACKET_PRIVATE_MESSAGE &&
+      (buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
+    return FALSE;
+
+  if (packet_type != SILC_PACKET_CHANNEL_MESSAGE || 
       (packet_type == SILC_PACKET_CHANNEL_MESSAGE &&
        parse_ctx->sock->type == SILC_SOCKET_TYPE_ROUTER &&
        server->server_type == SILC_ROUTER))
@@ -1484,7 +1515,7 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
   silc_server_packet_parse_type(server, sock, packet);
 
  out:
-  silc_buffer_clear(sock->inbuf);
+  /*  silc_buffer_clear(sock->inbuf); */
   silc_packet_context_free(packet);
   silc_free(parse_ctx);
 }
@@ -1681,8 +1712,8 @@ void silc_server_packet_parse_type(SilcServer server,
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
 
-    if (sock->protocol && sock->protocol->protocol->type 
-       == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+    if (sock->protocol && sock->protocol->protocol &&
+       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
 
       SilcServerKEInternalContext *proto_ctx = 
        (SilcServerKEInternalContext *)sock->protocol->context;
@@ -1705,8 +1736,8 @@ void silc_server_packet_parse_type(SilcServer server,
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
 
-    if (sock->protocol && sock->protocol->protocol->type 
-       == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+    if (sock->protocol && sock->protocol->protocol &&
+       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
 
       SilcServerKEInternalContext *proto_ctx = 
        (SilcServerKEInternalContext *)sock->protocol->context;
@@ -1736,8 +1767,8 @@ void silc_server_packet_parse_type(SilcServer server,
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
 
-    if (sock->protocol && sock->protocol->protocol->type 
-       == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+    if (sock->protocol && sock->protocol->protocol &&
+       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
 
       SilcServerKEInternalContext *proto_ctx = 
        (SilcServerKEInternalContext *)sock->protocol->context;
@@ -1772,6 +1803,7 @@ void silc_server_packet_parse_type(SilcServer server,
     SILC_LOG_DEBUG(("Connection authentication request packet"));
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
+    silc_server_connection_auth_request(server, sock, packet);
     break;
 
     /*
@@ -1896,6 +1928,11 @@ void silc_server_create_connection(SilcServer server,
                     SILC_TASK_PRI_NORMAL);
 }
 
+SILC_TASK_CALLBACK(silc_server_close_connection_final)
+{
+  silc_socket_free((SilcSocketConnection)context);
+}
+
 /* Closes connection to socket connection */
 
 void silc_server_close_connection(SilcServer server,
@@ -1913,7 +1950,11 @@ void silc_server_close_connection(SilcServer server,
   /* Close the actual connection */
   silc_net_close_connection(sock->sock);
   server->sockets[sock->sock] = NULL;
-  silc_socket_free(sock);
+
+  silc_task_register(server->timeout_queue, 0, 
+                    silc_server_close_connection_final,
+                    (void *)sock, 0, 1, SILC_TASK_TIMEOUT, 
+                    SILC_TASK_PRI_NORMAL);
 }
 
 /* Sends disconnect message to remote connection and disconnects the 
@@ -1971,19 +2012,42 @@ SILC_TASK_CALLBACK(silc_server_free_client_data_timeout)
 
 void silc_server_free_client_data(SilcServer server, 
                                  SilcSocketConnection sock,
-                                 SilcClientEntry client, char *signoff)
+                                 SilcClientEntry client, 
+                                 int notify,
+                                 char *signoff)
 {
   FreeClientInternal i = silc_calloc(1, sizeof(*i));
 
-  /* Send REMOVE_ID packet to routers. */
-  if (!server->standalone && server->router)
+  /* If there is pending outgoing data for the client then purge it
+     to the network before removing the client entry. */
+  if (sock && SILC_IS_OUTBUF_PENDING(sock) && 
+      (SILC_IS_DISCONNECTED(sock) == FALSE)) {
+    server->stat.packets_sent++;
+
+    if (sock->outbuf->data - sock->outbuf->head)
+     silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+
+    silc_server_packet_send_real(server, sock, TRUE);
+
+    SILC_SET_CONNECTION_FOR_INPUT(sock->sock);
+    SILC_UNSET_OUTBUF_PENDING(sock);
+    silc_buffer_clear(sock->outbuf);
+  }
+
+  /* Send SIGNOFF notify to routers. */
+  if (notify && !server->standalone && server->router)
     silc_server_send_notify_signoff(server, server->router->connection,
                                    server->server_type == SILC_SERVER ?
                                    FALSE : TRUE, client->id, 
                                    SILC_ID_CLIENT_LEN, signoff);
 
   /* Remove client from all channels */
-  silc_server_remove_from_channels(server, sock, client, signoff);
+  if (notify)
+    silc_server_remove_from_channels(server, NULL, client, 
+                                    TRUE, signoff, TRUE);
+  else
+    silc_server_remove_from_channels(server, NULL, client, 
+                                    FALSE, NULL, FALSE);
 
   /* We will not delete the client entry right away. We will take it
      into history (for WHOWAS command) for 5 minutes */
@@ -2015,7 +2079,7 @@ void silc_server_free_sock_user_data(SilcServer server,
   case SILC_SOCKET_TYPE_CLIENT:
     {
       SilcClientEntry user_data = (SilcClientEntry)sock->user_data;
-      silc_server_free_client_data(server, sock, user_data, NULL);
+      silc_server_free_client_data(server, sock, user_data, TRUE, NULL);
       break;
     }
   case SILC_SOCKET_TYPE_SERVER:
@@ -2094,7 +2158,8 @@ int silc_server_remove_clients_by_server(SilcServer server,
        }
 
        /* Remove the client entry */
-       silc_server_remove_from_channels(server, NULL, client, NULL);
+       silc_server_remove_from_channels(server, NULL, client, TRUE, 
+                                        NULL, TRUE);
        silc_idlist_del_client(server->local_list, client);
 
        if (!silc_idcache_list_next(list, &id_cache))
@@ -2119,7 +2184,8 @@ int silc_server_remove_clients_by_server(SilcServer server,
        }
 
        /* Remove the client entry */
-       silc_server_remove_from_channels(server, NULL, client, NULL);
+       silc_server_remove_from_channels(server, NULL, client, TRUE,
+                                        NULL, TRUE);
        silc_idlist_del_client(server->global_list, client);
 
        if (!silc_idcache_list_next(list, &id_cache))
@@ -2171,7 +2237,9 @@ int silc_server_channel_has_local(SilcChannelEntry channel)
 void silc_server_remove_from_channels(SilcServer server, 
                                      SilcSocketConnection sock,
                                      SilcClientEntry client,
-                                     char *signoff_message)
+                                     int notify,
+                                     char *signoff_message,
+                                     int keygen)
 {
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
@@ -2196,9 +2264,26 @@ void silc_server_remove_from_channels(SilcServer server,
     /* Remove channel if there is no users anymore */
     if (server->server_type == SILC_ROUTER &&
        silc_list_count(channel->user_list) < 2) {
+      server->stat.my_channels--;
+
+      if (channel->founder_key) {
+       /* The founder auth data exists, do not remove the channel entry */
+       SilcChannelClientEntry chl2;
+
+       silc_free(channel->id);
+       channel->id = NULL;
+
+       silc_list_start(channel->user_list);
+       while ((chl2 = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+         silc_list_del(chl2->client->channels, chl2);
+         silc_list_del(channel->user_list, chl2);
+         silc_free(chl2);
+       }
+       continue;
+      }
+
       if (!silc_idlist_del_channel(server->local_list, channel))
        silc_idlist_del_channel(server->global_list, channel);
-      server->stat.my_channels--;
       continue;
     }
 
@@ -2218,7 +2303,7 @@ void silc_server_remove_from_channels(SilcServer server,
     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)
+      if (notify && channel->global_users)
        silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
                                           SILC_NOTIFY_TYPE_SIGNOFF, 
                                           signoff_message ? 2 : 1,
@@ -2226,20 +2311,49 @@ void silc_server_remove_from_channels(SilcServer server,
                                           signoff_message, signoff_message ?
                                           strlen(signoff_message) : 0);
 
+      server->stat.my_channels--;
+
+      if (channel->founder_key) {
+       /* The founder auth data exists, do not remove the channel entry */
+       SilcChannelClientEntry chl2;
+
+       silc_free(channel->id);
+       channel->id = NULL;
+
+       silc_list_start(channel->user_list);
+       while ((chl2 = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+         silc_list_del(chl2->client->channels, chl2);
+         silc_list_del(channel->user_list, chl2);
+         silc_free(chl2);
+       }
+       continue;
+      }
+
       if (!silc_idlist_del_channel(server->local_list, channel))
        silc_idlist_del_channel(server->global_list, channel);
-      server->stat.my_channels--;
       continue;
     }
 
     /* 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, 
-                                      signoff_message ? 2 : 1,
-                                      clidp->data, clidp->len,
-                                      signoff_message, signoff_message ?
-                                      strlen(signoff_message) : 0);
+    if (notify)
+      silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                        SILC_NOTIFY_TYPE_SIGNOFF, 
+                                        signoff_message ? 2 : 1,
+                                        clidp->data, clidp->len,
+                                        signoff_message, signoff_message ?
+                                        strlen(signoff_message) : 0);
+
+    if (keygen && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+      /* Re-generate channel key */
+      silc_server_create_channel_key(server, channel, 0);
+      
+      /* Send the channel key to the channel. The key of course is not sent
+        to the client who was removed f rom the channel. */
+      silc_server_send_channel_key(server, client->connection, channel, 
+                                  server->server_type == SILC_ROUTER ? 
+                                  FALSE : !server->standalone);
+    }
   }
 
   silc_buffer_free(clidp);
@@ -2308,10 +2422,27 @@ int silc_server_remove_from_one_channel(SilcServer server,
                                           SILC_NOTIFY_TYPE_LEAVE, 1,
                                           clidp->data, clidp->len);
 
+      server->stat.my_channels--;
+      silc_buffer_free(clidp);
+
+      if (channel->founder_key) {
+       /* The founder auth data exists, do not remove the channel entry */
+       SilcChannelClientEntry chl2;
+
+       silc_free(channel->id);
+       channel->id = NULL;
+
+       silc_list_start(channel->user_list);
+       while ((chl2 = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+         silc_list_del(chl2->client->channels, chl2);
+         silc_list_del(channel->user_list, chl2);
+         silc_free(chl2);
+       }
+       return FALSE;
+      }
+
       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;
     }
 
@@ -2414,16 +2545,19 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
     return NULL;
   }
 
+  entry->cipher = strdup(cipher);
+  entry->hmac_name = strdup(hmac);
+
   /* Now create the actual key material */
   silc_server_create_channel_key(server, entry, 
                                 silc_cipher_get_key_len(key) / 8);
 
   /* Notify other routers about the new channel. We send the packet
      to our primary route. */
-  if (broadcast && server->standalone == FALSE) {
+  if (broadcast && server->standalone == FALSE)
     silc_server_send_new_channel(server, server->router->connection, TRUE, 
-                                channel_name, entry->id, SILC_ID_CHANNEL_LEN);
-  }
+                                channel_name, entry->id, SILC_ID_CHANNEL_LEN,
+                                entry->mode);
 
   server->stat.my_channels++;
 
@@ -2478,10 +2612,10 @@ silc_server_create_new_channel_with_id(SilcServer server,
 
   /* Notify other routers about the new channel. We send the packet
      to our primary route. */
-  if (broadcast && server->standalone == FALSE) {
+  if (broadcast && server->standalone == FALSE)
     silc_server_send_new_channel(server, server->router->connection, TRUE, 
-                                channel_name, entry->id, SILC_ID_CHANNEL_LEN);
-  }
+                                channel_name, entry->id, SILC_ID_CHANNEL_LEN,
+                                entry->mode);
 
   server->stat.my_channels++;
 
@@ -2500,6 +2634,13 @@ void silc_server_create_channel_key(SilcServer server,
   unsigned char channel_key[32], hash[32];
   unsigned int len;
 
+  SILC_LOG_DEBUG(("Generating channel key"));
+
+  if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
+    SILC_LOG_DEBUG(("Channel has private keys, will not generate new key"));
+    return;
+  }
+
   if (!channel->channel_key)
     if (!silc_cipher_alloc("aes-256-cbc", &channel->channel_key))
       return;
@@ -2551,6 +2692,8 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
   unsigned int tmp_len;
   char *cipher;
 
+  SILC_LOG_DEBUG(("Start"));
+
   /* Decode channel key payload */
   payload = silc_channel_key_payload_parse(key_payload);
   if (!payload) {
@@ -2605,6 +2748,10 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
     goto out;
   }
 
+  if (channel->cipher)
+    silc_free(channel->cipher);
+  channel->cipher = strdup(cipher);
+
   /* Save the key */
   channel->key_len = tmp_len * 8;
   channel->key = silc_calloc(tmp_len, sizeof(unsigned char));
@@ -2791,24 +2938,61 @@ silc_server_announce_encode_join(unsigned int argc, ...)
   return silc_notify_payload_encode(SILC_NOTIFY_TYPE_JOIN, argc, ap);
 }
 
+/* Returns assembled packets for channel users of the `channel'. */
+
+void silc_server_announce_get_channel_users(SilcServer server,
+                                           SilcChannelEntry channel,
+                                           SilcBuffer *channel_users)
+{
+  SilcChannelClientEntry chl;
+  SilcBuffer chidp, clidp;
+  SilcBuffer tmp;
+  int len;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Now find all users on the channel */
+  chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+    clidp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
+    tmp = silc_server_announce_encode_join(2, clidp->data, clidp->len,
+                                          chidp->data, chidp->len);
+    len = tmp->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_put(*channel_users, tmp->data, tmp->len);
+    silc_buffer_pull(*channel_users, len);
+    silc_buffer_free(clidp);
+    silc_buffer_free(tmp);
+  }
+  silc_buffer_free(chidp);
+}
+
 /* 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)
+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;
-  SilcBuffer chidp;
   unsigned char *cid;
   unsigned short name_len;
   int len;
 
+  SILC_LOG_DEBUG(("Start"));
+
   /* Go through all channels in the list */
   if (silc_idcache_find_by_id(id_list->channels, SILC_ID_CACHE_ANY, 
                              SILC_ID_CHANNEL, &list)) {
@@ -2819,7 +3003,7 @@ static void silc_server_announce_get_channels(SilcServer server,
        cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
        name_len = strlen(channel->channel_name);
 
-       len = 4 + name_len + SILC_ID_CHANNEL_LEN;
+       len = 4 + name_len + SILC_ID_CHANNEL_LEN + 4;
        *channels = 
          silc_buffer_realloc(*channels, 
                              (*channels ? (*channels)->truelen + len : len));
@@ -2831,35 +3015,12 @@ static void silc_server_announce_get_channels(SilcServer server,
                                                name_len),
                           SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
                           SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
+                          SILC_STR_UI_INT(channel->mode),
                           SILC_STR_END);
        silc_buffer_pull(*channels, len);
 
-       /* Now find all users on the channel */
-       chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
-       silc_list_start(channel->user_list);
-       while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-         SilcBuffer clidp;
-         SilcBuffer tmp;
-
-         clidp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
-
-         tmp = silc_server_announce_encode_join(2, clidp->data, clidp->len,
-                                                chidp->data, chidp->len);
-         len = tmp->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_put(*channel_users, tmp->data, tmp->len);
-         silc_buffer_pull(*channel_users, len);
-         silc_buffer_free(clidp);
-         silc_buffer_free(tmp);
-       }
-       silc_buffer_free(chidp);
+       silc_server_announce_get_channel_users(server, channel,
+                                              channel_users);
 
        silc_free(cid);
 
@@ -3037,13 +3198,16 @@ void silc_server_save_users_on_channel(SilcServer server,
       /* We don't have that client anywhere, add it. The client is added
         to global list since server didn't have it in the lists so it must be 
         global. */
-      client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL, 
+      client = silc_idlist_add_client(server->global_list, NULL, 0, NULL, 
+                                     NULL, 
                                      silc_id_dup(client_id, SILC_ID_CLIENT), 
                                      sock->user_data, NULL);
       if (!client) {
        silc_free(client_id);
        continue;
       }
+
+      client->data.registered = TRUE;
     }
 
     silc_free(client_id);
@@ -3059,3 +3223,164 @@ void silc_server_save_users_on_channel(SilcServer server,
     }
   }
 }
+
+/* Lookups route to the client indicated by `id' client ID. The connection
+   object and internal data object is returned. Returns NULL if route
+   could not be found to the client. If the `client_id' is specified then
+   it is used and the `id_data' is ignored. */
+
+SilcSocketConnection silc_server_get_client_route(SilcServer server,
+                                                 unsigned char *id_data,
+                                                 unsigned int id_len,
+                                                 SilcClientID *client_id,
+                                                 SilcIDListData *idata)
+{
+  SilcClientID *id;
+  SilcClientEntry client;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Decode destination Client ID */
+  if (!client_id) {
+    id = silc_id_str2id(id_data, id_len, SILC_ID_CLIENT);
+    if (!id) {
+      SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
+      return NULL;
+    }
+  } else {
+    id = silc_id_dup(client_id, SILC_ID_CLIENT);
+  }
+
+  /* If the destination belongs to our server we don't have to route
+     the packet anywhere but to send it to the local destination. */
+  client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
+  if (client) {
+    silc_free(id);
+
+    if (client && client->data.registered == FALSE)
+      return NULL;
+
+    /* If we are router and the client has router then the client is in
+       our cell but not directly connected to us. */
+    if (server->server_type == SILC_ROUTER && client->router) {
+      /* We are of course in this case the client's router thus the real
+        "router" of the client is the server who owns the client. Thus
+        we will send the packet to that server. */
+      *idata = (SilcIDListData)client->router;
+      return client->router->connection;
+    }
+
+    /* Seems that client really is directly connected to us */
+    *idata = (SilcIDListData)client;
+    return client->connection;
+  }
+
+  /* Destination belongs to someone not in this server. If we are normal
+     server our action is to send the packet to our router. */
+  if (server->server_type == SILC_SERVER && !server->standalone) {
+    silc_free(id);
+    *idata = (SilcIDListData)server->router;
+    return server->router->connection;
+  }
+
+  /* We are router and we will perform route lookup for the destination 
+     and send the packet to fastest route. */
+  if (server->server_type == SILC_ROUTER && !server->standalone) {
+    /* Check first that the ID is valid */
+    client = silc_idlist_find_client_by_id(server->global_list, id, NULL);
+    if (client) {
+      SilcSocketConnection dst_sock;
+
+      dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
+
+      silc_free(id);
+      *idata = (SilcIDListData)dst_sock->user_data;
+      return dst_sock;
+    }
+  }
+
+  silc_free(id);
+  return NULL;
+}
+
+/* Encodes and returns channel list of channels the `client' has joined.
+   Secret channels are not put to the list. */
+
+SilcBuffer silc_server_get_client_channel_list(SilcServer server,
+                                              SilcClientEntry client)
+{
+  SilcBuffer buffer = NULL;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  unsigned char *cid;
+  unsigned short name_len;
+  int len;
+
+  silc_list_start(client->channels);
+  while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
+    channel = chl->channel;
+
+    if (channel->mode & SILC_CHANNEL_MODE_SECRET)
+      continue;
+
+    cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+    name_len = strlen(channel->channel_name);
+    
+    len = 4 + name_len + SILC_ID_CHANNEL_LEN + 4;
+    buffer = silc_buffer_realloc(buffer, 
+                                (buffer ? (buffer)->truelen + len : len));
+    silc_buffer_pull_tail(buffer, ((buffer)->end - (buffer)->data));
+    silc_buffer_format(buffer,
+                      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_UI_INT(chl->mode), /* Client's mode */
+                      SILC_STR_END);
+    silc_buffer_pull(buffer, len);
+    silc_free(cid);
+  }
+
+  if (buffer)
+    silc_buffer_push(buffer, buffer->data - buffer->head);
+
+  return buffer;
+}
+
+/* Finds client entry by Client ID and if it is not found then resolves
+   it using WHOIS command. */
+
+SilcClientEntry silc_server_get_client_resolve(SilcServer server,
+                                              SilcClientID *client_id)
+{
+  SilcClientEntry client;
+
+  client = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
+  if (!client) {
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, NULL);
+    if (!client && server->server_type == SILC_ROUTER)
+      return NULL;
+  }
+
+  if (!client && server->standalone)
+    return NULL;
+
+  if (!client || !client->nickname || !client->username) {
+    SilcBuffer buffer, idp;
+
+    idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
+                                           ++server->cmd_ident, 1,
+                                           3, idp->data, idp->len);
+    silc_server_packet_send(server, client ? client->router->connection :
+                           server->router->connection,
+                           SILC_PACKET_COMMAND, 0,
+                           buffer->data, buffer->len, FALSE);
+    silc_buffer_free(idp);
+    silc_buffer_free(buffer);
+  }
+
+  return client;
+}