updates.
[silc.git] / apps / silcd / packet_receive.c
index 9321add3326ad09bd97862424c46e38531c8ae6a..65aba7318907e6c024dc0769da3c76a1570b54b2 100644 (file)
 
 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). */
+/* Received notify packet. Server can receive notify packets from router. 
+   Server then relays the notify messages to clients if needed. */
 
-void silc_server_private_message(SilcServer server,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet)
+void silc_server_notify(SilcServer server,
+                       SilcSocketConnection sock,
+                       SilcPacketContext *packet)
 {
-  SilcClientID *id;
-  SilcServerEntry router;
-  SilcSocketConnection dst_sock;
+  SilcNotifyPayload payload;
+  SilcNotifyType type;
+  SilcArgumentPayload args;
+  SilcChannelID *channel_id = NULL, *channel_id2;
+  SilcClientID *client_id, *client_id2;
+  SilcServerID *server_id;
+  SilcChannelEntry channel;
   SilcClientEntry client;
-  SilcIDListData idata;
+  SilcServerEntry server_entry;
+  SilcChannelClientEntry chl;
+  SilcIDCacheEntry cache;
+  SilcHashTableList htl;
+  uint32 mode;
+  unsigned char *tmp;
+  uint32 tmp_len;
+  bool local;
 
   SILC_LOG_DEBUG(("Start"));
 
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      packet->src_id_type != SILC_ID_SERVER)
+    return;
+
   if (!packet->dst_id)
-    goto err;
+    return;
 
-  /* 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"));
-    goto err;
+  /* If the packet is destined directly to a client then relay the packet
+     before processing it. */
+  if (packet->dst_id_type == SILC_ID_CLIENT) {
+    SilcIDListData idata;
+    SilcSocketConnection dst_sock;
+
+    /* Get the route to the client */
+    dst_sock = silc_server_get_client_route(server, packet->dst_id,
+                                           packet->dst_id_len, NULL, &idata);
+    if (dst_sock)
+      /* Relay the packet */
+      silc_server_relay_packet(server, dst_sock, idata->send_key,
+                              idata->hmac_receive, idata->psn_send++,
+                              packet, TRUE);
   }
 
-  /* 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 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(server, router->connection,
-                                      idata->send_key,
-                                      idata->hmac,
-                                      packet);
-      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);
+  /* Parse the Notify Payload */
+  payload = silc_notify_payload_parse(packet->buffer->data,
+                                     packet->buffer->len);
+  if (!payload)
     return;
-  }
 
-  /* 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(server, dst_sock, 
-                                      idata->send_key,
-                                      idata->hmac, packet);
-    }
-    return;
-  }
+  /* 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"));
+    if (packet->dst_id_type == SILC_ID_CHANNEL) {
+      /* Packet is destined to channel */
+      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                 packet->dst_id_type);
+      if (!channel_id)
+       goto out;
 
-  /* 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;
-
-      /* Get fastest route and send packet. */
-      if (router)
-       silc_server_send_private_message(server, dst_sock, 
-                                        idata->send_key,
-                                        idata->hmac, packet);
-      return;
+      silc_server_packet_send_dest(server, server->router->connection, 
+                                  packet->type,
+                                  packet->flags | SILC_PACKET_FLAG_BROADCAST, 
+                                  channel_id, SILC_ID_CHANNEL,
+                                  packet->buffer->data, packet->buffer->len, 
+                                  FALSE);
+      silc_server_backup_send_dest(server, (SilcServerEntry)sock->user_data, 
+                                  packet->type, packet->flags,
+                                  channel_id, SILC_ID_CHANNEL,
+                                  packet->buffer->data, packet->buffer->len, 
+                                  FALSE, TRUE);
+    } else {
+      /* Packet is destined to client or server */
+      silc_server_packet_send(server, server->router->connection, 
+                             packet->type,
+                             packet->flags | SILC_PACKET_FLAG_BROADCAST, 
+                             packet->buffer->data, packet->buffer->len, 
+                             FALSE);
+      silc_server_backup_send(server, (SilcServerEntry)sock->user_data,
+                             packet->type, packet->flags,
+                             packet->buffer->data, packet->buffer->len, 
+                             FALSE, TRUE);
     }
   }
 
- 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. */
+  type = silc_notify_get_type(payload);
+  args = silc_notify_get_args(payload);
+  if (!args)
+    goto out;
 
-void silc_server_command_reply(SilcServer server,
-                              SilcSocketConnection sock,
-                              SilcPacketContext *packet)
-{
-  SilcBuffer buffer = packet->buffer;
-  SilcClientEntry client = NULL;
-  SilcSocketConnection dst_sock;
-  SilcIDListData idata;
-  SilcClientID *id = NULL;
+  switch(type) {
+  case SILC_NOTIFY_TYPE_JOIN:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
+    SILC_LOG_DEBUG(("JOIN notify"));
 
-  SILC_LOG_DEBUG(("Start"));
+    /* 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;
 
-  /* Source must be server or router */
-  if (packet->src_id_type != SILC_ID_SERVER &&
-      sock->type != SILC_SOCKET_TYPE_ROUTER)
-    return;
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+    silc_free(channel_id);
 
-  if (packet->dst_id_type == SILC_ID_CHANNEL)
-    return;
+    /* 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;
 
-  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 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->global_list, 
+                                          client_id, server->server_type, 
+                                          NULL);
     if (!client) {
-      SILC_LOG_ERROR(("Cannot process command reply to unknown client"));
-      silc_free(id);
-      return;
+      client = silc_idlist_find_client_by_id(server->local_list, 
+                                            client_id, server->server_type,
+                                            NULL);
+      if (!client) {
+       /* If router did not find the client the it is bogus */
+       if (server->server_type != SILC_SERVER)
+         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, 0);
+       if (!client) {
+         SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
+         silc_free(client_id);
+         goto out;
+       }
+
+       client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+      }
     }
-  }
 
-  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;
+    /* Do not process the notify if the client is not registered */
+    if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
+      break;
+
+    /* Do not add client to channel if it is there already */
+    if (silc_server_client_on_channel(client, channel)) {
+      SILC_LOG_DEBUG(("Client already on channel"));
+      break;
     }
-  }
 
-  /* Execute command reply locally for the command */
-  silc_server_command_reply_process(server, sock, buffer);
+    /* Send to channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
 
-  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);
+    if (server->server_type != SILC_ROUTER && 
+       sock->type == SILC_SOCKET_TYPE_ROUTER)
+      /* The channel is global now */
+      channel->global_users = TRUE;
 
-    silc_free(id);
-  }
-}
+    SILC_LOG_DEBUG(("Joining to channel %s", channel->channel_name));
 
-/* Process received channel message. The message can be originated from
-   client or server. */
+    /* 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;
 
-void silc_server_channel_message(SilcServer server,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet)
-{
-  SilcChannelEntry channel = NULL;
-  SilcChannelClientEntry chl;
-  SilcChannelID *id = NULL;
-  void *sender = NULL;
+    /* If this is the first one on the channel then it is the founder of
+       the channel. */
+    if (!silc_hash_table_count(channel->user_list))
+      chl->mode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
 
-  SILC_LOG_DEBUG(("Processing channel message"));
+    silc_hash_table_add(channel->user_list, client, chl);
+    silc_hash_table_add(client->channels, channel, chl);
+    silc_free(client_id);
+    channel->user_count++;
 
-  /* Sanity checks */
-  if (packet->dst_id_type != SILC_ID_CHANNEL) {
-    SILC_LOG_DEBUG(("Received bad message for channel, dropped"));
-    goto out;
-  }
+    break;
 
-  /* 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_LOG_DEBUG(("Could not find channel"));
-      goto out;
+  case SILC_NOTIFY_TYPE_LEAVE:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
+    SILC_LOG_DEBUG(("LEAVE notify"));
+
+    if (!channel_id) {
+      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                 packet->dst_id_type);
+      if (!channel_id)
+       goto out;
     }
-  }
 
-  /* 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;
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) { 
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
     }
-    if (chl == SILC_LIST_END)
+
+    /* 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;
+    }
 
-  /* 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);
+    /* Get client entry */
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, TRUE, NULL);
+    if (!client) {
+      client = silc_idlist_find_client_by_id(server->local_list, 
+                                            client_id, TRUE, NULL);
+      if (!client) {
+       silc_free(client_id);
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+    silc_free(client_id);
 
- out:
-  if (sender)
-    silc_free(sender);
-  if (id)
-    silc_free(id);
-}
+    /* Check if on channel */
+    if (!silc_server_client_on_channel(client, channel))
+      break;
 
-/* Received channel key packet. We distribute the key to all of our locally
-   connected clients on the channel. */
+    /* Send the leave notify to channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
 
-void silc_server_channel_key(SilcServer server,
-                            SilcSocketConnection sock,
-                            SilcPacketContext *packet)
-{
-  SilcBuffer buffer = packet->buffer;
-  SilcChannelEntry channel;
+    /* Remove the user from channel */
+    silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
+    break;
 
-  if (packet->src_id_type != SILC_ID_SERVER)
-    return;
+  case SILC_NOTIFY_TYPE_SIGNOFF:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
+    SILC_LOG_DEBUG(("SIGNOFF notify"));
 
-  /* Save the channel key */
-  channel = silc_server_save_channel_key(server, buffer, NULL);
-  if (!channel)
-    return;
+    /* 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 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 entry */
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, TRUE, &cache);
+    if (!client) {
+      client = silc_idlist_find_client_by_id(server->local_list, 
+                                            client_id, TRUE, &cache);
+      if (!client) {
+       silc_free(client_id);
+       goto out;
+      }
+    }
+    silc_free(client_id);
 
-/* Received packet to replace a ID. This checks that the requested ID
-   exists and replaces it with the new one. */
+    /* Get signoff message */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (tmp_len > 128)
+      tmp = NULL;
 
-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;
-  int ret;
+    /* Update statistics */
+    server->stat.clients--;
+    if (server->server_type == SILC_ROUTER)
+      server->stat.cell_clients--;
+    SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+    SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
 
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      packet->src_id_type == SILC_ID_CLIENT)
-    return;
+    /* Remove the client from all channels. */
+    silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, FALSE);
 
