updates.
[silc.git] / apps / silcd / packet_receive.c
index 38b2a86a5b5d2923f10c6854f0b5204f00aa33db..9f175376d55afb4e9aa43f58659fb6a0eec9b061 100644 (file)
@@ -39,13 +39,16 @@ void silc_server_notify(SilcServer server,
   SilcArgumentPayload args;
   SilcChannelID *channel_id, *channel_id2;
   SilcClientID *client_id, *client_id2;
+  SilcServerID *server_id;
   SilcChannelEntry channel;
   SilcClientEntry client;
+  SilcServerEntry server_entry;
   SilcChannelClientEntry chl;
   SilcIDCacheEntry cache;
-  unsigned int mode;
+  SilcHashTableList htl;
+  uint32 mode;
   unsigned char *tmp;
-  unsigned int tmp_len;
+  uint32 tmp_len;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -68,7 +71,7 @@ void silc_server_notify(SilcServer server,
     if (dst_sock)
       /* Relay the packet */
       silc_server_relay_packet(server, dst_sock, idata->send_key,
-                              idata->hmac, packet, TRUE);
+                              idata->hmac_receive, packet, TRUE);
   }
 
   /* If we are router and this packet is not already broadcast packet
@@ -148,7 +151,7 @@ void silc_server_notify(SilcServer server,
          goto out;
 
        client = 
-         silc_idlist_add_client(server->global_list, NULL, 0, NULL, NULL,
+         silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
                                 silc_id_dup(client_id, SILC_ID_CLIENT), 
                                 sock->user_data, NULL);
        if (!client) {
@@ -174,8 +177,8 @@ void silc_server_notify(SilcServer server,
     chl = silc_calloc(1, sizeof(*chl));
     chl->client = client;
     chl->channel = channel;
-    silc_list_add(channel->user_list, chl);
-    silc_list_add(client->channels, chl);
+    silc_hash_table_add(channel->user_list, client, chl);
+    silc_hash_table_add(client->channels, channel, chl);
     silc_free(client_id);
 
     break;
@@ -270,17 +273,11 @@ void silc_server_notify(SilcServer server,
     if (tmp_len > 128)
       tmp = NULL;
 
-    /* Remove the client from all channels */
-    silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, TRUE);
+    /* Remove the client from all channels. */
+    silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, FALSE);
 
     client->data.registered = FALSE;
     cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
-
-#if 0
-    /* Remove the client entry */
-    if (!silc_idlist_del_client(server->global_list, client))
-      silc_idlist_del_client(server->local_list, client);
-#endif
     break;
 
   case SILC_NOTIFY_TYPE_TOPIC_SET:
@@ -516,13 +513,38 @@ void silc_server_notify(SilcServer server,
     silc_free(client_id);
 
     /* Get entry to the channel user list */
-    silc_list_start(channel->user_list);
-    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
+    silc_hash_table_list(channel->user_list, &htl);
+    while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+      SilcChannelClientEntry chl2 = NULL;
+
+      /* 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) {
+       silc_server_send_notify_cumode(server, sock, FALSE, channel,
+                                      (mode & (~SILC_CHANNEL_UMODE_CHANFO)),
+                                      server->id, SILC_ID_SERVER, 
+                                      client->id);
+       silc_free(channel_id);
+
+       /* Change the mode back if we changed it */
+       if (chl2)
+         chl2->mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+       goto out;
+      }
+
       if (chl->client == client) {
        /* Change the mode */
        chl->mode = mode;
-       break;
+       if (!(mode & SILC_CHANNEL_UMODE_CHANFO))
+         break;
+
+       chl2 = chl;
       }
+    }
 
     /* Send the same notify to the channel */
     silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
@@ -611,6 +633,9 @@ void silc_server_notify(SilcServer server,
 
     SILC_LOG_DEBUG(("CHANNEL CHANGE"));
 
+    if (sock->type != SILC_SOCKET_TYPE_ROUTER)
+      break;
+
     /* Get the old Channel ID */
     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
     if (!tmp)
@@ -644,19 +669,84 @@ void silc_server_notify(SilcServer server,
     if (!channel_id2)
       goto out;
 
+    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->global_list, channel_id,
                                        channel_id2))
