updates.
[silc.git] / apps / silcd / packet_receive.c
index 65a60a72853c874efd1ac21d473322609a72b5b7..57bdc9e867d38d0cbc69604056a2e21b34ae0096 100644 (file)
@@ -45,10 +45,8 @@ void silc_server_private_message(SilcServer server,
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (!packet->dst_id) {
-    SILC_LOG_ERROR(("Bad Client ID in private message packet, dropped"));
+  if (!packet->dst_id)
     goto err;
-  }
 
   /* Decode destination Client ID */
   id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
@@ -59,7 +57,7 @@ void silc_server_private_message(SilcServer server,
 
   /* If the destination belongs to our server we don't have to route
      the message anywhere but to send it to the local destination. */
-  client = silc_idlist_find_client_by_id(server->local_list, id);
+  client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
   if (client) {
     /* It exists, now deliver the message to the destination */
     dst_sock = (SilcSocketConnection)client->connection;
@@ -72,7 +70,7 @@ void silc_server_private_message(SilcServer server,
         we will send the packet to that server. */
       router = (SilcServerEntry)dst_sock->user_data;
       idata = (SilcIDListData)router;
-      //      assert(client->router == server->id_entry);
+      //assert(client->router == server->id_entry);
 
       silc_server_send_private_message(server, dst_sock,
                                       idata->send_key,
@@ -108,16 +106,20 @@ void silc_server_private_message(SilcServer server,
   /* We are router and we will perform route lookup for the destination 
      and send the message to fastest route. */
   if (server->server_type == SILC_ROUTER && !server->standalone) {
-    dst_sock = silc_server_get_route(server, id, SILC_ID_CLIENT);
-    router = (SilcServerEntry)dst_sock->user_data;
-    idata = (SilcIDListData)router;
+    /* Check first that the ID is valid */
+    client = silc_idlist_find_client_by_id(server->global_list, id, NULL);
+    if (client) {
+      dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
+      router = (SilcServerEntry)dst_sock->user_data;
+      idata = (SilcIDListData)router;
 
-    /* Get fastest route and send packet. */
-    if (router)
-      silc_server_send_private_message(server, dst_sock, 
-                                      idata->send_key,
-                                      idata->hmac, packet);
-    return;
+      /* Get fastest route and send packet. */
+      if (router)
+       silc_server_send_private_message(server, dst_sock, 
+                                        idata->send_key,
+                                        idata->hmac, packet);
+      return;
+    }
   }
 
  err:
@@ -125,21 +127,19 @@ void silc_server_private_message(SilcServer server,
                         "No such nickname: Private message not sent");
 }
 
-/* Relays received command reply packet to the correct destination. The
-   destination must be one of our locally connected client or the packet
-   will be ignored. This is called when server has forwarded one of
-   client's command request to router and router has now replied to the 
-   command. */
+/* Processes incoming command reply packet. The command reply packet may
+   be destined to one of our clients or it may directly for us. We will 
+   call the command reply routine after processing the packet. */
 
-void silc_server_packet_relay_command_reply(SilcServer server,
-                                           SilcSocketConnection sock,
-                                           SilcPacketContext *packet)
+void silc_server_command_reply(SilcServer server,
+                              SilcSocketConnection sock,
+                              SilcPacketContext *packet)
 {
   SilcBuffer buffer = packet->buffer;
-  SilcClientEntry client;
-  SilcClientID *id;
+  SilcClientEntry client = NULL;
   SilcSocketConnection dst_sock;
   SilcIDListData idata;
+  SilcClientID *id = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -148,42 +148,52 @@ void silc_server_packet_relay_command_reply(SilcServer server,
       sock->type != SILC_SOCKET_TYPE_ROUTER)
     return;
 
-  /* Destination must be client */
-  if (packet->dst_id_type != SILC_ID_CLIENT)
+  if (packet->dst_id_type == SILC_ID_CHANNEL)
     return;
 
-  /* Execute command reply locally for the command */
-  silc_server_command_reply_process(server, sock, buffer);
-
-  id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
-
-  /* Destination must be one of ours */
-  client = silc_idlist_find_client_by_id(server->local_list, id);
-  if (!client) {
-    SILC_LOG_ERROR(("Cannot relay command reply to unknown client"));
-    silc_free(id);
-    return;
+  if (packet->dst_id_type == SILC_ID_CLIENT) {
+    /* Destination must be one of ours */
+    id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
+    client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
+    if (!client) {
+      SILC_LOG_ERROR(("Cannot process command reply to unknown client"));
+      silc_free(id);
+      return;
+    }
   }
 
-  /* Relay the packet to the client */
-
-  dst_sock = (SilcSocketConnection)client->connection;
-  silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
-                  + packet->dst_id_len + packet->padlen);
-
-  silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
-  silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
+  if (packet->dst_id_type == SILC_ID_SERVER) {
+    /* For now this must be for us */
+    if (SILC_ID_SERVER_COMPARE(packet->dst_id, server->id_string)) {
+      SILC_LOG_ERROR(("Cannot process command reply to unknown server"));
+      return;
+    }
+  }
 
-  idata = (SilcIDListData)client;
+  /* Execute command reply locally for the command */
+  silc_server_command_reply_process(server, sock, buffer);
 
-  /* Encrypt packet */
-  silc_packet_encrypt(idata->send_key, idata->hmac, dst_sock->outbuf, 
-                     buffer->len);
+  if (packet->dst_id_type == SILC_ID_CLIENT && client && id) {
+    /* Relay the packet to the client */
+    
+    dst_sock = (SilcSocketConnection)client->connection;
+    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
+                    + packet->dst_id_len + packet->padlen);
+    
+    silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
+    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
+    
+    idata = (SilcIDListData)client;
+    
+    /* Encrypt packet */
+    silc_packet_encrypt(idata->send_key, idata->hmac, dst_sock->outbuf, 
+                       buffer->len);
     
-  /* Send the packet */
-  silc_server_packet_send_real(server, dst_sock, TRUE);
+    /* Send the packet */
+    silc_server_packet_send_real(server, dst_sock, TRUE);
 
-  silc_free(id);
+    silc_free(id);
+  }
 }
 
 /* Process received channel message. The message can be originated from
@@ -202,17 +212,19 @@ void silc_server_channel_message(SilcServer server,
 
   /* Sanity checks */
   if (packet->dst_id_type != SILC_ID_CHANNEL) {
-    SILC_LOG_ERROR(("Received bad message for channel, dropped"));
     SILC_LOG_DEBUG(("Received bad message for channel, dropped"));
     goto out;
   }
 
   /* Find channel entry */
   id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
-  channel = silc_idlist_find_channel_by_id(server->local_list, id);
+  channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
   if (!channel) {
-    SILC_LOG_DEBUG(("Could not find channel"));
-    goto out;
+    channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+    if (!channel) {
+      SILC_LOG_DEBUG(("Could not find channel"));
+      goto out;
+    }
   }
 
   /* See that this client is on the channel. If the message is coming
@@ -247,74 +259,25 @@ void silc_server_channel_message(SilcServer server,
 
 /* Received channel key packet. We distribute the key to all of our locally
    connected clients on the channel. */
-/* XXX Router must accept this packet and distribute the key to all its
-   server that has clients on the channel */
 
 void silc_server_channel_key(SilcServer server,
                             SilcSocketConnection sock,
                             SilcPacketContext *packet)
 {
   SilcBuffer buffer = packet->buffer;
-  SilcChannelKeyPayload payload = NULL;
-  SilcChannelID *id = NULL;
   SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
-  unsigned char *tmp;
-  unsigned int tmp_len;
-  char *cipher;
-
-  if (packet->src_id_type != SILC_ID_SERVER &&
-      sock->type != SILC_SOCKET_TYPE_ROUTER)
-    goto out;
-
-  /* Decode channel key payload */
-  payload = silc_channel_key_payload_parse(buffer);
-  if (!payload) {
-    SILC_LOG_ERROR(("Bad channel key payload, dropped"));
-    goto out;
-  }
-
-  /* Get channel ID */
-  tmp = silc_channel_key_get_id(payload, &tmp_len);
-  id = silc_id_payload_parse_id(tmp, tmp_len);
-  if (!id)
-    goto out;
 
-  /* Get the channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, id);
-  if (!channel) {
-    SILC_LOG_ERROR(("Received key for non-existent channel"));
-    goto out;
-  }
-
-  /* Save the key for us as well */
-  tmp = silc_channel_key_get_key(payload, &tmp_len);
-  if (!tmp)
-    goto out;
-  cipher = silc_channel_key_get_cipher(payload, NULL);;
-  if (!cipher)
-    goto out;
-  if (!silc_cipher_alloc(cipher, &channel->channel_key))
-    goto out;
+  if (packet->src_id_type != SILC_ID_SERVER)
+    return;
 
-  /* Distribute the key to all clients on the channel */
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-    silc_server_packet_send(server, chl->client->connection,
-                           SILC_PACKET_CHANNEL_KEY, 0,
-                           buffer->data, buffer->len, TRUE);
-  }
+  /* Save the channel key */
+  channel = silc_server_save_channel_key(server, buffer, NULL);
+  if (!channel)
+    return;
 
-  channel->key_len = tmp_len * 8;
-  channel->key = silc_calloc(tmp_len, sizeof(unsigned char));
-  memcpy(channel->key, tmp, tmp_len);
-  channel->channel_key->cipher->set_key(channel->channel_key->context, 
-                                       tmp, tmp_len);
- out:
-  if (id)
-    silc_free(id);
-  if (payload)
-    silc_channel_key_payload_free(payload);
+  /* Distribute the key to everybody who is on the channel. If we are router
+     we will also send it to locally connected servers. */
+  silc_server_send_channel_key(server, channel, FALSE);
 }
 
 /* Received packet to replace a ID. This checks that the requested ID
@@ -374,12 +337,20 @@ void silc_server_replace_id(SilcServer server,
   /* Replace the old ID */
   switch(old_id_type) {
   case SILC_ID_CLIENT:
+    SILC_LOG_DEBUG(("Old Client ID id(%s)", 
+                   silc_id_render(id, SILC_ID_CLIENT)));
+    SILC_LOG_DEBUG(("New Client ID id(%s)", 
+                   silc_id_render(id2, SILC_ID_CLIENT)));
     if (silc_idlist_replace_client_id(server->local_list, id, id2) == NULL)
       if (server->server_type == SILC_ROUTER)
        silc_idlist_replace_client_id(server->global_list, id, id2);
     break;
 
   case SILC_ID_SERVER:
+    SILC_LOG_DEBUG(("Old Server ID id(%s)", 
+                   silc_id_render(id, SILC_ID_CLIENT)));
+    SILC_LOG_DEBUG(("New Server ID id(%s)", 
+                   silc_id_render(id2, SILC_ID_CLIENT)));
     if (silc_idlist_replace_server_id(server->local_list, id, id2) == NULL)
       if (server->server_type == SILC_ROUTER)
        silc_idlist_replace_server_id(server->global_list, id, id2);
@@ -581,19 +552,19 @@ SilcServerEntry silc_server_new_server(SilcServer server,
 }
 
 /* Processes incoming New ID packet. New ID Payload is used to distribute
-   information about newly registered clients, servers and created 
-   channels. */
+   information about newly registered clients and servers. */
 
 void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
                        SilcPacketContext *packet)
 {
   SilcBuffer buffer = packet->buffer;
   SilcIDList id_list;
-  SilcServerEntry tmpserver, router;
+  SilcServerEntry router;
   SilcSocketConnection router_sock;
   SilcIDPayload idp;
   SilcIdType id_type;
-  void *id, *tmpid;
+  unsigned char *hash = NULL;
+  void *id;
 
   SILC_LOG_DEBUG(("Processing new ID"));
 
@@ -628,6 +599,7 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
                            buffer->data, buffer->len, FALSE);
   }
 
+#if 0
   /* If the packet is originated from the one who sent it to us we know
      that the ID belongs to our cell, unless the sender was router. */
   tmpid = silc_id_str2id(packet->src_id, SILC_ID_SERVER);
@@ -646,52 +618,77 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
   }
 
   silc_free(tmpid);
+#endif
+
+  if (sock->type == SILC_SOCKET_TYPE_SERVER)
+    id_list = server->local_list;
+  else
+    id_list = server->global_list;
+
+  router_sock = sock;
+  router = sock->user_data;
 
   switch(id_type) {
   case SILC_ID_CLIENT:
     {
-      SilcClientEntry idlist;
+      SilcClientEntry entry;
 
       SILC_LOG_DEBUG(("New client id(%s) from [%s] %s",
                      silc_id_render(id, SILC_ID_CLIENT),
                      sock->type == SILC_SOCKET_TYPE_SERVER ?
                      "Server" : "Router", sock->hostname));
+    
+      /* As a router we keep information of all global information in our
+        global list. Cell wide information however is kept in the local
+        list. The client is put to global list and we will take the hash
+        value of the Client ID and save it to the ID Cache system for fast
+        searching in the future. */
+      hash = silc_calloc(sizeof(((SilcClientID *)id)->hash), 
+                        sizeof(unsigned char));
+      memcpy(hash, ((SilcClientID *)id)->hash, 
+            sizeof(((SilcClientID *)id)->hash));
+      entry = silc_idlist_add_client(id_list, hash, NULL, NULL, id, 
+                                    router, router_sock);
+      entry->nickname = NULL;
 
-      /* Add the client to our local list. We are router and we keep
-        cell specific local database of all clients in the cell. */
-      idlist = silc_idlist_add_client(id_list, NULL, NULL, NULL,
-                                     id, router, router_sock);
+#if 0
+      /* XXX Adding two ID's with same IP number replaces the old entry thus
+        gives wrong route. Thus, now disabled until figured out a better way
+        to do this or when removed the whole thing. This could be removed
+        because entry->router->connection gives always the most optimal route
+        for the ID anyway (unless new routes (faster perhaps) are established
+        after receiving this ID, this we don't know however). */
+      /* Add route cache for this ID */
+      silc_server_route_add(silc_server_route_hash(
+                           ((SilcClientID *)id)->ip.s_addr,
+                           server->id->port), ((SilcClientID *)id)->ip.s_addr,
+                           router);
+#endif
     }
     break;
 
   case SILC_ID_SERVER:
-    {
-      SilcServerEntry idlist;
-
-      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));
+    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);
 
