updates.
[silc.git] / apps / silcd / packet_receive.c
index a5c2493012bb6476f403860d20a85fdf4a4d40c2..83e19b8ef7ef4d8949adcc009ca129b43ea7ce65 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 
 extern char *server_version;
 
-/* Received private message. This resolves the destination of the message 
-   and sends the packet. This is used by both server and router.  If the
-   destination is our locally connected client this sends the packet to
-   the client. This may also send the message for further routing if
-   the destination is not in our server (or router). */
+/* Check whereto relay the received notify packet that was destined
+   to a client. */
 
-void silc_server_private_message(SilcServer server,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet)
+static void 
+silc_server_packet_process_relay_notify(SilcServer server,
+                                       SilcSocketConnection sock,
+                                       SilcPacketContext *packet)
 {
   SilcClientID *id;
   SilcServerEntry router;
@@ -45,21 +43,18 @@ void silc_server_private_message(SilcServer server,
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (!packet->dst_id)
-    goto err;
-
   /* Decode destination Client ID */
-  id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
+  id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
   if (!id) {
     SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
-    goto err;
+    return;
   }
 
   /* If the destination belongs to our server we don't have to route
-     the message anywhere but to send it to the local destination. */
+     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) {
-    /* It exists, now deliver the message to the destination */
+    /* It exists, now deliver the packet to the destination */
     dst_sock = (SilcSocketConnection)client->connection;
 
     /* If we are router and the client has router then the client is in
@@ -68,21 +63,22 @@ void silc_server_private_message(SilcServer server,
       /* 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;
+      router = (SilcServerEntry)client->router;
       idata = (SilcIDListData)router;
-
-      silc_server_send_private_message(server, dst_sock,
-                                      idata->send_key,
-                                      idata->hmac,
-                                      packet);
+      silc_server_packet_relay_notify(server, router->connection,
+                                     idata->send_key,
+                                     idata->hmac,
+                                     packet);
+      silc_free(id);
       return;
     }
 
     /* Seems that client really is directly connected to us */
     idata = (SilcIDListData)client;
-    silc_server_send_private_message(server, dst_sock, 
-                                    idata->send_key,
-                                    idata->hmac, packet);
+    silc_server_packet_relay_notify(server, dst_sock, 
+                                   idata->send_key,
+                                   idata->hmac, packet);
+    silc_free(id);
     return;
   }
 
@@ -95,15 +91,16 @@ void silc_server_private_message(SilcServer server,
     if (router) {
       dst_sock = (SilcSocketConnection)router->connection;
       idata = (SilcIDListData)router;
-      silc_server_send_private_message(server, dst_sock, 
-                                      idata->send_key,
-                                      idata->hmac, packet);
+      silc_server_packet_relay_notify(server, dst_sock, 
+                                     idata->send_key,
+                                     idata->hmac, packet);
     }
+    silc_free(id);
     return;
   }
 
   /* We are router and we will perform route lookup for the destination 
-     and send the message to fastest route. */
+     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);
@@ -114,1390 +111,1754 @@ void silc_server_private_message(SilcServer server,
 
       /* Get fastest route and send packet. */
       if (router)
-       silc_server_send_private_message(server, dst_sock, 
-                                        idata->send_key,
-                                        idata->hmac, packet);
+       silc_server_packet_relay_notify(server, dst_sock, 
+                                       idata->send_key,
+                                       idata->hmac, packet);
+      silc_free(id);
       return;
     }
   }
-
- err:
-  silc_server_send_error(server, sock, 
-                        "No such nickname: Private message not sent");
 }
 
-/* Processes incoming command reply packet. The command reply packet may
-   be destined to one of our clients or it may directly for us. We will 
-   call the command reply routine after processing the packet. */
+/* Received notify packet. Server can receive notify packets from router. 
+   Server then relays the notify messages to clients if needed. */
 
-void silc_server_command_reply(SilcServer server,
-                              SilcSocketConnection sock,
-                              SilcPacketContext *packet)
+void silc_server_notify(SilcServer server,
+                       SilcSocketConnection sock,
+                       SilcPacketContext *packet)
 {
-  SilcBuffer buffer = packet->buffer;
-  SilcClientEntry client = NULL;
-  SilcSocketConnection dst_sock;
-  SilcIDListData idata;
-  SilcClientID *id = NULL;
+  SilcNotifyPayload payload;
+  SilcNotifyType type;
+  SilcArgumentPayload args;
+  SilcChannelID *channel_id;
+  SilcClientID *client_id, *client_id2;
+  SilcChannelEntry channel;
+  SilcClientEntry client;
+  SilcChannelClientEntry chl;
+  unsigned int mode;
+  unsigned char *tmp;
+  unsigned int tmp_len;
 
   SILC_LOG_DEBUG(("Start"));
 
-  /* Source must be server or router */
-  if (packet->src_id_type != SILC_ID_SERVER &&
-      sock->type != SILC_SOCKET_TYPE_ROUTER)
-    return;
-
-  if (packet->dst_id_type == SILC_ID_CHANNEL)
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      packet->src_id_type != SILC_ID_SERVER)
     return;
 
+  /* If the packet is destined directly to a client, then we don't
+     process the packet at all but just relay it to the client. */
   if (packet->dst_id_type == SILC_ID_CLIENT) {
-    /* Destination must be one of ours */
-    id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
-    client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
-    if (!client) {
-      SILC_LOG_ERROR(("Cannot process command reply to unknown client"));
-      silc_free(id);
-      return;
-    }
-  }
-
-  if (packet->dst_id_type == SILC_ID_SERVER) {
-    /* For now this must be for us */
-    if (SILC_ID_SERVER_COMPARE(packet->dst_id, server->id_string)) {
-      SILC_LOG_ERROR(("Cannot process command reply to unknown server"));
-      return;
-    }
+    silc_server_packet_process_relay_notify(server, sock, packet);
+    return;
   }
 
-  /* Execute command reply locally for the command */
-  silc_server_command_reply_process(server, sock, buffer);
-
-  if (packet->dst_id_type == SILC_ID_CLIENT && client && id) {
-    /* Relay the packet to the client */
-    
-    dst_sock = (SilcSocketConnection)client->connection;
-    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
-                    + packet->dst_id_len + packet->padlen);
-    
-    silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
-    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
-    
-    idata = (SilcIDListData)client;
-    
-    /* Encrypt packet */
-    silc_packet_encrypt(idata->send_key, idata->hmac, dst_sock->outbuf, 
-                       buffer->len);
-    
-    /* Send the packet */
-    silc_server_packet_send_real(server, dst_sock, TRUE);
-
-    silc_free(id);
+  /* If we are router and this packet is not already broadcast packet
+     we will broadcast it. The sending socket really cannot be router or
+     the router is buggy. If this packet is coming from router then it must
+     have the broadcast flag set already and we won't do anything. */
+  if (!server->standalone && server->server_type == SILC_ROUTER &&
+      sock->type == SILC_SOCKET_TYPE_SERVER &&
+      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
+    SILC_LOG_DEBUG(("Broadcasting received Notify packet"));
+    silc_server_packet_send(server, server->router->connection, packet->type,
+                           packet->flags | SILC_PACKET_FLAG_BROADCAST, 
+                           packet->buffer->data, packet->buffer->len, FALSE);
   }
-}
 
-/* Process received channel message. The message can be originated from
-   client or server. */
+  payload = silc_notify_payload_parse(packet->buffer);
+  if (!payload)
+    return;
 
-void silc_server_channel_message(SilcServer server,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet)
-{
-  SilcChannelEntry channel = NULL;
-  SilcChannelClientEntry chl;
-  SilcChannelID *id = NULL;
-  void *sender = NULL;
+  type = silc_notify_get_type(payload);
+  args = silc_notify_get_args(payload);
+  if (!args)
+    goto out;
 
-  SILC_LOG_DEBUG(("Processing channel message"));
+  switch(type) {
+  case SILC_NOTIFY_TYPE_JOIN:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
+    SILC_LOG_DEBUG(("JOIN notify"));
 
-  /* Sanity checks */
-  if (packet->dst_id_type != SILC_ID_CHANNEL) {
-    SILC_LOG_DEBUG(("Received bad message for channel, dropped"));
-    goto out;
-  }
+    /* Get Channel ID */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id)
+      goto out;
 
-  /* Find channel entry */
-  id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
-  channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
-  if (!channel) {
-    channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                            channel_id, NULL);
     if (!channel) {
-      SILC_LOG_DEBUG(("Could not find channel"));
-      goto out;
+      channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
     }
-  }
+    silc_free(channel_id);
 
-  /* 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);
-  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)
+    /* Get client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
       goto out;
-  }
 
-  /* Distribute the packet to our local clients. This will send the
-     packet for further routing as well, if needed. */
-  silc_server_packet_relay_to_channel(server, sock, channel, sender,
-                                     packet->src_id_type,
-                                     packet->buffer->data,
-                                     packet->buffer->len, FALSE);
+    /* Send to channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
 
- out:
-  if (sender)
-    silc_free(sender);
-  if (id)
-    silc_free(id);
-}
+    /* If the the client is not in local list we check global list (ie. the
+       channel will be global channel) and if it does not exist then create
+       entry for the 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) {
+       /* If router did not find the client the it is bogus */
+       if (server->server_type == SILC_ROUTER)
+         goto out;
+
+       client = 
+         silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
+                                silc_id_dup(client_id, SILC_ID_CLIENT), 
+                                sock->user_data, NULL);
+       if (!client) {
+         silc_free(client_id);
+         goto out;
+       }
+      }
+    }
 
-/* Received channel key packet. We distribute the key to all of our locally
-   connected clients on the channel. */
+    /* Do not add client to channel if it is there already */
+    if (silc_server_client_on_channel(client, channel))
+      break;
 
-void silc_server_channel_key(SilcServer server,
-                            SilcSocketConnection sock,
-                            SilcPacketContext *packet)
-{
-  SilcBuffer buffer = packet->buffer;
-  SilcChannelEntry channel;
+    if (server->server_type == SILC_SERVER && 
+       sock->type == SILC_SOCKET_TYPE_ROUTER)
+      /* The channel is global now */
+      channel->global_users = TRUE;
 
-  if (packet->src_id_type != SILC_ID_SERVER)
-    return;
+    /* JOIN the global client to the channel (local clients (if router 
+       created the channel) is joined in the pending JOIN command). */
+    chl = silc_calloc(1, sizeof(*chl));
+    chl->client = client;
+    chl->channel = channel;
+    silc_list_add(channel->user_list, chl);
+    silc_list_add(client->channels, chl);
+    silc_free(client_id);
 
-  /* Save the channel key */
-  channel = silc_server_save_channel_key(server, buffer, NULL);
-  if (!channel)
-    return;
+    break;
 
-  /* Distribute the key to everybody who is on the channel. If we are router
-     we will also send it to locally connected servers. */
-  silc_server_send_channel_key(server, sock, channel, FALSE);
-}
+  case SILC_NOTIFY_TYPE_LEAVE:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
+    SILC_LOG_DEBUG(("LEAVE notify"));
 
-/* Received packet to replace a ID. This checks that the requested ID
-   exists and replaces it with the new one. */
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               packet->dst_id_type);
+    if (!channel_id)
+      goto out;
 
