updates.
[silc.git] / apps / silcd / packet_receive.c
index 57bdc9e867d38d0cbc69604056a2e21b34ae0096..9321add3326ad09bd97862424c46e38531c8ae6a 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -49,7 +49,7 @@ void silc_server_private_message(SilcServer server,
     goto err;
 
   /* Decode destination Client ID */
-  id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
+  id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
   if (!id) {
     SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
     goto err;
@@ -68,11 +68,10 @@ void silc_server_private_message(SilcServer server,
       /* We are of course in this case the client's router thus the real
         "router" of the client is the server who owns the client. Thus
         we will send the packet to that server. */
-      router = (SilcServerEntry)dst_sock->user_data;
+      router = (SilcServerEntry)client->router;
       idata = (SilcIDListData)router;
-      //assert(client->router == server->id_entry);
 
-      silc_server_send_private_message(server, dst_sock,
+      silc_server_send_private_message(server, router->connection,
                                       idata->send_key,
                                       idata->hmac,
                                       packet);
@@ -153,7 +152,9 @@ void silc_server_command_reply(SilcServer server,
 
   if (packet->dst_id_type == SILC_ID_CLIENT) {
     /* Destination must be one of ours */
-    id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
+    id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
+    if (!id)
+      return;
     client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
     if (!client) {
       SILC_LOG_ERROR(("Cannot process command reply to unknown client"));
@@ -217,7 +218,9 @@ void silc_server_channel_message(SilcServer server,
   }
 
   /* Find channel entry */
-  id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
+  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);
@@ -231,7 +234,10 @@ void silc_server_channel_message(SilcServer server,
      from router we won't do the check as the message is from client that
      we don't know about. Also, if the original sender is not client
      (as it can be server as well) we don't do the check. */
-  sender = silc_id_str2id(packet->src_id, packet->src_id_type);
+  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);
@@ -277,7 +283,7 @@ void silc_server_channel_key(SilcServer server,
 
   /* 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, channel, FALSE);
+  silc_server_send_channel_key(server, sock, channel, FALSE);
 }
 
 /* Received packet to replace a ID. This checks that the requested ID
@@ -292,6 +298,7 @@ void silc_server_replace_id(SilcServer server,
   SilcIdType old_id_type, new_id_type;
   unsigned short old_id_len, new_id_len;
   void *id = NULL, *id2 = NULL;
+  int ret;
 
   if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
       packet->src_id_type == SILC_ID_CLIENT)
@@ -299,12 +306,14 @@ void silc_server_replace_id(SilcServer server,
 
   SILC_LOG_DEBUG(("Replacing ID"));
 
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI_SHORT(&old_id_type),
-                      SILC_STR_UI16_NSTRING_ALLOC(&old_id, &old_id_len),
-                      SILC_STR_UI_SHORT(&new_id_type),
-                      SILC_STR_UI16_NSTRING_ALLOC(&new_id, &new_id_len),
-                      SILC_STR_END);
+  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;
 
   if (old_id_type != new_id_type)
     goto out;
@@ -313,11 +322,11 @@ void silc_server_replace_id(SilcServer server,
       new_id_len != silc_id_get_len(new_id_type))
     goto out;
 
-  id = silc_id_str2id(old_id, old_id_type);
+  id = silc_id_str2id(old_id, old_id_len, old_id_type);
   if (!id)
     goto out;
 
-  id2 = silc_id_str2id(new_id, new_id_type);
+  id2 = silc_id_str2id(new_id, new_id_len, new_id_type);
   if (!id2)
     goto out;
 
@@ -325,7 +334,7 @@ void silc_server_replace_id(SilcServer server,
      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->server_type == SILC_ROUTER &&
+  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"));
@@ -337,29 +346,60 @@ void silc_server_replace_id(SilcServer server,
   /* Replace the old ID */
   switch(old_id_type) {
   case SILC_ID_CLIENT:
-    SILC_LOG_DEBUG(("Old Client ID id(%s)", 
-                   silc_id_render(id, SILC_ID_CLIENT)));
-    SILC_LOG_DEBUG(("New Client ID id(%s)", 
-                   silc_id_render(id2, SILC_ID_CLIENT)));
-    if (silc_idlist_replace_client_id(server->local_list, id, id2) == NULL)
-      if (server->server_type == SILC_ROUTER)
-       silc_idlist_replace_client_id(server->global_list, id, id2);
-    break;
+    {
+      SilcBuffer nidp, oidp;
+      SilcClientEntry client = NULL;
+
+      SILC_LOG_DEBUG(("Old Client ID id(%s)", 
+                     silc_id_render(id, SILC_ID_CLIENT)));
+      SILC_LOG_DEBUG(("New Client ID id(%s)", 
+                     silc_id_render(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);
+
+       /* The nickname is not valid anymore, set it NULL. This causes that
+          the nickname will be queried if someone wants to know it. */
+       if (client->nickname)
+         silc_free(client->nickname);
+       client->nickname = NULL;
+
+       /* Send the NICK_CHANGE notify type to local clients on the channels
+          this client is joined to. */
+       silc_server_send_notify_on_channels(server, client, 
+                                           SILC_NOTIFY_TYPE_NICK_CHANGE, 2,
+                                           oidp->data, oidp->len, 
+                                           nidp->data, nidp->len);
+       
+       silc_buffer_free(nidp);
+       silc_buffer_free(oidp);
+      }
+      break;
+    }
 
   case SILC_ID_SERVER:
     SILC_LOG_DEBUG(("Old Server ID id(%s)", 
-                   silc_id_render(id, SILC_ID_CLIENT)));
+                   silc_id_render(id, SILC_ID_SERVER)));
     SILC_LOG_DEBUG(("New Server ID id(%s)", 
-                   silc_id_render(id2, SILC_ID_CLIENT)));
+                   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:
-    /* XXX Hmm... Basically this cannot occur. Channel ID's cannot be
-       re-generated. */
-    silc_free(id2);
+    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;
 
   default:
@@ -391,6 +431,7 @@ SilcClientEntry silc_server_new_client(SilcServer server,
   SilcBuffer reply;
   SilcIDListData idata;
   char *username = NULL, *realname = NULL, *id_string;
+  int ret;
 
   SILC_LOG_DEBUG(("Creating new client"));
 
@@ -409,10 +450,17 @@ SilcClientEntry silc_server_new_client(SilcServer server,
   }
 
   /* Parse incoming packet */
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI16_STRING_ALLOC(&username),
-                      SILC_STR_UI16_STRING_ALLOC(&realname),
-                      SILC_STR_END);
+  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;
+  }
 
   /* Create Client ID */
   silc_id_create_client_id(server->id, server->rng, server->md5hash,
@@ -460,6 +508,36 @@ SilcClientEntry silc_server_new_client(SilcServer server,
                          ("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",
@@ -494,6 +572,7 @@ SilcServerEntry silc_server_new_server(SilcServer server,
   SilcIDListData idata;
   unsigned char *server_name, *id_string;
   unsigned short id_len;
+  int ret;
 
   SILC_LOG_DEBUG(("Creating new server"));
 
@@ -513,10 +592,17 @@ SilcServerEntry silc_server_new_server(SilcServer server,
   }
 
   /* Parse the incoming packet */
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI16_NSTRING_ALLOC(&id_string, &id_len),
-                      SILC_STR_UI16_STRING_ALLOC(&server_name),
-                      SILC_STR_END);
+  ret = silc_buffer_unformat(buffer,
+                            SILC_STR_UI16_NSTRING_ALLOC(&id_string, &id_len),
+                            SILC_STR_UI16_STRING_ALLOC(&server_name),
+                            SILC_STR_END);
+  if (ret == -1) {
+    if (id_string)
+      silc_free(id_string);
+    if (server_name)
+      silc_free(server_name);
+    return NULL;
+  }
 
   if (id_len > buffer->len) {
     silc_free(id_string);
@@ -525,7 +611,12 @@ SilcServerEntry silc_server_new_server(SilcServer server,
   }
 
   /* Get Server ID */
-  server_id = silc_id_str2id(id_string, SILC_ID_SERVER);
+  server_id = silc_id_str2id(id_string, id_len, SILC_ID_SERVER);
+  if (!server_id) {
+    silc_free(id_string);
+    silc_free(server_name);
+    return NULL;
+  }
   silc_free(id_string);
 
   /* Update client entry */
@@ -548,6 +639,9 @@ SilcServerEntry silc_server_new_server(SilcServer server,
                            TRUE, new_server->id, SILC_ID_SERVER, 
                            SILC_ID_SERVER_LEN);
 
+  if (server->server_type == SILC_ROUTER)
+    server->stat.cell_servers++;
+
   return new_server;
 }
 
@@ -599,27 +693,6 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
                            buffer->data, buffer->len, FALSE);
   }
 
-#if 0
-  /* If the packet is originated from the one who sent it to us we know
-     that the ID belongs to our cell, unless the sender was router. */
-  tmpid = silc_id_str2id(packet->src_id, SILC_ID_SERVER);
-  tmpserver = (SilcServerEntry)sock->user_data;
-
-  if (!SILC_ID_SERVER_COMPARE(tmpid, tmpserver->id) &&
-      sock->type == SILC_SOCKET_TYPE_SERVER) {
-    id_list = server->local_list;
-    router_sock = sock;
-    router = sock->user_data;
-    /*    router = server->id_entry; */
-  } else {
-    id_list = server->global_list;
-    router_sock = (SilcSocketConnection)server->router->connection;
-    router = server->router;
-  }
-
-  silc_free(tmpid);
-#endif
-
   if (sock->type == SILC_SOCKET_TYPE_SERVER)
     id_list = server->local_list;
   else
@@ -648,9 +721,13 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
       memcpy(hash, ((SilcClientID *)id)->hash, 
             sizeof(((SilcClientID *)id)->hash));
       entry = silc_idlist_add_client(id_list, hash, NULL, NULL, id, 
-                                    router, router_sock);
+                                    router, NULL);
       entry->nickname = NULL;
 
+      if (sock->type == SILC_SOCKET_TYPE_SERVER)
+       server->stat.cell_clients++;
+      server->stat.clients++;
+
 #if 0
       /* XXX Adding two ID's with same IP number replaces the old entry thus
         gives wrong route. Thus, now disabled until figured out a better way
@@ -677,6 +754,10 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
        list. Cell wide information however is kept in the local list. */
     silc_idlist_add_server(id_list, NULL, 0, id, router, router_sock);
 
+    if (sock->type == SILC_SOCKET_TYPE_SERVER)
+      server->stat.cell_servers++;
+    server->stat.servers++;
+
 #if 0
     /* Add route cache for this ID */
     silc_server_route_add(silc_server_route_hash(
@@ -699,6 +780,245 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
   silc_id_payload_free(idp);
 }
 
+/* Receoved New Id List packet, list of New ID payloads inside one
+   packet. Process the New ID payloads one by one. */
+
+void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock,
+                            SilcPacketContext *packet)
+{
+  SilcPacketContext *new_id;
+  SilcBuffer idp;
+  unsigned short id_len;
+
+  SILC_LOG_DEBUG(("Processing New ID List"));
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      packet->src_id_type != SILC_ID_SERVER)
+    return;
+
+  /* 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(server, sock, new_id);
+
+    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)
+{
+  unsigned char *id;
+  SilcChannelID *channel_id;
+  unsigned short channel_id_len;
+  char *channel_name;
+  int ret;
+
+  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 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;
+
+  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. */
+
+    SILC_LOG_DEBUG(("New channel id(%s) from [Router] %s",
+                   silc_id_render(channel_id, SILC_ID_CHANNEL), 
+                   sock->hostname));
+    
+    silc_idlist_add_channel(server->global_list, channel_name, 0, channel_id, 
+                           server->router->connection, NULL);
+
+    server->stat.channels++;
+  } else {
+    /* The channel is coming from our server, thus it is in our cell
+       we will add it to our local list. */
+    SilcChannelEntry channel;
+    SilcBuffer chk;
+
+    SILC_LOG_DEBUG(("New channel id(%s) from [Server] %s",
+                   silc_id_render(channel_id, SILC_ID_CHANNEL), 
+                   sock->hostname));
+    
+    /* Check that we don't already have this channel */
+    channel = silc_idlist_find_channel_by_name(server->local_list, 
+                                              channel_name, NULL);
+    if (!channel)
+      channel = silc_idlist_find_channel_by_name(server->global_list, 
+                                                channel_name, NULL);
+
+    /* If the channel does not exist, then create it. We create the channel
+       with the channel ID provided by the server. This creates a new
+       key to the channel as well that we will send to the server. */
+    if (!channel) {
+      channel = silc_server_create_new_channel_with_id(server, NULL,
+                                                      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);
+      }
+
+      /* Create new key for the channel and send it to the server and
+        everybody else possibly on the channel. */
+
+      silc_server_create_channel_key(server, channel, 0);
+
+      /* Send to the channel */
+      silc_server_send_channel_key(server, sock, channel, FALSE);
+
+      /* Send to the server */
+      chk = silc_channel_key_payload_encode(channel_id_len, id,
+                                           strlen(channel->channel_key->
+                                                  cipher->name),
+                                           channel->channel_key->cipher->name,
+                                           channel->key_len / 8, 
+                                           channel->key);
+      silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
+                             chk->data, chk->len, FALSE);
+      silc_buffer_free(chk);
+    }
+  }
+
+  silc_free(id);
+}
+
+/* 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)
+{
+  SilcPacketContext *new;
+  SilcBuffer buffer;
+  unsigned short len1, len2;
+
+  SILC_LOG_DEBUG(("Processing New Channel List"));
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      packet->src_id_type != SILC_ID_SERVER ||
+      server->server_type == SILC_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->flags = packet->flags;
+  new->src_id = packet->src_id;
+  new->src_id_len = packet->src_id_len;
+  new->src_id_type = packet->src_id_type;
+  new->dst_id = packet->dst_id;
+  new->dst_id_len = packet->dst_id_len;
+  new->dst_id_type = packet->dst_id_type;
+
+  buffer = silc_buffer_alloc(512);
+  new->buffer = buffer;
+
+  while (packet->buffer->len) {
+    SILC_GET16_MSB(len1, packet->buffer->data);
+    if ((len1 > packet->buffer->len) ||
+       (len1 > buffer->truelen))
+      break;
+
+    SILC_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 */
+    silc_server_new_channel(server, sock, new);
+
+    silc_buffer_push_tail(buffer, 4 + len1 + len2);
+    silc_buffer_pull(packet->buffer, 4 + len1 + len2);
+  }
+
+  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
@@ -710,27 +1030,29 @@ void silc_server_remove_channel_user(SilcServer server,
 {
   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;
 
   SILC_LOG_DEBUG(("Removing user from channel"));
 
   if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      packet->src_id_type != SILC_ID_SERVER ||
       server->server_type == SILC_SERVER)
     return;
 
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI16_STRING_ALLOC(&tmp1),
-                      SILC_STR_UI16_STRING_ALLOC(&tmp2),
-                      SILC_STR_END);
-
-  if (!tmp1 || !tmp2)
+  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, SILC_ID_CLIENT);
-  channel_id = silc_id_str2id(tmp2, SILC_ID_CHANNEL);
+  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;
 