-      /* Add the server to our local list. We are router and we keep
-        cell specific local database of all servers in the cell. */
-      idlist = silc_idlist_add_server(id_list, NULL, 0, id, router, 
-                                     router_sock);
-    }
+#if 0
+    /* Add route cache for this ID */
+    silc_server_route_add(silc_server_route_hash(
+                         ((SilcServerID *)id)->ip.s_addr,
+                         ((SilcServerID *)id)->port), 
+                         ((SilcServerID *)id)->ip.s_addr,
+                         router);
+#endif
     break;
 
   case SILC_ID_CHANNEL:
     SILC_LOG_ERROR(("Channel cannot be registered with NEW_ID packet"));
-#if 0
-    SILC_LOG_DEBUG(("New channel id(%s) from [%s] %s",
-                   silc_id_render(id, SILC_ID_CHANNEL),
-                   sock->type == SILC_SOCKET_TYPE_SERVER ?
-                   "Server" : "Router", sock->hostname));
-
-    /* Add the channel to our local list. We are router and we keep
-       cell specific local database of all channels in the cell. */
-    silc_idlist_add_channel(id_list, NULL, 0, id, router, NULL);
-#endif
     break;
 
   default:
@@ -704,7 +701,7 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
 
 /* Received Remove Channel User packet to remove a user from a channel. 
    Routers notify other routers that user has left a channel. Client must
-   not send this packet.. Normal server may send this packet but must not
+   not send this packet. Normal server may send this packet but must not
    receive it. */
 
 void silc_server_remove_channel_user(SilcServer server,
@@ -750,20 +747,27 @@ void silc_server_remove_channel_user(SilcServer server,
                            buffer->data, buffer->len, FALSE);
   }
 
