A LOT updates. Cannot separate. :)
[silc.git] / apps / silcd / server.c
index c17d2c7ffe30f1103c5846f40b61c12777af11d9..64b3319ba0b333718a7d6ea5307e332834068c4d 100644 (file)
  * servicing the SILC connections. This is also a SILC router as a router 
  * is also normal server.
  */
-/*
- * $Id$
- * $Log$
- * Revision 1.10  2000/07/17 11:47:30  priikone
- *     Added command lagging support. Added idle counting support.
- *
- * Revision 1.9  2000/07/14 06:15:47  priikone
- *     Moved all the generic packet sending, encryption, reception,
- *     decryption and processing functions to library as they were
- *     duplicated code. Now server uses the generic routine which is
- *     a lot cleaner. Channel message sending uses now also generic
- *     routines instead of duplicating the packet sending for every
- *     channel message sending function. Same was done for private
- *     message sending as well.
- *
- * Revision 1.8  2000/07/12 05:59:41  priikone
- *     Major rewrite of ID Cache system. Support added for the new
- *     ID cache system. Major rewrite of ID List stuff on server.  All
- *     SilcXXXList's are now called SilcXXXEntry's and they are pointers
- *     by default. A lot rewritten ID list functions.
- *
- * Revision 1.7  2000/07/10 05:43:00  priikone
- *     Removed command packet processing from server.c and added it to
- *     command.c.
- *     Implemented INFO command. Added support for testing that
- *     connections are registered before executing commands.
- *
- * Revision 1.6  2000/07/07 06:55:59  priikone
- *     Added SILC style public key support and made server to use
- *     it at all time.
- *
- * Revision 1.5  2000/07/06 13:18:07  priikone
- *     Check for NULL in client_on_channel.
- *
- * Revision 1.4  2000/07/05 06:14:01  priikone
- *     Global costemic changes.
- *
- * Revision 1.3  2000/07/04 08:13:53  priikone
- *     Changed message route discovery to use silc_server_get_route.
- *     Added silc_server_client_on_channel function.
- *
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Imported from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "serverincludes.h"
 #include "server_internal.h"
@@ -83,7 +38,7 @@ SILC_TASK_CALLBACK(silc_server_packet_process);
 SILC_TASK_CALLBACK(silc_server_packet_parse_real);
 SILC_TASK_CALLBACK(silc_server_timeout_remote);
 
-extern char server_version[];
+extern char *server_version;
 
 /* Allocates a new SILC server object. This has to be done before the server
    can be used. After allocation one must call silc_server_init to initialize
@@ -238,11 +193,14 @@ int silc_server_init(SilcServer server)
   server->local_list->servers = silc_idcache_alloc(0);
   server->local_list->channels = silc_idcache_alloc(0);
 
-  if (server->server_type == SILC_ROUTER) {
-    server->global_list->clients = silc_idcache_alloc(0);
-    server->global_list->servers = silc_idcache_alloc(0);
-    server->global_list->channels = silc_idcache_alloc(0);
-  }
+  /* XXX for now these are allocated for normal server as well as these
+     hold some global information that the server has fetched from its
+     router. For router these are used as they are supposed to be used
+     on router. The XXX can be remoevd later if this is the way we are
+     going to do this in the normal server as well. */
+  server->global_list->clients = silc_idcache_alloc(0);
+  server->global_list->servers = silc_idcache_alloc(0);
+  server->global_list->channels = silc_idcache_alloc(0);
 
   /* Allocate the entire socket list that is used in server. Eventually 
      all connections will have entry in this table (it is a table of 
@@ -315,9 +273,12 @@ int silc_server_init(SilcServer server)
     goto err1;
   }
 
+  /* Register protocols */
+  silc_server_protocols_register();
+
   /* Initialize the scheduler */
-  silc_schedule_init(server->io_queue, server->timeout_queue, 
-                    server->generic_queue, 
+  silc_schedule_init(&server->io_queue, &server->timeout_queue, 
+                    &server->generic_queue, 
                     SILC_SERVER_MAX_CONNECTIONS);
   
   /* Add the first task to the queue. This is task that is executed by
@@ -367,6 +328,8 @@ void silc_server_stop(SilcServer server)
   silc_schedule_stop();
   silc_schedule_uninit();
 
+  silc_server_protocols_unregister();
+
   SILC_LOG_DEBUG(("Server stopped"));
 }
 
@@ -1192,11 +1155,8 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
  
     /* Process the packet. This will call the parser that will then
        decrypt and parse the packet. */
-    if (!silc_packet_receive_process(sock, cipher, hmac,
-                                    silc_server_packet_parse, server)) {
-      silc_buffer_clear(sock->inbuf);
-      return;
-    }
+    silc_packet_receive_process(sock, cipher, hmac,
+                               silc_server_packet_parse, server);
   }
 }
 
@@ -1236,6 +1196,10 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
 
  out:
   silc_buffer_clear(sock->inbuf);
+  if (packet->src_id)
+    silc_free(packet->src_id);
+  if (packet->dst_id)
+    silc_free(packet->dst_id);
   silc_free(packet);
   silc_free(parse_ctx);
 }
@@ -1302,7 +1266,18 @@ void silc_server_packet_parse_type(SilcServer server,
     }
     break;
   case SILC_PACKET_FAILURE:
+    /*
+     * Failure received for something. For now we can have only
+     * one protocol for connection executing at once hence this
+     * failure message is for whatever protocol is executing currently.
+     */
     SILC_LOG_DEBUG(("Failure packet"));
+    if (sock->protocol) {
+      /* XXX Audit the failure type */
+      sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
+      sock->protocol->execute(server->timeout_queue, 0,
+                             sock->protocol, sock->sock, 0, 0);
+    }
     break;
   case SILC_PACKET_REJECT:
     SILC_LOG_DEBUG(("Reject packet"));