-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;
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                            channel_id, NULL);
+    if (!channel) { 
+      channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
 
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      packet->src_id_type == SILC_ID_CLIENT)
-    return;
+    /* Get client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp) {
+      silc_free(channel_id);
+      goto out;
+    }
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id) {
+      silc_free(channel_id);
+      goto out;
+    }
 
-  SILC_LOG_DEBUG(("Replacing ID"));
+    /* Send to channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
 
-  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);
+    /* Get client entry */
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, NULL);
+    if (!client) {
+      client = silc_idlist_find_client_by_id(server->local_list, 
+                                            client_id, NULL);
+      if (!client) {
+       silc_free(client_id);
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+    silc_free(client_id);
 
-  if (old_id_type != new_id_type)
-    goto out;
+    /* Remove the user from channel */
+    silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
+    break;
 
-  if (old_id_len != silc_id_get_len(old_id_type) ||
-      new_id_len != silc_id_get_len(new_id_type))
-    goto out;
+  case SILC_NOTIFY_TYPE_SIGNOFF:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
+    SILC_LOG_DEBUG(("SIGNOFF notify"));
 
-  id = silc_id_str2id(old_id, old_id_type);
-  if (!id)
-    goto out;
+    /* Get client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
 
-  id2 = silc_id_str2id(new_id, new_id_type);
-  if (!id2)
-    goto out;
+    /* Get client entry */
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, NULL);
+    if (!client) {
+      client = silc_idlist_find_client_by_id(server->local_list, 
+                                            client_id, NULL);
+      if (!client) {
+       silc_free(client_id);
+       goto out;
+      }
+    }
+    silc_free(client_id);
 
-  /* If we are router and this packet is not already broadcast packet
-     we will broadcast it. The sending socket really cannot be router or
-     the router is buggy. If this packet is coming from router then it must
-     have the broadcast flag set already and we won't do anything. */
-  if (!server->standalone && server->server_type == SILC_ROUTER &&
-      sock->type == SILC_SOCKET_TYPE_SERVER &&
-      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
-    SILC_LOG_DEBUG(("Broadcasting received Replace ID packet"));
-    silc_server_packet_send(server, server->router->connection, packet->type,
-                           packet->flags | SILC_PACKET_FLAG_BROADCAST, 
-                           buffer->data, buffer->len, FALSE);
-  }
+    /* Get signoff message */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (tmp_len > 128)
+      tmp = NULL;
 
-  /* Replace the old ID */
-  switch(old_id_type) {
-  case SILC_ID_CLIENT:
+    /* Remove the client from all channels */
+    silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, TRUE);
+
+    /* Remove the client entry */
+    if (!silc_idlist_del_client(server->global_list, client))
+      silc_idlist_del_client(server->local_list, client);
+    break;
+
+  case SILC_NOTIFY_TYPE_TOPIC_SET:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
+
+    SILC_LOG_DEBUG(("TOPIC SET notify"));
+
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               packet->dst_id_type);
+    if (!channel_id)
+      goto out;
+
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+
+    /* Get the topic */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp) {
+      silc_free(channel_id);
+      goto out;
+    }
+
+    if (channel->topic)
+      silc_free(channel->topic);
+    channel->topic = silc_calloc(tmp_len, sizeof(*channel->topic));
+    memcpy(channel->topic, tmp, tmp_len);
+
+    /* Send the same notify to the channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
+    silc_free(channel_id);
+    break;
+
+  case SILC_NOTIFY_TYPE_NICK_CHANGE:
     {
-      SilcBuffer nidp, oidp;
-      SilcClientEntry client = NULL;
+      /* 
+       * Distribute the notify to local clients on the channel
+       */
+      unsigned char *id, *id2;
 
+      SILC_LOG_DEBUG(("NICK CHANGE notify"));
+      
+      /* Get old client ID */
+      id = silc_argument_get_arg_type(args, 1, &tmp_len);
+      if (!id)
+       goto out;
+      client_id = silc_id_payload_parse_id(id, tmp_len);
+      if (!client_id)
+       goto out;
+      
+      /* Get new client ID */
+      id2 = silc_argument_get_arg_type(args, 2, &tmp_len);
+      if (!id2)
+       goto out;
+      client_id2 = silc_id_payload_parse_id(id2, tmp_len);
+      if (!client_id2)
+       goto out;
+      
       SILC_LOG_DEBUG(("Old Client ID id(%s)", 
-                     silc_id_render(id, SILC_ID_CLIENT)));
+                     silc_id_render(client_id, SILC_ID_CLIENT)));
       SILC_LOG_DEBUG(("New Client ID id(%s)", 
-                     silc_id_render(id2, SILC_ID_CLIENT)));
+                     silc_id_render(client_id2, SILC_ID_CLIENT)));
+
+      /* Replace the Client ID */
+      client = silc_idlist_replace_client_id(server->global_list, client_id,
+                                            client_id2);
+      if (!client)
+       client = silc_idlist_replace_client_id(server->local_list, client_id, 
+                                              client_id2);
 
-      if ((client = silc_idlist_replace_client_id(server->local_list, 
-                                                 id, id2)) == NULL)
-       if (server->server_type == SILC_ROUTER)
-         client = silc_idlist_replace_client_id(server->global_list, id, id2);
-      
       if (client) {
-       oidp = silc_id_payload_encode(id, SILC_ID_CLIENT);
-       nidp = silc_id_payload_encode(id2, SILC_ID_CLIENT);
+       /* The nickname is not valid anymore, set it NULL. This causes that
+          the nickname will be queried if someone wants to know it. */
+       if (client->nickname)
+         silc_free(client->nickname);
+       client->nickname = NULL;
 
        /* Send the NICK_CHANGE notify type to local clients on the channels
           this client is joined to. */
-       silc_server_send_notify_on_channels(server, client, 
+       silc_server_send_notify_on_channels(server, NULL, client, 
                                            SILC_NOTIFY_TYPE_NICK_CHANGE, 2,
-                                           oidp->data, oidp->len, 
-                                           nidp->data, nidp->len);
-       
-       silc_buffer_free(nidp);
-       silc_buffer_free(oidp);
+                                           id, tmp_len, 
+                                           id2, tmp_len);
       }
+
+      silc_free(client_id);
+      if (!client)
+       silc_free(client_id2);
       break;
     }
 
-  case SILC_ID_SERVER:
-    SILC_LOG_DEBUG(("Old Server ID id(%s)", 
-                   silc_id_render(id, SILC_ID_CLIENT)));
-    SILC_LOG_DEBUG(("New Server ID id(%s)", 
-                   silc_id_render(id2, SILC_ID_CLIENT)));
-    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;
+  case SILC_NOTIFY_TYPE_CMODE_CHANGE:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
+    
+    SILC_LOG_DEBUG(("CMODE CHANGE notify"));
+      
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               packet->dst_id_type);
+    if (!channel_id)
+      goto out;
 
-  default:
-    silc_free(id2);
-    break;
-  }
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
 
- out:
-  if (id)
-    silc_free(id);
-  if (old_id)
-    silc_free(old_id);
-  if (new_id)
-    silc_free(new_id);
-}
+    /* Send the same notify to the channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
 
+    /* Get the mode */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp) {
+      silc_free(channel_id);
+      goto out;
+    }
 
-/* Received New Client packet and processes it.  Creates Client ID for the
-   client. Client becomes registered after calling this functions. */
+    SILC_GET32_MSB(mode, tmp);
 
-SilcClientEntry silc_server_new_client(SilcServer server,
-                                      SilcSocketConnection sock,
-                                      SilcPacketContext *packet)
-{
-  SilcBuffer buffer = packet->buffer;
-  SilcClientEntry client;
-  SilcIDCacheEntry cache;
-  SilcClientID *client_id;
-  SilcBuffer reply;
-  SilcIDListData idata;
-  char *username = NULL, *realname = NULL, *id_string;
+    /* Change mode */
+    channel->mode = mode;
+    silc_free(channel_id);
+    break;
 
-  SILC_LOG_DEBUG(("Creating new client"));
+  case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
 
-  if (sock->type != SILC_SOCKET_TYPE_CLIENT)
-    return NULL;
+    SILC_LOG_DEBUG(("CUMODE CHANGE notify"));
 
-  /* Take client entry */
-  client = (SilcClientEntry)sock->user_data;
-  idata = (SilcIDListData)client;
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               packet->dst_id_type);
+    if (!channel_id)
+      goto out;
 
-  /* Fetch the old client cache entry so that we can update it. */
-  if (!silc_idcache_find_by_context(server->local_list->clients,
-                                   sock->user_data, &cache)) {
-    SILC_LOG_ERROR(("Lost client's cache entry - bad thing"));
-    return NULL;
-  }
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
 
-  /* Parse incoming packet */
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI16_STRING_ALLOC(&username),
-                      SILC_STR_UI16_STRING_ALLOC(&realname),
-                      SILC_STR_END);
+    /* Get the mode */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp) {
+      silc_free(channel_id);
+      goto out;
+    }
+      
+    SILC_GET32_MSB(mode, tmp);
 
-  /* Create Client ID */
-  silc_id_create_client_id(server->id, server->rng, server->md5hash,
-                          username, &client_id);
+    /* Get target client */
+    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+    if (!tmp)
+      goto out;
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+    
+    /* Get client entry */
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, NULL);
+    if (!client) {
+      client = silc_idlist_find_client_by_id(server->local_list, 
+                                            client_id, NULL);
+      if (!client) {
+       silc_free(client_id);
+       goto out;
+      }
+    }
+    silc_free(client_id);
 
-  /* Update client entry */
-  idata->registered = TRUE;
-  client->nickname = strdup(username);
-  client->username = username;
-  client->userinfo = realname;
-  client->id = client_id;
+    /* Get entry to the channel user list */
+    silc_list_start(channel->user_list);
+    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
+      if (chl->client == client) {
+       /* Change the mode */
+       chl->mode = mode;
+       break;
+      }
 
-  /* Update the cache entry */
-  cache->id = (void *)client_id;
-  cache->type = SILC_ID_CLIENT;
-  cache->data = username;
-  silc_idcache_sort_by_data(server->local_list->clients);
+    /* Send the same notify to the channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
+    silc_free(channel_id);
+    break;
 
-  /* Notify our router about new client on the SILC network */
-  if (!server->standalone)
-    silc_server_send_new_id(server, (SilcSocketConnection) 
-                           server->router->connection, 
-                           server->server_type == SILC_ROUTER ? TRUE : FALSE,
-                           client->id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
-  
-  /* Send the new client ID to the client. */
-  id_string = silc_id_id2str(client->id, SILC_ID_CLIENT);
-  reply = silc_buffer_alloc(2 + 2 + SILC_ID_CLIENT_LEN);
-  silc_buffer_pull_tail(reply, SILC_BUFFER_END(reply));
-  silc_buffer_format(reply,
-                    SILC_STR_UI_SHORT(SILC_ID_CLIENT),
-                    SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN),
-                    SILC_STR_UI_XNSTRING(id_string, SILC_ID_CLIENT_LEN),
-                    SILC_STR_END);
-  silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 0, 
-                         reply->data, reply->len, FALSE);
-  silc_free(id_string);
-  silc_buffer_free(reply);
+  case SILC_NOTIFY_TYPE_INVITE:
+    SILC_LOG_DEBUG(("INVITE notify (not-impl XXX)"));
+    break;
 