-  /* XXX routers should check server->global_list as well */
   /* Get channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, channel_id);
-  if (!channel)
-    goto out;
-  
-  /* XXX routers should check server->global_list as well */
+  channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                          channel_id, NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel)
+      goto out;
+  }
+
   /* Get client entry */
-  client = silc_idlist_find_client_by_id(server->local_list, client_id);
-  if (!client)
-    goto out;
+  client = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
+  if (!client) {
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, NULL);
+    if (!client)
+      goto out;
+  }
 
-  /* Remove from channel */
-  silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
+  /* Remove user from channel */
+  silc_server_remove_from_one_channel(server, sock, channel, client, TRUE);
 
  out:
   if (tmp1)
@@ -833,15 +837,22 @@ void silc_server_notify(SilcServer server,
   SilcNotifyPayload payload;
   SilcNotifyType type;
   SilcArgumentPayload args;
-  int i;
+  SilcChannelID *channel_id;
+  SilcClientID *client_id;
+  SilcChannelEntry channel;
+  SilcClientEntry client;
+  unsigned char *tmp;
+  unsigned int tmp_len;
+
+  SILC_LOG_DEBUG(("Start"));
 
   if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
       packet->src_id_type != SILC_ID_SERVER)
     return;
 
-  /* For now we expect that the we are normal server and that the
+  /* XXX: For now we expect that the we are normal server and that the
      sender is router. Server could send (protocol allows it) notify to
-     router but we don't support it yet. XXX! */
+     router but we don't support it yet. */
   if (server->server_type != SILC_SERVER &&
       sock->type != SILC_SOCKET_TYPE_ROUTER)
     return;