@@ -780,51 +1102,62 @@ void silc_server_remove_channel_user(SilcServer server,
     silc_free(channel_id);
 }
 
-/* Received New Channel packet. Information about new channels in the 
-   network are distributed using this packet. Save the information about
-   the new channel. */
+/* Received New Channel User List packet, list of New Channel User payloads
+   inside one packet.  Process the payloads one by one. */
 
-void silc_server_new_channel(SilcServer server,
-                            SilcSocketConnection sock,
-                            SilcPacketContext *packet)
+void silc_server_new_channel_user_list(SilcServer server,
+                                      SilcSocketConnection sock,
+                                      SilcPacketContext *packet)
 {
-  unsigned char *id;
-  SilcChannelID *channel_id;
-  unsigned short channel_id_len;
-  char *channel_name;
+  SilcPacketContext *new;
+  SilcBuffer buffer;
+  unsigned short len1, len2;
 
-  SILC_LOG_DEBUG(("Processing New Channel"));
+  SILC_LOG_DEBUG(("Processing New Channel User List"));
 
-  if (sock->type != SILC_SOCKET_TYPE_ROUTER ||
-      server->server_type == SILC_SERVER ||
-      packet->src_id_type != SILC_ID_SERVER)
-    return;
-
-  /* Parse payload */
-  if (!silc_buffer_unformat(packet->buffer, 
-                           SILC_STR_UI16_STRING_ALLOC(&channel_name),
-                           SILC_STR_UI16_NSTRING_ALLOC(&id, &channel_id_len),
-                           SILC_STR_END))
-    return;
-    
-  if (!channel_name || !id)
-    return;
-
-  /* Decode the channel ID */
-  channel_id = silc_id_str2id(id, SILC_ID_CHANNEL);
-  if (!channel_id)
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      packet->src_id_type != SILC_ID_SERVER ||
+      server->server_type == SILC_SERVER)
     return;