-      silc_idlist_replace_channel_id(server->local_list, channel_id,
-                                    channel_id2);
+      if (!silc_idlist_replace_channel_id(server->local_list, channel_id,
+                                         channel_id2)) {
+       silc_free(channel_id2);
+       channel_id2 = NULL;
+      }
+
+    if (channel_id2) {
+      SilcBuffer users = NULL, users_modes = NULL;
+      
+      /* 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(server, sock,
+                               SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                               users_modes->data, users_modes->len, FALSE);
+       silc_buffer_free(users_modes);
+      }
+    }
 
     silc_free(channel_id);
-    silc_free(channel_id2);
 
     break;
 
   case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
-    SILC_LOG_DEBUG(("SERVER SIGNOFF notify (not-impl XXX)"));
+    /* 
+     * Remove the server entry and all clients that this server owns.
+     */
+
+    SILC_LOG_DEBUG(("SERVER SIGNOFF notify"));
+
+    /* 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;
+
+    /* Get server entry */
+    server_entry = silc_idlist_find_server_by_id(server->global_list, 
+                                                server_id, NULL);
+    if (!server_entry) {
+      server_entry = silc_idlist_find_server_by_id(server->local_list, 
+                                                  server_id, NULL);
+      if (!server_entry) {
+       silc_free(server_id);
+       goto out;
+      }
+    }
+    silc_free(server_id);
+
+    /* 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);
+
+    /* Remove the server entry */
+    if (!silc_idlist_del_server(server->global_list, server_entry))
+      silc_idlist_del_server(server->local_list, server_entry);
+
+    /* XXX update statistics */
+
     break;
 
   case SILC_NOTIFY_TYPE_KICKED:
@@ -720,7 +810,7 @@ void silc_server_notify(SilcServer server,
        * Distribute the notify to local clients on channels
        */
       unsigned char *id;
-      unsigned int id_len;
+      uint32 id_len;
     
       SILC_LOG_DEBUG(("KILLED notify"));
       
@@ -898,9 +988,9 @@ void silc_server_notify_list(SilcServer server,
 {
   SilcPacketContext *new;
   SilcBuffer buffer;
-  unsigned short len;
+  uint16 len;
 
-  SILC_LOG_DEBUG(("Processing New Notify List"));
+  SILC_LOG_DEBUG(("Processing Notify List"));
 
   if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
       packet->src_id_type != SILC_ID_SERVER)
@@ -975,7 +1065,7 @@ void silc_server_private_message(SilcServer server,
 
   /* Send the private message */
   silc_server_send_private_message(server, dst_sock, idata->send_key,
-                                  idata->hmac, packet);
+                                  idata->hmac_send, packet);
 }
 
 /* Received private message key packet.. This packet is never for us. It is to
@@ -1007,7 +1097,7 @@ void silc_server_private_message_key(SilcServer server,
 
   /* Relay the packet */
   silc_server_relay_packet(server, dst_sock, idata->send_key,
-                          idata->hmac, packet, FALSE);
+                          idata->hmac_send, packet, FALSE);
 }
 
 /* Processes incoming command reply packet. The command reply packet may
@@ -1049,7 +1139,7 @@ void silc_server_command_reply(SilcServer server,
 
   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)) {
+    if (memcmp(packet->dst_id, server->id_string, packet->dst_id_len)) {
       SILC_LOG_ERROR(("Cannot process command reply to unknown server"));
       return;
     }
@@ -1071,7 +1161,7 @@ void silc_server_command_reply(SilcServer server,
     idata = (SilcIDListData)client;
     
     /* Encrypt packet */