@@ -857,12 +868,140 @@ void silc_server_notify(SilcServer server,
 
   switch(type) {
   case SILC_NOTIFY_TYPE_JOIN:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
+    SILC_LOG_DEBUG(("JOIN notify"));
+
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_type);
+    if (!channel_id)
+      goto out;
+
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      silc_free(channel_id);
+      goto out;
+    }
+
+    /* Get client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp) {
+      silc_free(channel_id);
+      goto out;
+    }
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+
+    /* If the the client is not in local list we check global list (ie. the
+       channel will be global channel) and if it does not exist then create
+       entry for the client. */
+    client = silc_idlist_find_client_by_id(server->local_list, 
+                                          client_id, NULL);
+    if (!client) {
+      SilcChannelClientEntry chl;
+
+      client = silc_idlist_find_client_by_id(server->global_list, 
+                                            client_id, NULL);
+      if (!client)
+       client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
+                                       client_id, sock->user_data, sock);
+
+      /* The channel is global now */
+      channel->global_users = TRUE;
+
+      /* Now actually JOIN the global client to the channel */
+      chl = silc_calloc(1, sizeof(*chl));
+      chl->client = client;
+      chl->channel = channel;
+      silc_list_add(channel->user_list, chl);
+      silc_list_add(client->channels, chl);
+    } else {
+      silc_free(client_id);
+    }
+
+    /* Send to channel */
+    silc_server_packet_send_to_channel(server, channel, packet->type, FALSE,
+                                      packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
     break;
 
   case SILC_NOTIFY_TYPE_LEAVE:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
+    SILC_LOG_DEBUG(("LEAVE notify"));
+
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_type);
+    if (!channel_id)
+      goto out;
+
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                            channel_id, NULL);
+    if (!channel) { 
+      silc_free(channel_id);
+      goto out;
+    }
+
+    /* Get client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp) {
+      silc_free(channel_id);
+      goto out;
+    }
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+
+    /* Send to channel */
+    silc_server_packet_send_to_channel(server, channel, packet->type, FALSE,
+                                      packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
+
+    /* Get client entry */
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, NULL);
+    if (!client) {
+      client = silc_idlist_find_client_by_id(server->local_list, 
+                                            client_id, NULL);
+      if (!client) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+    silc_free(client_id);
+
+    /* Remove the user from channel */
+    silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
     break;
 
   case SILC_NOTIFY_TYPE_SIGNOFF:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
+    SILC_LOG_DEBUG(("SIGNOFF notify"));
+
+    /* Get client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+
+    /* 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)
+       goto out;
+    }
+    silc_free(client_id);
+
+    /* Remove the client from all channels */
+    silc_server_remove_from_channels(server, NULL, client);
+
+    /* Remove the client entry */
+    silc_idlist_del_client(server->global_list, client);
     break;
 
     /* Ignore rest notify types for now */
@@ -904,6 +1043,8 @@ void silc_server_new_channel_user(SilcServer server,
   SilcBuffer clidp;
   void *tmpid;
 
+  SILC_LOG_DEBUG(("Start"));
+
   if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
       server->server_type != SILC_ROUTER ||
       packet->src_id_type != SILC_ID_SERVER)
@@ -927,10 +1068,11 @@ void silc_server_new_channel_user(SilcServer server,
     goto out;
 
   /* Decode the client ID */
-  client_id = silc_id_str2id(tmpid1, SILC_ID_CLIENT);
+  client_id = silc_id_str2id(tmpid2, SILC_ID_CLIENT);
   if (!client_id)
     goto out;
 
+#if 0
   /* If the packet is originated from the one who sent it to us we know
      that the ID belongs to our cell, unless the sender was router. */
   tmpid = silc_id_str2id(packet->src_id, SILC_ID_SERVER);
@@ -947,12 +1089,19 @@ void silc_server_new_channel_user(SilcServer server,
     router = server->router;
   }
   silc_free(tmpid);
+#endif
+
+  router_sock = sock;
+  router = sock->user_data;
 
   /* Find the channel */
-  channel = silc_idlist_find_channel_by_id(id_list, channel_id);
+  channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                          channel_id, NULL);
   if (!channel) {
-    SILC_LOG_ERROR(("Received channel user for non-existent channel"));
-    goto out;
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel)
+      goto out;
   }
 
   /* If we are router and this packet is not already broadcast packet
@@ -966,11 +1115,10 @@ void silc_server_new_channel_user(SilcServer server,
   }
 
   /* Get client entry */
