Integer type name change.
[silc.git] / apps / silcd / packet_receive.c
index 65ceb85dfb77001d369aa8439338658b2406b077..07d1e1c86fad97b154863f60dee0baf6003a9169 100644 (file)
@@ -37,7 +37,7 @@ void silc_server_notify(SilcServer server,
   SilcNotifyPayload payload;
   SilcNotifyType type;
   SilcArgumentPayload args;
-  SilcChannelID *channel_id, *channel_id2;
+  SilcChannelID *channel_id = NULL, *channel_id2;
   SilcClientID *client_id, *client_id2;
   SilcServerID *server_id;
   SilcChannelEntry channel;
@@ -45,9 +45,11 @@ void silc_server_notify(SilcServer server,
   SilcServerEntry server_entry;
   SilcChannelClientEntry chl;
   SilcIDCacheEntry cache;
-  unsigned int mode;
+  SilcHashTableList htl;
+  SilcUInt32 mode;
   unsigned char *tmp;
-  unsigned int tmp_len;
+  SilcUInt32 tmp_len;
+  bool local;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -70,9 +72,16 @@ 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, idata->psn_send++,
+                              packet, TRUE);
   }
 
+  /* Parse the Notify Payload */
+  payload = silc_notify_payload_parse(packet->buffer->data,
+                                     packet->buffer->len);
+  if (!payload)
+    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
@@ -81,14 +90,37 @@ void silc_server_notify(SilcServer server,
       sock->type == SILC_SOCKET_TYPE_SERVER &&
       !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
     SILC_LOG_DEBUG(("Broadcasting received Notify packet"));
-    silc_server_packet_send(server, server->router->connection, packet->type,
-                           packet->flags | SILC_PACKET_FLAG_BROADCAST, 
-                           packet->buffer->data, packet->buffer->len, FALSE);
-  }
+    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;
 
-  payload = silc_notify_payload_parse(packet->buffer);
-  if (!payload)
-    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);
+    }
+  }
 
   type = silc_notify_get_type(payload);
   args = silc_notify_get_args(payload);
@@ -131,54 +163,72 @@ void silc_server_notify(SilcServer server,
     if (!client_id)
       goto out;
 
-    /* Send to channel */
-    silc_server_packet_send_to_channel(server, sock, 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
        entry for the client. */
     client = silc_idlist_find_client_by_id(server->global_list, 
-                                          client_id, NULL);
+                                          client_id, server->server_type, 
+                                          NULL);
     if (!client) {
       client = silc_idlist_find_client_by_id(server->local_list, 
-                                            client_id, NULL);
+                                            client_id, server->server_type,
+                                            NULL);
       if (!client) {
        /* If router did not find the client the it is bogus */
-       if (server->server_type == SILC_ROUTER)
+       if (server->server_type != SILC_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);
+                                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.registered = TRUE;
+       client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
       }
     }
 
+    /* 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))
+    if (silc_server_client_on_channel(client, channel)) {
+      SILC_LOG_DEBUG(("Client already on channel"));
       break;
+    }
+
+    /* Send to channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
 
-    if (server->server_type == SILC_SERVER && 
+    if (server->server_type != SILC_ROUTER && 
        sock->type == SILC_SOCKET_TYPE_ROUTER)
       /* The channel is global now */
       channel->global_users = TRUE;
 
+    SILC_LOG_DEBUG(("Joining to channel %s", channel->channel_name));
+
     /* JOIN the global client to the channel (local clients (if router 
        created the channel) is joined in the pending JOIN command). */
     chl = silc_calloc(1, sizeof(*chl));
     chl->client = client;
     chl->channel = channel;