-  /* Send some nice info to the client */
-  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));
-  if (server->server_type == SILC_ROUTER) {
-    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                           ("There are %d clients on %d servers in SILC "
-                            "Network", server->stat.clients,
-                            server->stat.servers + 1));
-    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                           ("There are %d clients on %d server in our cell",
-                            server->stat.cell_clients,
-                            server->stat.cell_servers));
-    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                           ("I have %d clients, %d channels, %d servers and "
-                            "%d routers",
-                            server->stat.my_clients, 
-                            server->stat.my_channels,
-                            server->stat.my_servers,
-                            server->stat.my_routers));
-    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                           ("%d server operators and %d router operators "
-                            "online",
-                            server->stat.my_server_ops,
-                            server->stat.my_router_ops));
-  } else {
-    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                           ("I have %d clients and %d channels formed",
-                            server->stat.my_clients,
-                            server->stat.my_channels));
-    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                           ("%d operators online",
-                            server->stat.my_server_ops));
-  }
-  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                         ("Your connection is secured with %s cipher, "
-                          "key length %d bits",
-                          idata->send_key->cipher->name,
-                          idata->send_key->cipher->key_len));
-  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                         ("Your current nickname is %s",
-                          client->nickname));
+  case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
+    SILC_LOG_DEBUG(("CHANNEL CHANGE notify (not-impl XXX)"));
+    break;
 
-  /* Send motd */
-  silc_server_send_motd(server, sock);
+  case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
+    SILC_LOG_DEBUG(("SERVER SIGNOFF notify (not-impl XXX)"));
+    break;
 
-  return client;
-}
+  case SILC_NOTIFY_TYPE_KICKED:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
+    
+    SILC_LOG_DEBUG(("KICKED notify"));
+      
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               packet->dst_id_type);
+    if (!channel_id)
+      goto out;
 
-/* Create new server. This processes received New Server packet and
-   saves the received Server ID. The server is our locally connected
-   server thus we save all the information and save it to local list. 
-   This funtion can be used by both normal server and router server.
-   If normal server uses this it means that its router has connected
-   to the server. If router uses this it means that one of the cell's
-   servers is connected to the router. */
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+    silc_free(channel_id);
 
-SilcServerEntry silc_server_new_server(SilcServer server,
-                                      SilcSocketConnection sock,
-                                      SilcPacketContext *packet)
-{
-  SilcBuffer buffer = packet->buffer;
-  SilcServerEntry new_server;
-  SilcIDCacheEntry cache;
-  SilcServerID *server_id;
-  SilcIDListData idata;
-  unsigned char *server_name, *id_string;
-  unsigned short id_len;
+    /* Get client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
 
-  SILC_LOG_DEBUG(("Creating new server"));
+    /* Send to channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
 
-  if (sock->type != SILC_SOCKET_TYPE_SERVER &&
-      sock->type != SILC_SOCKET_TYPE_ROUTER)
-    return NULL;
+    /* If the the client is not in local list we check global list */
+    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) {
+       silc_free(client_id);
+       goto out;
+      }
+    }
 
-  /* Take server entry */
-  new_server = (SilcServerEntry)sock->user_data;
-  idata = (SilcIDListData)new_server;
+    /* Remove the client from channel */
+    silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
 
-  /* Fetch the old server cache entry so that we can update it. */
-  if (!silc_idcache_find_by_context(server->local_list->servers,
-                                   sock->user_data, &cache)) {
-    SILC_LOG_ERROR(("Lost server's cache entry - bad thing"));
-    return NULL;
-  }
+    break;
 
-  /* Parse the incoming packet */
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI16_NSTRING_ALLOC(&id_string, &id_len),
-                      SILC_STR_UI16_STRING_ALLOC(&server_name),
-                      SILC_STR_END);
+  case SILC_NOTIFY_TYPE_KILLED:
+    /* 
+     * Distribute the notify to local clients on channels
+     */
+    
+    SILC_LOG_DEBUG(("KILLED notify"));
+      
+    break;
 
-  if (id_len > buffer->len) {
-    silc_free(id_string);
-    silc_free(server_name);
-    return NULL;
+    /* Ignore rest of the notify types for now */
+  case SILC_NOTIFY_TYPE_NONE:
+  case SILC_NOTIFY_TYPE_MOTD:
+    break;
+  default:
+    break;
   }
 
-  /* Get Server ID */
-  server_id = silc_id_str2id(id_string, SILC_ID_SERVER);
-  silc_free(id_string);
+ out:
+  silc_notify_payload_free(payload);
+}
 
-  /* Update client entry */
-  idata->registered = TRUE;
-  new_server->server_name = server_name;
-  new_server->id = server_id;
+void silc_server_notify_list(SilcServer server,
+                            SilcSocketConnection sock,
+                            SilcPacketContext *packet)
+{
+  SilcPacketContext *new;
+  SilcBuffer buffer;
+  unsigned short len;
 
-  /* Update the cache entry */
-  cache->id = (void *)server_id;
-  cache->type = SILC_ID_SERVER;
-  cache->data = server_name;
-  silc_idcache_sort_by_data(server->local_list->servers);
+  SILC_LOG_DEBUG(("Processing New Notify List"));
 
-  /* Distribute the information about new server in the SILC network
-     to our router. If we are normal server we won't send anything
-     since this connection must be our router connection. */
-  if (server->server_type == SILC_ROUTER && !server->standalone &&
-      server->router->connection != sock)
-    silc_server_send_new_id(server, server->router->connection,
-                           TRUE, new_server->id, SILC_ID_SERVER, 
-                           SILC_ID_SERVER_LEN);
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      packet->src_id_type != SILC_ID_SERVER)
+    return;
 
-  if (server->server_type == SILC_ROUTER)
-    server->stat.cell_servers++;
+  /* Make copy of the original packet context, except for the actual
+     data buffer, which we will here now fetch from the original buffer. */
+  new = silc_packet_context_alloc();
+  new->type = SILC_PACKET_NOTIFY;
+  new->flags = packet->flags;
+  new->src_id = packet->src_id;
+  new->src_id_len = packet->src_id_len;
+  new->src_id_type = packet->src_id_type;
+  new->dst_id = packet->dst_id;
+  new->dst_id_len = packet->dst_id_len;
+  new->dst_id_type = packet->dst_id_type;
+
+  buffer = silc_buffer_alloc(1024);
+  new->buffer = buffer;
+
+  while (packet->buffer->len) {
+    SILC_GET16_MSB(len, packet->buffer->data + 2);
+    if (len > packet->buffer->len)
+      break;
 
-  return new_server;
+    if (len > buffer->truelen) {
+      silc_buffer_free(buffer);
+      buffer = silc_buffer_alloc(1024 + len);
+    }
+
+    silc_buffer_pull_tail(buffer, len);
+    silc_buffer_put(buffer, packet->buffer->data, len);
+
+    /* Process the Notify */
+    silc_server_notify(server, sock, new);
+
+    silc_buffer_push_tail(buffer, len);
+    silc_buffer_pull(packet->buffer, len);
+  }
+
+  silc_buffer_free(buffer);
+  silc_free(new);
 }
 
-/* Processes incoming New ID packet. New ID Payload is used to distribute
-   information about newly registered clients and servers. */
+/* Received private message. This resolves the destination of the message 
+   and sends the packet. This is used by both server and router.  If the
+   destination is our locally connected client this sends the packet to
+   the client. This may also send the message for further routing if
+   the destination is not in our server (or router). */
 
-void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
-                       SilcPacketContext *packet)
+void silc_server_private_message(SilcServer server,
+                                SilcSocketConnection sock,
+                                SilcPacketContext *packet)
 {
-  SilcBuffer buffer = packet->buffer;
-  SilcIDList id_list;
+  SilcClientID *id;
   SilcServerEntry router;
-  SilcSocketConnection router_sock;
-  SilcIDPayload idp;
-  SilcIdType id_type;
-  unsigned char *hash = NULL;
-  void *id;
+  SilcSocketConnection dst_sock;
+  SilcClientEntry client;
+  SilcIDListData idata;
 
-  SILC_LOG_DEBUG(("Processing new ID"));
+  SILC_LOG_DEBUG(("Start"));
 
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      server->server_type == SILC_SERVER ||
-      packet->src_id_type != SILC_ID_SERVER)
+  if (packet->src_id_type != SILC_ID_CLIENT ||
+      packet->dst_id_type != SILC_ID_CLIENT)
     return;
 
-  idp = silc_id_payload_parse(buffer);
-  if (!idp)
+  if (!packet->dst_id)
     return;
 
-  id_type = silc_id_payload_get_type(idp);
+  /* Decode destination Client ID */
+  id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
+  if (!id) {
+    SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
+    return;
+  }
 
-  /* 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_payload_get_id(idp);
-  if (!id)
-    goto out;
-
-  /* If the sender of this packet is server and we are router we need to
-     broadcast this packet to other routers in the network. */
-  if (!server->standalone && server->server_type == SILC_ROUTER &&
-      sock->type == SILC_SOCKET_TYPE_SERVER &&
-      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
-    SILC_LOG_DEBUG(("Broadcasting received New ID packet"));
-    silc_server_packet_send(server, server->router->connection,
-                           packet->type, 
-                           packet->flags | SILC_PACKET_FLAG_BROADCAST,
-                           buffer->data, buffer->len, FALSE);
-  }
-
-  if (sock->type == SILC_SOCKET_TYPE_SERVER)
-    id_list = server->local_list;
-  else
-    id_list = server->global_list;
-
-  router_sock = sock;
-  router = sock->user_data;
-
-  switch(id_type) {
-  case SILC_ID_CLIENT:
-    {
-      SilcClientEntry entry;
-
-      SILC_LOG_DEBUG(("New client id(%s) from [%s] %s",
-                     silc_id_render(id, SILC_ID_CLIENT),
-                     sock->type == SILC_SOCKET_TYPE_SERVER ?
-                     "Server" : "Router", sock->hostname));
-    
-      /* As a router we keep information of all global information in our
-        global list. Cell wide information however is kept in the local
-        list. The client is put to global list and we will take the hash
-        value of the Client ID and save it to the ID Cache system for fast
-        searching in the future. */
-      hash = silc_calloc(sizeof(((SilcClientID *)id)->hash), 
-                        sizeof(unsigned char));
-      memcpy(hash, ((SilcClientID *)id)->hash, 
-            sizeof(((SilcClientID *)id)->hash));
-      entry = silc_idlist_add_client(id_list, hash, NULL, NULL, id, 
-                                    router, router_sock);
-      entry->nickname = NULL;
+  /* If the destination belongs to our server we don't have to route
+     the message anywhere but to send it to the local destination. */
+  client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
+  if (client) {
+    /* It exists, now deliver the message to the destination */
+    dst_sock = (SilcSocketConnection)client->connection;
 
-      if (sock->type == SILC_SOCKET_TYPE_SERVER)
-       server->stat.cell_clients++;
-      server->stat.clients++;
+    /* 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)client->router;
+      idata = (SilcIDListData)router;
 
-#if 0
-      /* XXX Adding two ID's with same IP number replaces the old entry thus
-        gives wrong route. Thus, now disabled until figured out a better way
-        to do this or when removed the whole thing. This could be removed
-        because entry->router->connection gives always the most optimal route
-        for the ID anyway (unless new routes (faster perhaps) are established
-        after receiving this ID, this we don't know however). */
-      /* Add route cache for this ID */
-      silc_server_route_add(silc_server_route_hash(
-                           ((SilcClientID *)id)->ip.s_addr,
-                           server->id->port), ((SilcClientID *)id)->ip.s_addr,
-                           router);
-#endif
+      silc_server_send_private_message(server, router->connection,
+                                      idata->send_key,
+                                      idata->hmac,
+                                      packet);
+      silc_free(id);
+      return;
     }
-    break;
 
-  case SILC_ID_SERVER:
-    SILC_LOG_DEBUG(("New server id(%s) from [%s] %s",
-                   silc_id_render(id, SILC_ID_SERVER),
-                   sock->type == SILC_SOCKET_TYPE_SERVER ?
-                   "Server" : "Router", sock->hostname));
-    
-    /* As a router we keep information of all global information in our global
-       list. Cell wide information however is kept in the local list. */
-    silc_idlist_add_server(id_list, NULL, 0, id, router, router_sock);
+    /* Seems that client really is directly connected to us */
+    idata = (SilcIDListData)client;
+    silc_server_send_private_message(server, dst_sock, 
+                                    idata->send_key,
+                                    idata->hmac, packet);
+    silc_free(id);
+    return;
+  }
 
-    if (sock->type == SILC_SOCKET_TYPE_SERVER)
-      server->stat.cell_servers++;
-    server->stat.servers++;
+  /* 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) {
+    router = server->router;
 
-#if 0
-    /* Add route cache for this ID */
-    silc_server_route_add(silc_server_route_hash(
-                         ((SilcServerID *)id)->ip.s_addr,
-                         ((SilcServerID *)id)->port), 
-                         ((SilcServerID *)id)->ip.s_addr,
-                         router);
-#endif
-    break;
+    /* Send to primary route */
+    if (router) {
+      dst_sock = (SilcSocketConnection)router->connection;
+      idata = (SilcIDListData)router;
+      silc_server_send_private_message(server, dst_sock, 
+                                      idata->send_key,
+                                      idata->hmac, packet);
+    }
+    silc_free(id);
+    return;
+  }
 