-  silc_free(id);
 
-  SILC_LOG_DEBUG(("New channel id(%s) from [Router] %s",
-                 silc_id_render(channel_id, SILC_ID_CHANNEL), 
-                 sock->hostname));
+  /* 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);
+
+    silc_buffer_push_tail(buffer, 4 + len1 + len2);
+    silc_buffer_pull(packet->buffer, 4 + len1 + len2);
+  }
 
-  /* Add the new channel. Add it always to global list since if we receive
-     this packet then it cannot be created by ourselves but some other 
-     router hence global channel. */
-  silc_idlist_add_channel(server->global_list, channel_name, 0, channel_id, 
-                         server->router->connection, NULL);
+  silc_buffer_free(buffer);
+  silc_free(new);
 }
 
 /* Received notify packet. Server can receive notify packets from router. 
@@ -838,7 +1171,7 @@ void silc_server_notify(SilcServer server,
   SilcNotifyType type;
   SilcArgumentPayload args;
   SilcChannelID *channel_id;
-  SilcClientID *client_id;
+  SilcClientID *client_id, *client_id2;
   SilcChannelEntry channel;
   SilcClientEntry client;
   unsigned char *tmp;
@@ -873,7 +1206,8 @@ void silc_server_notify(SilcServer server,
      */
     SILC_LOG_DEBUG(("JOIN notify"));
 
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_type);
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               packet->dst_id_type);
     if (!channel_id)
       goto out;
 