-  SILC_LOG_DEBUG(("Replacing ID"));
+    client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+    cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+    break;
 
-  ret = silc_buffer_unformat(buffer,
-                            SILC_STR_UI_SHORT(&old_id_type),
-                            SILC_STR_UI16_NSTRING_ALLOC(&old_id, &old_id_len),
-                            SILC_STR_UI_SHORT(&new_id_type),
-                            SILC_STR_UI16_NSTRING_ALLOC(&new_id, &new_id_len),
-                            SILC_STR_END);
-  if (ret == -1)
-    goto out;
+  case SILC_NOTIFY_TYPE_TOPIC_SET:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
 
-  if (old_id_type != new_id_type)
-    goto out;
+    SILC_LOG_DEBUG(("TOPIC SET notify"));
 
-  if (old_id_len != silc_id_get_len(old_id_type) ||
-      new_id_len != silc_id_get_len(new_id_type))
-    goto out;
+    if (!channel_id) {
+      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                 packet->dst_id_type);
+      if (!channel_id)
+       goto out;
+    }
 
-  id = silc_id_str2id(old_id, old_id_len, old_id_type);
-  if (!id)
-    goto out;
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
 
-  id2 = silc_id_str2id(new_id, new_id_len, new_id_type);
-  if (!id2)
-    goto out;
+    /* Get the topic */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp) {
+      silc_free(channel_id);
+      goto out;
+    }
 
-  /* 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);
-  }
+    silc_free(channel->topic);
+    channel->topic = strdup(tmp);
 
-  /* Replace the old ID */
-  switch(old_id_type) {
-  case SILC_ID_CLIENT:
+    /* 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)));
 
-      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);
+      /* 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) {
        /* 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)
@@ -372,617 +431,780 @@ void silc_server_replace_id(SilcServer server,
 
        /* 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_SERVER)));
-    SILC_LOG_DEBUG(("New Server ID id(%s)", 
-                   silc_id_render(id2, SILC_ID_SERVER)));
-    if (silc_idlist_replace_server_id(server->local_list, id, id2) == NULL)
-      if (server->server_type == SILC_ROUTER)
-       silc_idlist_replace_server_id(server->global_list, id, id2);
-    break;
-
-  case SILC_ID_CHANNEL:
-    SILC_LOG_DEBUG(("Old Channel ID id(%s)", 
-                   silc_id_render(id, SILC_ID_CHANNEL)));
-    SILC_LOG_DEBUG(("New Channel ID id(%s)", 
-                   silc_id_render(id2, SILC_ID_CHANNEL)));
-    if (silc_idlist_replace_channel_id(server->local_list, id, id2) == NULL)
-      silc_idlist_replace_channel_id(server->global_list, id, id2);
-    break;
+  case SILC_NOTIFY_TYPE_CMODE_CHANGE:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
+    
+    SILC_LOG_DEBUG(("CMODE CHANGE notify"));
+      
+    if (!channel_id) {
+      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->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->local_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);
-}
+    /* 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);
 
-/* Received New Client packet and processes it.  Creates Client ID for the
-   client. Client becomes registered after calling this functions. */
+    /* Check if mode changed */
+    if (channel->mode == mode)
+      break;
 
-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;
+    /* 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_LOG_DEBUG(("Creating new client"));
+    /* If the channel had private keys set and the mode was removed then
+       we must re-generate and re-distribute a new channel key */
+    if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY &&
+       !(mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+      /* Re-generate channel key */
+      if (!silc_server_create_channel_key(server, channel, 0))
+       goto out;
+      
+      /* Send the channel key. This sends it to our local clients and if
+        we are normal server to our router as well. */
+      silc_server_send_channel_key(server, NULL, channel, 
+                                  server->server_type == SILC_ROUTER ? 
+                                  FALSE : !server->standalone);
+    }
 
-  if (sock->type != SILC_SOCKET_TYPE_CLIENT)
-    return NULL;
+    /* Change mode */
+    channel->mode = mode;
+    silc_free(channel_id);
 
-  /* Take client entry */
-  client = (SilcClientEntry)sock->user_data;
-  idata = (SilcIDListData)client;
+    /* Get the hmac */
+    tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
+    if (tmp) {
+      unsigned char hash[32];
 
-  /* 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;
-  }
+      if (channel->hmac)
+       silc_hmac_free(channel->hmac);
+      if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
+       goto out;
 
-  /* 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;
-  }
+      /* Set the HMAC key out of current channel key. The client must do
+        this locally. */
+      silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, 
+                    channel->key_len / 8, 
+                    hash);
+      silc_hmac_set_key(channel->hmac, hash, 
+                       silc_hash_len(silc_hmac_get_hash(channel->hmac)));
+      memset(hash, 0, sizeof(hash));
+    }
 
-  /* Create Client ID */
-  silc_id_create_client_id(server->id, server->rng, server->md5hash,
-                          username, &client_id);
+    /* Get the passphrase */
+    tmp = silc_argument_get_arg_type(args, 5, &tmp_len);
+    if (tmp) {
+      silc_free(channel->passphrase);
+      channel->passphrase = strdup(tmp);
+    }
 
-  /* Update client entry */
-  idata->registered = TRUE;
-  client->nickname = strdup(username);
-  client->username = username;
-  client->userinfo = realname;
-  client->id = client_id;
+    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);
+  case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
+    {
+      /* 
+       * Distribute the notify to local clients on the channel
+       */
+      SilcChannelClientEntry chl2 = NULL;
+      bool notify_sent = FALSE;
+      
+      SILC_LOG_DEBUG(("CUMODE CHANGE notify"));
+      
+      if (!channel_id) {
+       channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                   packet->dst_id_type);
+       if (!channel_id)
+         goto out;
+      }
 
-  /* 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);
+      /* Get channel entry */
+      channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                                channel_id, NULL);
+       if (!channel) {
+         silc_free(channel_id);
+         goto out;
+       }
+      }
 
-  /* 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);
+      /* 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);
+      
+      /* 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, TRUE, NULL);
+      if (!client) {
+       client = silc_idlist_find_client_by_id(server->local_list, 
+                                              client_id, TRUE, NULL);
+       if (!client) {
+         silc_free(client_id);
+         goto out;
+       }
+      }
+      silc_free(client_id);
 
-  return client;
-}
+      /* Get entry to the channel user list */
+      silc_hash_table_list(channel->user_list, &htl);
+      while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+       /* If the mode is channel founder and we already find a client 
+          to have that mode on the channel we will enforce the sender
+          to change the channel founder mode away. There can be only one
+          channel founder on the channel. */
+       if (server->server_type == SILC_ROUTER &&
+           mode & SILC_CHANNEL_UMODE_CHANFO &&
+           chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+         SilcBuffer idp;
+         unsigned char cumode[4];
+
+         if (chl->client == client && chl->mode == mode) {
+           notify_sent = TRUE;
+           break;
+         }
+
+         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+         silc_server_send_notify_cumode(server, sock, FALSE, channel, mode,
+                                        client->id, SILC_ID_CLIENT,
+                                        client->id);
+         
+         idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+         SILC_PUT32_MSB(mode, cumode);
+         silc_server_send_notify_to_channel(server, sock, channel, FALSE, 
+                                            SILC_NOTIFY_TYPE_CUMODE_CHANGE,
+                                            3, idp->data, idp->len,
+                                            cumode, 4,
+                                            idp->data, idp->len);
+         silc_buffer_free(idp);
+         notify_sent = TRUE;
+
+         /* Force the mode change if we alredy set the mode */
+         if (chl2) {
+           chl2->mode = mode;
+           silc_free(channel_id);
+           silc_hash_table_list_reset(&htl);
+           goto out;
+         }
+       }
+       
+       if (chl->client == client) {
+         if (chl->mode == mode) {
+           notify_sent = TRUE;
+           break;
+         }
+
+         SILC_LOG_DEBUG(("Changing the channel user mode"));
+
+         /* Change the mode */
+         chl->mode = mode;
+         if (!(mode & SILC_CHANNEL_UMODE_CHANFO))
+           break;
+         
+         chl2 = chl;
+       }
+      }
+      silc_hash_table_list_reset(&htl);
+      
+      /* Send the same notify to the channel */
+      if (!notify_sent)
+       silc_server_packet_send_to_channel(server, sock, channel, 
+                                          packet->type, 
+                                          FALSE, packet->buffer->data, 
+                                          packet->buffer->len, FALSE);
+      
+      silc_free(channel_id);
+      break;
+    }
 
-/* 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. */
+  case SILC_NOTIFY_TYPE_INVITE:
 
-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;
+    if (packet->dst_id_type == SILC_ID_CLIENT)
+      goto out;
 
-  SILC_LOG_DEBUG(("Creating new server"));
+    SILC_LOG_DEBUG(("INVITE notify"));
 
-  if (sock->type != SILC_SOCKET_TYPE_SERVER &&
-      sock->type != SILC_SOCKET_TYPE_ROUTER)
-    return NULL;
+    /* 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;
 
-  /* Take server entry */
-  new_server = (SilcServerEntry)sock->user_data;
-  idata = (SilcIDListData)new_server;
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+    silc_free(channel_id);
 
-  /* 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;
-  }
+    /* Get the added invite */
+    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+    if (tmp) {
+      if (!channel->invite_list)
+       channel->invite_list = silc_calloc(tmp_len + 2, 
+                                          sizeof(*channel->invite_list));
+      else
+       channel->invite_list = silc_realloc(channel->invite_list, 
+                                           sizeof(*channel->invite_list) * 
+                                           (tmp_len + 
+                                            strlen(channel->invite_list) + 
+                                            2));
+      if (tmp[tmp_len - 1] == ',')
+       tmp[tmp_len - 1] = '\0';
+      
+      strncat(channel->invite_list, tmp, tmp_len);
+      strncat(channel->invite_list, ",", 1);
+    }
 