-  case SILC_ID_CHANNEL:
-    SILC_LOG_ERROR(("Channel cannot be registered with NEW_ID packet"));
-    break;
+  /* We are router and we will perform route lookup for the destination 
+     and send the message 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) {
+      dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
+      router = (SilcServerEntry)dst_sock->user_data;
+      idata = (SilcIDListData)router;
 
-  default:
-    break;
+      /* Get fastest route and send packet. */
+      if (router)
+       silc_server_send_private_message(server, dst_sock, 
+                                        idata->send_key,
+                                        idata->hmac, packet);
+      silc_free(id);
+      return;
+    }
   }
-
- out:
-  silc_id_payload_free(idp);
 }
 
-/* Received Remove Channel User 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 must not
-   receive it. */
+/* Received private message key packet.. This packet is never for us. It is to
+   the client in the packet's destination ID. Sending of this sort of packet
+   equals sending private message, ie. it is sent point to point from
+   one client to another. */
 
-void silc_server_remove_channel_user(SilcServer server,
+void silc_server_private_message_key(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;
+  SilcClientID *id;
+  SilcServerEntry router;
+  SilcSocketConnection dst_sock;
   SilcClientEntry client;
+  SilcIDListData idata;
 
-  SILC_LOG_DEBUG(("Removing user from channel"));
+  SILC_LOG_DEBUG(("Start"));
 
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      server->server_type == SILC_SERVER)
+  if (packet->src_id_type != SILC_ID_CLIENT ||
+      packet->dst_id_type != SILC_ID_CLIENT)
     return;
 
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI16_STRING_ALLOC(&tmp1),
-                      SILC_STR_UI16_STRING_ALLOC(&tmp2),
-                      SILC_STR_END);
+  if (!packet->dst_id)
+    return;
 
-  if (!tmp1 || !tmp2)
-    goto out;
+  /* Decode destination Client ID */
+  id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
+  if (!id) {
+    SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
+    return;
+  }
 
-  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;
+  /* If the destination belongs to our server we don't have to route
+     the message anywhere but to send it to the local destination. */
+  client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
+  if (client) {
+    /* It exists, now deliver the message to the destination */
+    dst_sock = (SilcSocketConnection)client->connection;
 
-  /* If we are router and this packet is not already broadcast packet
-     we will broadcast it. The sending socket really cannot be router or
-     the router is buggy. If this packet is coming from router then it must
-     have the broadcast flag set already and we won't do anything. */
-  if (!server->standalone && server->server_type == SILC_ROUTER &&
-      sock->type == SILC_SOCKET_TYPE_SERVER &&
-      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
-    SILC_LOG_DEBUG(("Broadcasting received Remove Channel User packet"));
-    silc_server_packet_send(server, server->router->connection, packet->type,
-                           packet->flags | SILC_PACKET_FLAG_BROADCAST, 
-                           buffer->data, buffer->len, FALSE);
-  }
+    /* 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)client->router;
+      idata = (SilcIDListData)router;
+      silc_server_send_private_message_key(server, router->connection,
+                                          idata->send_key,
+                                          idata->hmac,
+                                          packet);
+      silc_free(id);
+      return;
+    }
 
-  /* Get channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                          channel_id, NULL);
-  if (!channel) {
-    channel = silc_idlist_find_channel_by_id(server->global_list, 
-                                            channel_id, NULL);
-    if (!channel)
-      goto out;
+    /* Seems that client really is directly connected to us */
+    idata = (SilcIDListData)client;
+    silc_server_send_private_message_key(server, dst_sock, 
+                                        idata->send_key,
+                                        idata->hmac, packet);
+    silc_free(id);
+    return;
   }
 
-  /* Get client entry */
-  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)
-      goto out;
+  /* 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) {
+    router = server->router;
+
+    /* Send to primary route */
+    if (router) {
+      dst_sock = (SilcSocketConnection)router->connection;
+      idata = (SilcIDListData)router;
+      silc_server_send_private_message_key(server, dst_sock, 
+                                          idata->send_key,
+                                          idata->hmac, packet);
+    }
+    silc_free(id);
+    return;
   }
 
-  /* Remove user from channel */
-  silc_server_remove_from_one_channel(server, sock, channel, client, TRUE);
+  /* 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) {
+      dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
+      router = (SilcServerEntry)dst_sock->user_data;
+      idata = (SilcIDListData)router;
 
- 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);
+      /* Get fastest route and send packet. */
+      if (router)
+       silc_server_send_private_message_key(server, dst_sock, 
+                                            idata->send_key,
+                                            idata->hmac, packet);
+      silc_free(id);
+      return;
+    }
+  }
 }
 
-/* Received New Channel packet. Information about new channels in the 
-   network are distributed using this packet. Save the information about
-   the new channel. */
+/* Processes incoming command reply packet. The command reply packet may
+   be destined to one of our clients or it may directly for us. We will 
+   call the command reply routine after processing the packet. */
 
-void silc_server_new_channel(SilcServer server,
-                            SilcSocketConnection sock,
-                            SilcPacketContext *packet)
+void silc_server_command_reply(SilcServer server,
+                              SilcSocketConnection sock,
+                              SilcPacketContext *packet)
 {
-  unsigned char *id;
-  SilcChannelID *channel_id;
-  unsigned short channel_id_len;
-  char *channel_name;
-
-  SILC_LOG_DEBUG(("Processing New Channel"));
+  SilcBuffer buffer = packet->buffer;
+  SilcClientEntry client = NULL;
+  SilcSocketConnection dst_sock;
+  SilcIDListData idata;
+  SilcClientID *id = NULL;
 
-  if (sock->type != SILC_SOCKET_TYPE_ROUTER ||
-      server->server_type == SILC_SERVER ||
-      packet->src_id_type != SILC_ID_SERVER)
-    return;
+  SILC_LOG_DEBUG(("Start"));
 
-  /* Parse payload */
-  if (!silc_buffer_unformat(packet->buffer, 
-                           SILC_STR_UI16_STRING_ALLOC(&channel_name),
-                           SILC_STR_UI16_NSTRING_ALLOC(&id, &channel_id_len),
-                           SILC_STR_END))
-    return;
-    
-  if (!channel_name || !id)
+  /* Source must be server or router */
+  if (packet->src_id_type != SILC_ID_SERVER &&
+      sock->type != SILC_SOCKET_TYPE_ROUTER)
     return;
 
-  /* Decode the channel ID */
-  channel_id = silc_id_str2id(id, SILC_ID_CHANNEL);
-  if (!channel_id)
+  if (packet->dst_id_type == SILC_ID_CHANNEL)
     return;
-  silc_free(id);
-
-  SILC_LOG_DEBUG(("New channel id(%s) from [Router] %s",
-                 silc_id_render(channel_id, SILC_ID_CHANNEL), 
-                 sock->hostname));
 
-  /* Add the new channel. Add it always to global list since if we receive
-     this packet then it cannot be created by ourselves but some other 
-     router hence global channel. */
-  silc_idlist_add_channel(server->global_list, channel_name, 0, channel_id, 
-                         server->router->connection, NULL);
+  if (packet->dst_id_type == SILC_ID_CLIENT) {
+    /* Destination must be one of ours */
+    id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
+    if (!id)
+      return;
+    client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
+    if (!client) {
+      SILC_LOG_ERROR(("Cannot process command reply to unknown client"));
+      silc_free(id);
+      return;
+    }
+  }
 
-  server->stat.channels++;
-}
+  if (packet->dst_id_type == SILC_ID_SERVER) {
+    /* For now this must be for us */
+    if (SILC_ID_SERVER_COMPARE(packet->dst_id, server->id_string)) {
+      SILC_LOG_ERROR(("Cannot process command reply to unknown server"));
+      return;
+    }
+  }
 
-/* Received notify packet. Server can receive notify packets from router. 
-   Server then relays the notify messages to clients if needed. */
+  /* Execute command reply locally for the command */
+  silc_server_command_reply_process(server, sock, buffer);
 
-void silc_server_notify(SilcServer server,
-                       SilcSocketConnection sock,
-                       SilcPacketContext *packet)
-{
-  SilcNotifyPayload payload;
-  SilcNotifyType type;
-  SilcArgumentPayload args;
-  SilcChannelID *channel_id;
-  SilcClientID *client_id, *client_id2;
-  SilcChannelEntry channel;
-  SilcClientEntry client;
-  unsigned char *tmp;
-  unsigned int tmp_len;
+  if (packet->dst_id_type == SILC_ID_CLIENT && client && id) {
+    /* Relay the packet to the client */
+    
+    dst_sock = (SilcSocketConnection)client->connection;
+    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
+                    + packet->dst_id_len + packet->padlen);
+    
+    silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
+    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
+    
+    idata = (SilcIDListData)client;
+    
+    /* Encrypt packet */
+    silc_packet_encrypt(idata->send_key, idata->hmac, dst_sock->outbuf, 
+                       buffer->len);
+    
+    /* Send the packet */
+    silc_server_packet_send_real(server, dst_sock, TRUE);
 
-  SILC_LOG_DEBUG(("Start"));
+    silc_free(id);
+  }
+}
 
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      packet->src_id_type != SILC_ID_SERVER)
-    return;
+/* Process received channel message. The message can be originated from
+   client or server. */
 