-  client = silc_idlist_find_client_by_id(id_list, client_id);
+  client = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
   if (!client) {
-    /* This is new client to us, add entry to ID list */
-    client = silc_idlist_add_client(id_list, NULL, NULL, NULL, 
-                                   client_id, router, router_sock);
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, NULL);
     if (!client)
       goto out;
   }
@@ -988,7 +1136,7 @@ void silc_server_new_channel_user(SilcServer server,
      it is assured that this is sent only to our local clients and locally
      connected servers if needed. */
   clidp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
-  silc_server_send_notify_to_channel(server, channel, TRUE,
+  silc_server_send_notify_to_channel(server, channel, FALSE,
                                     SILC_NOTIFY_TYPE_JOIN, 
                                     1, clidp->data, clidp->len);
   silc_buffer_free(clidp);
@@ -1003,3 +1151,102 @@ void silc_server_new_channel_user(SilcServer server,
   silc_free(tmpid1);
   silc_free(tmpid2);
 }
+
+/* Processes incoming REMOVE_ID packet. The packet is used to notify routers
+   that certain ID should be removed. After that the ID will become invalid. */
+
+void silc_server_remove_id(SilcServer server,
+                          SilcSocketConnection sock,
+                          SilcPacketContext *packet)
+{
+  SilcIDList id_list;
+  SilcIDPayload idp;
+  SilcIdType id_type;
+  void *id, *id_entry;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      server->server_type == SILC_SERVER ||
+      packet->src_id_type != SILC_ID_SERVER)
+    return;
+
+  idp = silc_id_payload_parse(packet->buffer);
+  if (!idp)
+    return;
+
+  id_type = silc_id_payload_get_type(idp);
+
+  id = silc_id_payload_get_id(idp);
+  if (!id)
+    goto out;
+
+  /* If the sender of this packet is server and we are router we need to
+     broadcast this packet to other routers in the network. */
+  if (!server->standalone && server->server_type == SILC_ROUTER &&
+      sock->type == SILC_SOCKET_TYPE_SERVER &&
+      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
+    SILC_LOG_DEBUG(("Broadcasting received Remove ID packet"));
+    silc_server_packet_send(server, server->router->connection,
+                           packet->type, 
+                           packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                           packet->buffer->data, packet->buffer->len, FALSE);
+  }
+
+  if (sock->type == SILC_SOCKET_TYPE_SERVER)
+    id_list = server->local_list;
+  else
+    id_list = server->global_list;
+
+  /* Remove the ID */
+  switch (id_type) {
+  case SILC_ID_CLIENT:
+    id_entry = silc_idlist_find_client_by_id(id_list, (SilcClientID *)id, 
+                                            NULL);
+    if (id_entry) {
+      /* Remove from channels */
+      silc_server_remove_from_channels(server, NULL, id_entry);
+
+      /* Remove the client entry */
+      silc_idlist_del_client(id_list, (SilcClientEntry)id_entry);
+
+      SILC_LOG_DEBUG(("Removed client id(%s) from [%s] %s",
+                     silc_id_render(id, SILC_ID_CLIENT),
+                     sock->type == SILC_SOCKET_TYPE_SERVER ?
+                     "Server" : "Router", sock->hostname));
+    }
+    break;
+
+  case SILC_ID_SERVER:
+    id_entry = silc_idlist_find_server_by_id(id_list, (SilcServerID *)id,
+                                            NULL);
+    if (id_entry) {
+      silc_idlist_del_server(id_list, (SilcServerEntry)id_entry);
+
+      SILC_LOG_DEBUG(("Removed server id(%s) from [%s] %s",
+                     silc_id_render(id, SILC_ID_SERVER),
+                     sock->type == SILC_SOCKET_TYPE_SERVER ?
+                     "Server" : "Router", sock->hostname));
+    }
+    break;
+
+  case SILC_ID_CHANNEL:
+    id_entry = silc_idlist_find_channel_by_id(id_list, (SilcChannelID *)id,
+                                             NULL);
+    if (id_entry) {
+      silc_idlist_del_channel(id_list, (SilcChannelEntry)id_entry);
+
+      SILC_LOG_DEBUG(("Removed channel id(%s) from [%s] %s",
+                     silc_id_render(id, SILC_ID_CHANNEL),
+                     sock->type == SILC_SOCKET_TYPE_SERVER ?
+                     "Server" : "Router", sock->hostname));
+    }
+    break;
+
+  default:
+    break;
+  }
+
+ out:
+  silc_id_payload_free(idp);
+}