-  /* 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;
-  }
+    /* Get the deleted invite */
+    tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
+    if (tmp && channel->invite_list) {
+      char *start, *end, *n;
+      
+      if (!strncmp(channel->invite_list, tmp, 
+                  strlen(channel->invite_list) - 1)) {
+       silc_free(channel->invite_list);
+       channel->invite_list = NULL;
+      } else {
+       start = strstr(channel->invite_list, tmp);
+       if (start && strlen(start) >= tmp_len) {
+         end = start + tmp_len;
+         n = silc_calloc(strlen(channel->invite_list) - tmp_len, sizeof(*n));
+         strncat(n, channel->invite_list, start - channel->invite_list);
+         strncat(n, end + 1, ((channel->invite_list + 
+                               strlen(channel->invite_list)) - end) - 1);
+         silc_free(channel->invite_list);
+         channel->invite_list = n;
+       }
+      }
+    }
 
-  if (id_len > buffer->len) {
-    silc_free(id_string);
-    silc_free(server_name);
-    return NULL;
-  }
+    break;
 
-  /* 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);
+  case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
+    /*
+     * Distribute to the local clients on the channel and change the
+     * channel ID.
+     */
 
-  /* Update client entry */
-  idata->registered = TRUE;
-  new_server->server_name = server_name;
-  new_server->id = server_id;
+    SILC_LOG_DEBUG(("CHANNEL CHANGE"));
 
-  /* 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);
+    if (sock->type != SILC_SOCKET_TYPE_ROUTER)
+      break;
 
-  /* 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);
+    /* Get the old 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 (server->server_type == SILC_ROUTER)
-    server->stat.cell_servers++;
+    /* Get the 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;
+      }
+    }
 
-  return new_server;
-}
+    /* Send the notify to the channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
 
-/* Processes incoming New ID packet. New ID Payload is used to distribute
-   information about newly registered clients and servers. */
+    /* Get the new Channel ID */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+    channel_id2 = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id2)
+      goto out;
 
-void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
-                       SilcPacketContext *packet)
-{
-  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(("Old Channel ID id(%s)", 
+                   silc_id_render(channel_id, SILC_ID_CHANNEL)));
+    SILC_LOG_DEBUG(("New Channel ID id(%s)", 
+                   silc_id_render(channel_id2, SILC_ID_CHANNEL)));
+
+    /* Replace the Channel ID */
+    if (!silc_idlist_replace_channel_id(server->local_list, channel_id,
+                                       channel_id2))
+      if (!silc_idlist_replace_channel_id(server->global_list, channel_id,
+                                         channel_id2)) {
+       silc_free(channel_id2);
+       channel_id2 = NULL;
+      }
 
-  SILC_LOG_DEBUG(("Processing new ID"));
+    if (channel_id2) {
+      SilcBuffer users = NULL, users_modes = NULL;
+
+      /* Re-announce this channel which ID was changed. */
+      silc_server_send_new_channel(server, sock, FALSE, channel->channel_name,
+                                  channel->id, 
+                                  silc_id_get_len(channel->id, 
+                                                  SILC_ID_CHANNEL),
+                                  channel->mode);
+
+      /* Re-announce our clients on the channel as the ID has changed now */
+      silc_server_announce_get_channel_users(server, channel, &users,
+                                            &users_modes);
+      if (users) {
+       silc_buffer_push(users, users->data - users->head);
+       silc_server_packet_send(server, sock,
+                               SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                               users->data, users->len, FALSE);
+       silc_buffer_free(users);
+      }
+      if (users_modes) {
+       silc_buffer_push(users_modes, users_modes->data - users_modes->head);
+       silc_server_packet_send_dest(server, sock,
+                                    SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                    channel->id, SILC_ID_CHANNEL,
+                                    users_modes->data, 
+                                    users_modes->len, FALSE);
+       silc_buffer_free(users_modes);
+      }
 
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      server->server_type == SILC_SERVER ||
-      packet->src_id_type != SILC_ID_SERVER)
-    return;
+      /* Re-announce channel's topic */
+      if (channel->topic) {
+       silc_server_send_notify_topic_set(server, sock,
+                                         server->server_type == SILC_ROUTER ?
+                                         TRUE : FALSE, channel, 
+                                         channel->id, SILC_ID_CHANNEL,
+                                         channel->topic);
+      }
+    }
 
-  idp = silc_id_payload_parse(buffer);
-  if (!idp)
-    return;
+    silc_free(channel_id);
 
-  id_type = silc_id_payload_get_type(idp);
+    break;
 
-  /* Normal server cannot have other normal server connections */
-  if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER)
-    goto out;
+  case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
+    /* 
+     * Remove the server entry and all clients that this server owns.
+     */
 
-  id = silc_id_payload_get_id(idp);
-  if (!id)
-    goto out;
+    SILC_LOG_DEBUG(("SERVER SIGNOFF notify"));
 
-  /* 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);
-  }
+    /* Get Server ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    server_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!server_id)
+      goto out;
 
-  if (sock->type == SILC_SOCKET_TYPE_SERVER)
-    id_list = server->local_list;
-  else
-    id_list = server->global_list;
+    /* Get server entry */
+    server_entry = silc_idlist_find_server_by_id(server->global_list, 
+                                                server_id, TRUE, NULL);
+    local = TRUE;
+    if (!server_entry) {
+      server_entry = silc_idlist_find_server_by_id(server->local_list, 
+                                                  server_id, TRUE, NULL);
+      local = TRUE;
+      if (!server_entry) {
+       /* If we are normal server then we might not have the server. Check
+          whether router was kind enough to send the list of all clients
+          that actually was to be removed. Remove them if the list is
+          available. */
+       if (server->server_type != SILC_ROUTER &&
+           silc_argument_get_arg_num(args) > 1) {
+         int i;
+
+         for (i = 1; i < silc_argument_get_arg_num(args); i++) {
+           /* Get Client ID */
+           tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
+           if (!tmp)
+             continue;
+           client_id = silc_id_payload_parse_id(tmp, tmp_len);
+           if (!client_id)
+             continue;
+
+           /* Get client entry */
+           client = silc_idlist_find_client_by_id(server->global_list, 
+                                                  client_id, TRUE, &cache);
+           local = TRUE;
+           if (!client) {
+             client = silc_idlist_find_client_by_id(server->local_list, 
+                                                    client_id, TRUE, &cache);
+             local = FALSE;
+             if (!client) {
+               silc_free(client_id);
+               continue;
+             }
+           }
+           silc_free(client_id);
+
+           /* Update statistics */
+           server->stat.clients--;
+           if (server->server_type == SILC_ROUTER)
+             server->stat.cell_clients--;
+           SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+           SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+
+           /* Remove the client from all channels. */
+           silc_server_remove_from_channels(server, NULL, client, 
+                                            TRUE, NULL, FALSE);
+
+           /* Remove the client */
+           silc_idlist_del_client(local ? server->local_list :
+                                  server->global_list, client);
+         }
+       }
 
-  router_sock = sock;
-  router = sock->user_data;
+       silc_free(server_id);
+       goto out;
+      }
+    }
+    silc_free(server_id);
 
-  switch(id_type) {
-  case SILC_ID_CLIENT:
-    {
-      SilcClientEntry entry;
+    /* Free all client entries that this server owns as they will
+       become invalid now as well. */
+    silc_server_remove_clients_by_server(server, server_entry, TRUE);
 
-      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;
+    /* Remove the server entry */
+    silc_idlist_del_server(local ? server->local_list :
+                          server->global_list, server_entry);
 
-      if (sock->type == SILC_SOCKET_TYPE_SERVER)
-       server->stat.cell_clients++;
-      server->stat.clients++;
+    /* XXX update statistics */
 
-#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
-    }
     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));
+  case SILC_NOTIFY_TYPE_KICKED:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
     
-    /* 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++;
+    SILC_LOG_DEBUG(("KICKED notify"));
+      
+    if (!channel_id) {
+      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                 packet->dst_id_type);
+      if (!channel_id)
+       goto out;
+    }
 
-#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_ID_CHANNEL:
-    SILC_LOG_ERROR(("Channel cannot be registered with NEW_ID packet"));
-    break;
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+    silc_free(channel_id);
 
-  default:
-    break;
-  }
+    /* 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;
 
- out:
-  silc_id_payload_free(idp);
-}
+    /* If the the client is not in local list we check global list */
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, TRUE, NULL);
+    if (!client) {
+      client = silc_idlist_find_client_by_id(server->local_list, 
+                                            client_id, TRUE, NULL);
+      if (!client) {
+       silc_free(client_id);
+       goto out;
+      }
+    }
 
-/* Receoved New Id List packet, list of New ID payloads inside one
-   packet. Process the New ID payloads one by one. */
+    /* Send to channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
 
-void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock,
-                            SilcPacketContext *packet)
-{
-  SilcPacketContext *new_id;
-  SilcBuffer idp;
-  unsigned short id_len;
+    /* Remove the client from channel */
+    silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
 
-  SILC_LOG_DEBUG(("Processing New ID List"));
+    break;
 
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      packet->src_id_type != SILC_ID_SERVER)
-    return;
+  case SILC_NOTIFY_TYPE_KILLED:
+    {
+      /* 
+       * Distribute the notify to local clients on channels
+       */
+      unsigned char *id;
+      uint32 id_len;
+    
+      SILC_LOG_DEBUG(("KILLED notify"));
+      
+      /* Get client ID */
+      id = silc_argument_get_arg_type(args, 1, &id_len);
+      if (!id)
+       goto out;
+      client_id = silc_id_payload_parse_id(id, id_len);
+      if (!client_id)
+       goto out;
 
-  /* 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;
+      /* If the the client is not in local list we check global list */
+      client = silc_idlist_find_client_by_id(server->global_list, 
+                                            client_id, TRUE, NULL);
+      if (!client) {
+       client = silc_idlist_find_client_by_id(server->local_list, 
+                                              client_id, TRUE, NULL);
+       if (!client) {
+         silc_free(client_id);
+         goto out;
+       }
+      }
+      silc_free(client_id);
 
-  idp = silc_buffer_alloc(256);
-  new_id->buffer = idp;
+      /* If the client is one of ours, then close the connection to the
+        client now. This removes the client from all channels as well. */
+      if (packet->dst_id_type == SILC_ID_CLIENT && client->connection) {
+       sock = client->connection;
+       silc_server_free_client_data(server, NULL, client, FALSE, NULL);
+       silc_server_close_connection(server, sock);
+       break;
+      }
 
-  while (packet->buffer->len) {
-    SILC_GET16_MSB(id_len, packet->buffer->data + 2);
-    if ((id_len > packet->buffer->len) ||
-       (id_len > idp->truelen))
-      break;
+      /* Get comment */
+      tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+      if (tmp_len > 128)
+       tmp = NULL;
 
-    silc_buffer_pull_tail(idp, 4 + id_len);
-    silc_buffer_put(idp, packet->buffer->data, 4 + id_len);
+      /* Send the notify to local clients on the channels except to the
+        client who is killed. */
+      silc_server_send_notify_on_channels(server, client, client,
+                                         SILC_NOTIFY_TYPE_KILLED, 
+                                         tmp ? 2 : 1,
+                                         id, id_len, 
+                                         tmp, tmp_len);
 
-    /* Process the New ID */
-    silc_server_new_id(server, sock, new_id);
+      /* Remove the client from all channels */
+      silc_server_remove_from_channels(server, NULL, client, FALSE, NULL, 
+                                      FALSE);
 
-    silc_buffer_push_tail(idp, 4 + id_len);
-    silc_buffer_pull(packet->buffer, 4 + id_len);
-  }
+      break;
+    }
 