-  /* XXX: For now we expect that the we are normal server and that the
-     sender is router. Server could send (protocol allows it) notify to
-     router but we don't support it yet. */
-  if (server->server_type != SILC_SERVER &&
-      sock->type != SILC_SOCKET_TYPE_ROUTER)
-    return;
+void silc_server_channel_message(SilcServer server,
+                                SilcSocketConnection sock,
+                                SilcPacketContext *packet)
+{
+  SilcChannelEntry channel = NULL;
+  SilcChannelClientEntry chl;
+  SilcChannelID *id = NULL;
+  void *sender = NULL;
 
-  payload = silc_notify_payload_parse(packet->buffer);
-  if (!payload)
-    return;
+  SILC_LOG_DEBUG(("Processing channel message"));
 
-  type = silc_notify_get_type(payload);
-  args = silc_notify_get_args(payload);
-  if (!args)
+  /* Sanity checks */
+  if (packet->dst_id_type != SILC_ID_CHANNEL) {
+    SILC_LOG_DEBUG(("Received bad message for channel, dropped"));
     goto out;
+  }
 
-  switch(type) {
-  case SILC_NOTIFY_TYPE_JOIN:
-    /* 
-     * Distribute the notify to local clients on the channel
-     */
-    SILC_LOG_DEBUG(("JOIN notify"));
-
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_type);
-    if (!channel_id)
-      goto out;
-
-    /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                            channel_id, NULL);
+  /* Find channel entry */
+  id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL);
+  if (!id)
+    goto out;
+  channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
     if (!channel) {
-      silc_free(channel_id);
+      SILC_LOG_DEBUG(("Could not find channel"));
       goto out;
     }
+  }
 
-    /* Get client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp) {
-      silc_free(channel_id);
+  /* 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_len, 
+                         packet->src_id_type);
+  if (!sender)
+    goto out;
+  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) {
+      SILC_LOG_DEBUG(("Client not on channel"));
       goto out;
     }
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+  }
 
-    /* Send to channel */
-    silc_server_packet_send_to_channel(server, NULL, channel, packet->type, 
-                                      FALSE, packet->buffer->data, 
-                                      packet->buffer->len, FALSE);
+  /* If we are router and the packet came from router and private key
+     has not been set for the channel then we must encrypt the packet
+     as it was decrypted with the session key shared between us and the
+     router which sent it. This is so, because cells does not share the
+     same channel key */
+  if (server->server_type == SILC_ROUTER &&
+      sock->type == SILC_SOCKET_TYPE_ROUTER &&
+      !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+    SilcBuffer chp;
+    unsigned int iv_len, i, data_len;
+
+    iv_len = silc_cipher_get_block_len(channel->channel_key);
+    if (channel->iv[0] == '\0')
+      for (i = 0; i < iv_len; i++) channel->iv[i] = 
+                                    silc_rng_get_byte(server->rng);
+    else
+      silc_hash_make(server->md5hash, channel->iv, iv_len, channel->iv);
+    
+    /* Encode new payload. This encrypts it also. */
+    SILC_GET16_MSB(data_len, packet->buffer->data);
+    chp = silc_channel_payload_encode(data_len, packet->buffer->data + 2,
+                                     iv_len, channel->iv,
+                                     channel->channel_key,
+                                     channel->hmac, server->rng);
+    silc_buffer_put(packet->buffer, chp->data, chp->len);
+    silc_buffer_free(chp);
+  }
 
-    /* If the the client is not in local list we check global list (ie. the
-       channel will be global channel) and if it does not exist then create
-       entry for the client. */
-    client = silc_idlist_find_client_by_id(server->local_list, 
-                                          client_id, NULL);
-    if (!client) {
-      SilcChannelClientEntry chl;
+  /* Distribute the packet to our local clients. This will send the
+     packet for further routing as well, if needed. */
+  silc_server_packet_relay_to_channel(server, sock, channel, sender,
+                                     packet->src_id_type,
+                                     packet->buffer->data,
+                                     packet->buffer->len, FALSE);
 
-      client = silc_idlist_find_client_by_id(server->global_list, 
-                                            client_id, NULL);
-      if (!client)
-       client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
-                                       client_id, sock->user_data, sock);
+ out:
+  if (sender)
+    silc_free(sender);
+  if (id)
+    silc_free(id);
+}
 
-      /* The channel is global now */
-      channel->global_users = TRUE;
+/* Received channel key packet. We distribute the key to all of our locally
+   connected clients on the channel. */
 
-      /* Now actually JOIN the global client to the channel */
-      chl = silc_calloc(1, sizeof(*chl));
-      chl->client = client;
-      chl->channel = channel;
-      silc_list_add(channel->user_list, chl);
-      silc_list_add(client->channels, chl);
-    } else {
-      silc_free(client_id);
-    }
-    break;
+void silc_server_channel_key(SilcServer server,
+                            SilcSocketConnection sock,
+                            SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  SilcChannelEntry channel;
 
-  case SILC_NOTIFY_TYPE_LEAVE:
-    /* 
-     * Distribute the notify to local clients on the channel
-     */
-    SILC_LOG_DEBUG(("LEAVE notify"));
+  if (packet->src_id_type != SILC_ID_SERVER)
+    return;
 
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_type);
-    if (!channel_id)
-      goto out;
+  /* Save the channel key */
+  channel = silc_server_save_channel_key(server, buffer, NULL);
+  if (!channel)
+    return;
 
-    /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                            channel_id, NULL);
-    if (!channel) { 
-      silc_free(channel_id);
-      goto out;
-    }
+  /* Distribute the key to everybody who is on the channel. If we are router
+     we will also send it to locally connected servers. */
+  silc_server_send_channel_key(server, sock, channel, FALSE);
+}
 
-    /* Get client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp) {
-      silc_free(channel_id);
-      goto out;
-    }
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+/* Received New Client packet and processes it.  Creates Client ID for the
+   client. Client becomes registered after calling this functions. */
 
-    /* Send to channel */
-    silc_server_packet_send_to_channel(server, NULL, channel, packet->type, 
-                                      FALSE, packet->buffer->data, 
-                                      packet->buffer->len, FALSE);
+SilcClientEntry silc_server_new_client(SilcServer server,
+                                      SilcSocketConnection sock,
+                                      SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  SilcClientEntry client;
+  SilcIDCacheEntry cache;
+  SilcClientID *client_id;
+  SilcBuffer reply;
+  SilcIDListData idata;
+  char *username = NULL, *realname = NULL, *id_string;
+  int ret;
 
-    /* Get client entry */
-    client = silc_idlist_find_client_by_id(server->global_list, 
-                                          client_id, NULL);
-    if (!client) {
-      client = silc_idlist_find_client_by_id(server->local_list, 
-                                            client_id, NULL);
-      if (!client) {
-       silc_free(channel_id);
-       goto out;
-      }
-    }
-    silc_free(client_id);
+  SILC_LOG_DEBUG(("Creating new client"));
 
-    /* Remove the user from channel */
-    silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
-    break;
+  if (sock->type != SILC_SOCKET_TYPE_CLIENT)
+    return NULL;
 
-  case SILC_NOTIFY_TYPE_SIGNOFF:
-    /* 
-     * Distribute the notify to local clients on the channel
-     */
-    SILC_LOG_DEBUG(("SIGNOFF notify"));
+  /* Take client entry */
+  client = (SilcClientEntry)sock->user_data;
+  idata = (SilcIDListData)client;
 
-    /* Get client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+  /* Fetch the old client cache entry so that we can update it. */
+  if (!silc_idcache_find_by_context(server->local_list->clients,
+                                   sock->user_data, &cache)) {
+    SILC_LOG_ERROR(("Lost client's cache entry - bad thing"));
+    return NULL;
+  }
 
-    /* Get client entry */
-    client = silc_idlist_find_client_by_id(server->global_list, 
-                                          client_id, NULL);
-    if (!client) {
-      client = silc_idlist_find_client_by_id(server->local_list, 
-                                            client_id, NULL);
-      if (!client) {
-       silc_free(client_id);
-       goto out;
-      }
-    }
-    silc_free(client_id);
+  /* Parse incoming packet */
+  ret = silc_buffer_unformat(buffer,
+                            SILC_STR_UI16_STRING_ALLOC(&username),
+                            SILC_STR_UI16_STRING_ALLOC(&realname),
+                            SILC_STR_END);
+  if (ret == -1) {
+    if (username)
+      silc_free(username);
+    if (realname)
+      silc_free(realname);
+    return NULL;
+  }
 
-    /* Remove the client from all channels */
-    silc_server_remove_from_channels(server, NULL, client);
+  if (!username) {
+    silc_free(username);
+    if (realname)
+      silc_free(realname);
+    silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                                  "Incomplete client information");
+    return NULL;
+  }
 
-    /* Remove the client entry */
-    silc_idlist_del_client(server->global_list, client);
-    break;
+  /* Create Client ID */
+  silc_id_create_client_id(server->id, server->rng, server->md5hash,
+                          username, &client_id);
 
-  case SILC_NOTIFY_TYPE_NICK_CHANGE:
-    {
-      /* 
-       * Distribute the notify to local clients on the channel
-       */
-      unsigned char *id, *id2;
+  if (strlen(username) > 128)
+    username[127] = '\0';
 
-      SILC_LOG_DEBUG(("NICK CHANGE notify"));
-      
-      /* Get old client ID */
-      id = silc_argument_get_arg_type(args, 1, &tmp_len);
-      if (!id)
-       goto out;
-      client_id = silc_id_payload_parse_id(id, tmp_len);
-      
-      /* Get new client ID */
-      id2 = silc_argument_get_arg_type(args, 2, &tmp_len);
-      if (!id2)
-       goto out;
-      client_id2 = silc_id_payload_parse_id(id2, tmp_len);
-      
-      SILC_LOG_DEBUG(("Old Client ID id(%s)", 
-                     silc_id_render(client_id, SILC_ID_CLIENT)));
-      SILC_LOG_DEBUG(("New Client ID id(%s)", 
-                     silc_id_render(client_id2, SILC_ID_CLIENT)));
+  /* Update client entry */
+  idata->registered = TRUE;
+  client->nickname = strdup(username);
+  client->username = username;
+  client->userinfo = realname ? realname : strdup(" ");
+  client->id = client_id;
 
-      /* Replace the Client ID */
-      client = silc_idlist_replace_client_id(server->global_list, client_id,
-                                            client_id2);
-      if (!client)
-       client = silc_idlist_replace_client_id(server->local_list, client_id, 
-                                              client_id2);
+  /* Update the cache entry */
+  cache->id = (void *)client_id;
+  cache->type = SILC_ID_CLIENT;
+  cache->data = username;
+  silc_idcache_sort_by_data(server->local_list->clients);
 
-      if (client)
-       /* Send the NICK_CHANGE notify type to local clients on the channels
-          this client is joined to. */
-       silc_server_send_notify_on_channels(server, client, 
-                                           SILC_NOTIFY_TYPE_NICK_CHANGE, 2,
-                                           id, tmp_len, 
-                                           id2, tmp_len);
+  /* Notify our router about new client on the SILC network */
+  if (!server->standalone)
+    silc_server_send_new_id(server, (SilcSocketConnection) 
+                           server->router->connection, 
+                           server->server_type == SILC_ROUTER ? TRUE : FALSE,
+                           client->id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
+  
+  /* Send the new client ID to the client. */
+  id_string = silc_id_id2str(client->id, SILC_ID_CLIENT);
+  reply = silc_buffer_alloc(2 + 2 + SILC_ID_CLIENT_LEN);
+  silc_buffer_pull_tail(reply, SILC_BUFFER_END(reply));
+  silc_buffer_format(reply,
+                    SILC_STR_UI_SHORT(SILC_ID_CLIENT),
+                    SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN),
+                    SILC_STR_UI_XNSTRING(id_string, SILC_ID_CLIENT_LEN),
+                    SILC_STR_END);
+  silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 0, 
+                         reply->data, reply->len, FALSE);
+  silc_free(id_string);
+  silc_buffer_free(reply);
 