@@ -1497,12 +1472,47 @@ void silc_server_packet_parse_type(SilcServer server,
   case SILC_PACKET_NEW_SERVER:
     /*
      * Received new server packet. This includes Server ID and some other
-     * information that we may save. This is after server as connected to us.
+     * information that we may save. This is received after server has 
+     * connected to us.
      */
     SILC_LOG_DEBUG(("New Server packet"));
     silc_server_new_server(server, sock, packet);
     break;
 
+  case SILC_PACKET_NEW_CHANNEL:
+    break;
+
+  case SILC_PACKET_NEW_CHANNEL_USER:
+    break;
+
+  case SILC_PACKET_NEW_CHANNEL_LIST:
+    break;
+
+  case SILC_PACKET_NEW_CHANNEL_USER_LIST:
+    break;
+
+  case SILC_PACKET_REPLACE_ID:
+    /*
+     * Received replace ID packet. This sends the old ID that is to be
+     * replaced with the new one included into the packet. Client must not
+     * send this packet.
+     */
+    SILC_LOG_DEBUG(("Replace ID packet"));
+    silc_server_replace_id(server, sock, packet);
+    break;
+
+  case SILC_PACKET_REMOVE_ID:
+    break;
+
+  case SILC_PACKET_REMOVE_CHANNEL_USER:
+    /*
+     * Received packet to remove user from a channel. Routers notify other
+     * routers about a user leaving a channel.
+     */
+    SILC_LOG_DEBUG(("Remove Channel User packet"));
+    silc_server_remove_channel_user(server, sock, packet);
+    break;
+
   default:
     SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
     break;
@@ -1718,7 +1728,9 @@ void silc_server_packet_forward(SilcServer server,
 }
 
 /* Internal routine to actually create the channel packet and send it
-   to network. This is common function in channel message sending. */
+   to network. This is common function in channel message sending. If
+   `channel_message' is TRUE this encrypts the message as it is strictly
+   a channel message. If FALSE normal encryption process is used. */
 
 static void
 silc_server_packet_send_to_channel_real(SilcServer server,
@@ -1728,6 +1740,7 @@ silc_server_packet_send_to_channel_real(SilcServer server,
                                        SilcHmac hmac,
                                        unsigned char *data,
                                        unsigned int data_len,
+                                       int channel_message,
                                        int force_send)
 {
   packet->truelen = data_len + SILC_PACKET_HEADER_LEN + 
@@ -1747,9 +1760,12 @@ silc_server_packet_send_to_channel_real(SilcServer server,
      is encrypted with normal session key shared with the client. */
   silc_buffer_put(sock->outbuf, data, data_len);
   silc_packet_assemble(packet);
-  silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                     packet->src_id_len + packet->dst_id_len +
-                     packet->padlen);
+  if (channel_message)
+    silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN + 
+                       packet->src_id_len + packet->dst_id_len +
+                       packet->padlen);
+  else
+    silc_packet_encrypt(cipher, hmac, sock->outbuf, sock->outbuf->len);
     
   SILC_LOG_HEXDUMP(("Channel packet, len %d", sock->outbuf->len),
                   sock->outbuf->data, sock->outbuf->len);
@@ -1765,6 +1781,7 @@ silc_server_packet_send_to_channel_real(SilcServer server,
 
 void silc_server_packet_send_to_channel(SilcServer server,
                                        SilcChannelEntry channel,
+                                       SilcPacketType type,
                                        unsigned char *data,
                                        unsigned int data_len,
                                        int force_send)
@@ -1774,35 +1791,20 @@ void silc_server_packet_send_to_channel(SilcServer server,
   SilcPacketContext packetdata;
   SilcClientEntry client = NULL;
   SilcServerEntry *routed = NULL;
+  SilcChannelClientEntry chl;
   unsigned int routed_count = 0;
   SilcCipher cipher;
   SilcHmac hmac;
-  SilcBuffer payload;
-
-  SILC_LOG_DEBUG(("Sending packet to channel"));
-
-  /* Generate IV */
-  for (i = 0; i < 16; i++)
-    channel->iv[i] = silc_rng_get_byte(server->rng);
 
-  /* Encode the channel payload */
-  payload = silc_channel_encode_payload(0, "", data_len, data, 
-                                       16, channel->iv, server->rng);
-  if (!payload) {
-    SILC_LOG_ERROR(("Could not encode channel payload, message not sent"));
+  /* This doesn't send channel message packets */
+  if (type == SILC_PACKET_CHANNEL_MESSAGE)
     return;
-  }
   
-  /* Encrypt payload of the packet. This is encrypted with the 
-     channel key. */
-  channel->channel_key->cipher->encrypt(channel->channel_key->context,
-                                       payload->data, payload->data,
-                                       payload->len - 16, /* -IV_LEN */
-                                       channel->iv);
+  SILC_LOG_DEBUG(("Sending packet to channel"));
 
   /* Set the packet context pointers. */
   packetdata.flags = 0;
-  packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
+  packetdata.type = type;
   packetdata.src_id = silc_id_id2str(server->id, SILC_ID_SERVER);
   packetdata.src_id_len = SILC_ID_SERVER_LEN;
   packetdata.src_id_type = SILC_ID_SERVER;
@@ -1810,9 +1812,9 @@ void silc_server_packet_send_to_channel(SilcServer server,
   packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
   packetdata.dst_id_type = SILC_ID_CHANNEL;
   packetdata.rng = server->rng;
-  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
-                                         packetdata.src_id_len +
-                                         packetdata.dst_id_len));
+  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
+    packetdata.src_id_len + packetdata.dst_id_len;
+  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
 
   /* If there are global users in the channel we will send the message
      first to our router for further routing. */
@@ -1829,13 +1831,14 @@ void silc_server_packet_send_to_channel(SilcServer server,
     SILC_LOG_DEBUG(("Sending channel message to router for routing"));
 
     silc_server_packet_send_to_channel_real(server, sock, &packetdata,
-                                           cipher, hmac, payload->data,
-                                           payload->len, force_send);
+                                           cipher, hmac, data,
+                                           data_len, FALSE, force_send);
   }
 
   /* Send the message to clients on the channel's client list. */
-  for (i = 0; i < channel->user_list_count; i++) {
-    client = channel->user_list[i].client;
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+    client = chl->client;
 
     /* If client has router set it is not locally connected client and
        we will route the message to the router set in the client. */
@@ -1845,7 +1848,9 @@ void silc_server_packet_send_to_channel(SilcServer server,
       /* Check if we have sent the packet to this route already */
       for (k = 0; k < routed_count; k++)
        if (routed[k] == client->router)
-         continue;
+         break;
+      if (k < routed_count)
+       continue;
 
       /* Get data used in packet header encryption, keys and stuff. */
       sock = (SilcSocketConnection)client->router->connection;
@@ -1854,8 +1859,8 @@ void silc_server_packet_send_to_channel(SilcServer server,
 
       /* Send the packet */
       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
-                                             cipher, hmac, payload->data,
-                                             payload->len, force_send);
+                                             cipher, hmac, data,
+                                             data_len, FALSE, force_send);
 
       /* We want to make sure that the packet is routed to same router
         only once. Mark this route as sent route. */
@@ -1879,8 +1884,8 @@ void silc_server_packet_send_to_channel(SilcServer server,
       
       /* Send the packet */
       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
-                                             cipher, hmac, payload->data,
-                                             payload->len, force_send);
+                                             cipher, hmac, data,
+                                             data_len, FALSE, force_send);
     }
   }
 
@@ -1888,7 +1893,6 @@ void silc_server_packet_send_to_channel(SilcServer server,
     silc_free(routed);
   silc_free(packetdata.src_id);
   silc_free(packetdata.dst_id);
-  silc_buffer_free(payload);
 }
 
 /* This routine is explicitly used to relay messages to some channel.
@@ -1914,14 +1918,13 @@ void silc_server_packet_relay_to_channel(SilcServer server,
   SilcPacketContext packetdata;
   SilcClientEntry client = NULL;
   SilcServerEntry *routed = NULL;
+  SilcChannelClientEntry chl;
   unsigned int routed_count = 0;
   SilcCipher cipher;
   SilcHmac hmac;
 
   SILC_LOG_DEBUG(("Relaying packet to channel"));
 
-  SILC_LOG_HEXDUMP(("XXX %d", data_len), data, data_len);
-
   /* Set the packet context pointers. */
   packetdata.flags = 0;
   packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
@@ -1956,13 +1959,14 @@ void silc_server_packet_relay_to_channel(SilcServer server,
 
       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
                                              cipher, hmac, data,
-                                             data_len, force_send);
+                                             data_len, TRUE, force_send);
     }
   }
 
   /* Send the message to clients on the channel's client list. */
-  for (i = 0; i < channel->user_list_count; i++) {
-    client = channel->user_list[i].client;
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+    client = chl->client;
 
     if (client) {
 
@@ -1987,7 +1991,9 @@ void silc_server_packet_relay_to_channel(SilcServer server,
        /* Check if we have sent the packet to this route already */
        for (k = 0; k < routed_count; k++)
          if (routed[k] == client->router)
-           continue;
+           break;
+       if (k < routed_count)
+         continue;
        
        /* Get data used in packet header encryption, keys and stuff. */
        sock = (SilcSocketConnection)client->router->connection;
@@ -1997,7 +2003,7 @@ void silc_server_packet_relay_to_channel(SilcServer server,
        /* Send the packet */
        silc_server_packet_send_to_channel_real(server, sock, &packetdata,
                                                cipher, hmac, data,
-                                               data_len, force_send);
+                                               data_len, TRUE, force_send);
        
        /* We want to make sure that the packet is routed to same router
           only once. Mark this route as sent route. */
@@ -2022,7 +2028,7 @@ void silc_server_packet_relay_to_channel(SilcServer server,
       /* Send the packet */
       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
                                              cipher, hmac, data,
-                                             data_len, force_send);
+                                             data_len, TRUE, force_send);
     }
   }
 
@@ -2034,7 +2040,8 @@ void silc_server_packet_relay_to_channel(SilcServer server,
    on a particular channel.  This is used for example to distribute new
    channel key to all our locally connected clients on the channel. 
    The packets are always encrypted with the session key shared between
-   the client. */
+   the client, this means these are not _to the channel_ but _to the client_
+   on the channel. */
 
 void silc_server_packet_send_local_channel(SilcServer server,
                                           SilcChannelEntry channel,
@@ -2046,13 +2053,15 @@ void silc_server_packet_send_local_channel(SilcServer server,
 {
   int i;
   SilcClientEntry client;
+  SilcChannelClientEntry chl;
   SilcSocketConnection sock = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
   /* Send the message to clients on the channel's client list. */
-  for (i = 0; i < channel->user_list_count; i++) {
-    client = channel->user_list[i].client;
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+    client = chl->client;
 
     if (client) {
       sock = (SilcSocketConnection)client->connection;
@@ -2083,10 +2092,8 @@ void silc_server_packet_relay_command_reply(SilcServer server,
   SILC_LOG_DEBUG(("Start"));
 
   /* Source must be server or router */
-  /* XXX: actually it must be only router */
   if (packet->src_id_type != SILC_ID_SERVER &&
-      (sock->type != SILC_SOCKET_TYPE_SERVER ||
-       sock->type != SILC_SOCKET_TYPE_ROUTER))
+      sock->type != SILC_SOCKET_TYPE_ROUTER)
     goto out;
 
   /* Destination must be client */
@@ -2177,7 +2184,7 @@ void silc_server_disconnect_remote(SilcServer server,
 }
 
 /* Free's user_data pointer from socket connection object. As this 
-   pointer maybe anything we wil switch here to find the corrent
+   pointer maybe anything we wil switch here to find the correct
    data type and free it the way it needs to be free'd. */
 
 void silc_server_free_sock_user_data(SilcServer server, 
@@ -2240,6 +2247,12 @@ void silc_server_remove_from_channels(SilcServer server,
 {
   int i, k;
   SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcBuffer chidp, clidp;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
   /* Remove the client from all channels. The client is removed from
      the channels' user list. */
@@ -2248,46 +2261,70 @@ void silc_server_remove_from_channels(SilcServer server,
     if (!channel)
       continue;
 
+    chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+
     /* Remove from channel */
-    for (k = 0; k < channel->user_list_count; k++) {
-      if (channel->user_list[k].client == client) {
+    silc_list_start(channel->user_list);
+    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+      if (chl->client == client) {
 
        /* If this client is last one on the channel the channel
           is removed all together. */
-       if (channel->user_list_count == 1) {
+       if (silc_list_count(channel->user_list) == 1) {
+
+         /* 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 (channel->global_users)
+           silc_server_send_notify_to_channel(server, channel,
+                                              SILC_NOTIFY_TYPE_SIGNOFF, 1,
+                                              clidp->data, clidp->len);
+
          silc_idlist_del_channel(server->local_list, channel);
          break;
        }
 
-       channel->user_list[k].client = NULL;
-       channel->user_list[k].mode = SILC_CHANNEL_UMODE_NONE;
+       silc_list_del(channel->user_list, chl);
+       silc_free(chl);
 
        /* Send notify to channel about client leaving SILC and thus
           the entire channel. */
        silc_server_send_notify_to_channel(server, channel,
-                                          "Signoff: %s",
-                                          client->nickname);
+                                          SILC_NOTIFY_TYPE_SIGNOFF, 1,
+                                          clidp->data, clidp->len);
       }
     }
+
+    silc_buffer_free(chidp);
   }
 
   if (client->channel_count)
     silc_free(client->channel);
   client->channel = NULL;
+  silc_buffer_free(clidp);
 }
 
 /* Removes client from one channel. This is used for example when client
    calls LEAVE command to remove itself from the channel. Returns TRUE
    if channel still exists and FALSE if the channel is removed when
-   last client leaves the channel. */
+   last client leaves the channel. If `notify' is FALSE notify messages
+   are not sent. */
 
 int silc_server_remove_from_one_channel(SilcServer server, 
                                        SilcSocketConnection sock,
                                        SilcChannelEntry channel,
-                                       SilcClientEntry client)
+                                       SilcClientEntry client,
+                                       int notify)
 {
   int i, k;
   SilcChannelEntry ch;
+  SilcChannelClientEntry chl;
+  SilcBuffer clidp;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
   /* Remove the client from the channel. The client is removed from
      the channel's user list. */
@@ -2296,32 +2333,41 @@ int silc_server_remove_from_one_channel(SilcServer server,
     if (!ch || ch != channel)
       continue;
 
-    /* XXX */
     client->channel[i] = NULL;
 
     /* Remove from channel */
-    for (k = 0; k < channel->user_list_count; k++) {
-      if (channel->user_list[k].client == client) {
+    silc_list_start(channel->user_list);
+    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+      if (chl->client == client) {
        
        /* If this client is last one on the channel the channel
           is removed all together. */
-       if (channel->user_list_count == 1) {
+       if (silc_list_count(channel->user_list) == 1) {
+         /* Notify about leaving client if this channel has global users,
+            ie. the channel is not created locally. */
+         if (notify && channel->global_users)
+           silc_server_send_notify_to_channel(server, channel,
+                                              SILC_NOTIFY_TYPE_LEAVE, 1,
+                                              clidp->data, clidp->len);
+
          silc_idlist_del_channel(server->local_list, channel);
+         silc_buffer_free(clidp);
          return FALSE;
        }
        
-       channel->user_list[k].client = NULL;
-       channel->user_list[k].mode = SILC_CHANNEL_UMODE_NONE;
+       silc_list_del(channel->user_list, chl);
+       silc_free(chl);
 
        /* Send notify to channel about client leaving the channel */
-       silc_server_send_notify_to_channel(server, channel,
-                                          "%s has left channel %s",
-                                          client->nickname,
-                                          channel->channel_name);
+       if (notify)
+         silc_server_send_notify_to_channel(server, channel,
+                                            SILC_NOTIFY_TYPE_LEAVE, 1,
+                                            clidp->data, clidp->len);
       }
     }
   }
 
+  silc_buffer_free(clidp);
   return TRUE;
 }
 
@@ -2329,7 +2375,6 @@ int silc_server_remove_from_one_channel(SilcServer server,
    This works because we assure that the user list on the channel is
    always in up to date thus we can only check the channel list from 
    `client' which is faster than checking the user list from `channel'. */
-/* XXX This really is utility function and should be in eg. serverutil.c */
 
 int silc_server_client_on_channel(SilcClientEntry client,
                                  SilcChannelEntry channel)
@@ -2438,9 +2483,15 @@ void silc_server_private_message(SilcServer server,
     /* 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. */
+      router = (SilcServerEntry)dst_sock->user_data;
+      //      assert(client->router == server->id_entry);
+
       silc_server_private_message_send_internal(server, dst_sock,
-                                               client->router->send_key,
-                                               client->router->hmac,
+                                               router->send_key,
+                                               router->hmac,
                                                packet);
       goto out;
     }
@@ -2489,16 +2540,18 @@ void silc_server_private_message(SilcServer server,
   silc_buffer_free(buffer);
 }
 
-/* Process received channel message. */
+/* Process received channel message. The message can be originated from
+   client or server. */
 
 void silc_server_channel_message(SilcServer server,
                                 SilcSocketConnection sock,
                                 SilcPacketContext *packet)
 {
   SilcChannelEntry channel = NULL;
+  SilcChannelClientEntry chl;
   SilcClientEntry client = NULL;
   SilcChannelID *id = NULL;
-  SilcClientID *sender = NULL;
+  void *sender = NULL;
   SilcBuffer buffer = packet->buffer;
   int i;
 
@@ -2519,15 +2572,21 @@ void silc_server_channel_message(SilcServer server,
     goto out;
   }
 
-  /* See that this client is on the channel */
+  /* See that this client is on the channel. If the message is coming
+     from router we won't do the check as the message is from client that
+     we don't know about. Also, if the original sender is not client
+     (as it can be server as well) we don't do the check. */
   sender = silc_id_str2id(packet->src_id, packet->src_id_type);
-  for (i = 0; i < channel->user_list_count; i++) {
-    client = channel->user_list[i].client;
-    if (client && !SILC_ID_CLIENT_COMPARE(client->id, sender))
-      break;
+  if (sock->type != SILC_SOCKET_TYPE_ROUTER && 
+      packet->src_id_type == SILC_ID_CLIENT) {
+    silc_list_start(channel->user_list);
+    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+      if (chl->client && !SILC_ID_CLIENT_COMPARE(chl->client->id, sender))
+       break;
+    }
+    if (chl == SILC_LIST_END)
+      goto out;
   }
-  if (i >= channel->user_list_count)
-    goto out;
 
   /* Distribute the packet to our local clients. This will send the
      packet for further routing as well, if needed. */
@@ -2557,9 +2616,10 @@ void silc_server_channel_key(SilcServer server,
   SilcChannelKeyPayload payload = NULL;
   SilcChannelID *id = NULL;
   SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
   SilcClientEntry client;
-  unsigned char *key;
-  unsigned int key_len;
+  unsigned char *tmp;
+  unsigned int tmp_len;
   char *cipher;
   int i;
 
@@ -2568,14 +2628,15 @@ void silc_server_channel_key(SilcServer server,
     goto out;
 
   /* Decode channel key payload */
-  payload = silc_channel_key_parse_payload(buffer);
+  payload = silc_channel_key_payload_parse(buffer);
   if (!payload) {
     SILC_LOG_ERROR(("Bad channel key payload, dropped"));
     goto out;
   }
 
   /* Get channel ID */
-  id = silc_id_str2id(silc_channel_key_get_id(payload, NULL), SILC_ID_CHANNEL);
+  tmp = silc_channel_key_get_id(payload, &tmp_len);
+  id = silc_id_payload_parse_id(tmp, tmp_len);
   if (!id)
     goto out;
 
@@ -2587,22 +2648,26 @@ void silc_server_channel_key(SilcServer server,
   }
 
   /* Save the key for us as well */
-  key = silc_channel_key_get_key(payload, &key_len);
-  if (!key)
+  tmp = silc_channel_key_get_key(payload, &tmp_len);
+  if (!tmp)
     goto out;
   cipher = silc_channel_key_get_cipher(payload, NULL);;
   if (!cipher)
     goto out;
-  channel->key_len = key_len * 8;
-  channel->key = silc_calloc(key_len, sizeof(unsigned char));
-  memcpy(channel->key, key, key_len);
-  silc_cipher_alloc(cipher, &channel->channel_key);
+  if (!silc_cipher_alloc(cipher, &channel->channel_key))
+    goto out;
+
+  channel->key_len = tmp_len * 8;
+  channel->key = silc_calloc(tmp_len, sizeof(unsigned char));
+  memcpy(channel->key, tmp, tmp_len);
   channel->channel_key->cipher->set_key(channel->channel_key->context, 
-                                       key, key_len);
+                                       tmp, tmp_len);
 
   /* Distribute the key to all clients on the channel */
-  for (i = 0; i < channel->user_list_count; i++) {
-    client = channel->user_list[i].client;
+  /* XXX Some other sender should be used, I think this is not correct */
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+    client = chl->client;
 
     if (client)
       silc_server_packet_send_dest(server, client->connection,
@@ -2615,10 +2680,31 @@ void silc_server_channel_key(SilcServer server,
   if (id)
     silc_free(id);
   if (payload)
-    silc_channel_key_free_payload(payload);
+    silc_channel_key_payload_free(payload);
   silc_buffer_free(buffer);
 }
 
+/* Sends current motd to client */
+
+void silc_server_send_motd(SilcServer server,
+                          SilcSocketConnection sock)
+{
+  char *motd;
+  int motd_len;
+
+  if (server->config && server->config->motd && 
+      server->config->motd->motd_file) {
+
+    motd = silc_file_read(server->config->motd->motd_file, &motd_len);
+    if (!motd)
+      return;
+
+    silc_server_send_notify(server, sock, SILC_NOTIFY_TYPE_MOTD, 1,
+                           motd, motd_len);
+    silc_free(motd);
+  }
+}
+
 /* Sends error message. Error messages may or may not have any 
    implications. */
 
@@ -2638,22 +2724,25 @@ void silc_server_send_error(SilcServer server,
                          buf, strlen(buf), FALSE);
 }
 
-/* Sends notify message */
+/* Sends notify message. If format is TRUE the variable arguments are
+   formatted and the formatted string is sent as argument payload. If it is
+   FALSE then each argument is sent as separate argument and their format
+   in the argument list must be { argument data, argument length }. */
 
 void silc_server_send_notify(SilcServer server,
                             SilcSocketConnection sock,
-                            const char *fmt, ...)
+                            SilcNotifyType type,
+                            unsigned int argc, ...)
 {
   va_list ap;
-  unsigned char buf[4096];
+  SilcBuffer packet;
 
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
+  va_start(ap, argc);
 
+  packet = silc_notify_payload_encode(type, argc, ap);
   silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY, 0, 
-                         buf, strlen(buf), FALSE);
+                         packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
 }
 
 /* Sends notify message destined to specific entity. */
@@ -2662,41 +2751,177 @@ void silc_server_send_notify_dest(SilcServer server,
                                  SilcSocketConnection sock,
                                  void *dest_id,
                                  SilcIdType dest_id_type,
-                                 const char *fmt, ...)
+                                 SilcNotifyType type,
+                                 unsigned int argc, ...)
 {
   va_list ap;
-  unsigned char buf[4096];
+  SilcBuffer packet;
 
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
+  va_start(ap, argc);
 
+  packet = silc_notify_payload_encode(type, argc, ap);
   silc_server_packet_send_dest(server, sock, SILC_PACKET_NOTIFY, 0, 
                               dest_id, dest_id_type,
-                              buf, strlen(buf), FALSE);
+                              packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
 }
 
 /* Sends notify message to a channel. The notify message sent is 
-   distributed to all clients on the channel. Actually this is not real
-   notify message, instead it is message to channel sent by server. But
-   as server is sending it it will appear as notify type message on the
-   client side. */
+   distributed to all clients on the channel. */
 
 void silc_server_send_notify_to_channel(SilcServer server,
                                        SilcChannelEntry channel,
-                                       const char *fmt, ...)
+                                       SilcNotifyType type,
+                                       unsigned int argc, ...)
 {
   va_list ap;
-  unsigned char buf[4096];
+  SilcBuffer packet;
 
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
+  va_start(ap, argc);
 
-  silc_server_packet_send_to_channel(server, channel, buf, 
-                                    strlen(buf), FALSE);
+  packet = silc_notify_payload_encode(type, argc, ap);
+  silc_server_packet_send_to_channel(server, channel, 
+                                    SILC_PACKET_NOTIFY,
+                                    packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
+}
+
+/* Send notify message to all clients the client has joined. It is quaranteed
+   that the message is sent only once to a client (ie. if a client is joined
+   on two same channel it will receive only one notify message). Also, this
+   sends only to local clients (locally connected if we are server, and to
+   local servers if we are router). */
+
+void silc_server_send_notify_on_channels(SilcServer server,
+                                        SilcClientEntry client,
+                                        SilcNotifyType type,
+                                        unsigned int argc, ...)
+{
+  int i, j, k;
+  SilcSocketConnection sock = NULL;
+  SilcPacketContext packetdata;
+  SilcClientEntry c;
+  SilcClientEntry *sent_clients = NULL;
+  unsigned int sent_clients_count = 0;
+  SilcServerEntry *routed = NULL;
+  unsigned int routed_count = 0;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcCipher cipher;
+  SilcHmac hmac;
+  SilcBuffer packet;
+  unsigned char *data;
+  unsigned int data_len;
+  int force_send = FALSE;
+  va_list ap;
+
+  if (!client->channel_count)
+    return;
+
+  va_start(ap, argc);
+  packet = silc_notify_payload_encode(type, argc, ap);
+  data = packet->data;
+  data_len = packet->len;
+
+  /* Set the packet context pointers. */
+  packetdata.flags = 0;
+  packetdata.type = SILC_PACKET_NOTIFY;
+  packetdata.src_id = silc_id_id2str(server->id, SILC_ID_SERVER);
+  packetdata.src_id_len = SILC_ID_SERVER_LEN;
+  packetdata.src_id_type = SILC_ID_SERVER;
+  packetdata.rng = server->rng;
+
+  for (i = 0; i < client->channel_count; i++) {
+    channel = client->channel[i];
+
+    /* Send the message to clients on the channel's client list. */
+    silc_list_start(channel->user_list);
+    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+      c = chl->client;
+      
+      /* Check if we have sent the packet to this client already */
+      for (k = 0; k < sent_clients_count; k++)
+       if (sent_clients[k] == c)
+         break;
+      if (k < sent_clients_count)
+       continue;
+
+      /* If we are router and if this client has router set it is not
+        locally connected client and we will route the message to the
+        router set in the client. */
+      if (c && c->router && server->server_type == SILC_ROUTER) {
+       /* Check if we have sent the packet to this route already */
+       for (k = 0; k < routed_count; k++)
+         if (routed[k] == c->router)
+           break;
+       if (k < routed_count)
+         continue;
+       
+       /* Get data used in packet header encryption, keys and stuff. */
+       sock = (SilcSocketConnection)c->router->connection;
+       cipher = c->router->send_key;
+       hmac = c->router->hmac;
+       
+       packetdata.dst_id = silc_id_id2str(c->router->id, SILC_ID_SERVER);
+       packetdata.dst_id_len = SILC_ID_SERVER_LEN;
+       packetdata.dst_id_type = SILC_ID_SERVER;
+       packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
+         packetdata.src_id_len + packetdata.dst_id_len;
+       packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
+
+       /* Send the packet */
+       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                               cipher, hmac, data,
+                                               data_len, FALSE, force_send);
+       
+       silc_free(packetdata.dst_id);
+
+       /* We want to make sure that the packet is routed to same router
+          only once. Mark this route as sent route. */
+       k = routed_count;
+       routed = silc_realloc(routed, sizeof(*routed) * (k + 1));
+       routed[k] = c->router;
+       routed_count++;
+
+       continue;
+      }
+
+      /* Send to locally connected client */
+      if (c) {
+       
+       /* Get data used in packet header encryption, keys and stuff. */
+       sock = (SilcSocketConnection)c->connection;
+       cipher = c->send_key;
+       hmac = c->hmac;
+       
+       packetdata.dst_id = silc_id_id2str(c->id, SILC_ID_CLIENT);
+       packetdata.dst_id_len = SILC_ID_CLIENT_LEN;
+       packetdata.dst_id_type = SILC_ID_CLIENT;
+       packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
+         packetdata.src_id_len + packetdata.dst_id_len;
+       packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
+
+       /* Send the packet */
+       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                               cipher, hmac, data,
+                                               data_len, FALSE, force_send);
+
+       silc_free(packetdata.dst_id);
+
+       /* Make sure that we send the notify only once per client. */
+       sent_clients = silc_realloc(sent_clients, sizeof(*sent_clients) * 
+                                   (sent_clients_count + 1));
+       sent_clients[sent_clients_count] = c;
+       sent_clients_count++;
+      }
+    }
+  }
+
+  if (routed_count)
+    silc_free(routed);
+  if (sent_clients_count)
+    silc_free(sent_clients);
+  silc_free(packetdata.src_id);
 }
 
 /* Sends New ID Payload to remote end. The packet is used to distribute
@@ -2711,26 +2936,13 @@ void silc_server_send_new_id(SilcServer server,
                             void *id, SilcIdType id_type, 
                             unsigned int id_len)
 {
-  SilcBuffer packet;
-  unsigned char *id_string;
-
-  id_string = silc_id_id2str(id, id_type);
-  if (!id_string)
-    return;
-
-  packet = silc_buffer_alloc(2 + 2 + id_len);
-  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-  silc_buffer_format(packet,
-                    SILC_STR_UI_SHORT(id_type),
-                    SILC_STR_UI_SHORT(id_len),
-                    SILC_STR_UI_XNSTRING(id_string, id_len),
-                    SILC_STR_END);
+  SilcBuffer idp;
 
+  idp = silc_id_payload_encode(id, id_type);
   silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 
                          broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
-                         packet->data, packet->len, FALSE);
-  silc_free(id_string);
-  silc_buffer_free(packet);
+                         idp->data, idp->len, FALSE);
+  silc_buffer_free(idp);
 }
 
 /* Sends Replace ID payload to remote end. This is used to replace old
@@ -2780,6 +2992,120 @@ void silc_server_send_replace_id(SilcServer server,
   silc_buffer_free(packet);
 }
 
+/* This function is used to send Remove Channel User payload. This may sent
+   by server but is usually used only by router to notify other routers that
+   user has left a channel. Normal server sends this packet to its router
+   to notify that the router should not hold a record about this client
+   on a channel anymore. Router distributes it further to other routers. */
+
+void silc_server_send_remove_channel_user(SilcServer server,
+                                         SilcSocketConnection sock,
+                                         int broadcast,
+                                         void *client_id, void *channel_id)
+{
+  SilcBuffer packet;
+  unsigned char *clid, *chid;
+
+  clid = silc_id_id2str(client_id, SILC_ID_CLIENT);
+  if (!clid)
+    return;
+
+  chid = silc_id_id2str(channel_id, SILC_ID_CHANNEL);
+  if (!chid)
+    return;
+
+  packet = silc_buffer_alloc(2 + 2 + SILC_ID_CLIENT_LEN + SILC_ID_CHANNEL_LEN);
+  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+  silc_buffer_format(packet,
+                    SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN),
+                    SILC_STR_UI_XNSTRING(clid, SILC_ID_CLIENT_LEN),
+                    SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
+                    SILC_STR_UI_XNSTRING(chid, SILC_ID_CHANNEL_LEN),
+                    SILC_STR_END);
+
+  silc_server_packet_send(server, sock, SILC_PACKET_REMOVE_CHANNEL_USER, 
+                         broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
+                         packet->data, packet->len, FALSE);
+  silc_free(clid);
+  silc_free(chid);
+  silc_buffer_free(packet);
+}
+
+/* Received packet to replace a ID. This checks that the requested ID
+   exists and replaces it with the new one. */
+
+void silc_server_replace_id(SilcServer server,
+                           SilcSocketConnection sock,
+                           SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  unsigned char *old_id = NULL, *new_id = NULL;
+  SilcIdType old_id_type, new_id_type;
+  unsigned short old_id_len, new_id_len;
+  void *id = NULL, *id2 = NULL;
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      packet->src_id_type == SILC_ID_CLIENT)
+    return;
+
+  SILC_LOG_DEBUG(("Replacing ID"));
+
+  silc_buffer_unformat(buffer,
+                      SILC_STR_UI_SHORT(&old_id_type),
+                      SILC_STR_UI16_NSTRING_ALLOC(&old_id, &old_id_len),
+                      SILC_STR_UI_SHORT(&new_id_type),
+                      SILC_STR_UI16_NSTRING_ALLOC(&new_id, &new_id_len),
+                      SILC_STR_END);
+
+  if (old_id_type != new_id_type)
+    goto out;
+
+  if (old_id_len != silc_id_get_len(old_id_type) ||
+      new_id_len != silc_id_get_len(new_id_type))
+    goto out;
+
+  id = silc_id_str2id(old_id, old_id_type);
+  if (!id)
+    goto out;
+
+  id2 = silc_id_str2id(new_id, new_id_type);
+  if (!id2)
+    goto out;
+
+  /* Replace the old ID */
+  switch(old_id_type) {
+  case SILC_ID_CLIENT:
+    if (silc_idlist_replace_client_id(server->local_list, id, id2) == NULL)
+      if (server->server_type == SILC_ROUTER)
+       silc_idlist_replace_client_id(server->global_list, id, id2);
+    break;
+
+  case SILC_ID_SERVER:
+    if (silc_idlist_replace_server_id(server->local_list, id, id2) == NULL)
+      if (server->server_type == SILC_ROUTER)
+       silc_idlist_replace_server_id(server->global_list, id, id2);
+    break;
+
+  case SILC_ID_CHANNEL:
+    /* XXX Hmm... Basically this cannot occur. Channel ID's cannot be
+       re-generated. */
+    silc_free(id2);
+    break;
+
+  default:
+    silc_free(id2);
+    break;
+  }
+
+ out:
+  if (id)
+    silc_free(id);
+  if (old_id)
+    silc_free(old_id);
+  if (new_id)
+    silc_free(new_id);
+}
+
 /* Creates new channel. */
 
 SilcChannelEntry silc_server_new_channel(SilcServer server, 
@@ -2807,17 +3133,23 @@ SilcChannelEntry silc_server_new_channel(SilcServer server,
   silc_cipher_alloc(cipher, &key);
   key->cipher->set_key(key->context, channel_key, key_len);
 
+  channel_name = strdup(channel_name);
+
   /* Create the channel */
   silc_id_create_channel_id(router_id, server->rng, &channel_id);
   entry = silc_idlist_add_channel(server->local_list, channel_name, 
                                  SILC_CHANNEL_MODE_NONE, channel_id, 
                                  NULL, key);
-  if (!entry)
+  if (!entry) {
+    silc_free(channel_name);
     return NULL;
+  }
 
+#if 0
   /* Add to cache */
   silc_idcache_add(server->local_list->channels, channel_name,
                   SILC_ID_CHANNEL, channel_id, (void *)entry, TRUE);
+#endif
 
   entry->key = silc_calloc(key_len, sizeof(*entry->key));
   entry->key_len = key_len * 8;
@@ -2925,26 +3257,26 @@ SilcClientEntry silc_server_new_client(SilcServer server,
                          reply->data, reply->len, FALSE);
   silc_free(id_string);
   silc_buffer_free(reply);
-  
+
   /* Send some nice info to the client */
-  silc_server_send_notify(server, sock, 
-                         "Welcome to the SILC Network %s@%s",
-                         username, 
-                         sock->hostname ? sock->hostname : sock->ip);
-  silc_server_send_notify(server, sock,
-                         "Your host is %s, running version %s",
-                         server->config->server_info->server_name,
-                         server_version);
-  silc_server_send_notify(server, sock, 
-                         "Your connection is secured with %s cipher, "
-                         "key length %d bits",
-                         client->send_key->cipher->name,
-                         client->send_key->cipher->key_len);
-  silc_server_send_notify(server, sock, 
-                         "Your current nickname is %s",
-                         client->nickname);
-
-  /* XXX Send motd */
+  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                         ("Welcome to the SILC Network %s@%s",
+                          username, sock->hostname));
+  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                         ("Your host is %s, running version %s",
+                          server->config->server_info->server_name,
+                          server_version));
+  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                         ("Your connection is secured with %s cipher, "
+                          "key length %d bits",
+                          client->send_key->cipher->name,
+                          client->send_key->cipher->key_len));
+  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                         ("Your current nickname is %s",
+                          client->nickname));
+
+  /* Send motd */
+  silc_server_send_motd(server, sock);
 
   return client;
 }