-  silc_buffer_free(idp);
-  silc_free(new_id);
-}
+  case SILC_NOTIFY_TYPE_UMODE_CHANGE:
+    /*
+     * Save the mode of the client.
+     */
 
-/* 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. */
+    SILC_LOG_DEBUG(("UMODE_CHANGE notify"));
 
-void silc_server_new_channel(SilcServer server,
-                            SilcSocketConnection sock,
-                            SilcPacketContext *packet)
-{
-  unsigned char *id;
-  SilcChannelID *channel_id;
-  unsigned short channel_id_len;
-  char *channel_name;
-  int ret;
+    /* 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(("Processing New Channel"));
+    /* Get client entry */
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, TRUE, NULL);
+    if (!client) {
+      client = silc_idlist_find_client_by_id(server->local_list, 
+                                            client_id, TRUE, NULL);
+      if (!client) {
+       silc_free(client_id);
+       goto out;
+      }
+    }
+    silc_free(client_id);
 
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      packet->src_id_type != SILC_ID_SERVER ||
-      server->server_type == SILC_SERVER)
-    return;
+    /* Get the mode */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+    SILC_GET32_MSB(mode, tmp);
+
+#define SILC_UMODE_STATS_UPDATE(oper, mod)     \
+do {                                           \
+    if (client->mode & (mod)) {                        \
+      if (!(mode & (mod))) {                   \
+       if (client->connection)                 \
+         server->stat.my_ ## oper ## _ops--;   \
+        if (server->server_type == SILC_ROUTER)        \
+         server->stat. oper ## _ops--;         \
+      }                                                \
+    } else {                                   \
+      if (mode & (mod)) {                      \
+       if (client->connection)                 \
+         server->stat.my_ ## oper ## _ops++;   \
+        if (server->server_type == SILC_ROUTER)        \
+         server->stat. oper ## _ops++;         \
+      }                                                \
+    }                                          \
+} while(0)
+
+    /* Update statistics */
+    SILC_UMODE_STATS_UPDATE(server, SILC_UMODE_SERVER_OPERATOR);
+    SILC_UMODE_STATS_UPDATE(router, SILC_UMODE_ROUTER_OPERATOR);
+
+    /* Save the mode */
+    client->mode = mode;
 
-  /* Parse payload */
-  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(id, channel_id_len, SILC_ID_CHANNEL);
-  if (!channel_id)
-    return;
+    break;
 
-  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. */
+  case SILC_NOTIFY_TYPE_BAN:
+    /*
+     * Save the ban
+     */
 
-    SILC_LOG_DEBUG(("New channel id(%s) from [Router] %s",
-                   silc_id_render(channel_id, SILC_ID_CHANNEL), 
-                   sock->hostname));
+    SILC_LOG_DEBUG(("BAN notify"));
     
-    silc_idlist_add_channel(server->global_list, channel_name, 0, channel_id, 
-                           server->router->connection, NULL);
-
-    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));
+    /* 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;
     
-    /* 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);
-
-    /* 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. */
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
     if (!channel) {
-      channel = silc_server_create_new_channel_with_id(server, NULL,
-                                                      channel_name,
-                                                      channel_id);
-      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);
-
-    } 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 Replace ID packet to the server to
-          force the ID change. */
-       SILC_LOG_DEBUG(("Forcing the server to change Channel ID"));
-       silc_server_send_replace_id(server, sock, FALSE, 
-                                   channel_id, SILC_ID_CHANNEL,
-                                   SILC_ID_CHANNEL_LEN,
-                                   channel->id, SILC_ID_CHANNEL,
-                                   SILC_ID_CHANNEL_LEN);
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
       }
+    }
+    silc_free(channel_id);
 
-      /* 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);
+    /* Get the new ban and add it to the ban list */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (tmp) {
+      if (!channel->ban_list)
+       channel->ban_list = silc_calloc(tmp_len + 2, 
+                                       sizeof(*channel->ban_list));
+      else
+       channel->ban_list = silc_realloc(channel->ban_list, 
+                                        sizeof(*channel->ban_list) * 
+                                        (tmp_len + 
+                                         strlen(channel->ban_list) + 2));
+      strncat(channel->ban_list, tmp, tmp_len);
+      strncat(channel->ban_list, ",", 1);
+    }
 
-      /* 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);
+    /* Get the ban to be removed and remove it from the list */
+    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+    if (tmp && channel->ban_list) {
+      char *start, *end, *n;
+      
+      if (!strncmp(channel->ban_list, tmp, strlen(channel->ban_list) - 1)) {
+       silc_free(channel->ban_list);
+       channel->ban_list = NULL;
+      } else {
+       start = strstr(channel->ban_list, tmp);
+       if (start && strlen(start) >= tmp_len) {
+         end = start + tmp_len;
+         n = silc_calloc(strlen(channel->ban_list) - tmp_len, sizeof(*n));
+         strncat(n, channel->ban_list, start - channel->ban_list);
+         strncat(n, end + 1, ((channel->ban_list + 
+                               strlen(channel->ban_list)) - end) - 1);
+         silc_free(channel->ban_list);
+         channel->ban_list = n;
+       }
+      }
     }
+    break;
+
+    /* Ignore rest of the notify types for now */
+  case SILC_NOTIFY_TYPE_NONE:
+  case SILC_NOTIFY_TYPE_MOTD:
+    break;
+  default:
+    break;
   }
 
-  silc_free(id);
+ out:
+  silc_notify_payload_free(payload);
 }
 
-/* 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_new_channel_list(SilcServer server,
-                                 SilcSocketConnection sock,
-                                 SilcPacketContext *packet)
+void silc_server_notify_list(SilcServer server,
+                            SilcSocketConnection sock,
+                            SilcPacketContext *packet)
 {
   SilcPacketContext *new;
   SilcBuffer buffer;
-  unsigned short len1, len2;
+  uint16 len;
 
-  SILC_LOG_DEBUG(("Processing New Channel List"));
+  SILC_LOG_DEBUG(("Processing Notify List"));
 
   if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      packet->src_id_type != SILC_ID_SERVER ||
-      server->server_type == SILC_SERVER)
+      packet->src_id_type != SILC_ID_SERVER)
     return;
 
   /* 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->type = SILC_PACKET_NOTIFY;
   new->flags = packet->flags;
   new->src_id = packet->src_id;
   new->src_id_len = packet->src_id_len;
@@ -991,839 +1213,1393 @@ void silc_server_new_channel_list(SilcServer server,
   new->dst_id_len = packet->dst_id_len;
   new->dst_id_type = packet->dst_id_type;
 
-  buffer = silc_buffer_alloc(512);
+  buffer = silc_buffer_alloc(1024);
   new->buffer = buffer;
 
   while (packet->buffer->len) {
-    SILC_GET16_MSB(len1, packet->buffer->data);
-    if ((len1 > packet->buffer->len) ||
-       (len1 > buffer->truelen))
+    SILC_GET16_MSB(len, packet->buffer->data + 2);
+    if (len > packet->buffer->len)
       break;
 
-    SILC_GET16_MSB(len2, packet->buffer->data + 2 + len1);
-    if ((len2 > packet->buffer->len) ||
-       (len2 > buffer->truelen))
-      break;
+    if (len > buffer->truelen) {
+      silc_buffer_free(buffer);
+      buffer = silc_buffer_alloc(1024 + len);
+    }
 
-    silc_buffer_pull_tail(buffer, 4 + len1 + len2);
-    silc_buffer_put(buffer, packet->buffer->data, 4 + len1 + len2);
+    silc_buffer_pull_tail(buffer, len);
+    silc_buffer_put(buffer, packet->buffer->data, len);
 
-    /* Process the New Channel */
-    silc_server_new_channel(server, sock, new);
+    /* Process the Notify */
+    silc_server_notify(server, sock, new);
 
-    silc_buffer_push_tail(buffer, 4 + len1 + len2);
-    silc_buffer_pull(packet->buffer, 4 + len1 + len2);
+    silc_buffer_push_tail(buffer, len);
+    silc_buffer_pull(packet->buffer, len);
   }
 
   silc_buffer_free(buffer);
   silc_free(new);
 }
 