-      silc_free(client_id);
-      if (!client)
-       silc_free(client_id2);
-      break;
+  /* Send some nice info to the client */
+  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));
+  if (server->server_type == SILC_ROUTER) {
+    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                           ("There are %d clients on %d servers in SILC "
+                            "Network", server->stat.clients,
+                            server->stat.servers + 1));
+    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                           ("There are %d clients on %d server in our cell",
+                            server->stat.cell_clients,
+                            server->stat.cell_servers + 1));
+    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                           ("I have %d clients, %d channels, %d servers and "
+                            "%d routers",
+                            server->stat.my_clients, 
+                            server->stat.my_channels,
+                            server->stat.my_servers,
+                            server->stat.my_routers));
+    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                           ("%d server operators and %d router operators "
+                            "online",
+                            server->stat.my_server_ops,
+                            server->stat.my_router_ops));
+  } else {
+    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                           ("I have %d clients and %d channels formed",
+                            server->stat.my_clients,
+                            server->stat.my_channels));
+    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                           ("%d operators online",
+                            server->stat.my_server_ops));
+  }
+  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                         ("Your connection is secured with %s cipher, "
+                          "key length %d bits",
+                          idata->send_key->cipher->name,
+                          idata->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;
+}
+
+/* Create new server. This processes received New Server packet and
+   saves the received Server ID. The server is our locally connected
+   server thus we save all the information and save it to local list. 
+   This funtion can be used by both normal server and router server.
+   If normal server uses this it means that its router has connected
+   to the server. If router uses this it means that one of the cell's
+   servers is connected to the router. */
+
+SilcServerEntry silc_server_new_server(SilcServer server,
+                                      SilcSocketConnection sock,
+                                      SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  SilcServerEntry new_server;
+  SilcIDCacheEntry cache;
+  SilcServerID *server_id;
+  SilcIDListData idata;
+  unsigned char *server_name, *id_string;
+  unsigned short id_len;
+  int ret;
+
+  SILC_LOG_DEBUG(("Creating new server"));
+
+  if (sock->type != SILC_SOCKET_TYPE_SERVER &&
+      sock->type != SILC_SOCKET_TYPE_ROUTER)
+    return NULL;
+
+  /* Take server entry */
+  new_server = (SilcServerEntry)sock->user_data;
+  idata = (SilcIDListData)new_server;
+
+  /* Fetch the old server cache entry so that we can update it. */
+  if (!silc_idcache_find_by_context(server->local_list->servers,
+                                   sock->user_data, &cache)) {
+    SILC_LOG_ERROR(("Lost server's cache entry - bad thing"));
+    return NULL;
+  }
+
+  /* Parse the incoming packet */
+  ret = silc_buffer_unformat(buffer,
+                            SILC_STR_UI16_NSTRING_ALLOC(&id_string, &id_len),
+                            SILC_STR_UI16_STRING_ALLOC(&server_name),
+                            SILC_STR_END);
+  if (ret == -1) {
+    if (id_string)
+      silc_free(id_string);
+    if (server_name)
+      silc_free(server_name);
+    return NULL;
+  }
+
+  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, id_len, SILC_ID_SERVER);
+  if (!server_id) {
+    silc_free(id_string);
+    silc_free(server_name);
+    return NULL;
+  }
+  silc_free(id_string);
+
+  /* Update client entry */
+  idata->registered = TRUE;
+  new_server->server_name = server_name;
+  new_server->id = server_id;
+
+  /* Update the cache entry */
+  cache->id = (void *)server_id;
+  cache->type = SILC_ID_SERVER;
+  cache->data = server_name;
+  silc_idcache_sort_by_data(server->local_list->servers);
+
+  /* Distribute the information about new server in the SILC network
+     to our router. If we are normal server we won't send anything
+     since this connection must be our router connection. */
+  if (server->server_type == SILC_ROUTER && !server->standalone &&
+      server->router->connection != sock)
+    silc_server_send_new_id(server, server->router->connection,
+                           TRUE, new_server->id, SILC_ID_SERVER, 
+                           SILC_ID_SERVER_LEN);
+
+  if (server->server_type == SILC_ROUTER)
+    server->stat.cell_servers++;
+
+  return new_server;
+}
+
+/* Processes incoming New ID packet. New ID Payload is used to distribute
+   information about newly registered clients and servers. */
+
+static void silc_server_new_id_real(SilcServer server, 
+                                   SilcSocketConnection sock,
+                                   SilcPacketContext *packet,
+                                   int broadcast)
+{
+  SilcBuffer buffer = packet->buffer;
+  SilcIDList id_list;
+  SilcServerEntry router;
+  SilcSocketConnection router_sock;
+  SilcIDPayload idp;
+  SilcIdType id_type;
+  unsigned char *hash = NULL;
+  void *id;
+
+  SILC_LOG_DEBUG(("Processing new ID"));
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      server->server_type == SILC_SERVER ||
+      packet->src_id_type != SILC_ID_SERVER)
+    return;
+
+  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_payload_get_id(idp);
+  if (!id)
+    goto out;
+
+  /* If the sender of this packet is server and we are router we need to
+     broadcast this packet to other routers in the network. */
+  if (broadcast && !server->standalone && server->server_type == SILC_ROUTER &&
+      sock->type == SILC_SOCKET_TYPE_SERVER &&
+      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
+    SILC_LOG_DEBUG(("Broadcasting received New ID packet"));
+    silc_server_packet_send(server, server->router->connection,
+                           packet->type, 
+                           packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                           buffer->data, buffer->len, FALSE);
+  }
+
+  if (sock->type == SILC_SOCKET_TYPE_SERVER)
+    id_list = server->local_list;
+  else
+    id_list = server->global_list;
+
+  router_sock = sock;
+  router = sock->user_data;
+
+  switch(id_type) {
+  case SILC_ID_CLIENT:
+    {
+      SilcClientEntry entry;
+
+      SILC_LOG_DEBUG(("New client id(%s) from [%s] %s",
+                     silc_id_render(id, SILC_ID_CLIENT),
+                     sock->type == SILC_SOCKET_TYPE_SERVER ?
+                     "Server" : "Router", sock->hostname));
+    
+      /* As a router we keep information of all global information in our
+        global list. Cell wide information however is kept in the local
+        list. The client is put to global list and we will take the hash
+        value of the Client ID and save it to the ID Cache system for fast
+        searching in the future. */
+      hash = silc_calloc(sizeof(((SilcClientID *)id)->hash), 
+                        sizeof(unsigned char));
+      memcpy(hash, ((SilcClientID *)id)->hash, 
+            sizeof(((SilcClientID *)id)->hash));
+      entry = silc_idlist_add_client(id_list, hash, NULL, NULL, id, 
+                                    router, NULL);
+      entry->nickname = NULL;
+
+      if (sock->type == SILC_SOCKET_TYPE_SERVER)
+       server->stat.cell_clients++;
+      server->stat.clients++;
+
+#if 0
+      /* XXX Adding two ID's with same IP number replaces the old entry thus
+        gives wrong route. Thus, now disabled until figured out a better way
+        to do this or when removed the whole thing. This could be removed
+        because entry->router->connection gives always the most optimal route
+        for the ID anyway (unless new routes (faster perhaps) are established
+        after receiving this ID, this we don't know however). */
+      /* Add route cache for this ID */
+      silc_server_route_add(silc_server_route_hash(
+                           ((SilcClientID *)id)->ip.s_addr,
+                           server->id->port), ((SilcClientID *)id)->ip.s_addr,
+                           router);
+#endif
     }
-  case SILC_NOTIFY_TYPE_TOPIC_SET:
-    /* 
-     * Distribute the notify to local clients on the channel
-     */
-    SILC_LOG_DEBUG(("TOPIC SET notify (not-impl XXX)"));
     break;
 
-  case SILC_NOTIFY_TYPE_CMODE_CHANGE:
-    /* 
-     * Distribute the notify to local clients on the channel
-     */
-    SILC_LOG_DEBUG(("CMODE CHANGE notify (not-impl XXX)"));
+  case SILC_ID_SERVER:
+    SILC_LOG_DEBUG(("New server id(%s) from [%s] %s",
+                   silc_id_render(id, SILC_ID_SERVER),
+                   sock->type == SILC_SOCKET_TYPE_SERVER ?
+                   "Server" : "Router", sock->hostname));
+    
+    /* As a router we keep information of all global information in our global
+       list. Cell wide information however is kept in the local list. */
+    silc_idlist_add_server(id_list, NULL, 0, id, router, router_sock);
+
+    if (sock->type == SILC_SOCKET_TYPE_SERVER)
+      server->stat.cell_servers++;
+    server->stat.servers++;
+
+#if 0
+    /* Add route cache for this ID */
+    silc_server_route_add(silc_server_route_hash(
+                         ((SilcServerID *)id)->ip.s_addr,
+                         ((SilcServerID *)id)->port), 
+                         ((SilcServerID *)id)->ip.s_addr,
+                         router);
+#endif
     break;
 
-  case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
-    /* 
-     * Distribute the notify to local clients on the channel
-     */
-    SILC_LOG_DEBUG(("CUMODE CHANGE notify (not-impl XXX)"));
+  case SILC_ID_CHANNEL:
+    SILC_LOG_ERROR(("Channel cannot be registered with NEW_ID packet"));
     break;
 
-    /* Ignore rest notify types for now */
-  case SILC_NOTIFY_TYPE_NONE:
-  case SILC_NOTIFY_TYPE_INVITE:
-  case SILC_NOTIFY_TYPE_MOTD:
   default:
     break;
   }
 
  out:
-  silc_notify_payload_free(payload);
+  silc_id_payload_free(idp);
+}
+
+
+/* Processes incoming New ID packet. New ID Payload is used to distribute
+   information about newly registered clients and servers. */
+
+void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
+                       SilcPacketContext *packet)
+{
+  silc_server_new_id_real(server, sock, packet, TRUE);
+}
+
+/* Receoved New Id List packet, list of New ID payloads inside one
+   packet. Process the New ID payloads one by one. */
+
+void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock,
+                            SilcPacketContext *packet)
+{
+  SilcPacketContext *new_id;
+  SilcBuffer idp;
+  unsigned short id_len;
+
+  SILC_LOG_DEBUG(("Processing New ID List"));
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      packet->src_id_type != SILC_ID_SERVER)
+    return;
+
+  /* If the sender of this packet is server and we are router we need to
+     broadcast this packet to other routers in the network. Broadcast
+     this list packet instead of multiple New ID packets. */
+  if (!server->standalone && server->server_type == SILC_ROUTER &&
+      sock->type == SILC_SOCKET_TYPE_SERVER &&
+      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
+    SILC_LOG_DEBUG(("Broadcasting received New ID List packet"));
+    silc_server_packet_send(server, server->router->connection,
+                           packet->type, 
+                           packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                           packet->buffer->data, packet->buffer->len, FALSE);
+  }
+
+  /* Make copy of the original packet context, except for the actual
+     data buffer, which we will here now fetch from the original buffer. */
+  new_id = silc_packet_context_alloc();
+  new_id->type = SILC_PACKET_NEW_ID;
+  new_id->flags = packet->flags;
+  new_id->src_id = packet->src_id;
+  new_id->src_id_len = packet->src_id_len;
+  new_id->src_id_type = packet->src_id_type;
+  new_id->dst_id = packet->dst_id;
+  new_id->dst_id_len = packet->dst_id_len;
+  new_id->dst_id_type = packet->dst_id_type;
+
+  idp = silc_buffer_alloc(256);
+  new_id->buffer = idp;
+
+  while (packet->buffer->len) {
+    SILC_GET16_MSB(id_len, packet->buffer->data + 2);
+    if ((id_len > packet->buffer->len) ||
+       (id_len > idp->truelen))
+      break;
+
+    silc_buffer_pull_tail(idp, 4 + id_len);
+    silc_buffer_put(idp, packet->buffer->data, 4 + id_len);
+
+    /* Process the New ID */
+    silc_server_new_id_real(server, sock, new_id, FALSE);
+
+    silc_buffer_push_tail(idp, 4 + id_len);
+    silc_buffer_pull(packet->buffer, 4 + id_len);
+  }
+
+  silc_buffer_free(idp);
+  silc_free(new_id);
 }
 
-/* Received new channel user packet. Information about new users on a
-   channel are distributed between routers using this packet.  The
-   router receiving this will redistribute it and also sent JOIN notify
-   to local clients on the same channel. Normal server sends JOIN notify
-   to its local clients on the channel. */
+/* Received New Channel packet. Information about new channels in the 
+   network are distributed using this packet. Save the information about
+   the new channel. This usually comes from router but also normal server
+   can send this to notify channels it has when it connects to us. */
 
-void silc_server_new_channel_user(SilcServer server,
-                                 SilcSocketConnection sock,
-                                 SilcPacketContext *packet)
+void silc_server_new_channel(SilcServer server,
+                            SilcSocketConnection sock,
+                            SilcPacketContext *packet)
 {
-  unsigned char *tmpid1, *tmpid2;
-  SilcClientID *client_id = NULL;
-  SilcChannelID *channel_id = NULL;
+  unsigned char *id;
+  SilcChannelID *channel_id;
   unsigned short channel_id_len;
-  unsigned short client_id_len;
-  SilcClientEntry client;
-  SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
-  SilcBuffer clidp;
+  char *channel_name;
+  int ret;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Processing New Channel"));
 
   if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      server->server_type != SILC_ROUTER ||
-      packet->src_id_type != SILC_ID_SERVER)
+      packet->src_id_type != SILC_ID_SERVER ||
+      server->server_type == SILC_SERVER)
     return;
 
   /* Parse payload */
-  if (!silc_buffer_unformat(packet->buffer, 
-                           SILC_STR_UI16_NSTRING_ALLOC(&tmpid1, 
-                                                       &channel_id_len),
-                           SILC_STR_UI16_NSTRING_ALLOC(&tmpid2, 
-                                                       &client_id_len),
-                           SILC_STR_END))
-    return;
-
-  if (!tmpid1 || !tmpid2)
+  ret = silc_buffer_unformat(packet->buffer, 
+                            SILC_STR_UI16_STRING_ALLOC(&channel_name),
+                            SILC_STR_UI16_NSTRING_ALLOC(&id, &channel_id_len),
+                            SILC_STR_END);
+  if (ret == -1) {
+    if (channel_name)
+      silc_free(channel_name);
+    if (id)
+      silc_free(id);
     return;
-
+  }
+    
   /* Decode the channel ID */
-  channel_id = silc_id_str2id(tmpid1, SILC_ID_CHANNEL);
+  channel_id = silc_id_str2id(id, channel_id_len, SILC_ID_CHANNEL);
   if (!channel_id)
-    goto out;
-
-  /* Decode the client ID */
-  client_id = silc_id_str2id(tmpid2, SILC_ID_CLIENT);
-  if (!client_id)
-    goto out;
-
-  /* Find the channel */
-  channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                          channel_id, NULL);
-  if (!channel) {
-    channel = silc_idlist_find_channel_by_id(server->global_list, 
-                                            channel_id, NULL);
-    if (!channel)
-      goto out;
-  }
+    return;
 
-  /* If we are router and this packet is not already broadcast packet
-     we will broadcast it. */
-  if (!server->standalone && server->server_type == SILC_ROUTER &&
-      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
-    SILC_LOG_DEBUG(("Broadcasting received New Channel User packet"));
-    silc_server_packet_send(server, server->router->connection, packet->type,
-                           packet->flags | SILC_PACKET_FLAG_BROADCAST, 
-                           packet->buffer->data, packet->buffer->len, FALSE);
-  }
+  if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
+    /* Add the server to global list as it is coming from router. It 
+       cannot be our own channel as it is coming from router. */
 
-  /* Get client entry */
-  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)
-      goto out;
-  }
+    SILC_LOG_DEBUG(("New channel id(%s) from [Router] %s",
+                   silc_id_render(channel_id, SILC_ID_CHANNEL), 
+                   sock->hostname));
+    
+    silc_idlist_add_channel(server->global_list, channel_name, 0, channel_id, 
+                           server->router->connection, NULL, NULL);
 
-  /* Join the client to the channel by adding it to channel's user list.
-     Add also the channel to client entry's channels list for fast cross-
-     referencing. */
-  chl = silc_calloc(1, sizeof(*chl));
-  chl->client = client;
-  chl->channel = channel;
-  silc_list_add(channel->user_list, chl);
-  silc_list_add(client->channels, chl);
+    server->stat.channels++;
+  } else {
+    /* The channel is coming from our server, thus it is in our cell
+       we will add it to our local list. */
+    SilcChannelEntry channel;
+    SilcBuffer chk;
+
+    SILC_LOG_DEBUG(("New channel id(%s) from [Server] %s",
+                   silc_id_render(channel_id, SILC_ID_CHANNEL), 
+                   sock->hostname));
+    
+    /* Check that we don't already have this channel */
+    channel = silc_idlist_find_channel_by_name(server->local_list, 
+                                              channel_name, NULL);
+    if (!channel)
+      channel = silc_idlist_find_channel_by_name(server->global_list, 
+                                                channel_name, NULL);
 
-  server->stat.chanclients++;
+    /* If the channel does not exist, then create it. We create the channel
+       with the channel ID provided by the server. This creates a new
+       key to the channel as well that we will send to the server. */
+    if (!channel) {
+      channel = silc_server_create_new_channel_with_id(server, NULL, NULL,
+                                                      channel_name,
+                                                      channel_id, FALSE);
+      if (!channel)
+       return;
+
+      /* Send the new channel key to the server */
+      chk = silc_channel_key_payload_encode(channel_id_len, id,
+                                           strlen(channel->channel_key->
+                                                  cipher->name),
+                                           channel->channel_key->cipher->name,
+                                           channel->key_len / 8, 
+                                           channel->key);
+      silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
+                             chk->data, chk->len, FALSE);
+      silc_buffer_free(chk);
 
-  /* Send JOIN notify to local clients on the channel. As we are router
-     it is assured that this is sent only to our local clients and locally
-     connected servers if needed. */
-  clidp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
-  silc_server_send_notify_to_channel(server, sock, channel, FALSE,
-                                    SILC_NOTIFY_TYPE_JOIN, 
-                                    1, clidp->data, clidp->len);
-  silc_buffer_free(clidp);
+    } else {
+      /* The channel exist by that name, check whether the ID's match.
+        If they don't then we'll force the server to use the ID we have.
+        We also create a new key for the channel. */
+
+      if (SILC_ID_CHANNEL_COMPARE(channel_id, channel->id)) {
+       /* They don't match, send CHANNEL_CHANGE notify to the server to
+          force the ID change. */
+       SILC_LOG_DEBUG(("Forcing the server to change Channel ID"));
+       silc_server_send_notify_channel_change(server, sock, FALSE, 
+                                              channel_id,
+                                              channel->id, 
+                                              SILC_ID_CHANNEL_LEN);
+      }
 
-  client_id = NULL;
+      /* Create new key for the channel and send it to the server and
+        everybody else possibly on the channel. */
+
+      silc_server_create_channel_key(server, channel, 0);
+
+      /* Send to the channel */
+      silc_server_send_channel_key(server, sock, channel, FALSE);
+
+      /* Send to the server */
+      chk = silc_channel_key_payload_encode(channel_id_len, id,
+                                           strlen(channel->channel_key->
+                                                  cipher->name),
+                                           channel->channel_key->cipher->name,
+                                           channel->key_len / 8, 
+                                           channel->key);
+      silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
+                             chk->data, chk->len, FALSE);
+      silc_buffer_free(chk);
+
+      /* Since the channel is coming from server and we also know about it
+        then send the JOIN notify to the server so that it see's our
+        users on the channel "joining" the channel. */
+      /* XXX TODO **/
+    }
+  }
 
- out:
-  if (client_id)
-    silc_free(client_id);
-  if (channel_id)
-    silc_free(channel_id);
-  silc_free(tmpid1);
-  silc_free(tmpid2);
+  silc_free(id);
 }
 
-/* Processes incoming REMOVE_ID packet. The packet is used to notify routers
-   that certain ID should be removed. After that the ID will become invalid. */
+/* Received New Channel List packet, list of New Channel List payloads inside
+   one packet. Process the New Channel payloads one by one. */
 
-void silc_server_remove_id(SilcServer server,
-                          SilcSocketConnection sock,
-                          SilcPacketContext *packet)
+void silc_server_new_channel_list(SilcServer server,
+                                 SilcSocketConnection sock,
+                                 SilcPacketContext *packet)
 {
-  SilcIDList id_list;
-  SilcIDPayload idp;
-  SilcIdType id_type;
-  void *id, *id_entry;
+  SilcPacketContext *new;
+  SilcBuffer buffer;
+  unsigned short len1, len2;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Processing New Channel List"));
 
   if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      server->server_type == SILC_SERVER ||
-      packet->src_id_type != SILC_ID_SERVER)
-    return;
-
-  idp = silc_id_payload_parse(packet->buffer);
-  if (!idp)
+      packet->src_id_type != SILC_ID_SERVER ||
+      server->server_type == SILC_SERVER)
     return;
 