@@ -892,6 +1226,15 @@ void silc_server_notify(SilcServer server,
       goto out;
     }
     client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id) {
+      silc_free(channel_id);
+      goto out;
+    }
+
+    /* Send to channel */
+    silc_server_packet_send_to_channel(server, NULL, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
 
     /* 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
@@ -903,9 +1246,15 @@ void silc_server_notify(SilcServer server,
 
       client = silc_idlist_find_client_by_id(server->global_list, 
                                             client_id, NULL);
-      if (!client)
+      if (!client) {
        client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
-                                       client_id, sock->user_data, sock);
+                                       client_id, sock->user_data, NULL);
+       if (!client) {
+         silc_free(channel_id);
+         silc_free(client_id);
+         goto out;
+       }
+      }
 
       /* The channel is global now */
       channel->global_users = TRUE;
@@ -919,11 +1268,6 @@ void silc_server_notify(SilcServer server,
     } else {
       silc_free(client_id);
     }
-
-    /* Send to channel */
-    silc_server_packet_send_to_channel(server, channel, packet->type, FALSE,
-                                      packet->buffer->data, 
-                                      packet->buffer->len, FALSE);
     break;
 
   case SILC_NOTIFY_TYPE_LEAVE:
@@ -932,7 +1276,8 @@ void silc_server_notify(SilcServer server,
      */
     SILC_LOG_DEBUG(("LEAVE notify"));
 
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_type);
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               packet->dst_id_type);
     if (!channel_id)
       goto out;
 