-/* 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. 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_remove_channel_user(SilcServer server,
-                                    SilcSocketConnection sock,
-                                    SilcPacketContext *packet)
+void silc_server_private_message(SilcServer server,
+                                SilcSocketConnection sock,
+                                SilcPacketContext *packet)
 {
-  SilcBuffer buffer = packet->buffer;
-  unsigned char *tmp1 = NULL, *tmp2 = NULL;
-  unsigned short tmp1_len, tmp2_len;
-  SilcClientID *client_id = NULL;
-  SilcChannelID *channel_id = NULL;
-  SilcChannelEntry channel;
-  SilcClientEntry client;
-  int ret;
+  SilcSocketConnection dst_sock;
+  SilcIDListData idata;
 
-  SILC_LOG_DEBUG(("Removing user from channel"));
+  SILC_LOG_DEBUG(("Start"));
 
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      packet->src_id_type != SILC_ID_SERVER ||
-      server->server_type == SILC_SERVER)
+  if (packet->src_id_type != SILC_ID_CLIENT ||
+      packet->dst_id_type != SILC_ID_CLIENT || !packet->dst_id)
     return;
 
-  ret = silc_buffer_unformat(buffer,
-                            SILC_STR_UI16_NSTRING_ALLOC(&tmp1, &tmp1_len),
-                            SILC_STR_UI16_NSTRING_ALLOC(&tmp2, &tmp2_len),
-                            SILC_STR_END);
-  if (ret == -1)
-    goto out;
-
-  client_id = silc_id_str2id(tmp1, tmp1_len, SILC_ID_CLIENT);
-  channel_id = silc_id_str2id(tmp2, tmp2_len, SILC_ID_CHANNEL);
-  if (!client_id || !channel_id)
-    goto out;
-
-  /* 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);
-  }
+  /* Get the route to the client */
+  dst_sock = silc_server_get_client_route(server, packet->dst_id,
+                                         packet->dst_id_len, NULL, &idata);
+  if (!dst_sock) {
+    /* Send IDENTIFY command reply with error status to indicate that
+       such destination ID does not exist or is invalid */
+    SilcBuffer idp = silc_id_payload_encode_data(packet->dst_id,
+                                                packet->dst_id_len,
+                                                packet->dst_id_type);
+    if (!idp)
+      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;
-  }
+    if (packet->src_id_type == SILC_ID_CLIENT) {
+      SilcClientID *client_id = silc_id_str2id(packet->src_id,
+                                              packet->src_id_len,
+                                              packet->src_id_type);
+      silc_server_send_dest_command_reply(server, sock, 
+                                         client_id, SILC_ID_CLIENT,
+                                         SILC_COMMAND_IDENTIFY,
+                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, 
+                                         0, 1, 2, idp->data, idp->len);
+      silc_free(client_id);
+    } else {
+      silc_server_send_command_reply(server, sock, SILC_COMMAND_IDENTIFY,
+                                    SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, 
+                                    0, 1, 2, idp->data, idp->len);
+    }
 
-  /* 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_buffer_free(idp);
+    return;
   }
 
-  /* Remove user from channel */
-  silc_server_remove_from_one_channel(server, sock, channel, client, TRUE);
-
- 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);
+  /* Send the private message */
+  silc_server_send_private_message(server, dst_sock, idata->send_key,
+                                  idata->hmac_send, idata->psn_send++,
+                                  packet);
 }
 
-/* Received New Channel User List packet, list of New Channel User payloads
-   inside one packet.  Process the payloads one by one. */
+/* 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_new_channel_user_list(SilcServer server,
-                                      SilcSocketConnection sock,
-                                      SilcPacketContext *packet)
+void silc_server_private_message_key(SilcServer server,
+                                    SilcSocketConnection sock,
+                                    SilcPacketContext *packet)
 {
-  SilcPacketContext *new;
-  SilcBuffer buffer;
-  unsigned short len1, len2;
+  SilcSocketConnection dst_sock;
+  SilcIDListData idata;
 
-  SILC_LOG_DEBUG(("Processing New Channel User List"));
+  SILC_LOG_DEBUG(("Start"));
 
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      packet->src_id_type != SILC_ID_SERVER ||
-      server->server_type == SILC_SERVER)
+  if (packet->src_id_type != SILC_ID_CLIENT ||
+      packet->dst_id_type != SILC_ID_CLIENT)
     return;
 
-  /* 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_USER;
-  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(256);
-  new->buffer = buffer;
-
-  while (packet->buffer->len) {
-    SILC_GET16_MSB(len1, packet->buffer->data);
-    if ((len1 > packet->buffer->len) ||
-       (len1 > buffer->truelen))
-      break;
-
-    SILC_GET16_MSB(len2, packet->buffer->data + 2 + len1);
-    if ((len2 > packet->buffer->len) ||
-       (len2 > buffer->truelen))
-      break;
-
-    silc_buffer_pull_tail(buffer, 4 + len1 + len2);
-    silc_buffer_put(buffer, packet->buffer->data, 4 + len1 + len2);
-
-    /* Process the New Channel User */
-    silc_server_new_channel_user(server, sock, new);
+  if (!packet->dst_id)
+    return;
 
-    silc_buffer_push_tail(buffer, 4 + len1 + len2);
-    silc_buffer_pull(packet->buffer, 4 + len1 + len2);
-  }
+  /* Get the route to the client */
+  dst_sock = silc_server_get_client_route(server, packet->dst_id,
+                                         packet->dst_id_len, NULL, &idata);
+  if (!dst_sock)
+    return;
 
-  silc_buffer_free(buffer);
-  silc_free(new);
+  /* Relay the packet */
+  silc_server_relay_packet(server, dst_sock, idata->send_key,
+                          idata->hmac_send, idata->psn_send++, packet, FALSE);
 }
 
-/* Received notify packet. Server can receive notify packets from router. 
-   Server then relays the notify messages to clients if needed. */
+/* 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_notify(SilcServer server,
-                       SilcSocketConnection sock,
-                       SilcPacketContext *packet)
+void silc_server_command_reply(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;
+  SilcBuffer buffer = packet->buffer;
+  SilcClientEntry client = NULL;
+  SilcSocketConnection dst_sock;
+  SilcIDListData idata;
+  SilcClientID *id = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      packet->src_id_type != SILC_ID_SERVER)
-    return;
-
-  /* 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 &&
+  /* Source must be server or router */
+  if (packet->src_id_type != SILC_ID_SERVER &&
       sock->type != SILC_SOCKET_TYPE_ROUTER)
     return;
 
-  payload = silc_notify_payload_parse(packet->buffer);
-  if (!payload)
+  if (packet->dst_id_type == SILC_ID_CHANNEL)
     return;
 
-  type = silc_notify_get_type(payload);
-  args = silc_notify_get_args(payload);
-  if (!args)
-    goto out;
+  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, TRUE, NULL);
+    if (!client) {
+      SILC_LOG_ERROR(("Cannot process command reply to unknown client"));
+      silc_free(id);
+      return;
+    }
+  }
 
-  switch(type) {
-  case SILC_NOTIFY_TYPE_JOIN:
-    /* 
-     * Distribute the notify to local clients on the channel
-     */
-    SILC_LOG_DEBUG(("JOIN notify"));
+  if (packet->dst_id_type == SILC_ID_SERVER) {
+    /* For now this must be for us */
+    if (memcmp(packet->dst_id, server->id_string, server->id_string_len)) {
+      SILC_LOG_ERROR(("Cannot process command reply to unknown server"));
+      return;
+    }
+  }
 
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               packet->dst_id_type);
-    if (!channel_id)
-      goto out;
+  /* Execute command reply locally for the command */
+  silc_server_command_reply_process(server, sock, buffer);
 
-    /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                            channel_id, NULL);
-    if (!channel) {
-      silc_free(channel_id);
-      goto out;
-    }
+  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_send, idata->psn_send++,
+                       dst_sock->outbuf, buffer->len);
+    
+    /* Send the packet */
+    silc_server_packet_send_real(server, dst_sock, TRUE);
 
-    /* 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_free(id);
+  }
+}
 
-    /* Send to channel */
-    silc_server_packet_send_to_channel(server, NULL, channel, packet->type, 
-                                      FALSE, packet->buffer->data, 
-                                      packet->buffer->len, FALSE);
+/* Process received channel message. The message can be originated from
+   client or server. */
 
-    /* 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;
+void silc_server_channel_message(SilcServer server,
+                                SilcSocketConnection sock,
+                                SilcPacketContext *packet)
+{
+  SilcChannelEntry channel = NULL;
+  SilcChannelID *id = NULL;
+  void *sender = NULL;
+  void *sender_entry = NULL;
+  bool local = TRUE;
 
-      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, NULL);
-       if (!client) {
-         silc_free(channel_id);
-         silc_free(client_id);
-         goto out;
-       }
-      }
+  SILC_LOG_DEBUG(("Processing channel message"));
 
-      /* The channel is global now */
-      channel->global_users = TRUE;
+  /* Sanity checks */
+  if (packet->dst_id_type != SILC_ID_CHANNEL) {
+    SILC_LOG_DEBUG(("Received bad message for channel, dropped"));
+    goto out;
+  }
 
-      /* 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);
+  /* 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_LOG_DEBUG(("Could not find channel"));
+      goto out;
     }
-    break;
-
-  case SILC_NOTIFY_TYPE_LEAVE:
-    /* 
-     * Distribute the notify to local clients on the channel
-     */
-    SILC_LOG_DEBUG(("LEAVE notify"));
+  }
 
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               packet->dst_id_type);
-    if (!channel_id)
+  /* See that this client is on the channel. 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 (packet->src_id_type == SILC_ID_CLIENT) {
+    sender_entry = silc_idlist_find_client_by_id(server->local_list, 
+                                                sender, TRUE, NULL);
+    if (!sender_entry) {
+      local = FALSE;
+      sender_entry = silc_idlist_find_client_by_id(server->global_list, 
+                                                  sender, TRUE, NULL);
+    }
+    if (!sender_entry || !silc_server_client_on_channel(sender_entry, 
+                                                       channel)) {
+      SILC_LOG_DEBUG(("Client not on channel"));
       goto out;
+    }
 
-    /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                            channel_id, NULL);
-    if (!channel) { 
-      silc_free(channel_id);
+    /* If the packet is coming from router, but the client entry is
+       local entry to us then some router is rerouting this to us and it is
+       not allowed. */
+    if (server->server_type == SILC_ROUTER &&
+       sock->type == SILC_SOCKET_TYPE_ROUTER && local) {
+      SILC_LOG_DEBUG(("Channel message rerouted to the sender, drop it"));
       goto out;
     }
+  }
 