-    silc_packet_encrypt(idata->send_key, idata->hmac, dst_sock->outbuf, 
+    silc_packet_encrypt(idata->send_key, idata->hmac_send, dst_sock->outbuf, 
                        buffer->len);
     
     /* Send the packet */
@@ -1089,9 +1179,9 @@ void silc_server_channel_message(SilcServer server,
                                 SilcPacketContext *packet)
 {
   SilcChannelEntry channel = NULL;
-  SilcChannelClientEntry chl;
   SilcChannelID *id = NULL;
   void *sender = NULL;
+  void *sender_entry = NULL;
 
   SILC_LOG_DEBUG(("Processing channel message"));
 
@@ -1114,62 +1204,29 @@ void silc_server_channel_message(SilcServer server,
     }
   }
 
-  /* 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. */
+  /* 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 (sock->type != SILC_SOCKET_TYPE_ROUTER && 
-      packet->src_id_type == SILC_ID_CLIENT) {
-    silc_list_start(channel->user_list);
-    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-      if (chl->client && !SILC_ID_CLIENT_COMPARE(chl->client->id, sender))
-       break;
-    }
-    if (chl == SILC_LIST_END) {
+  if (packet->src_id_type == SILC_ID_CLIENT) {
+    sender_entry = silc_idlist_find_client_by_id(server->local_list, 
+                                                sender, NULL);
+    if (!sender_entry)
+      sender_entry = silc_idlist_find_client_by_id(server->global_list, 
+                                                  sender, NULL);
+    if (!sender_entry || !silc_server_client_on_channel(sender_entry, 
+                                                       channel)) {
       SILC_LOG_DEBUG(("Client not on channel"));
       goto out;
     }
   }
 
-  /* If we are router and the packet came from router and private key
-     has not been set for the channel then we must encrypt the packet
-     as it was decrypted with the session key shared between us and the
-     router which sent it. This is so, because cells does not share the
-     same channel key */
-  if (server->server_type == SILC_ROUTER &&
-      sock->type == SILC_SOCKET_TYPE_ROUTER &&
-      !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
-    SilcBuffer chp;
-    unsigned int iv_len, i;
-    unsigned short data_len, flags;
-
-    iv_len = silc_cipher_get_block_len(channel->channel_key);
-    if (channel->iv[0] == '\0')
-      for (i = 0; i < iv_len; i++) channel->iv[i] = 
-                                    silc_rng_get_byte(server->rng);
-    else
-      silc_hash_make(server->md5hash, channel->iv, iv_len, channel->iv);
-    
-    /* Encode new payload. This encrypts it also. */
-    SILC_GET16_MSB(flags, packet->buffer->data);
-    SILC_GET16_MSB(data_len, packet->buffer->data + 2);
-    chp = silc_channel_message_payload_encode(flags, data_len, 
-                                             packet->buffer->data + 4,
-                                             iv_len, channel->iv,
-                                             channel->channel_key,
-                                             channel->hmac);
-    silc_buffer_put(packet->buffer, chp->data, chp->len);
-    silc_buffer_free(chp);
-  }
-
   /* 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->src_id_type, sender_entry,
                                      packet->buffer->data,
                                      packet->buffer->len, FALSE);
 
@@ -1214,11 +1271,11 @@ SilcClientEntry silc_server_new_client(SilcServer server,
 {
   SilcBuffer buffer = packet->buffer;
   SilcClientEntry client;
-  SilcIDCacheEntry cache;
   SilcClientID *client_id;
   SilcBuffer reply;
   SilcIDListData idata;
   char *username = NULL, *realname = NULL, *id_string;
+  uint32 id_len;
   int ret;
 
   SILC_LOG_DEBUG(("Creating new client"));
@@ -1230,10 +1287,11 @@ SilcClientEntry silc_server_new_client(SilcServer server,
   client = (SilcClientEntry)sock->user_data;
   idata = (SilcIDListData)client;
 
-  /* 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)) {
+  /* 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;
   }
 
@@ -1247,6 +1305,8 @@ SilcClientEntry silc_server_new_client(SilcServer server,
       silc_free(username);
     if (realname)
       silc_free(realname);
+    silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                                  "Incomplete client information");
     return NULL;
   }
 
@@ -1272,28 +1332,27 @@ SilcClientEntry silc_server_new_client(SilcServer server,
   client->username = username;
   client->userinfo = realname ? realname : strdup(" ");
   client->id = client_id;
+  id_len = silc_id_get_len(client_id, SILC_ID_CLIENT);
 
-  /* 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);
+  /* Add the client again to the ID cache */
+  silc_idcache_add(server->local_list->clients, client->nickname,
+                  client_id, client, FALSE);
 
   /* 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);
+                           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 + SILC_ID_CLIENT_LEN);
+  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(SILC_ID_CLIENT_LEN),
-                    SILC_STR_UI_XNSTRING(id_string, SILC_ID_CLIENT_LEN),
+                    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);
@@ -1367,11 +1426,10 @@ SilcServerEntry silc_server_new_server(SilcServer server,
 {
   SilcBuffer buffer = packet->buffer;
   SilcServerEntry new_server;
-  SilcIDCacheEntry cache;
   SilcServerID *server_id;
   SilcIDListData idata;
   unsigned char *server_name, *id_string;
-  unsigned short id_len, name_len;
+  uint16 id_len, name_len;
   int ret;
 
   SILC_LOG_DEBUG(("Creating new server"));
@@ -1384,12 +1442,8 @@ SilcServerEntry silc_server_new_server(SilcServer server,
   new_server = (SilcServerEntry)sock->user_data;
   idata = (SilcIDListData)new_server;
 
-  /* Fetch the old server cache entry so that we can update it. */
-  if (!silc_idcache_find_by_context(server->local_list->servers,
-                                   sock->user_data, &cache)) {
-    SILC_LOG_ERROR(("Lost server's cache entry - bad thing"));
-    return NULL;
-  }
+  /* Remove the old cache entry */
+  silc_idcache_del_by_context(server->local_list->servers, new_server);
 
   /* Parse the incoming packet */
   ret = silc_buffer_unformat(buffer,
@@ -1423,16 +1477,14 @@ SilcServerEntry silc_server_new_server(SilcServer server,
   }
   silc_free(id_string);
 
-  /* Update client entry */
+  /* Update server entry */
   idata->registered = TRUE;
   new_server->server_name = server_name;
   new_server->id = server_id;
 
-  /* Update the cache entry */
-  cache->id = (void *)server_id;
-  cache->type = SILC_ID_SERVER;
-  cache->data = server_name;
-  silc_idcache_sort_by_data(server->local_list->servers);
+  /* Add again the entry to the ID cache. */
+  silc_idcache_add(server->local_list->servers, server_name, server_id, 
+                  server, FALSE);
 
   /* Distribute the information about new server in the SILC network
      to our router. If we are normal server we won't send anything
@@ -1441,7 +1493,7 @@ SilcServerEntry silc_server_new_server(SilcServer server,
       server->router->connection != sock)
     silc_server_send_new_id(server, server->router->connection,
                            TRUE, new_server->id, SILC_ID_SERVER, 
-                           SILC_ID_SERVER_LEN);
+                           silc_id_get_len(server_id, SILC_ID_SERVER));
 
   if (server->server_type == SILC_ROUTER)
     server->stat.cell_servers++;
@@ -1463,7 +1515,6 @@ static void silc_server_new_id_real(SilcServer server,
   SilcSocketConnection router_sock;
   SilcIDPayload idp;
   SilcIdType id_type;
-  unsigned char *hash = NULL;
   void *id;
 
   SILC_LOG_DEBUG(("Processing new ID"));
@@ -1504,8 +1555,25 @@ static void silc_server_new_id_real(SilcServer server,
   else
     id_list = server->global_list;
 
-  router_sock = sock;
-  router = sock->user_data;
+  /* 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) {
+    router_sock = sock;
+    router = sock->user_data;
+  } 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, NULL);
+    if (!router)
+      router = silc_idlist_find_server_by_id(server->local_list,
+                                            sender_id, NULL);
+    silc_free(sender_id);
+    if (!router)
+      goto out;
+    router_sock = sock;
+  }
 
   switch(id_type) {
   case SILC_ID_CLIENT:
@@ -1519,16 +1587,9 @@ static void silc_server_new_id_real(SilcServer server,
     
       /* 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, 
-                                    sizeof(((SilcClientID *)id)->hash),
-                                    NULL, NULL, id, router, NULL);
+        list. */
+      entry = silc_idlist_add_client(id_list, NULL, NULL, NULL, 
+                                    id, router, NULL);
       entry->nickname = NULL;
       entry->data.registered = TRUE;
 
@@ -1539,11 +1600,17 @@ static void silc_server_new_id_real(SilcServer server,
     break;
 
   case SILC_ID_SERVER:
+    /* 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;
+    }
+
     SILC_LOG_DEBUG(("New server id(%s) from [%s] %s",
                    silc_id_render(id, SILC_ID_SERVER),
                    sock->type == SILC_SOCKET_TYPE_SERVER ?
                    "Server" : "Router", sock->hostname));
-    
+
     /* As a router we keep information of all global information in our global
        list. Cell wide information however is kept in the local list. */
     silc_idlist_add_server(id_list, NULL, 0, id, router, router_sock);
@@ -1583,7 +1650,7 @@ void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock,
 {
   SilcPacketContext *new_id;
   SilcBuffer idp;
-  unsigned short id_len;
+  uint16 id_len;
 
   SILC_LOG_DEBUG(("Processing New ID List"));
 
@@ -1651,9 +1718,10 @@ void silc_server_new_channel(SilcServer server,
   SilcChannelPayload payload;
   SilcChannelID *channel_id;
   char *channel_name;
-  unsigned int name_len;
+  uint32 name_len;
   unsigned char *id;
-  unsigned int id_len;
+  uint32 id_len;
+  uint32 mode;
 
   SILC_LOG_DEBUG(("Processing New Channel"));
 
@@ -1738,15 +1806,28 @@ void silc_server_new_channel(SilcServer server,
       /* 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 (SILC_ID_CHANNEL_COMPARE(channel_id, channel->id)) {
+      if (!channel->id)
+       channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
+
+      if (!SILC_ID_CHANNEL_COMPARE(channel_id, channel->id)) {
        /* They don't match, send CHANNEL_CHANGE notify to the server to
           force the ID change. */
        SILC_LOG_DEBUG(("Forcing the server to change Channel ID"));
        silc_server_send_notify_channel_change(server, sock, FALSE, 
-                                              channel_id,
-                                              channel->id, 
-                                              SILC_ID_CHANNEL_LEN);
+                                              channel_id, channel->id);
+      }
+
+      /* 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);
       }
 
       /* Create new key for the channel and send it to the server and
@@ -1757,6 +1838,8 @@ void silc_server_new_channel(SilcServer server,
        
        /* 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_CHANNEL_LEN;
        
        /* Send to the server */
        chk = silc_channel_key_payload_encode(id_len, id,
@@ -1769,6 +1852,7 @@ void silc_server_new_channel(SilcServer server,
        silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
                                chk->data, chk->len, FALSE);
        silc_buffer_free(chk);
+       silc_free(id);
       }
 
       silc_free(channel_id);
@@ -1776,9 +1860,26 @@ void silc_server_new_channel(SilcServer server,
       /* Since the channel is coming from server and we also know about it
         then send the JOIN notify to the server so that it see's our
         users on the channel "joining" the channel. */
-      /* XXX TODO **/
+      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(server, sock,
+                               SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                               users_modes->data, users_modes->len, FALSE);
+       silc_buffer_free(users_modes);
+      }
     }
   }
+
+  silc_channel_payload_free(payload);
 }
 
 /* Received New Channel List packet, list of New Channel List payloads inside
@@ -1790,7 +1891,7 @@ void silc_server_new_channel_list(SilcServer server,
 {
   SilcPacketContext *new;
   SilcBuffer buffer;
-  unsigned short len1, len2;
+  uint16 len1, len2;
 
   SILC_LOG_DEBUG(("Processing New Channel List"));
 
@@ -1881,7 +1982,7 @@ void silc_server_key_agreement(SilcServer server,
 
   /* Relay the packet */
   silc_server_relay_packet(server, dst_sock, idata->send_key,
-                          idata->hmac, packet, FALSE);
+                          idata->hmac_send, packet, FALSE);
 }
 
 /* Received connection auth request packet that is used during connection
@@ -1897,7 +1998,7 @@ void silc_server_connection_auth_request(SilcServer server,
                                         SilcPacketContext *packet)
 {
   SilcServerConfigSectionClientConnection *client = NULL;
-  unsigned short conn_type;
+  uint16 conn_type;
   int ret;
   SilcAuthMethod auth_meth;
 
@@ -1934,3 +2035,35 @@ void silc_server_connection_auth_request(SilcServer server,
                                           conn_type,
                                           auth_meth);
 }
+
+/* Received REKEY packet. The sender of the packet wants to regenerate
+   its session keys. This starts the REKEY protocol. */
+
+void silc_server_rekey(SilcServer server,
+                      SilcSocketConnection sock,
+                      SilcPacketContext *packet)
+{
+  SilcProtocol protocol;
+  SilcServerRekeyInternalContext *proto_ctx;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* 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 */
+    protocol->execute(server->timeout_queue, 0, protocol, sock->sock, 0, 0);
+}