@@ -2966,6 +3298,7 @@ SilcServerEntry silc_server_new_server(SilcServer server,
   SilcIDCacheEntry cache;
   SilcServerID *server_id;
   unsigned char *server_name, *id_string;
+  unsigned short id_len;
 
   SILC_LOG_DEBUG(("Creating new server"));
 
@@ -2985,12 +3318,18 @@ SilcServerEntry silc_server_new_server(SilcServer server,
 
   /* Parse the incoming packet */
   silc_buffer_unformat(buffer,
-                      SILC_STR_UI16_STRING_ALLOC(&id_string),
+                      SILC_STR_UI16_NSTRING_ALLOC(&id_string, &id_len),
                       SILC_STR_UI16_STRING_ALLOC(&server_name),
                       SILC_STR_END);
 
+  if (id_len > buffer->len) {
+    silc_free(id_string);
+    silc_free(server_name);
+    return NULL;
+  }
+
   /* Get Server ID */
-  server_id = silc_id_str2id(id_string, SILC_ID_SERVER);
+  server_id = silc_id_payload_parse_id(id_string, id_len);
   silc_free(id_string);
 
   /* Update client entry */
@@ -3023,34 +3362,52 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
                        SilcPacketContext *packet)
 {
   SilcBuffer buffer = packet->buffer;
+  SilcIDList id_list;
+  SilcServerEntry tmpserver, router;
+  SilcSocketConnection router_sock;
+  SilcIDPayload idp;
   SilcIdType id_type;
-  unsigned char *id_string;
-  void *id;
+  void *id, *tmpid;
 
   SILC_LOG_DEBUG(("Processing new ID"));
 
   if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      server->server_type == SILC_SERVER)
+      server->server_type == SILC_SERVER ||
+      packet->src_id_type != SILC_ID_SERVER)
     return;
 
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI_SHORT(&id_type),
-                      SILC_STR_UI16_STRING_ALLOC(&id_string),
-                      SILC_STR_END);
+  idp = silc_id_payload_parse(buffer);
+  if (!idp)
+    return;
+
+  id_type = silc_id_payload_get_type(idp);
 
   /* Normal server cannot have other normal server connections */
   if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER)
     goto out;
 