-    /* Get client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp) {
-      silc_free(channel_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, sender_entry,
+                                     packet->buffer->data,
+                                     packet->buffer->len, FALSE);
+
+ out:
+  if (sender)
+    silc_free(sender);
+  if (id)
+    silc_free(id);
+}
+
+/* Received channel key packet. We distribute the key to all of our locally
+   connected clients on the channel. */
+
+void silc_server_channel_key(SilcServer server,
+                            SilcSocketConnection sock,
+                            SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  SilcChannelEntry channel;
+
+  if (packet->src_id_type != SILC_ID_SERVER ||
+      (server->server_type == SILC_ROUTER &&
+       sock->type == SILC_SOCKET_TYPE_ROUTER))
+    return;
+
+  /* Save the channel key */
+  channel = silc_server_save_channel_key(server, buffer, NULL);
+  if (!channel)
+    return;
+
+  /* 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);
+  
+  if (server->server_type != SILC_BACKUP_ROUTER) {
+    /* Distribute to local cell backup routers. */
+    silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
+                           SILC_PACKET_CHANNEL_KEY, 0,
+                           buffer->data, buffer->len, FALSE, TRUE);
+  }
+}
+
+/* Received New Client packet and processes it.  Creates Client ID for the
+   client. Client becomes registered after calling this functions. */
+
+SilcClientEntry silc_server_new_client(SilcServer server,
+                                      SilcSocketConnection sock,
+                                      SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  SilcClientEntry client;
+  SilcClientID *client_id;
+  SilcBuffer reply;
+  SilcIDListData idata;
+  char *username = NULL, *realname = NULL, *id_string;
+  uint16 username_len;
+  uint32 id_len;
+  int ret;
+  char *hostname, *nickname;
+  int nickfail = 0;
+
+  SILC_LOG_DEBUG(("Creating new client"));
+
+  if (sock->type != SILC_SOCKET_TYPE_CLIENT)
+    return NULL;
+
+  /* Take client entry */
+  client = (SilcClientEntry)sock->user_data;
+  idata = (SilcIDListData)client;
+
+  /* Remove the old cache entry. */
+  if (!silc_idcache_del_by_context(server->local_list->clients, client)) {
+    SILC_LOG_ERROR(("Lost client's cache entry - bad thing"));
+    silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                                  "Unknown client");
+    return NULL;
+  }
+
+  /* Parse incoming packet */
+  ret = silc_buffer_unformat(buffer,
+                            SILC_STR_UI16_NSTRING_ALLOC(&username, 
+                                                        &username_len),
+                            SILC_STR_UI16_STRING_ALLOC(&realname),
+                            SILC_STR_END);
+  if (ret == -1) {
+    silc_free(username);
+    silc_free(realname);
+    silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                                  "Incomplete client information");
+    return NULL;
+  }
+
+  if (!username) {
+    silc_free(username);
+    silc_free(realname);
+    silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                                  "Incomplete client information");
+    return NULL;
+  }
+
+  if (username_len > 128)
+    username[128] = '\0';
+
+  /* Check for bad characters for nickname, and modify the nickname if
+     it includes those. */
+  if (silc_server_name_bad_chars(username, username_len)) {
+    nickname = silc_server_name_modify_bad(username, username_len);
+  } else {
+    nickname = strdup(username);
+  }
+
+  /* Make sanity checks for the hostname of the client. If the hostname
+     is provided in the `username' check that it is the same than the
+     resolved hostname, or if not resolved the hostname that appears in
+     the client's public key. If the hostname is not present then put
+     it from the resolved name or from the public key. */
+  if (strchr(username, '@')) {
+    SilcPublicKeyIdentifier pident;
+    int tlen = strcspn(username, "@");
+    char *phostname = NULL;
+
+    hostname = silc_memdup(username + tlen + 1, strlen(username) - tlen - 1);
+
+    if (strcmp(sock->hostname, sock->ip) && 
+       strcmp(sock->hostname, hostname)) {
+      silc_free(username);
+      silc_free(hostname);
+      silc_free(realname);
+      silc_server_disconnect_remote(server, sock, 
+                                   "Server closed connection: "
+                                   "Incomplete client information");
+      return NULL;
     }
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!client_id) {
-      silc_free(channel_id);
-      goto out;
+    
+    pident = silc_pkcs_decode_identifier(client->data.public_key->identifier);
+    if (pident) {
+      phostname = strdup(pident->host);
+      silc_pkcs_free_identifier(pident);
     }
 
-    /* Send to channel */
-    silc_server_packet_send_to_channel(server, NULL, channel, packet->type, 
-                                      FALSE, packet->buffer->data, 
-                                      packet->buffer->len, FALSE);
-
-    /* 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;
+    if (!strcmp(sock->hostname, sock->ip) && 
+       phostname && strcmp(phostname, hostname)) {
+      silc_free(username);
+      silc_free(hostname);
+      silc_free(phostname);
+      silc_free(realname);
+      silc_server_disconnect_remote(server, sock, 
+                                   "Server closed connection: "
+                                   "Incomplete client information");
+      return NULL;
+    }
+    
+    silc_free(phostname);
+  } else {
+    /* The hostname is not present, add it. */
+    char *newusername;
+    /* XXX For now we cannot take the host name from the public key since
+       they are not trusted or we cannot verify them as trusted. Just take
+       what the resolved name or address is. */
+#if 0
+    if (strcmp(sock->hostname, sock->ip)) {
+#endif
+      newusername = silc_calloc(strlen(username) + 
+                               strlen(sock->hostname) + 2,
+                               sizeof(*newusername));
+      strncat(newusername, username, strlen(username));
+      strncat(newusername, "@", 1);
+      strncat(newusername, sock->hostname, strlen(sock->hostname));
+      silc_free(username);
+      username = newusername;
+#if 0
+    } else {
+      SilcPublicKeyIdentifier pident = 
+       silc_pkcs_decode_identifier(client->data.public_key->identifier);
+      
+      if (pident) {
+       newusername = silc_calloc(strlen(username) + 
+                                 strlen(pident->host) + 2,
+                                 sizeof(*newusername));
+       strncat(newusername, username, strlen(username));
+       strncat(newusername, "@", 1);
+       strncat(newusername, pident->host, strlen(pident->host));
+       silc_free(username);
+       username = newusername;
+       silc_pkcs_free_identifier(pident);
       }
     }
-    silc_free(client_id);
+#endif
+  }
 
-    /* Remove the user from channel */
-    silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
-    break;
+  /* Create Client ID */
+  while (!silc_id_create_client_id(server, server->id, server->rng, 
+                                  server->md5hash, nickname, &client_id)) {
+    nickfail++;
+    snprintf(&nickname[strlen(nickname) - 1], 1, "%d", nickfail);
+  }
+
+  /* Update client entry */
+  idata->status |= SILC_IDLIST_STATUS_REGISTERED;
+  client->nickname = nickname;
+  client->username = username;
+  client->userinfo = realname ? realname : strdup(" ");
+  client->id = client_id;
+  id_len = silc_id_get_len(client_id, SILC_ID_CLIENT);
+
+  /* Add the client again to the ID cache */
+  silc_idcache_add(server->local_list->clients, client->nickname,
+                  client_id, client, 0, NULL);
+
+  /* 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, id_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 + id_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(id_len),
+                    SILC_STR_UI_XNSTRING(id_string, id_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);
+
+  /* Send some nice info to the client */
+  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                         ("Welcome to the SILC Network %s",
+                          username));
+  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,
+                           ("There are %d server operators and %d router "
+                            "operators online",
+                            server->stat.server_ops,
+                            server->stat.router_ops));
+    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                           ("I have %d operators online",
+                            server->stat.my_router_ops +
+                            server->stat.my_server_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, server_entry;
+  SilcServerID *server_id;
+  SilcIDListData idata;
+  unsigned char *server_name, *id_string;
+  uint16 id_len, name_len;
+  int ret;
+  bool local = TRUE;
+
+  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;
+
+  /* Remove the old cache entry */
+  if (!silc_idcache_del_by_context(server->local_list->servers, new_server)) {
+    silc_idcache_del_by_context(server->global_list->servers, new_server);
+    local = FALSE;
+  }
+
+  /* Parse the incoming packet */
+  ret = silc_buffer_unformat(buffer,
+                            SILC_STR_UI16_NSTRING_ALLOC(&id_string, &id_len),
+                            SILC_STR_UI16_NSTRING_ALLOC(&server_name, 
+                                                        &name_len),
+                            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;
+  }
+
+  if (name_len > 256)
+    server_name[255] = '\0';
+
+  /* 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);
+
+  /* Check that we do not have this ID already */
+  server_entry = silc_idlist_find_server_by_id(server->local_list, 
+                                              server_id, TRUE, NULL);
+  if (server_entry) {
+    silc_idcache_del_by_context(server->local_list->servers, server_entry);
+  } else {
+    server_entry = silc_idlist_find_server_by_id(server->global_list, 
+                                                server_id, TRUE, NULL);
+    if (server_entry) 
+      silc_idcache_del_by_context(server->global_list->servers, server_entry);
+  }
+
+  /* Update server entry */
+  idata->status |= SILC_IDLIST_STATUS_REGISTERED;
+  new_server->server_name = server_name;
+  new_server->id = server_id;
+  
+  SILC_LOG_DEBUG(("New server id(%s)",
+                 silc_id_render(server_id, SILC_ID_SERVER)));
+
+  /* Add again the entry to the ID cache. */
+  silc_idcache_add(local ? server->local_list->servers : 
+                  server->global_list->servers, server_name, server_id, 
+                  new_server, 0, NULL);
+
+  /* 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_get_len(server_id, SILC_ID_SERVER));
+
+  if (server->server_type == SILC_ROUTER)
+    server->stat.cell_servers++;
+
+  /* Check whether this router connection has been replaced by an
+     backup router. If it has been then we'll disable the server and will
+     ignore everything it will send until the backup router resuming
+     protocol has been completed. */
+  if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
+      silc_server_backup_replaced_get(server, server_id, NULL)) {
+    /* Send packet to the server indicating that it cannot use this
+       connection as it has been replaced by backup router. */
+    SilcBuffer packet = silc_buffer_alloc(2);
+    silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+    silc_buffer_format(packet,
+                      SILC_STR_UI_CHAR(SILC_SERVER_BACKUP_REPLACED),
+                      SILC_STR_UI_CHAR(0),
+                      SILC_STR_END);
+    silc_server_packet_send(server, sock, 
+                           SILC_PACKET_RESUME_ROUTER, 0, 
+                           packet->data, packet->len, TRUE);
+    silc_buffer_free(packet);
+
+    /* Mark the router disabled. The data sent earlier will go but nothing
+       after this does not go to this connection. */
+    idata->status |= SILC_IDLIST_STATUS_DISABLED;
+  } else {
+    /* If it is router announce our stuff to it. */
+    if (sock->type == SILC_SOCKET_TYPE_ROUTER && 
+       server->server_type == SILC_ROUTER) {
+      silc_server_announce_servers(server, FALSE, 0, sock);
+      silc_server_announce_clients(server, 0, sock);
+      silc_server_announce_channels(server, 0, sock);
+    }
+  }
+
+  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, server_entry;
+  SilcSocketConnection router_sock;
+  SilcIDPayload idp;
+  SilcIdType id_type;
+  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->data, buffer->len);
+  if (!idp)
+    return;
+
+  id_type = silc_id_payload_get_type(idp);
+
+  /* Normal server cannot have other normal server connections */
+  server_entry = (SilcServerEntry)sock->user_data;
+  if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER &&
+      server_entry->server_type == SILC_SERVER)
+    goto out;
+
+  id = silc_id_payload_get_id(idp);
+  if (!id)
+    goto out;
+
+  /* If the packet is coming from server then use the sender as the
+     origin of the the packet. If it came from router then check the real
+     sender of the packet and use that as the origin. */
+  if (sock->type == SILC_SOCKET_TYPE_SERVER) {
+    id_list = server->local_list;
+    router_sock = sock;
+    router = sock->user_data;
+
+    /* If the sender is backup router and ID is server (and we are not
+       backup router) then switch the entry to global list. */
+    if (server_entry->server_type == SILC_BACKUP_ROUTER && 
+       id_type == SILC_ID_SERVER && 
+       server->id_entry->server_type != SILC_BACKUP_ROUTER) {
+      id_list = server->global_list;
+      router_sock = server->router ? server->router->connection : sock;
+    }
+  } else {
+    void *sender_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                                    packet->src_id_type);
+    router = silc_idlist_find_server_by_id(server->global_list,
+                                          sender_id, TRUE, NULL);
+    if (!router)
+      router = silc_idlist_find_server_by_id(server->local_list,
+                                            sender_id, TRUE, NULL);
+    silc_free(sender_id);
+    router_sock = sock;
+    id_list = server->global_list;
+  }
 