-  id_type = silc_id_payload_get_type(idp);
-
-  id = silc_id_payload_get_id(idp);
-  if (!id)
-    goto out;
-
   /* If the sender of this packet is server and we are router we need to
-     broadcast this packet to other routers in the network. */
+     broadcast this packet to other routers in the network. Broadcast
+     this list packet instead of multiple New Channel packets. */
   if (!server->standalone && server->server_type == SILC_ROUTER &&
       sock->type == SILC_SOCKET_TYPE_SERVER &&
       !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
-    SILC_LOG_DEBUG(("Broadcasting received Remove ID packet"));
+    SILC_LOG_DEBUG(("Broadcasting received New Channel List packet"));
     silc_server_packet_send(server, server->router->connection,
                            packet->type, 
                            packet->flags | SILC_PACKET_FLAG_BROADCAST,
                            packet->buffer->data, packet->buffer->len, FALSE);
   }
 
-  if (sock->type == SILC_SOCKET_TYPE_SERVER)
-    id_list = server->local_list;
-  else
-    id_list = server->global_list;
-
-  /* Remove the ID */
-  switch (id_type) {
-  case SILC_ID_CLIENT:
-    id_entry = silc_idlist_find_client_by_id(id_list, (SilcClientID *)id, 
-                                            NULL);
-    if (id_entry) {
-      /* Remove from channels */
-      silc_server_remove_from_channels(server, NULL, id_entry);
-
-      /* Remove the client entry */
-      silc_idlist_del_client(id_list, (SilcClientEntry)id_entry);
-      server->stat.clients--;
+  /* Make copy of the original packet context, except for the actual
+     data buffer, which we will here now fetch from the original buffer. */
+  new = silc_packet_context_alloc();
+  new->type = SILC_PACKET_NEW_CHANNEL;
+  new->flags = packet->flags;
+  new->src_id = packet->src_id;
+  new->src_id_len = packet->src_id_len;
+  new->src_id_type = packet->src_id_type;
+  new->dst_id = packet->dst_id;
+  new->dst_id_len = packet->dst_id_len;
+  new->dst_id_type = packet->dst_id_type;
+
+  buffer = silc_buffer_alloc(512);
+  new->buffer = buffer;
+
+  while (packet->buffer->len) {
+    SILC_GET16_MSB(len1, packet->buffer->data);
+    if ((len1 > packet->buffer->len) ||
+       (len1 > buffer->truelen))
+      break;
 
-      SILC_LOG_DEBUG(("Removed client id(%s) from [%s] %s",
-                     silc_id_render(id, SILC_ID_CLIENT),
-                     sock->type == SILC_SOCKET_TYPE_SERVER ?
-                     "Server" : "Router", sock->hostname));
-    }
-    break;
+    SILC_GET16_MSB(len2, packet->buffer->data + 2 + len1);
+    if ((len2 > packet->buffer->len) ||
+       (len2 > buffer->truelen))
+      break;
 
-  case SILC_ID_SERVER:
-    id_entry = silc_idlist_find_server_by_id(id_list, (SilcServerID *)id,
-                                            NULL);
-    if (id_entry) {
-      silc_idlist_del_server(id_list, (SilcServerEntry)id_entry);
-      server->stat.servers--;
-
-      SILC_LOG_DEBUG(("Removed server id(%s) from [%s] %s",
-                     silc_id_render(id, SILC_ID_SERVER),
-                     sock->type == SILC_SOCKET_TYPE_SERVER ?
-                     "Server" : "Router", sock->hostname));
-    }
-    break;
+    silc_buffer_pull_tail(buffer, 4 + len1 + len2);
+    silc_buffer_put(buffer, packet->buffer->data, 4 + len1 + len2);
 
-  case SILC_ID_CHANNEL:
-    id_entry = silc_idlist_find_channel_by_id(id_list, (SilcChannelID *)id,
-                                             NULL);
-    if (id_entry) {
-      silc_idlist_del_channel(id_list, (SilcChannelEntry)id_entry);
-      server->stat.channels--;
-
-      SILC_LOG_DEBUG(("Removed channel id(%s) from [%s] %s",
-                     silc_id_render(id, SILC_ID_CHANNEL),
-                     sock->type == SILC_SOCKET_TYPE_SERVER ?
-                     "Server" : "Router", sock->hostname));
-    }
-    break;
+    /* Process the New Channel */
+    silc_server_new_channel(server, sock, new);
 
-  default:
-    break;
+    silc_buffer_push_tail(buffer, 4 + len1 + len2);
+    silc_buffer_pull(packet->buffer, 4 + len1 + len2);
   }
 
- out:
-  silc_id_payload_free(idp);
+  silc_buffer_free(buffer);
+  silc_free(new);
 }
 
-/* Processes received SET_MODE packet. The packet is used to distribute
-   the information about changed channel's or client's channel modes. */
+/* Received key agreement packet. This packet is never for us. It is to
+   the client in the packet's destination ID. Sending of this sort of packet
+   equals sending private message, ie. it is sent point to point from
+   one client to another. */
 
-void silc_server_set_mode(SilcServer server,
-                         SilcSocketConnection sock,
-                         SilcPacketContext *packet)
+void silc_server_key_agreement(SilcServer server,
+                              SilcSocketConnection sock,
+                              SilcPacketContext *packet)
 {
-  SilcSetModePayload payload = NULL;
-  SilcArgumentPayload args = NULL;
-  unsigned short mode_type;
-  unsigned int mode_mask;
-  unsigned char *tmp, *tmp2;
-  unsigned int tmp_len, tmp_len2;
-  unsigned char mode[4];
-  SilcClientID *client_id;
-  SilcChannelID *channel_id = NULL;
+  SilcClientID *id;
+  SilcServerEntry router;
+  SilcSocketConnection dst_sock;
   SilcClientEntry client;
-  SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
-
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      packet->src_id_type == SILC_ID_CLIENT)
-    return;
+  SilcIDListData idata;
 
   SILC_LOG_DEBUG(("Start"));
 
-  /* If we are router and this packet is not already broadcast packet
-     we will broadcast it. The sending socket really cannot be router or
-     the router is buggy. If this packet is coming from router then it must
-     have the broadcast flag set already and we won't do anything. */
-  if (!server->standalone && server->server_type == SILC_ROUTER &&
-      sock->type == SILC_SOCKET_TYPE_SERVER &&
-      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
-    SILC_LOG_DEBUG(("Broadcasting received Set Mode packet"));
-    silc_server_packet_send(server, server->router->connection, packet->type,
-                           packet->flags | SILC_PACKET_FLAG_BROADCAST, 
-                           packet->buffer->data, packet->buffer->len, FALSE);
-  }
-
-  /* Parse Set Mode payload */
-  payload = silc_set_mode_payload_parse(packet->buffer);
-  if (!payload)
+  if (packet->src_id_type != SILC_ID_CLIENT ||
+      packet->dst_id_type != SILC_ID_CLIENT)
     return;
 
-  mode_type = silc_set_mode_get_type(payload);
-  args = silc_set_mode_get_args(payload);
-  if (!args)
-    goto out;
+  if (!packet->dst_id)
+    return;
 
-  mode_mask = silc_set_mode_get_mode(payload);
-  SILC_PUT32_MSB(mode_mask, mode);
+  /* Decode destination Client ID */
+  id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
+  if (!id) {
+    SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
+    return;
+  }
 
-  switch (mode_type) {
-  case SILC_MODE_TYPE_CHANNEL:
-    /* Get Channel ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!channel_id)
-      goto out;
+  /* 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) {
+    /* It exists, now deliver the packet to the destination */
+    dst_sock = (SilcSocketConnection)client->connection;
 
-    /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                            channel_id, NULL);
-    if (!channel) {
-      channel = silc_idlist_find_channel_by_id(server->global_list, 
-                                              channel_id, NULL);
-      if (!channel)
-       goto out;
+    /* 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)client->router;
+      idata = (SilcIDListData)router;
+      silc_server_send_key_agreement(server, router->connection,
+                                    idata->send_key,
+                                    idata->hmac,
+                                    packet);
+      silc_free(id);
+      return;
     }
 
-    /* Get Client ID payload */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    /* Send CMODE_CHANGE notify to local channel */
-    silc_server_send_notify_to_channel(server, sock, channel, FALSE,
-                                      SILC_NOTIFY_TYPE_CMODE_CHANGE, 
-                                      2, tmp, tmp_len,
-                                      mode, sizeof(mode));
-
-    /* Change the mode */
-    channel->mode = mode_mask;
-    break;
+    /* Seems that client really is directly connected to us */
+    idata = (SilcIDListData)client;
+    silc_server_send_key_agreement(server, dst_sock, 
+                                  idata->send_key,
+                                  idata->hmac, packet);
+    silc_free(id);
+    return;
+  }
 
-  case SILC_MODE_TYPE_UCHANNEL:
-    /* Get Channel ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!channel_id)
-      goto out;
+  /* 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) {
+    router = server->router;
 
-    /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                            channel_id, NULL);
-    if (!channel) {
-      channel = silc_idlist_find_channel_by_id(server->global_list, 
-                                              channel_id, NULL);
-      if (!channel)
-       goto out;
+    /* Send to primary route */
+    if (router) {
+      dst_sock = (SilcSocketConnection)router->connection;
+      idata = (SilcIDListData)router;
+      silc_server_send_key_agreement(server, dst_sock, 
+                                    idata->send_key,
+                                    idata->hmac, packet);
     }
+    silc_free(id);
+    return;
+  }
 
-    /* Get Client ID payload */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    /* Get target Client ID */
-    tmp2 = silc_argument_get_arg_type(args, 3, &tmp_len2);
-    if (!tmp2)
-      goto out;
-    client_id = silc_id_payload_parse_id(tmp2, tmp_len2);
-    if (!client_id)
-      goto out;
+  /* 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) {
+      dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
+      router = (SilcServerEntry)dst_sock->user_data;
+      idata = (SilcIDListData)router;
 
-    /* Get target client entry */
-    client = silc_idlist_find_client_by_id(server->global_list, 
-                                          client_id, NULL);
-    if (!client) {
-      client = silc_idlist_find_client_by_id(server->local_list, 
-                                            client_id, NULL);
-      if (!client) {
-       silc_free(client_id);
-       goto out;
-      }
+      /* Get fastest route and send packet. */
+      if (router)
+       silc_server_send_key_agreement(server, dst_sock, 
+                                      idata->send_key,
+                                      idata->hmac, packet);
+      silc_free(id);
+      return;
     }
-    silc_free(client_id);
-
-    /* Send CUMODE_CHANGE notify to local channel */
-    silc_server_send_notify_to_channel(server, sock, channel, FALSE,
-                                      SILC_NOTIFY_TYPE_CUMODE_CHANGE, 2, 
-                                      tmp, tmp_len,
-                                      mode, sizeof(mode),
-                                      tmp2, tmp_len2);
-
-    /* Get entry to the channel user list */
-    silc_list_start(channel->user_list);
-    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
-      if (chl->client == client) {
-       /* Change the mode */
-       chl->mode = mode_mask;
-       break;
-      }
-
-    break;
-
-  default:
-    break;
   }
-
- out:
-  if (channel_id)
-    silc_free(channel_id);
-  if (args)
-    silc_argument_payload_free(args);
-  if (payload)
-    silc_set_mode_payload_free(payload);
 }