-  id = silc_id_str2id(id_string, id_type);
+  id = silc_id_payload_get_id(idp);
   if (!id)
     goto out;
 
-  /* XXX Do check whether the packet is coming outside the cell or
-     from someone inside the cell.  If outside use global lists otherwise
-     local lists. */
-  /* XXX If using local list set the idlist->connection to the sender's
-     socket connection as it is used in packet sending */
+  /* If the packet is originated from the one who sent it to us we know
+     that the ID belongs to our cell, unless the sender was router. */
+  tmpid = silc_id_str2id(packet->src_id, SILC_ID_SERVER);
+  tmpserver = (SilcServerEntry)sock->user_data;
+
+  if (!SILC_ID_SERVER_COMPARE(tmpid, tmpserver->id) &&
+      sock->type == SILC_SOCKET_TYPE_SERVER) {
+    id_list = server->local_list;
+    router_sock = sock;
+    router = sock->user_data;
+    /*    router = server->id_entry; */
+  } else {
+    id_list = server->global_list;
+    router_sock = (SilcSocketConnection)server->id_entry->router->connection;
+    router = server->id_entry->router;
+  }
+
+  silc_free(tmpid);
 
   switch(id_type) {
   case SILC_ID_CLIENT:
@@ -3059,9 +3416,9 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
 
       /* Add the client to our local list. We are router and we keep
         cell specific local database of all clients in the cell. */
-      idlist = silc_idlist_add_client(server->local_list, NULL, NULL, NULL,
-                                     id, sock->user_data, NULL, NULL, 
-                                     NULL, NULL, NULL, sock);
+      idlist = silc_idlist_add_client(id_list, NULL, NULL, NULL,
+                                     id, router, NULL, NULL, 
+                                     NULL, NULL, NULL, router_sock);
     }
     break;
 