-  case SILC_NOTIFY_TYPE_SIGNOFF:
-    /* 
-     * Distribute the notify to local clients on the channel
-     */
-    SILC_LOG_DEBUG(("SIGNOFF notify"));
+  if (!router)
+    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;
+  switch(id_type) {
+  case SILC_ID_CLIENT:
+    {
+      SilcClientEntry entry;
 
-    /* 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);
+      /* Check that we do not have this client already */
+      entry = silc_idlist_find_client_by_id(server->global_list, 
+                                           id, server->server_type, 
+                                           NULL);
+      if (!entry)
+       entry = silc_idlist_find_client_by_id(server->local_list, 
+                                             id, server->server_type,
+                                             NULL);
+      if (entry) {
+       SILC_LOG_DEBUG(("Ignoring client that we already have"));
        goto out;
       }
-    }
-    silc_free(client_id);
 
-    /* Remove the client from all channels */
-    silc_server_remove_from_channels(server, NULL, client);
+      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. */
+      entry = silc_idlist_add_client(id_list, NULL, NULL, NULL, 
+                                    id, router, NULL, 0);
+      if (!entry) {
+       SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
+
+       /* Inform the sender that the ID is not usable */
+       silc_server_send_notify_signoff(server, sock, FALSE, id, NULL);
+       goto out;
+      }
+      entry->nickname = NULL;
+      entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
 
-    /* Remove the client entry */
-    silc_idlist_del_client(server->global_list, client);
+      if (sock->type == SILC_SOCKET_TYPE_SERVER)
+       server->stat.cell_clients++;
+      server->stat.clients++;
+    }
     break;
 
-  case SILC_NOTIFY_TYPE_NICK_CHANGE:
+  case SILC_ID_SERVER:
     {
-      /* 
-       * Distribute the notify to local clients on the channel
-       */
-      unsigned char *id, *id2;
+      SilcServerEntry entry;
 
-      SILC_LOG_DEBUG(("NICK CHANGE notify"));
+      /* If the ID is mine, ignore it. */
+      if (SILC_ID_SERVER_COMPARE(id, server->id)) {
+       SILC_LOG_DEBUG(("Ignoring my own ID as new ID"));
+       break;
+      }
+
+      /* If the ID is the sender's ID, ignore it (we have it already) */
+      if (SILC_ID_SERVER_COMPARE(id, router->id)) {
+       SILC_LOG_DEBUG(("Ignoring sender's own ID"));
+       break;
+      }
       
-      /* 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)
+      /* Check that we do not have this server already */
+      entry = silc_idlist_find_server_by_id(server->global_list, 
+                                           id, server->server_type, 
+                                           NULL);
+      if (!entry)
+       entry = silc_idlist_find_server_by_id(server->local_list, 
+                                             id, server->server_type,
+                                             NULL);
+      if (entry) {
+       SILC_LOG_DEBUG(("Ignoring server that we already have"));
        goto out;
+      }
+
+      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));
       
-      /* 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)
+      /* As a router we keep information of all global information in our 
+        global list. Cell wide information however is kept in the local
+        list. */
+      entry = silc_idlist_add_server(id_list, NULL, 0, id, router, 
+                                    router_sock);
+      if (!entry) {
+       SILC_LOG_ERROR(("Could not add new server to the ID Cache"));
        goto out;
+      }
+      entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
       
-      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)));
-
-      /* 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)
-       /* 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);
-
-      silc_free(client_id);
-      if (!client)
-       silc_free(client_id2);
-      break;
+      if (sock->type == SILC_SOCKET_TYPE_SERVER)
+       server->stat.cell_servers++;
+      server->stat.servers++;
     }
-  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)"));
     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"));
+    goto out;
     break;
 
-    /* Ignore rest notify types for now */
-  case SILC_NOTIFY_TYPE_NONE:
-  case SILC_NOTIFY_TYPE_INVITE:
-  case SILC_NOTIFY_TYPE_MOTD:
   default:
+    goto out;
     break;
   }
 
+  /* 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);
+    silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
+                           packet->type, packet->flags,
+                           packet->buffer->data, packet->buffer->len, 
+                           FALSE, TRUE);
+  }
+
  out:
-  silc_notify_payload_free(payload);
+  silc_id_payload_free(idp);
 }
 
-/* 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. */
 
-void silc_server_new_channel_user(SilcServer server,
-                                 SilcSocketConnection sock,
-                                 SilcPacketContext *packet)
+/* 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)
 {
-  unsigned char *tmpid1, *tmpid2;
-  SilcClientID *client_id = NULL;
-  SilcChannelID *channel_id = NULL;
-  unsigned short channel_id_len;
-  unsigned short client_id_len;
-  SilcClientEntry client;
+  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;
+  uint16 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);
+    silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
+                           packet->type, packet->flags,
+                           packet->buffer->data, packet->buffer->len, 
+                           FALSE, TRUE);
+  }
+
+  /* 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 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(SilcServer server,
+                            SilcSocketConnection sock,
+                            SilcPacketContext *packet)
+{
+  SilcChannelPayload payload;
+  SilcChannelID *channel_id;
+  char *channel_name;
+  uint32 name_len;
+  unsigned char *id;
+  uint32 id_len;
+  uint32 mode;
+  SilcServerEntry server_entry;
   SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
-  SilcBuffer clidp;
-  int ret;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Processing New Channel"));
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      packet->src_id_type != SILC_ID_SERVER ||
+      server->server_type == SILC_SERVER)
+    return;
+
+  /* Parse the channel payload */
+  payload = silc_channel_payload_parse(packet->buffer->data,
+                                      packet->buffer->len);
+  if (!payload)
+    return;
+    
+  /* Get the channel ID */
+  channel_id = silc_channel_get_id_parse(payload);
+  if (!channel_id) {
+    silc_channel_payload_free(payload);
+    return;
+  }
+
+  channel_name = silc_channel_get_name(payload, &name_len);
+  if (name_len > 256)
+    channel_name[255] = '\0';
+
+  id = silc_channel_get_id(payload, &id_len);
+
+  server_entry = (SilcServerEntry)sock->user_data;
+
+  if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
+    /* Add the channel to global list as it is coming from router. It 
+       cannot be our own channel as it is coming from router. */
+
+    /* 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);
+    if (!channel) {
+      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, strdup(channel_name), 
+                             0, channel_id, sock->user_data, NULL, NULL, 0);
+      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. */
+    SilcBuffer chk;
+
+    SILC_LOG_DEBUG(("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);
 
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      server->server_type != SILC_ROUTER ||
-      packet->src_id_type != SILC_ID_SERVER)
-    return;
+    /* If the channel does not exist, then create it. This creates a new
+       key to the channel as well that we will send to the server. */
+    if (!channel) {
+      /* The protocol says that the Channel ID's IP address must be based
+        on the router's IP address.  Check whether the ID is based in our
+        IP and if it is not then create a new ID and enforce the server
+        to switch the ID. */
+      if (server_entry->server_type != SILC_BACKUP_ROUTER &&
+         !SILC_ID_COMPARE(channel_id, server->id, server->id->ip.data_len)) {
+       SilcChannelID *tmp;
+       SILC_LOG_DEBUG(("Forcing the server to change Channel ID"));
+       
+       if (silc_id_create_channel_id(server, server->id, server->rng, &tmp)) {
+         silc_server_send_notify_channel_change(server, sock, FALSE, 
+                                                channel_id, tmp);
+         silc_free(channel_id);
+         channel_id = tmp;
+       }
+      }
 
-  /* Parse payload */
-  ret = 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);
-  if (ret == -1) {
-    if (tmpid1)
-      silc_free(tmpid1);
-    if (tmpid2)
-      silc_free(tmpid2);
-    return;
-  }
+      /* Create the channel with the provided Channel ID */
+      channel = silc_server_create_new_channel_with_id(server, NULL, NULL,
+                                                      channel_name,
+                                                      channel_id, FALSE);
+      if (!channel) {
+       silc_channel_payload_free(payload);
+       silc_free(channel_id);
+       return;
+      }
 
-  /* Decode the channel ID */
-  channel_id = silc_id_str2id(tmpid1, channel_id_len, SILC_ID_CHANNEL);
-  if (!channel_id)
-    goto out;
+      /* Get the mode and set it to the channel */
+      channel->mode = silc_channel_get_mode(payload);
 
-  /* Decode the client ID */
-  client_id = silc_id_str2id(tmpid2, client_id_len, SILC_ID_CLIENT);
-  if (!client_id)
-    goto out;
+      /* Send the new channel key to the server */
+      id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+      id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+      chk = silc_channel_key_payload_encode(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);
 
-  /* 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;
-  }
+    } 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. */
+      SilcBuffer users = NULL, users_modes = NULL;
 
-  /* 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 (!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);
+      }
 
-  /* 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;
-  }
+      /* If the mode is different from what we have then enforce the
+        mode change. */
+      mode = silc_channel_get_mode(payload);
+      if (channel->mode != mode) {
+       SILC_LOG_DEBUG(("Forcing the server to change channel mode"));
+       silc_server_send_notify_cmode(server, sock, FALSE, channel,
+                                     channel->mode, server->id,
+                                     SILC_ID_SERVER,
+                                     channel->cipher, channel->hmac_name,
+                                     channel->passphrase);
+      }
 
-  /* 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);
+      /* Create new key for the channel and send it to the server and
+        everybody else possibly on the channel. */
 
-  server->stat.chanclients++;
+      if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+       if (!silc_server_create_channel_key(server, channel, 0))
+         return;
+       
+       /* Send to the channel */
+       silc_server_send_channel_key(server, sock, channel, FALSE);
+       id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+       id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+
+       /* Send to the server */
+       chk = silc_channel_key_payload_encode(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);
+       silc_free(id);
+      }
 