-    silc_list_add(channel->user_list, chl);
-    silc_list_add(client->channels, chl);
+
+    /* 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_hash_table_add(channel->user_list, client, chl);
+    silc_hash_table_add(client->channels, channel, chl);
     silc_free(client_id);
+    channel->user_count++;
 
     break;
 
@@ -188,10 +238,12 @@ void silc_server_notify(SilcServer server,
      */
     SILC_LOG_DEBUG(("LEAVE notify"));
 
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               packet->dst_id_type);
-    if (!channel_id)
-      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;
+    }
 
     /* Get channel entry */
     channel = silc_idlist_find_channel_by_id(server->global_list, 
@@ -217,17 +269,12 @@ void silc_server_notify(SilcServer server,
       goto out;
     }
 
-    /* Send to channel */
-    silc_server_packet_send_to_channel(server, sock, 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);
+                                          client_id, TRUE, NULL);
     if (!client) {
       client = silc_idlist_find_client_by_id(server->local_list, 
-                                            client_id, NULL);
+                                            client_id, TRUE, NULL);
       if (!client) {
        silc_free(client_id);
        silc_free(channel_id);
@@ -236,6 +283,15 @@ void silc_server_notify(SilcServer server,
     }
     silc_free(client_id);
 
+    /* Check if on channel */
+    if (!silc_server_client_on_channel(client, channel))
+      break;
+
+    /* 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);
+
     /* Remove the user from channel */
     silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
     break;
@@ -256,10 +312,10 @@ void silc_server_notify(SilcServer server,
 
     /* Get client entry */
     client = silc_idlist_find_client_by_id(server->global_list, 
-                                          client_id, &cache);
+                                          client_id, TRUE, &cache);
     if (!client) {
       client = silc_idlist_find_client_by_id(server->local_list, 
-                                            client_id, &cache);
+                                            client_id, TRUE, &cache);
       if (!client) {
        silc_free(client_id);
        goto out;
@@ -272,17 +328,18 @@ 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);
+    /* 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);
 
-    client->data.registered = FALSE;
-    cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+    /* Remove the client from all channels. */
+    silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, FALSE);
 
-#if 0
-    /* Remove the client entry */
-    if (!silc_idlist_del_client(server->global_list, client))
-      silc_idlist_del_client(server->local_list, client);
-#endif
+    client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+    cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
     break;
 
   case SILC_NOTIFY_TYPE_TOPIC_SET:
@@ -292,10 +349,12 @@ void silc_server_notify(SilcServer server,
 
     SILC_LOG_DEBUG(("TOPIC SET notify"));
 
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               packet->dst_id_type);
-    if (!channel_id)
-      goto out;
+    if (!channel_id) {
+      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                 packet->dst_id_type);
+      if (!channel_id)
+       goto out;
+    }
 
     /* Get channel entry */
     channel = silc_idlist_find_channel_by_id(server->global_list, 
@@ -316,10 +375,8 @@ void silc_server_notify(SilcServer server,
       goto out;
     }
 
-    if (channel->topic)
-      silc_free(channel->topic);
-    channel->topic = silc_calloc(tmp_len, sizeof(*channel->topic));
-    memcpy(channel->topic, tmp, tmp_len);
+    silc_free(channel->topic);
+    channel->topic = strdup(tmp);
 
     /* Send the same notify to the channel */
     silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
@@ -393,10 +450,12 @@ void silc_server_notify(SilcServer server,
     
     SILC_LOG_DEBUG(("CMODE CHANGE notify"));
       
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               packet->dst_id_type);
-    if (!channel_id)
-      goto out;
+    if (!channel_id) {
+      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                 packet->dst_id_type);
+      if (!channel_id)
+       goto out;
+    }
 
     /* Get channel entry */
     channel = silc_idlist_find_channel_by_id(server->global_list, 
@@ -410,11 +469,6 @@ void silc_server_notify(SilcServer server,
       }
     }
 
-    /* Send the same notify to the channel */
-    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
-                                      FALSE, packet->buffer->data, 
-                                      packet->buffer->len, FALSE);
-
     /* Get the mode */
     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
     if (!tmp) {
@@ -424,12 +478,22 @@ void silc_server_notify(SilcServer server,
 
     SILC_GET32_MSB(mode, tmp);
 
+    /* Check if mode changed */
+    if (channel->mode == mode)
+      break;
+
+    /* 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);
+
     /* 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 */
-      silc_server_create_channel_key(server, channel, 0);
+      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. */
@@ -454,84 +518,152 @@ void silc_server_notify(SilcServer server,
 
       /* Set the HMAC key out of current channel key. The client must do
         this locally. */
-      silc_hash_make(channel->hmac->hash, channel->key, channel->key_len / 8, 
+      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(channel->hmac->hash));
+                       silc_hash_len(silc_hmac_get_hash(channel->hmac)));
       memset(hash, 0, sizeof(hash));
     }
 
+    /* Get the passphrase */
+    tmp = silc_argument_get_arg_type(args, 5, &tmp_len);
+    if (tmp) {
+      silc_free(channel->passphrase);
+      channel->passphrase = strdup(tmp);
+    }
+
     break;
 
   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
-    /* 
-     * Distribute the notify to local clients on the channel
-     */
-
-    SILC_LOG_DEBUG(("CUMODE CHANGE notify"));
-
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               packet->dst_id_type);
-    if (!channel_id)
-      goto out;
+    {
+      /* 
+       * 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;
+      }
 
-    /* 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, 
+      /* 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;
+       }
+      }
+
+      /* Get the mode */
+      tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+      if (!tmp) {
        silc_free(channel_id);
        goto out;
       }
-    }
-
-    /* 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, NULL);
-    if (!client) {
-      client = silc_idlist_find_client_by_id(server->local_list, 
-                                            client_id, NULL);
-      if (!client) {
-       silc_free(client_id);
+      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);
+      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)
-      if (chl->client == client) {
-       /* Change the mode */
-       chl->mode = mode;
-       break;
+      /* 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;
+       }
       }
-
-    /* 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;
+      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;
+    }
 
   case SILC_NOTIFY_TYPE_INVITE:
 
@@ -625,10 +757,10 @@ void silc_server_notify(SilcServer server,
       goto out;
 
     /* Get the channel entry */