@@ -951,10 +1296,14 @@ void silc_server_notify(SilcServer server,
       goto out;
     }
     client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id) {
+      silc_free(channel_id);
+      goto out;
+    }
 
     /* Send to channel */
-    silc_server_packet_send_to_channel(server, channel, packet->type, FALSE,
-                                      packet->buffer->data, 
+    silc_server_packet_send_to_channel(server, NULL, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
                                       packet->buffer->len, FALSE);
 
     /* Get client entry */
@@ -964,6 +1313,7 @@ void silc_server_notify(SilcServer server,
       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;
       }
@@ -985,6 +1335,8 @@ void silc_server_notify(SilcServer server,
     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, 
@@ -992,8 +1344,10 @@ void silc_server_notify(SilcServer server,
     if (!client) {
       client = silc_idlist_find_client_by_id(server->local_list, 
                                             client_id, NULL);
-      if (!client)
+      if (!client) {
+       silc_free(client_id);
        goto out;
+      }
     }
     silc_free(client_id);
 
@@ -1004,12 +1358,80 @@ void silc_server_notify(SilcServer server,
     silc_idlist_del_client(server->global_list, client);
     break;
 
-    /* Ignore rest notify types for now */
-  case SILC_NOTIFY_TYPE_NONE:
-  case SILC_NOTIFY_TYPE_INVITE:
+  case SILC_NOTIFY_TYPE_NICK_CHANGE:
+    {
+      /* 
+       * 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(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;
+    }
   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)"));
+    break;
+
+    /* Ignore rest notify types for now */
+  case SILC_NOTIFY_TYPE_NONE:
+  case SILC_NOTIFY_TYPE_INVITE:
   case SILC_NOTIFY_TYPE_MOTD:
   default:
     break;
@@ -1037,11 +1459,8 @@ void silc_server_new_channel_user(SilcServer server,
   SilcClientEntry client;
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
-  SilcIDList id_list;
-  SilcServerEntry tmpserver, router;
-  SilcSocketConnection router_sock;
   SilcBuffer clidp;
-  void *tmpid;
+  int ret;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -1051,49 +1470,30 @@ void silc_server_new_channel_user(SilcServer server,
     return;
 
   /* Parse payload */
-  if (!silc_buffer_unformat(packet->buffer, 
-                           SILC_STR_UI16_NSTRING_ALLOC(&tmpid1, 
-                                                       &channel_id_len),
-                           SILC_STR_UI16_NSTRING_ALLOC(&tmpid2, 
-                                                       &client_id_len),
-                           SILC_STR_END))
-    return;
-
-  if (!tmpid1 || !tmpid2)
+  ret = silc_buffer_unformat(packet->buffer, 
+                            SILC_STR_UI16_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;
+  }
 
   /* Decode the channel ID */
-  channel_id = silc_id_str2id(tmpid1, SILC_ID_CHANNEL);
+  channel_id = silc_id_str2id(tmpid1, channel_id_len, SILC_ID_CHANNEL);
   if (!channel_id)
     goto out;
 
   /* Decode the client ID */
-  client_id = silc_id_str2id(tmpid2, SILC_ID_CLIENT);
+  client_id = silc_id_str2id(tmpid2, client_id_len, SILC_ID_CLIENT);
   if (!client_id)
     goto out;
 
-#if 0
-  /* If the packet is originated from the one who sent it to us we know
-     that the ID belongs to our cell, unless the sender was router. */
-  tmpid = silc_id_str2id(packet->src_id, SILC_ID_SERVER);
-  tmpserver = (SilcServerEntry)sock->user_data;
-
-  if (!SILC_ID_SERVER_COMPARE(tmpid, tmpserver->id) &&
-      sock->type == SILC_SOCKET_TYPE_SERVER) {
-    id_list = server->local_list;
-    router_sock = sock;
-    router = sock->user_data;
-  } else {
-    id_list = server->global_list;
-    router_sock = (SilcSocketConnection)server->router->connection;
-    router = server->router;
-  }
-  silc_free(tmpid);
-#endif
-
-  router_sock = sock;
-  router = sock->user_data;
-
   /* Find the channel */
   channel = silc_idlist_find_channel_by_id(server->local_list, 
                                           channel_id, NULL);
@@ -1132,11 +1532,13 @@ void silc_server_new_channel_user(SilcServer server,
   silc_list_add(channel->user_list, chl);
   silc_list_add(client->channels, chl);
 
+  server->stat.chanclients++;
+
   /* 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, channel, FALSE,
+  silc_server_send_notify_to_channel(server, sock, channel, FALSE,
                                     SILC_NOTIFY_TYPE_JOIN, 
                                     1, clidp->data, clidp->len);
   silc_buffer_free(clidp);
@@ -1209,6 +1611,10 @@ void silc_server_remove_id(SilcServer server,
 
       /* 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),
@@ -1222,6 +1628,10 @@ void silc_server_remove_id(SilcServer server,
                                             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),
@@ -1235,6 +1645,10 @@ void silc_server_remove_id(SilcServer server,
                                              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),
@@ -1250,3 +1664,166 @@ void silc_server_remove_id(SilcServer server,
  out:
   silc_id_payload_free(idp);
 }
+
+/* Processes received SET_MODE packet. The packet is used to distribute
+   the information about changed channel's or client's channel modes. */
+
+void silc_server_set_mode(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;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* If we are router and this packet is not already broadcast packet
+     we will broadcast it. The sending socket really cannot be router or
+     the router is buggy. If this packet is coming from router then it must
+     have the broadcast flag set already and we won't do anything. */
+  if (!server->standalone && server->server_type == SILC_ROUTER &&
+      sock->type == SILC_SOCKET_TYPE_SERVER &&
+      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
+    SILC_LOG_DEBUG(("Broadcasting received Set Mode packet"));
+    silc_server_packet_send(server, server->router->connection, packet->type,
+                           packet->flags | SILC_PACKET_FLAG_BROADCAST, 
+                           packet->buffer->data, packet->buffer->len, FALSE);
+  }
+
+  /* Parse Set Mode payload */
+  payload = silc_set_mode_payload_parse(packet->buffer);
+  if (!payload)
+    return;
+
+  mode_type = silc_set_mode_get_type(payload);
+  args = silc_set_mode_get_args(payload);
+  if (!args)
+    goto out;
+
+  mode_mask = silc_set_mode_get_mode(payload);
+  SILC_PUT32_MSB(mode_mask, mode);
+
+  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;
+
+    /* 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;
+    }
+
+    /* Get Client ID payload */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    /* Send CMODE_CHANGE notify to local channel */
+    silc_server_send_notify_to_channel(server, sock, channel, FALSE,
+                                      SILC_NOTIFY_TYPE_CMODE_CHANGE, 
+                                      2, tmp, tmp_len,
+                                      mode, sizeof(mode));
+
+    /* Change the mode */
+    channel->mode = mode_mask;
+    break;
+
+  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 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;
+    }
+
+    /* Get Client ID payload */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    /* Get target Client ID */
+    tmp2 = silc_argument_get_arg_type(args, 3, &tmp_len2);
+    if (!tmp2)
+      goto out;
+    client_id = silc_id_payload_parse_id(tmp2, tmp_len2);
+    if (!client_id)
+      goto out;
+
+    /* 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);
+
+    /* Send CUMODE_CHANGE notify to local channel */
+    silc_server_send_notify_to_channel(server, sock, channel, FALSE,
+                                      SILC_NOTIFY_TYPE_CUMODE_CHANGE, 2, 
+                                      tmp, tmp_len,
+                                      mode, sizeof(mode),
+                                      tmp2, tmp_len2);
+
+    /* Get entry to the channel user list */
+    silc_list_start(channel->user_list);
+    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
+      if (chl->client == client) {
+       /* Change the mode */
+       chl->mode = mode_mask;
+       break;
+      }
+
+    break;
+
+  default:
+    break;
+  }
+
+ out:
+  if (channel_id)
+    silc_free(channel_id);
+  if (args)
+    silc_argument_payload_free(args);
+  if (payload)
+    silc_set_mode_payload_free(payload);
+}