-  /* 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);
+      silc_free(channel_id);
 
-  client_id = NULL;
+      /* 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. */
+      silc_server_announce_get_channel_users(server, channel, &users,
+                                            &users_modes);
+      if (users) {
+       silc_buffer_push(users, users->data - users->head);
+       silc_server_packet_send(server, sock,
+                               SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                               users->data, users->len, FALSE);
+       silc_buffer_free(users);
+      }
+      if (users_modes) {
+       silc_buffer_push(users_modes, users_modes->data - users_modes->head);
+       silc_server_packet_send_dest(server, sock,
+                                    SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                    channel->id, SILC_ID_CHANNEL,
+                                    users_modes->data, 
+                                    users_modes->len, FALSE);
+       silc_buffer_free(users_modes);
+      }
+    }
+  }
 
- out:
-  if (client_id)
-    silc_free(client_id);
-  if (channel_id)
-    silc_free(channel_id);
-  silc_free(tmpid1);
-  silc_free(tmpid2);
+  silc_channel_payload_free(payload);
 }
 
-/* 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;
+  uint16 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);
+    silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
+                           packet->type, packet->flags,
+                           packet->buffer->data, packet->buffer->len, 
+                           FALSE, TRUE);
   }
 
-  if (sock->type == SILC_SOCKET_TYPE_SERVER)
-    id_list = server->local_list;
-  else
-    id_list = server->global_list;
+  /* 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;
 
-  /* 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--;
-      if (sock->type == SILC_SOCKET_TYPE_SERVER &&
-          server->server_type == SILC_ROUTER)
-        server->stat.cell_clients--;
-
-      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;
+  buffer = silc_buffer_alloc(512);
+  new->buffer = buffer;
 
-  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--;
-      if (sock->type == SILC_SOCKET_TYPE_SERVER &&
-          server->server_type == SILC_ROUTER)
-        server->stat.cell_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;
+  while (packet->buffer->len) {
+    SILC_GET16_MSB(len1, packet->buffer->data);
+    if ((len1 > packet->buffer->len) ||
+       (len1 > buffer->truelen))
+      break;
 
-  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--;
-      if (sock->type == SILC_SOCKET_TYPE_SERVER &&
-          server->server_type == SILC_ROUTER)
-        server->stat.cell_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;
+    SILC_GET16_MSB(len2, packet->buffer->data + 2 + len1);
+    if ((len2 > packet->buffer->len) ||
+       (len2 > buffer->truelen))
+      break;
 
-  default:
-    break;
+    silc_buffer_pull_tail(buffer, 8 + len1 + len2);
+    silc_buffer_put(buffer, packet->buffer->data, 8 + len1 + len2);
+
+    /* Process the New Channel */
+    silc_server_new_channel(server, sock, new);
+
+    silc_buffer_push_tail(buffer, 8 + len1 + len2);
+    silc_buffer_pull(packet->buffer, 8 + 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;
-  SilcClientEntry client;
-  SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
-
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      packet->src_id_type == SILC_ID_CLIENT)
-    return;
+  SilcSocketConnection dst_sock;
+  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);
-  }
+  if (packet->src_id_type != SILC_ID_CLIENT ||
+      packet->dst_id_type != SILC_ID_CLIENT)
+    return;
 
-  /* Parse Set Mode payload */
-  payload = silc_set_mode_payload_parse(packet->buffer);
-  if (!payload)
+  if (!packet->dst_id)
     return;
 
-  mode_type = silc_set_mode_get_type(payload);
-  args = silc_set_mode_get_args(payload);
-  if (!args)
-    goto out;
+  /* Get the route to the client */
+  dst_sock = silc_server_get_client_route(server, packet->dst_id,
+                                         packet->dst_id_len, NULL, &idata);
+  if (!dst_sock)
+    return;
 
-  mode_mask = silc_set_mode_get_mode(payload);
-  SILC_PUT32_MSB(mode_mask, mode);
+  /* Relay the packet */
+  silc_server_relay_packet(server, dst_sock, idata->send_key,
+                          idata->hmac_send, idata->psn_send++,
+                          packet, FALSE);
+}
 
-  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;
+/* Received connection auth request packet that is used during connection
+   phase to resolve the mandatory authentication method.  This packet can
+   actually be received at anytime but usually it is used only during
+   the connection authentication phase. Now, protocol says that this packet
+   can come from client or server, however, we support only this coming
+   from client and expect that server always knows what authentication
+   method to use. */
+
+void silc_server_connection_auth_request(SilcServer server,
+                                        SilcSocketConnection sock,
+                                        SilcPacketContext *packet)
+{
+  SilcServerConfigClient *client = NULL;
+  uint16 conn_type;
+  int ret;
+  SilcAuthMethod auth_meth = SILC_AUTH_NONE;
 
-    /* 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;
-    }
+  SILC_LOG_DEBUG(("Start"));
 
-    /* Get Client ID payload */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
+  if (packet->src_id_type && packet->src_id_type != SILC_ID_CLIENT)
+    return;
 
-    /* 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));
+  /* Parse the payload */
+  ret = silc_buffer_unformat(packet->buffer,
+                            SILC_STR_UI_SHORT(&conn_type),
+                            SILC_STR_UI_SHORT(NULL),
+                            SILC_STR_END);
+  if (ret == -1)
+    return;
 
-    /* Change the mode */
-    channel->mode = mode_mask;
-    break;
+  if (conn_type != SILC_SOCKET_TYPE_CLIENT)
+    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;
+  /* Get the authentication method for the client */
+  auth_meth = SILC_AUTH_NONE;
+  client = silc_server_config_find_client(server, sock->ip);
+  if (!client)
+    client = silc_server_config_find_client(server, sock->hostname);
+  if (client) {
+    if (client->passphrase) {
+      if (client->publickey && !server->config->prefer_passphrase_auth)
+       auth_meth = SILC_AUTH_PUBLIC_KEY;
+      else
+       auth_meth = SILC_AUTH_PASSWORD;
+    } else if (client->publickey)
+      auth_meth = SILC_AUTH_PUBLIC_KEY;
+  }
 
-    /* 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 it back to the client */
+  silc_server_send_connection_auth_request(server, sock, conn_type, auth_meth);
+}
 
-    /* Get Client ID payload */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
+/* Received REKEY packet. The sender of the packet wants to regenerate
+   its session keys. This starts the REKEY protocol. */
 
-    /* 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;
+void silc_server_rekey(SilcServer server,
+                      SilcSocketConnection sock,
+                      SilcPacketContext *packet)
+{
+  SilcProtocol protocol;
+  SilcServerRekeyInternalContext *proto_ctx;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
 
-    /* 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;
-      }
-    }
-    silc_free(client_id);
+  SILC_LOG_DEBUG(("Start"));
 
-    /* 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;
-      }
+  /* Allocate internal protocol context. This is sent as context
+     to the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->server = (void *)server;
+  proto_ctx->sock = sock;
+  proto_ctx->responder = TRUE;
+  proto_ctx->pfs = idata->rekey->pfs;
+      
+  /* Perform rekey protocol. Will call the final callback after the
+     protocol is over. */
+  silc_protocol_alloc(SILC_PROTOCOL_SERVER_REKEY, 
+                     &protocol, proto_ctx, silc_server_rekey_final);
+  sock->protocol = protocol;
+
+  if (proto_ctx->pfs == FALSE)
+    /* Run the protocol */
+    silc_protocol_execute(protocol, server->schedule, 0, 0);
+}
 
-    break;
+/* Received file transger 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. */
 
-  default:
-    break;
-  }
+void silc_server_ftp(SilcServer server,
+                    SilcSocketConnection sock,
+                    SilcPacketContext *packet)
+{
+  SilcSocketConnection dst_sock;
+  SilcIDListData idata;
 
- out:
-  if (channel_id)
-    silc_free(channel_id);
-  if (args)
-    silc_argument_payload_free(args);
-  if (payload)
-    silc_set_mode_payload_free(payload);
+  SILC_LOG_DEBUG(("Start"));
+
+  if (packet->src_id_type != SILC_ID_CLIENT ||
+      packet->dst_id_type != SILC_ID_CLIENT)
+    return;
+
+  if (!packet->dst_id)
+    return;
+
+  /* Get the route to the client */
+  dst_sock = silc_server_get_client_route(server, packet->dst_id,
+                                         packet->dst_id_len, NULL, &idata);
+  if (!dst_sock)
+    return;
+
+  /* Relay the packet */
+  silc_server_relay_packet(server, dst_sock, idata->send_key,
+                          idata->hmac_send, idata->psn_send++,
+                          packet, FALSE);
 }