-    channel = silc_idlist_find_channel_by_id(server->global_list, 
+    channel = silc_idlist_find_channel_by_id(server->local_list, 
                                             channel_id, NULL);
     if (!channel) {
-      channel = silc_idlist_find_channel_by_id(server->local_list, 
+      channel = silc_idlist_find_channel_by_id(server->global_list, 
                                               channel_id, NULL);
       if (!channel) {
        silc_free(channel_id);
@@ -655,19 +787,27 @@ void silc_server_notify(SilcServer server,
                    silc_id_render(channel_id2, SILC_ID_CHANNEL)));
 
     /* Replace the Channel ID */
-    if (!silc_idlist_replace_channel_id(server->global_list, channel_id,
+    if (!silc_idlist_replace_channel_id(server->local_list, channel_id,
                                        channel_id2))
-      if (!silc_idlist_replace_channel_id(server->local_list, channel_id,
+      if (!silc_idlist_replace_channel_id(server->global_list, channel_id,
                                          channel_id2)) {
        silc_free(channel_id2);
        channel_id2 = NULL;
       }
 
     if (channel_id2) {
-      SilcBuffer users = NULL;
-      
+      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);
+      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,
@@ -675,6 +815,24 @@ void silc_server_notify(SilcServer server,
                                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);
+      }
+
+      /* 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);
+      }
     }
 
     silc_free(channel_id);
@@ -698,11 +856,62 @@ void silc_server_notify(SilcServer server,
 
     /* Get server entry */
     server_entry = silc_idlist_find_server_by_id(server->global_list, 
-                                                server_id, NULL);
+                                                server_id, TRUE, NULL);
+    local = TRUE;
     if (!server_entry) {
       server_entry = silc_idlist_find_server_by_id(server->local_list, 
-                                                  server_id, NULL);
+                                                  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);
+         }
+       }
+
        silc_free(server_id);
        goto out;
       }
@@ -714,8 +923,8 @@ void silc_server_notify(SilcServer server,
     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);
+    silc_idlist_del_server(local ? server->local_list :
+                          server->global_list, server_entry);
 
     /* XXX update statistics */
 
@@ -728,10 +937,12 @@ void silc_server_notify(SilcServer server,
     
     SILC_LOG_DEBUG(("KICKED notify"));
       
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               packet->dst_id_type);
-    if (!channel_id)
-      goto out;
+    if (!channel_id) {
+      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                 packet->dst_id_type);
+      if (!channel_id)
+       goto out;
+    }
 
     /* Get channel entry */
     channel = silc_idlist_find_channel_by_id(server->global_list, 
@@ -754,23 +965,23 @@ void silc_server_notify(SilcServer server,
     if (!client_id)
       goto out;
 
-    /* Send to channel */
-    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
-                                      FALSE, packet->buffer->data, 
-                                      packet->buffer->len, FALSE);
-
     /* 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, NULL);
+                                          client_id, TRUE, NULL);
     if (!client) {
       client = silc_idlist_find_client_by_id(server->local_list, 
-                                            client_id, NULL);
+                                            client_id, TRUE, NULL);
       if (!client) {
        silc_free(client_id);
        goto out;
       }
     }
 
+    /* Send to channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
+
     /* Remove the client from channel */
     silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
 
@@ -782,7 +993,7 @@ void silc_server_notify(SilcServer server,
        * Distribute the notify to local clients on channels
        */
       unsigned char *id;
-      unsigned int id_len;
+      SilcUInt32 id_len;
     
       SILC_LOG_DEBUG(("KILLED notify"));
       
@@ -796,10 +1007,10 @@ void silc_server_notify(SilcServer server,
 
       /* 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, NULL);
+                                            client_id, TRUE, NULL);
       if (!client) {
        client = silc_idlist_find_client_by_id(server->local_list, 
-                                              client_id, NULL);
+                                              client_id, TRUE, NULL);
        if (!client) {
          silc_free(client_id);
          goto out;
@@ -809,8 +1020,7 @@ void silc_server_notify(SilcServer server,
 
       /* 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->data.registered &&
-         client->connection) {
+      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);
@@ -843,7 +1053,7 @@ void silc_server_notify(SilcServer server,
      */
 
     SILC_LOG_DEBUG(("UMODE_CHANGE notify"));
-      
+
     /* Get client ID */
     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
     if (!tmp)
@@ -854,10 +1064,10 @@ void silc_server_notify(SilcServer server,
 
     /* Get client entry */
     client = silc_idlist_find_client_by_id(server->global_list, 
-                                          client_id, NULL);
+                                          client_id, TRUE, NULL);
     if (!client) {
       client = silc_idlist_find_client_by_id(server->local_list, 
-                                            client_id, NULL);
+                                            client_id, TRUE, NULL);
       if (!client) {
        silc_free(client_id);
        goto out;
@@ -869,9 +1079,33 @@ void silc_server_notify(SilcServer server,
     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 */
-    SILC_GET32_MSB(client->mode, tmp);
+    client->mode = mode;
 
     break;
 
@@ -923,7 +1157,7 @@ void silc_server_notify(SilcServer server,
     if (tmp && channel->ban_list) {
       char *start, *end, *n;
       
-      if (!strcmp(channel->ban_list, tmp)) {
+      if (!strncmp(channel->ban_list, tmp, strlen(channel->ban_list) - 1)) {
        silc_free(channel->ban_list);
        channel->ban_list = NULL;
       } else {
@@ -939,7 +1173,6 @@ void silc_server_notify(SilcServer server,
        }
       }
     }
-
     break;
 
     /* Ignore rest of the notify types for now */
@@ -960,9 +1193,9 @@ void silc_server_notify_list(SilcServer server,
 {
   SilcPacketContext *new;
   SilcBuffer buffer;
-  unsigned short len;
+  SilcUInt16 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)
@@ -1023,21 +1256,45 @@ void silc_server_private_message(SilcServer server,
   SILC_LOG_DEBUG(("Start"));
 
   if (packet->src_id_type != SILC_ID_CLIENT ||
-      packet->dst_id_type != SILC_ID_CLIENT)
-    return;
-
-  if (!packet->dst_id)
+      packet->dst_id_type != SILC_ID_CLIENT || !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)
+  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;
+
+    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);
+    }
+
+    silc_buffer_free(idp);
     return;
+  }
 
   /* Send the private message */
   silc_server_send_private_message(server, dst_sock, idata->send_key,
-                                  idata->hmac, packet);
+                                  idata->hmac_send, idata->psn_send++,
+                                  packet);
 }
 
 /* Received private message key packet.. This packet is never for us. It is to
@@ -1069,7 +1326,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, idata->psn_send++, packet, FALSE);
 }
 
 /* Processes incoming command reply packet. The command reply packet may
@@ -1101,7 +1358,7 @@ void silc_server_command_reply(SilcServer server,
     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);
+    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);
@@ -1111,7 +1368,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, server->id_string_len)) {
       SILC_LOG_ERROR(("Cannot process command reply to unknown server"));
       return;
     }
@@ -1133,8 +1390,8 @@ void silc_server_command_reply(SilcServer server,
     idata = (SilcIDListData)client;
     
     /* Encrypt packet */
-    silc_packet_encrypt(idata->send_key, idata->hmac, dst_sock->outbuf, 
-                       buffer->len);
+    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);
@@ -1151,9 +1408,10 @@ void silc_server_channel_message(SilcServer server,
                                 SilcPacketContext *packet)
 {
   SilcChannelEntry channel = NULL;
-  SilcChannelClientEntry chl;
   SilcChannelID *id = NULL;
   void *sender = NULL;
+  void *sender_entry = NULL;
+  bool local = TRUE;
 
   SILC_LOG_DEBUG(("Processing channel message"));
 
@@ -1176,62 +1434,40 @@ 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 (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 (chl == SILC_LIST_END) {
+    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);
+    /* 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;
+    }
   }
 
   /* 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);
 
@@ -1265,6 +1501,13 @@ 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, 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
@@ -1276,12 +1519,15 @@ 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;
+  SilcUInt16 username_len;
+  SilcUInt32 id_len;
   int ret;
+  char *hostname, *nickname;
+  int nickfail = 0;
 
   SILC_LOG_DEBUG(("Creating new client"));
 
@@ -1292,71 +1538,160 @@ 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;
   }
 
   /* Parse incoming packet */
   ret = silc_buffer_unformat(buffer,
-                            SILC_STR_UI16_STRING_ALLOC(&username),
+                            SILC_STR_UI16_NSTRING_ALLOC(&username, 
+                                                        &username_len),
                             SILC_STR_UI16_STRING_ALLOC(&realname),
                             SILC_STR_END);
   if (ret == -1) {
-    if (username)
-      silc_free(username);
-    if (realname)
-      silc_free(realname);
+    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);
-    if (realname)
-      silc_free(realname);
+    silc_free(realname);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                   "Incomplete client information");
     return NULL;
   }
 
-  /* Create Client ID */
-  silc_id_create_client_id(server->id, server->rng, server->md5hash,
-                          username, &client_id);
+  if (username_len > 128)
+    username[128] = '\0';
 
-  if (strlen(username) > 128)
-    username[127] = '\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;
+    }
+    
+    pident = silc_pkcs_decode_identifier(client->data.public_key->identifier);
+    if (pident) {
+      phostname = strdup(pident->host);
+      silc_pkcs_free_identifier(pident);
+    }
+
+    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);
+      }
+    }
+#endif
+  }
+
+  /* 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->registered = TRUE;
-  client->nickname = strdup(username);
+  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);
 
-  /* Update the cache entry */
-  cache->id = (void *)client_id;
-  cache->type = SILC_ID_CLIENT;
-  cache->data = username;
-  cache->data_len = strlen(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, 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, 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);
@@ -1365,8 +1700,8 @@ SilcClientEntry silc_server_new_client(SilcServer server,
 
   /* 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));
+                         ("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,
@@ -1388,10 +1723,14 @@ SilcClientEntry silc_server_new_client(SilcServer server,
                             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));
+                           ("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",
@@ -1429,13 +1768,13 @@ SilcServerEntry silc_server_new_server(SilcServer server,
                                       SilcPacketContext *packet)
 {
   SilcBuffer buffer = packet->buffer;
-  SilcServerEntry new_server;
-  SilcIDCacheEntry cache;
+  SilcServerEntry new_server, server_entry;
   SilcServerID *server_id;
   SilcIDListData idata;
   unsigned char *server_name, *id_string;
-  unsigned short id_len, name_len;
+  SilcUInt16 id_len, name_len;
   int ret;
+  bool local = TRUE;
 
   SILC_LOG_DEBUG(("Creating new server"));
 
@@ -1447,11 +1786,10 @@ 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 */
+  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 */
@@ -1486,17 +1824,30 @@ SilcServerEntry silc_server_new_server(SilcServer server,
   }
   silc_free(id_string);
 
-  /* Update client entry */
-  idata->registered = TRUE;
+  /* 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)));
 
-  /* Update the cache entry */
-  cache->id = (void *)server_id;
-  cache->type = SILC_ID_SERVER;
-  cache->data = server_name;
-  cache->data_len = strlen(server_name);
-  silc_idcache_sort_by_data(server->local_list->servers);
+  /* 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
@@ -1505,11 +1856,43 @@ 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++;
 
+  /* 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;
 }
 
@@ -1523,11 +1906,10 @@ static void silc_server_new_id_real(SilcServer server,
 {
   SilcBuffer buffer = packet->buffer;
   SilcIDList id_list;
-  SilcServerEntry router;
+  SilcServerEntry router, server_entry;
   SilcSocketConnection router_sock;
   SilcIDPayload idp;
   SilcIdType id_type;
-  unsigned char *hash = NULL;
   void *id;
 
   SILC_LOG_DEBUG(("Processing new ID"));
@@ -1537,45 +1919,72 @@ static void silc_server_new_id_real(SilcServer server,
       packet->src_id_type != SILC_ID_SERVER)
     return;
 
-  idp = silc_id_payload_parse(buffer);
+  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 */
-  if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER)
+  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 sender of this packet is server and we are router we need to
-     broadcast this packet to other routers in the network. */
-  if (broadcast && !server->standalone && server->server_type == SILC_ROUTER &&
-      sock->type == SILC_SOCKET_TYPE_SERVER &&
-      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
-    SILC_LOG_DEBUG(("Broadcasting received New ID packet"));
-    silc_server_packet_send(server, server->router->connection,
-                           packet->type, 
-                           packet->flags | SILC_PACKET_FLAG_BROADCAST,
-                           buffer->data, buffer->len, FALSE);
-  }
-
-  if (sock->type == SILC_SOCKET_TYPE_SERVER)
+  /* 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;
-  else
+    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;
+  }
 
-  router_sock = sock;
-  router = sock->user_data;
+  if (!router)
+    goto out;
 
   switch(id_type) {
   case SILC_ID_CLIENT:
     {
       SilcClientEntry entry;
 
+      /* 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_LOG_DEBUG(("New client id(%s) from [%s] %s",
                      silc_id_render(id, SILC_ID_CLIENT),
                      sock->type == SILC_SOCKET_TYPE_SERVER ?
@@ -1583,18 +1992,18 @@ 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, 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.registered = TRUE;
+      entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
 
       if (sock->type == SILC_SOCKET_TYPE_SERVER)
        server->stat.cell_clients++;
@@ -1603,28 +2012,82 @@ static void silc_server_new_id_real(SilcServer server,
     break;
 
   case SILC_ID_SERVER:
-    SILC_LOG_DEBUG(("New server id(%s) from [%s] %s",
-                   silc_id_render(id, SILC_ID_SERVER),
-                   sock->type == SILC_SOCKET_TYPE_SERVER ?
-                   "Server" : "Router", sock->hostname));
-    
-    /* As a router we keep information of all global information in our global
-       list. Cell wide information however is kept in the local list. */
-    silc_idlist_add_server(id_list, NULL, 0, id, router, router_sock);
+    {
+      SilcServerEntry entry;
 
-    if (sock->type == SILC_SOCKET_TYPE_SERVER)
-      server->stat.cell_servers++;
-    server->stat.servers++;
+      /* 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;
+      }
+      
+      /* 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));
+      
+      /* 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;
+      
+      if (sock->type == SILC_SOCKET_TYPE_SERVER)
+       server->stat.cell_servers++;
+      server->stat.servers++;
+    }
     break;
 
   case SILC_ID_CHANNEL:
     SILC_LOG_ERROR(("Channel cannot be registered with NEW_ID packet"));
+    goto out;
     break;
 
   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_id_payload_free(idp);
 }
@@ -1647,7 +2110,7 @@ void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock,
 {
   SilcPacketContext *new_id;
   SilcBuffer idp;
-  unsigned short id_len;
+  SilcUInt16 id_len;
 
   SILC_LOG_DEBUG(("Processing New ID List"));
 
@@ -1666,6 +2129,10 @@ void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock,
                            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
@@ -1715,10 +2182,12 @@ void silc_server_new_channel(SilcServer server,
   SilcChannelPayload payload;
   SilcChannelID *channel_id;
   char *channel_name;
-  unsigned int name_len;
+  SilcUInt32 name_len;
   unsigned char *id;
-  unsigned int id_len;
-  unsigned int mode;
+  SilcUInt32 id_len;
+  SilcUInt32 mode;
+  SilcServerEntry server_entry;
+  SilcChannelEntry channel;
 
   SILC_LOG_DEBUG(("Processing New Channel"));
 
@@ -1728,7 +2197,8 @@ void silc_server_new_channel(SilcServer server,
     return;
 
   /* Parse the channel payload */
-  payload = silc_channel_payload_parse(packet->buffer);
+  payload = silc_channel_payload_parse(packet->buffer->data,
+                                      packet->buffer->len);
   if (!payload)
     return;
     
@@ -1745,29 +2215,36 @@ void silc_server_new_channel(SilcServer server,
 
   id = silc_channel_get_id(payload, &id_len);
 
+  server_entry = (SilcServerEntry)sock->user_data;
+
   if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
-    /* Add the server to global list as it is coming from router. It 
+    /* Add the channel 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));
+    /* 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, server->router->connection, 
-                           NULL, NULL);
-
-    server->stat.channels++;
+      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. */
-    SilcChannelEntry channel;
     SilcBuffer chk;
 
-    SILC_LOG_DEBUG(("New channel id(%s) from [Server] %s",
+    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);
@@ -1775,10 +2252,27 @@ void silc_server_new_channel(SilcServer server,
       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
+    /* 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;
+       }
+      }
+
+      /* Create the channel with the provided Channel ID */
       channel = silc_server_create_new_channel_with_id(server, NULL, NULL,
                                                       channel_name,
                                                       channel_id, FALSE);
@@ -1788,7 +2282,12 @@ void silc_server_new_channel(SilcServer server,
        return;
       }
 
+      /* Get the mode and set it to the channel */
+      channel->mode = silc_channel_get_mode(payload);
+
       /* 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),
@@ -1803,19 +2302,14 @@ 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;
+      SilcBuffer users = NULL, users_modes = NULL;
 
-      if (!channel->id)
-       channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
-
-      if (SILC_ID_CHANNEL_COMPARE(channel_id, channel->id)) {
+      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
@@ -1825,21 +2319,23 @@ void silc_server_new_channel(SilcServer server,
        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, SILC_ID_SERVER_LEN,
-                                     channel->cipher, channel->hmac_name);
+                                     SILC_ID_SERVER,
+                                     channel->cipher, channel->hmac_name,
+                                     channel->passphrase);
       }
 
       /* Create new key for the channel and send it to the server and
         everybody else possibly on the channel. */
 
       if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
-       silc_server_create_channel_key(server, channel, 0);
+       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_CHANNEL_LEN;
-       
+       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->
@@ -1859,7 +2355,8 @@ 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. */
-      silc_server_announce_get_channel_users(server, channel, &users);
+      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,
@@ -1867,6 +2364,15 @@ void silc_server_new_channel(SilcServer server,
                                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);
+      }
     }
   }
 
@@ -1882,7 +2388,7 @@ void silc_server_new_channel_list(SilcServer server,
 {
   SilcPacketContext *new;
   SilcBuffer buffer;
-  unsigned short len1, len2;
+  SilcUInt16 len1, len2;
 
   SILC_LOG_DEBUG(("Processing New Channel List"));
 
@@ -1902,6 +2408,10 @@ void silc_server_new_channel_list(SilcServer server,
                            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
@@ -1973,7 +2483,8 @@ 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, idata->psn_send++,
+                          packet, FALSE);
 }
 
 /* Received connection auth request packet that is used during connection
@@ -1981,17 +2492,17 @@ void silc_server_key_agreement(SilcServer server,
    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's always knows what authentication
+   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)
 {
-  SilcServerConfigSectionClientConnection *client = NULL;
-  unsigned short conn_type;
+  SilcServerConfigClient *client = NULL;
+  SilcUInt16 conn_type;
   int ret;
-  SilcAuthMethod auth_meth;
+  SilcAuthMethod auth_meth = SILC_AUTH_NONE;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -2011,18 +2522,84 @@ void silc_server_connection_auth_request(SilcServer server,
 
   /* Get the authentication method for the client */
   auth_meth = SILC_AUTH_NONE;
-  client = silc_server_config_find_client_conn(server->config,
-                                              sock->ip,
-                                              sock->port);
+  client = silc_server_config_find_client(server, sock->ip);
   if (!client)
-    client = silc_server_config_find_client_conn(server->config,
-                                                sock->hostname,
-                                                sock->port);
-  if (client)
-    auth_meth = client->auth_meth;
-         
+    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;
+  }
+
   /* Send it back to the client */
-  silc_server_send_connection_auth_request(server, sock,
-                                          conn_type,
-                                          auth_meth);
+  silc_server_send_connection_auth_request(server, sock, 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 */
+    silc_protocol_execute(protocol, server->schedule, 0, 0);
+}
+
+/* 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. */
+
+void silc_server_ftp(SilcServer server,
+                    SilcSocketConnection sock,
+                    SilcPacketContext *packet)
+{
+  SilcSocketConnection dst_sock;
+  SilcIDListData idata;
+
+  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);
 }