@@ -3071,24 +3428,82 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
 
       /* Add the server to our local list. We are router and we keep
         cell specific local database of all servers in the cell. */
-      idlist = silc_idlist_add_server(server->local_list, NULL, 0,
-                                     id, server->id_entry, NULL, NULL, 
-                                     NULL, NULL, NULL, sock);
+      idlist = silc_idlist_add_server(id_list, NULL, 0,
+                                     id, router, NULL, NULL, 
+                                     NULL, NULL, NULL, router_sock);
     }
     break;
 
   case SILC_ID_CHANNEL:
     /* Add the channel to our local list. We are router and we keep
        cell specific local database of all channels in the cell. */
-    silc_idlist_add_channel(server->local_list, NULL, 0, id, 
-                           server->id_entry, NULL);
+    silc_idlist_add_channel(id_list, NULL, 0, id, router, NULL);
     break;
 
   default:
-    goto out;
     break;
   }
 
  out:
-  silc_free(id_string);
+  silc_id_payload_free(idp);
+}
+
+/* Received packet to remove a user from a channel. Routers notify other
+   routers that user has left a channel. Client must not send this packet. 
+   Normal server may send this packet but ignores if it receives one. */
+
+void silc_server_remove_channel_user(SilcServer server,
+                                    SilcSocketConnection sock,
+                                    SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  unsigned char *tmp1 = NULL, *tmp2 = NULL;
+  SilcClientID *client_id = NULL;
+  SilcChannelID *channel_id = NULL;
+  SilcChannelEntry channel;
+  SilcClientEntry client;
+
+  SILC_LOG_DEBUG(("Removing user from channel"));
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      server->server_type == SILC_SERVER)
+    return;
+
+  silc_buffer_unformat(buffer,
+                      SILC_STR_UI16_STRING_ALLOC(&tmp1),
+                      SILC_STR_UI16_STRING_ALLOC(&tmp2),
+                      SILC_STR_END);
+
+  if (!tmp1 || !tmp2)
+    goto out;
+
+  client_id = silc_id_str2id(tmp1, SILC_ID_CLIENT);
+  channel_id = silc_id_str2id(tmp2, SILC_ID_CHANNEL);
+  if (!client_id || !channel_id)
+    goto out;
+
+  /* XXX routers should check server->global_list as well */
+  /* Get channel entry */
+  channel = silc_idlist_find_channel_by_id(server->local_list, channel_id);
+  if (!channel)
+    goto out;
+  
+  /* XXX routers should check server->global_list as well */
+  /* Get client entry */
+  client = silc_idlist_find_client_by_id(server->local_list, client_id);
+  if (!client)
+    goto out;
+
+  /* Remove from channel */
+  silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
+
+ out:
+  if (tmp1)
+    silc_free(tmp1);
+  if (tmp2)
+    silc_free(tmp2);
+  if (client_id)
+    silc_free(client_id);
+  if (channel_id)
+    silc_free(channel_id);
 }