Merge commit 'origin/silc.1.1.branch'
[silc.git] / apps / silcd / command_reply.c
index 55d62bb5921a59b8ca4e93471e23147387df0040..0b1645ec4fba9332a1c8e0dea17eba370916680d 100644 (file)
@@ -4,13 +4,12 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2002 Pekka Riikonen
+  Copyright (C) 1997 - 2005, 2007 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
@@ -52,6 +51,8 @@ SilcServerCommandReply silc_command_reply_list[] =
   SILC_SERVER_CMD_REPLY(users, USERS),
   SILC_SERVER_CMD_REPLY(getkey, GETKEY),
   SILC_SERVER_CMD_REPLY(list, LIST),
+  SILC_SERVER_CMD_REPLY(watch, WATCH),
+  SILC_SERVER_CMD_REPLY(ping, PING),
 
   { NULL, 0 },
 };
@@ -59,9 +60,10 @@ SilcServerCommandReply silc_command_reply_list[] =
 /* Process received command reply. */
 
 void silc_server_command_reply_process(SilcServer server,
-                                      SilcSocketConnection sock,
+                                      SilcPacketStream sock,
                                       SilcBuffer buffer)
 {
+  SilcIDListData idata = silc_packet_get_context(sock);
   SilcServerCommandReply *cmd;
   SilcServerCommandReplyContext ctx;
   SilcCommandPayload payload;
@@ -70,29 +72,37 @@ void silc_server_command_reply_process(SilcServer server,
   SILC_LOG_DEBUG(("Start"));
 
   /* Get command reply payload from packet */
-  payload = silc_command_payload_parse(buffer->data, buffer->len);
+  payload = silc_command_payload_parse(buffer->data, silc_buffer_len(buffer));
   if (!payload) {
     /* Silently ignore bad reply packet */
     SILC_LOG_DEBUG(("Bad command reply packet"));
     return;
   }
-  
+
   /* Allocate command reply context. This must be free'd by the
      command reply routine receiving it. */
   ctx = silc_calloc(1, sizeof(*ctx));
   ctx->server = server;
-  ctx->sock = silc_socket_dup(sock);
+  ctx->sock = sock;
   ctx->payload = payload;
   ctx->args = silc_command_get_args(ctx->payload);
   ctx->ident = silc_command_get_ident(ctx->payload);
-      
+  command = silc_command_get(ctx->payload);
+  silc_packet_stream_ref(sock);
+
+  /* Client is not allowed to send reply to all commands */
+  if (idata->conn_type == SILC_CONN_CLIENT &&
+      command != SILC_COMMAND_WHOIS) {
+    silc_server_command_reply_free(ctx);
+    return;
+  }
+
   /* Check for pending commands and mark to be exeucted */
-  ctx->callbacks = 
-    silc_server_command_pending_check(server, silc_command_get(ctx->payload), 
+  ctx->callbacks =
+    silc_server_command_pending_check(server, command,
                                      ctx->ident, &ctx->callbacks_count);
 
   /* Execute command reply */
-  command = silc_command_get(ctx->payload);
   for (cmd = silc_command_reply_list; cmd->cb; cmd++)
     if (cmd->cmd == command)
       break;
@@ -112,40 +122,39 @@ void silc_server_command_reply_free(SilcServerCommandReplyContext cmd)
   if (cmd) {
     silc_command_payload_free(cmd->payload);
     if (cmd->sock)
-      silc_socket_free(cmd->sock); /* Decrease the reference counter */
+      silc_packet_stream_unref(cmd->sock);
     silc_free(cmd->callbacks);
     silc_free(cmd);
   }
 }
 
-static void 
+static void
 silc_server_command_process_error(SilcServerCommandReplyContext cmd,
                                  SilcStatus error)
 {
   SilcServer server = cmd->server;
+  SilcIDListData idata = silc_packet_get_context(cmd->sock);
 
   /* If we received notify for invalid ID we'll remove the ID if we
      have it cached. */
   if (error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID &&
-      cmd->sock->type == SILC_SOCKET_TYPE_ROUTER) {
+      idata->conn_type == SILC_CONN_ROUTER) {
     SilcClientEntry client;
-    SilcUInt32 tmp_len;
-    unsigned char *tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-    if (tmp) {
-      SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-      if (client_id) {
-       SILC_LOG_DEBUG(("Received invalid client ID notification, deleting "
-                       "the entry from cache"));
-       client = silc_idlist_find_client_by_id(server->global_list, 
-                                              client_id, FALSE, NULL);
-       if (client) {
-         silc_server_remove_from_channels(server, NULL, client, TRUE, 
-                                          NULL, TRUE);
-         silc_idlist_del_data(client);
-         silc_idlist_del_client(server->global_list, client);
-       }
-       silc_free(client_id);
-      }
+    SilcID id;
+    if (silc_argument_get_decoded(cmd->args, 2, SILC_ARGUMENT_ID, &id,
+                                 NULL)) {
+      SILC_LOG_DEBUG(("Received invalid client ID notification, deleting "
+                     "the entry from cache"));
+      client = silc_idlist_find_client_by_id(server->global_list,
+                                            SILC_ID_GET_ID(id), FALSE, NULL);
+      if (!client)
+       return;
+
+      silc_server_remove_from_channels(server, NULL, client, TRUE,
+                                      NULL, TRUE, FALSE);
+      silc_dlist_del(server->expired_clients, client);
+      silc_idlist_del_data(client);
+      silc_idlist_del_client(server->global_list, client);
     }
   }
 }
@@ -156,15 +165,18 @@ static char
 silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
 {
   SilcServer server = cmd->server;
-  unsigned char *tmp, *id_data, *umodes;
-  char *nickname, *username, *realname, *servername = NULL;
+  unsigned char *id_data, *umodes;
+  char *nickname, *username, *realname, *tmp;
   unsigned char *fingerprint;
-  SilcClientID *client_id;
+  SilcID id;
   SilcClientEntry client;
-  SilcIDCacheEntry cache = NULL;
   char global = FALSE;
-  char *nick;
+  char nick[128 + 1], servername[256 + 1], uname[128 + 1];
   SilcUInt32 mode = 0, len, len2, id_len, flen;
+  const char *hostname, *ip;
+
+  silc_socket_stream_get_info(silc_packet_stream_get_stream(cmd->sock),
+                             NULL, &hostname, &ip, NULL);
 
   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
@@ -177,18 +189,19 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
   if (tmp)
     SILC_GET32_MSB(mode, tmp);
 
-  client_id = silc_id_payload_parse_id(id_data, id_len, NULL);
-  if (!client_id)
+  if (!silc_id_payload_parse_id(id_data, id_len, &id))
     return FALSE;
 
   fingerprint = silc_argument_get_arg_type(cmd->args, 9, &flen);
 
   /* Check if we have this client cached already. */
 
-  client = silc_idlist_find_client_by_id(server->local_list, client_id, 
+  client = silc_idlist_find_client_by_id(server->local_list,
+                                        SILC_ID_GET_ID(id),
                                         FALSE, NULL);
   if (!client) {
-    client = silc_idlist_find_client_by_id(server->global_list, client_id, 
+    client = silc_idlist_find_client_by_id(server->global_list,
+                                          SILC_ID_GET_ID(id),
                                           FALSE, NULL);
     global = TRUE;
   }
@@ -200,55 +213,72 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
       return FALSE;
 
     /* Take hostname out of nick string if it includes it. */
-    silc_parse_userfqdn(nickname, &nick, &servername);
+    silc_parse_userfqdn(nickname, nick, sizeof(nick), servername,
+                       sizeof(servername));
 
     /* We don't have that client anywhere, add it. The client is added
-       to global list since server didn't have it in the lists so it must be 
-       global. */
-    client = silc_idlist_add_client(server->global_list, nick, 
-                                   strdup(username), 
-                                   strdup(realname), client_id, 
-                                   cmd->sock->user_data, NULL, 0);
+       to global list since server didn't have it in the lists so it must be
+       global. This will check for valid nickname and username strings. */
+    client = silc_idlist_add_client(server->global_list,
+                                   strdup(nick), username,
+                                   strdup(realname),
+                                   silc_id_dup(SILC_ID_GET_ID(id),
+                                               SILC_ID_CLIENT),
+                                   silc_packet_get_context(cmd->sock),
+                                   NULL);
     if (!client) {
       SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
       return FALSE;
     }
 
-    client->data.status |= 
+    client->data.status |=
       (SILC_IDLIST_STATUS_REGISTERED | SILC_IDLIST_STATUS_RESOLVED);
     client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
     client->mode = mode;
-    client->servername = servername;
+    client->servername = servername[0] ? strdup(servername) : NULL;
   } else {
     /* We have the client already, update the data */
 
     SILC_LOG_DEBUG(("Updating client data"));
 
-    /* Take hostname out of nick string if it includes it. */
-    silc_parse_userfqdn(nickname, &nick, &servername);
+    /* Check nickname */
+    silc_parse_userfqdn(nickname, nick, sizeof(nick), servername,
+                       sizeof(servername));
+    nickname = silc_identifier_check(nick, strlen(nick), SILC_STRING_UTF8,
+                                    128, NULL);
+    if (!nickname) {
+      SILC_LOG_ERROR(("Malformed nickname '%s' received in WHOIS reply "
+                     "from %s",
+                     hostname ? hostname : "", nick));
+      return FALSE;
+    }
 
-    /* Remove the old cache entry  */
-    silc_idcache_del_by_context(global ? server->global_list->clients :
-                               server->local_list->clients, client);
+    /* Check username */
+    silc_parse_userfqdn(username, uname, sizeof(uname), NULL, 0);
+    if (!silc_identifier_verify(uname, strlen(uname), SILC_STRING_UTF8, 128)) {
+      SILC_LOG_ERROR(("Malformed username '%s' received in WHOIS reply "
+                     "from %s",
+                     hostname ? hostname : "", tmp));
+      return FALSE;
+    }
+
+    /* Update entry */
+    silc_idcache_update_by_context(global ? server->global_list->clients :
+                                  server->local_list->clients, client, NULL,
+                                  nickname, TRUE);
 
     silc_free(client->nickname);
     silc_free(client->username);
     silc_free(client->userinfo);
     silc_free(client->servername);
-    
-    client->nickname = nick;
+
+    client->nickname = strdup(nick);
     client->username = strdup(username);
     client->userinfo = strdup(realname);
-    client->servername = servername;
+    client->servername = servername[0] ? strdup(servername) : NULL;
     client->mode = mode;
     client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
     client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
-
-    /* Create new cache entry */
-    silc_idcache_add(global ? server->global_list->clients :
-                    server->local_list->clients, nick, client->id, 
-                    client, 0, NULL); 
-    silc_free(client_id);
   }
 
   /* Save channel list if it was sent to us */
@@ -264,17 +294,6 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
     } else {
       silc_server_save_user_channels(server, cmd->sock, client, NULL, NULL);
     }
-
-    /* If client is global and is not on any channel then add that we'll
-       expire the entry after a while. */
-    if (global) {
-      silc_idlist_find_client_by_id(server->global_list, client->id,
-                                   FALSE, &cache);
-      if (!silc_hash_table_count(client->channels))
-       cache->expire = time(NULL) + 300;
-      else
-       cache->expire = 0;
-    }
   }
 
   if (fingerprint && flen == sizeof(client->data.fingerprint))
@@ -285,11 +304,97 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
   if (tmp) {
     silc_free(client->attrs);
     client->attrs = silc_memdup(tmp, len);
+    client->attrs_len = len;
+
+    /* Try to take public key from attributes if present and we don't have
+       the key already.  Do this only on normal server.  Routers do GETKEY
+       for all clients anyway. */
+    if (server->server_type != SILC_ROUTER && !client->data.public_key) {
+      SilcAttributePayload attr;
+      SilcAttributeObjPk pk;
+      unsigned char f[SILC_HASH_MAXLEN];
+      SilcDList attrs = silc_attribute_payload_parse(tmp, len);
+
+      SILC_LOG_DEBUG(("Take client public key from attributes"));
+
+      if (attrs) {
+       silc_dlist_start(attrs);
+       while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) {
+         if (silc_attribute_get_attribute(attr) ==
+             SILC_ATTRIBUTE_USER_PUBLIC_KEY) {
+
+           if (!silc_attribute_get_object(attr, &pk, sizeof(pk)))
+             continue;
+
+           /* Take only SILC public keys */
+           if (strcmp(pk.type, "silc-rsa")) {
+             silc_free(pk.type);
+             silc_free(pk.data);
+             continue;
+           }
+
+           /* Verify that the server provided fingerprint matches the key */
+           silc_hash_make(server->sha1hash, pk.data, pk.data_len, f);
+           if (memcmp(f, client->data.fingerprint, sizeof(f))) {
+             silc_free(pk.type);
+             silc_free(pk.data);
+             continue;
+           }
+
+           /* Save the public key. */
+           if (!silc_pkcs_public_key_alloc(SILC_PKCS_SILC,
+                                           pk.data, pk.data_len,
+                                           &client->data.public_key)) {
+             silc_free(pk.type);
+             silc_free(pk.data);
+             continue;
+           }
+
+           SILC_LOG_DEBUG(("Saved client public key from attributes"));
+
+           /* Add client's public key to repository */
+           if (!silc_server_get_public_key_by_client(server, client, NULL))
+             silc_skr_add_public_key_simple(server->repository,
+                                            client->data.public_key,
+                                            SILC_SKR_USAGE_IDENTIFICATION,
+                                            client, NULL);
+
+           silc_free(pk.type);
+           silc_free(pk.data);
+           break;
+         }
+       }
+
+       silc_attribute_payload_list_free(attrs);
+      }
+    }
   }
 
   return TRUE;
 }
 
+/* Handle requested attributes reply in WHOIS from client */
+
+static char
+silc_server_command_reply_whois_save_client(SilcServerCommandReplyContext cmd)
+{
+  unsigned char *tmp;
+  SilcUInt32 len;
+  SilcClientEntry client = silc_packet_get_context(cmd->sock);
+
+  /* Take Requested Attributes if set. */
+  tmp = silc_argument_get_arg_type(cmd->args, 11, &len);
+  if (tmp && client) {
+    silc_free(client->attrs);
+    client->attrs = silc_memdup(tmp, len);
+    client->attrs_len = len;
+  }
+
+  client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
+
+  return TRUE;
+}
+
 /* Reiceved reply for WHOIS command. We sent the whois request to our
    primary router, if we are normal server, and thus has now received reply
    to the command. We will figure out what client originally sent us the
@@ -299,12 +404,18 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
 SILC_SERVER_CMD_REPLY_FUNC(whois)
 {
   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
+  SilcIDListData idata = silc_packet_get_context(cmd->sock);
   SilcStatus status, error;
 
   COMMAND_CHECK_STATUS;
 
-  if (!silc_server_command_reply_whois_save(cmd))
-    goto out;
+  if (idata->conn_type != SILC_CONN_CLIENT) {
+    if (!silc_server_command_reply_whois_save(cmd))
+      goto out;
+  } else {
+    if (!silc_server_command_reply_whois_save_client(cmd))
+      goto out;
+  }
 
   /* Pending callbacks are not executed if this was an list entry */
   if (status != SILC_STATUS_OK &&
@@ -332,12 +443,16 @@ silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
   SilcServer server = cmd->server;
   SilcUInt32 len, id_len;
   unsigned char *id_data;
-  char *nickname, *username, *realname, *servername = NULL;
-  SilcClientID *client_id;
+  char *nickname, *username, *realname;
+  SilcID id;
   SilcClientEntry client;
   SilcIDCacheEntry cache = NULL;
-  char *nick;
+  char nick[128 + 1], servername[256 + 1], uname[128 + 1];
   int global = FALSE;
+  const char *hostname, *ip;
+
+  silc_socket_stream_get_info(silc_packet_stream_get_stream(cmd->sock),
+                             NULL, &hostname, &ip, NULL);
 
   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
@@ -347,17 +462,18 @@ silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
 
   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
 
-  client_id = silc_id_payload_parse_id(id_data, id_len, NULL);
-  if (!client_id)
+  if (!silc_id_payload_parse_id(id_data, id_len, &id))
     return FALSE;
 
   /* Check if we have this client cached already. */
 
-  client = silc_idlist_find_client_by_id(server->local_list, client_id,
+  client = silc_idlist_find_client_by_id(server->local_list,
+                                        SILC_ID_GET_ID(id),
                                         FALSE, &cache);
   if (!client) {
-    client = silc_idlist_find_client_by_id(server->global_list, 
-                                          client_id, FALSE, &cache);
+    client = silc_idlist_find_client_by_id(server->global_list,
+                                          SILC_ID_GET_ID(id),
+                                          FALSE, &cache);
     global = TRUE;
   }
 
@@ -368,16 +484,18 @@ silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
       return FALSE;
 
     /* Take hostname out of nick string if it includes it. */
-    silc_parse_userfqdn(nickname, &nick, &servername);
+    silc_parse_userfqdn(nickname, nick, sizeof(nick), servername,
+                       sizeof(servername));
 
     /* We don't have that client anywhere, add it. The client is added
-       to global list since server didn't have it in the lists so it must be 
+       to global list since server didn't have it in the lists so it must be
        global. */
-    client = silc_idlist_add_client(server->global_list, nick,
-                                   strdup(username), strdup(realname), 
-                                   silc_id_dup(client_id, SILC_ID_CLIENT), 
-                                   cmd->sock->user_data, NULL,
-                                   SILC_ID_CACHE_EXPIRE_DEF);
+    client = silc_idlist_add_client(server->global_list,
+                                   strdup(nick), username,
+                                   strdup(realname),
+                                   silc_id_dup(SILC_ID_GET_ID(id),
+                                               SILC_ID_CLIENT),
+                                   silc_packet_get_context(cmd->sock), NULL);
     if (!client) {
       SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
       return FALSE;
@@ -385,45 +503,57 @@ silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
 
     client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
     client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
-    client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED; 
-    client->servername = servername;
+    client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+    client->servername = servername[0] ? strdup(servername) : NULL;
   } else {
     /* We have the client already, update the data */
 
-    /* Take hostname out of nick string if it includes it. */
-    silc_parse_userfqdn(nickname, &nick, &servername);
+    /* Check nickname */
+    silc_parse_userfqdn(nickname, nick, sizeof(nick), servername,
+                       sizeof(servername));
+    nickname = silc_identifier_check(nick, strlen(nick), SILC_STRING_UTF8,
+                                    128, NULL);
+    if (!nickname) {
+      SILC_LOG_ERROR(("Malformed nickname '%s' received in WHOWAS reply "
+                     "from %s",
+                     nick, hostname ? hostname : ""));
+      return FALSE;
+    }
+
+    /* Check username */
+    silc_parse_userfqdn(username, uname, sizeof(uname), NULL, 0);
+    if (!silc_identifier_verify(uname, strlen(uname), SILC_STRING_UTF8, 128))
+      return FALSE;
 
     silc_free(client->nickname);
     silc_free(client->username);
     silc_free(client->servername);
-    
-    client->nickname = nick;
+
+    client->nickname = strdup(nick);
     client->username = strdup(username);
-    client->servername = servername;
+    client->servername = servername[0] ? strdup(servername) : NULL;
     client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
     client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
+    client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
 
-    /* Remove the old cache entry and create a new one */
-    silc_idcache_del_by_context(global ? server->global_list->clients :
-                               server->local_list->clients, client);
-    silc_idcache_add(global ? server->global_list->clients :
-                    server->local_list->clients, nick, client->id, 
-                    client, 0, NULL);
+    /* Update cache entry */
+    silc_idcache_update_by_context(global ? server->global_list->clients :
+                                  server->local_list->clients, client, NULL,
+                                  nickname, TRUE);
   }
 
   /* If client is global and is not on any channel then add that we'll
      expire the entry after a while. */
   if (global) {
-    silc_idlist_find_client_by_id(server->global_list, client->id,
-                                 FALSE, &cache);
-    if (!silc_hash_table_count(client->channels))
-      cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
-    else
-      cache->expire = 0;
+    client = silc_idlist_find_client_by_id(server->global_list, client->id,
+                                          FALSE, &cache);
+    if (client && !silc_hash_table_count(client->channels)) {
+      client->data.created = silc_time();
+      silc_dlist_del(server->expired_clients, client);
+      silc_dlist_add(server->expired_clients, client);
+    }
   }
 
-  silc_free(client_id);
-
   return TRUE;
 }
 
@@ -467,17 +597,16 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
   SilcUInt32 len, id_len;
   unsigned char *id_data;
   char *name, *info;
-  SilcClientID *client_id = NULL;
-  SilcServerID *server_id = NULL;
-  SilcChannelID *channel_id = NULL;
+  SilcClientID client_id;
+  SilcServerID server_id;
+  SilcChannelID*channel_id;
   SilcClientEntry client;
   SilcServerEntry server_entry;
   SilcChannelEntry channel;
   char global = FALSE;
-  char *nick = NULL;
+  char nick[128 + 1];
   SilcIDPayload idp = NULL;
   SilcIdType id_type;
-  int expire = 0;
 
   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
   if (!id_data)
@@ -493,16 +622,15 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
 
   switch (id_type) {
   case SILC_ID_CLIENT:
-    client_id = silc_id_payload_get_id(idp);
-    if (!client_id)
+    if (!silc_id_payload_get_id(idp, &client_id, sizeof(client_id)))
       goto error;
 
     SILC_LOG_DEBUG(("Received client information"));
 
-    client = silc_idlist_find_client_by_id(server->local_list, 
-                                          client_id, FALSE, NULL);
+    client = silc_idlist_find_client_by_id(server->local_list,
+                                          &client_id, FALSE, NULL);
     if (!client) {
-      client = silc_idlist_find_client_by_id(server->global_list, client_id,
+      client = silc_idlist_find_client_by_id(server->global_list, &client_id,
                                             FALSE, NULL);
       global = TRUE;
     }
@@ -514,39 +642,51 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
 
       /* Take nickname */
       if (name)
-       silc_parse_userfqdn(name, &nick, NULL);
+       silc_parse_userfqdn(name, nick, sizeof(nick), NULL, 0);
 
       /* We don't have that client anywhere, add it. The client is added
-        to global list since server didn't have it in the lists so it must be 
+        to global list since server didn't have it in the lists so it must be
         global. */
-      client = silc_idlist_add_client(server->global_list, nick, 
-                                     info ? strdup(info) : NULL, NULL,
-                                     client_id, cmd->sock->user_data,
-                                     NULL, time(NULL) + 300);
+      client = silc_idlist_add_client(server->global_list,
+                                     nick[0] ? nick : NULL, info, NULL,
+                                     silc_id_dup(&client_id, SILC_ID_CLIENT),
+                                     silc_packet_get_context(cmd->sock),
+                                     NULL);
       if (!client) {
        SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
        goto error;
       }
+
       client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
       client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
       client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
     } else {
       /* We have the client already, update the data */
-      
+
       SILC_LOG_DEBUG(("Updating client data"));
-      
+
       /* Take nickname */
       if (name) {
-       silc_parse_userfqdn(name, &nick, NULL);
-
-       /* Remove the old cache entry */
-       silc_idcache_del_by_context(global ? server->global_list->clients :
-                                   server->local_list->clients, client);
+       silc_parse_userfqdn(name, nick, sizeof(nick), NULL, 0);
+
+       /* Check nickname */
+       name = silc_identifier_check(nick, strlen(nick), SILC_STRING_UTF8,
+                                    128, NULL);
+       if (!name) {
+         SILC_LOG_ERROR(("Malformed nickname '%s' received in IDENTIFY "
+                         "reply ", nick));
+         return FALSE;
+       }
 
        silc_free(client->nickname);
-       client->nickname = nick;
+       client->nickname = strdup(nick);
+
+       /* Update the context */
+       silc_idcache_update_by_context(global ? server->global_list->clients :
+                                      server->local_list->clients, client,
+                                      NULL, name, TRUE);
       }
-      
+
       if (info) {
        silc_free(client->username);
        client->username = strdup(info);
@@ -554,27 +694,6 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
 
       client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
       client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
-      
-      if (name) {
-       /* Add new cache entry */
-       silc_idcache_add(global ? server->global_list->clients :
-                        server->local_list->clients, nick, client->id, 
-                        client, expire, NULL);
-      }
-
-      /* If client is global and is not on any channel then add that we'll
-         expire the entry after a while. */
-      if (global && server->server_type == SILC_SERVER) {
-       SilcIDCacheEntry cache = NULL;
-        silc_idlist_find_client_by_id(server->global_list, client->id,
-                                     FALSE, &cache);
-        if (!silc_hash_table_count(client->channels))
-         cache->expire = time(NULL) + 300;
-        else
-         cache->expire = 0;
-      }
-
-      silc_free(client_id);
     }
 
     break;
@@ -583,74 +702,80 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
     if (!name)
       goto error;
 
-    server_id = silc_id_payload_get_id(idp);
-    if (!server_id)
+    if (!silc_id_payload_get_id(idp, &server_id, sizeof(server_id)))
       goto error;
 
     SILC_LOG_DEBUG(("Received server information"));
 
-    server_entry = silc_idlist_find_server_by_id(server->local_list, 
-                                                server_id, FALSE, NULL);
+    server_entry = silc_idlist_find_server_by_id(server->local_list,
+                                                &server_id, FALSE, NULL);
     if (!server_entry)
-      server_entry = silc_idlist_find_server_by_id(server->global_list, 
-                                                  server_id, FALSE, NULL);
+      server_entry = silc_idlist_find_server_by_id(server->global_list,
+                                                  &server_id, FALSE, NULL);
     if (!server_entry) {
       /* If router did not find such Server ID in its lists then this must
         be bogus server or some router in the net is buggy. */
       if (server->server_type != SILC_SERVER)
        goto error;
-      
+
       /* We don't have that server anywhere, add it. */
-      server_entry = silc_idlist_add_server(server->global_list, 
+      server_entry = silc_idlist_add_server(server->global_list,
                                            strdup(name), 0,
-                                           server_id, server->router,
+                                           silc_id_dup(&server_id,
+                                                       SILC_ID_SERVER),
+                                           server->router,
                                            SILC_PRIMARY_ROUTE(server));
-      if (!server_entry) {
-       silc_free(server_id);
+      if (!server_entry)
        goto error;
-      }
+
       server_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
       server_entry->data.status |= SILC_IDLIST_STATUS_RESOLVED;
       server_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
-      server_id = NULL;
     }
 
-    silc_free(server_id);
     break;
 
   case SILC_ID_CHANNEL:
     if (!name)
       goto error;
 
-    channel_id = silc_id_payload_get_id(idp);
-    if (!channel_id)
+    if (!silc_id_payload_get_id(idp, &channel_id, sizeof(channel_id)))
       goto error;
 
     SILC_LOG_DEBUG(("Received channel information"));
 
-    channel = silc_idlist_find_channel_by_name(server->local_list, 
-                                              name, NULL);
+    /* Check channel name */
+    info = silc_channel_name_check(name, strlen(name), SILC_STRING_UTF8,
+                                  256, NULL);
+    if (!info)
+      goto error;
+
+    channel = silc_idlist_find_channel_by_name(server->local_list,
+                                              info, NULL);
     if (!channel)
-      channel = silc_idlist_find_channel_by_name(server->global_list, 
-                                                name, NULL);
+      channel = silc_idlist_find_channel_by_name(server->global_list,
+                                                info, NULL);
     if (!channel) {
       /* If router did not find such Channel ID in its lists then this must
         be bogus channel or some router in the net is buggy. */
-      if (server->server_type != SILC_SERVER)
+      if (server->server_type != SILC_SERVER) {
+       silc_free(info);
        goto error;
-      
+      }
+
       /* We don't have that channel anywhere, add it. */
       channel = silc_idlist_add_channel(server->global_list, strdup(name),
-                                       SILC_CHANNEL_MODE_NONE, channel_id, 
+                                       SILC_CHANNEL_MODE_NONE,
+                                       silc_id_dup(&channel_id,
+                                                   SILC_ID_CHANNEL),
                                        server->router, NULL, NULL, 0);
       if (!channel) {
-       silc_free(channel_id);
+       silc_free(info);
        goto error;
       }
-      channel_id = NULL;
+      silc_free(info);
     }
 
-    silc_free(channel_id);
     break;
   }
 
@@ -703,40 +828,38 @@ SILC_SERVER_CMD_REPLY_FUNC(info)
   SilcServer server = cmd->server;
   SilcStatus status, error;
   SilcServerEntry entry;
-  SilcServerID *server_id;
+  SilcID id;
   SilcUInt32 tmp_len;
   unsigned char *tmp, *name;
 
   COMMAND_CHECK_STATUS;
 
   /* Get Server ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (!tmp)
-    goto out;
-  server_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-  if (!server_id)
+  if (!silc_argument_get_decoded(cmd->args, 2, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
   /* Get the name */
   name = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
-  if (tmp_len > 256)
+  if (!name)
     goto out;
 
-  entry = silc_idlist_find_server_by_id(server->local_list, server_id, 
+  entry = silc_idlist_find_server_by_id(server->local_list,
+                                       SILC_ID_GET_ID(id),
                                        FALSE, NULL);
   if (!entry) {
-    entry = silc_idlist_find_server_by_id(server->global_list, server_id, 
+    entry = silc_idlist_find_server_by_id(server->global_list,
+                                         SILC_ID_GET_ID(id),
                                          FALSE, NULL);
     if (!entry) {
       /* Add the server to global list */
-      server_id = silc_id_dup(server_id, SILC_ID_SERVER);
-      entry = silc_idlist_add_server(server->global_list, name, 0,
-                                    server_id, cmd->sock->user_data,
+      entry = silc_idlist_add_server(server->global_list, strdup(name), 0,
+                                    silc_id_dup(SILC_ID_GET_ID(id),
+                                                SILC_ID_SERVER),
+                                    silc_packet_get_context(cmd->sock),
                                     cmd->sock);
-      if (!entry) {
-       silc_free(server_id);
+      if (!entry)
        goto out;
-      }
+
       entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
     }
   }
@@ -762,27 +885,53 @@ SILC_SERVER_CMD_REPLY_FUNC(motd)
   SilcServer server = cmd->server;
   SilcStatus status, error;
   SilcServerEntry entry = NULL;
-  SilcServerID *server_id;
+  SilcID id;
   SilcUInt32 tmp_len;
   unsigned char *tmp;
 
   COMMAND_CHECK_STATUS;
 
   /* Get Server ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (!tmp)
-    goto out;
-  server_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-  if (!server_id)
+  if (!silc_argument_get_decoded(cmd->args, 2, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
-  entry = silc_idlist_find_server_by_id(server->local_list, server_id, 
+  entry = silc_idlist_find_server_by_id(server->local_list,
+                                       SILC_ID_GET_ID(id),
                                        TRUE, NULL);
   if (!entry) {
-    entry = silc_idlist_find_server_by_id(server->global_list, server_id, 
+    entry = silc_idlist_find_server_by_id(server->global_list,
+                                         SILC_ID_GET_ID(id),
                                          TRUE, NULL);
-    if (!entry)
-      goto out;
+    if (!entry) {
+      SilcBuffer buffer;
+
+      /* If router did not find such Server ID in its lists then this must
+        be bogus client or some router in the net is buggy. */
+      if (server->server_type != SILC_SERVER)
+       goto out;
+
+      /* Statistics */
+      cmd->server->stat.commands_sent++;
+
+      /* entry isn't known so we IDENTIFY it. otherwise the
+         silc_server_command_motd won't know about it and tell
+         the client that there is no such server */
+      tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+      buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                             ++server->cmd_ident, 5,
+                                             1, NULL, 0, 2, NULL, 0,
+                                             3, NULL, 0, 4, NULL, 0,
+                                             5, tmp, tmp_len);
+      silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+                             SILC_PACKET_COMMAND, 0, buffer->data,
+                             silc_buffer_len(buffer));
+      silc_server_command_pending(server, SILC_COMMAND_IDENTIFY,
+                                 server->cmd_ident,
+                                 silc_server_command_reply_motd,
+                                 cmd);
+      silc_buffer_free(buffer);
+      return;
+    }
   }
 
   /* Get the motd */
@@ -811,13 +960,11 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
   SilcServer server = cmd->server;
   SilcIDCacheEntry cache = NULL;
   SilcStatus status, error;
-  SilcChannelID *id;
-  SilcClientID *client_id = NULL;
+  SilcID id, id2;
   SilcChannelEntry entry;
   SilcHmac hmac = NULL;
-  SilcUInt32 id_len, len, list_count;
-  unsigned char *id_string;
-  char *channel_name, *tmp;
+  SilcUInt32 len, list_count;
+  char *channel_name, *channel_namec = NULL, *tmp;
   SilcUInt32 mode, created;
   SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
   SilcPublicKey founder_key = NULL;
@@ -830,16 +977,11 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
     goto out;
 
   /* Get channel ID */
-  id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
-  if (!id_string)
+  if (!silc_argument_get_decoded(cmd->args, 3, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
   /* Get client ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
-  if (!tmp)
-    goto out;
-  client_id = silc_id_payload_parse_id(tmp, len, NULL);
-  if (!client_id)
+  if (!silc_argument_get_decoded(cmd->args, 4, SILC_ARGUMENT_ID, &id2, NULL))
     goto out;
 
   /* Get mode mask */
@@ -860,15 +1002,10 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
   if (tmp) {
     keyp = silc_buffer_alloc(len);
-    silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
+    silc_buffer_pull_tail(keyp, silc_buffer_truelen(keyp));
     silc_buffer_put(keyp, tmp, len);
   }
 
-  /* Parse the Channel ID */
-  id = silc_id_payload_parse_id(id_string, id_len, NULL);
-  if (!id)
-    goto out;
-
   /* Get hmac */
   tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
   if (tmp) {
@@ -903,41 +1040,48 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
   /* Get founder key */
   tmp = silc_argument_get_arg_type(cmd->args, 15, &len);
   if (tmp)
-    silc_pkcs_public_key_decode(tmp, len, &founder_key);
+    silc_public_key_payload_decode(tmp, len, &founder_key);
 
   /* See whether we already have the channel. */
-  entry = silc_idlist_find_channel_by_name(server->local_list, 
-                                          channel_name, &cache);
+  channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
+                                         SILC_STRING_UTF8, 256, NULL);
+  if (!channel_namec)
+    goto out;
+  entry = silc_idlist_find_channel_by_name(server->local_list,
+                                          channel_namec, &cache);
   if (!entry) {
     /* Add new channel */
 
-    SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)", 
+    SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)",
                    (created == 0 ? "existing" : "created"), channel_name,
-                   silc_id_render(id, SILC_ID_CHANNEL)));
+                   silc_id_render(SILC_ID_GET_ID(id), SILC_ID_CHANNEL)));
 
     /* If the channel is found from global list we must move it to the
        local list. */
     entry = silc_idlist_find_channel_by_name(server->global_list,
-                                            channel_name, &cache);
+                                            channel_namec, &cache);
     if (entry)
       silc_idlist_del_channel(server->global_list, entry);
 
     /* Add the channel to our local list. */
     entry = silc_idlist_add_channel(server->local_list, strdup(channel_name),
-                                   SILC_CHANNEL_MODE_NONE, id,
-                                   server->router, NULL, hmac, 0);
-    if (!entry) {
-      silc_free(id);
+                                   SILC_CHANNEL_MODE_NONE,
+                                   silc_id_dup(SILC_ID_GET_ID(id),
+                                               SILC_ID_CHANNEL),
+                                   server->router, NULL, NULL, hmac);
+    if (!entry)
       goto out;
-    }
+
+    hmac = NULL;
     server->stat.my_channels++;
     server->stat.channels++;
   } else {
     /* The entry exists. */
 
     /* If ID has changed, then update it to the cache too. */
-    if (!SILC_ID_CHANNEL_COMPARE(entry->id, id))
-      silc_idlist_replace_channel_id(server->local_list, entry->id, id);
+    if (!SILC_ID_CHANNEL_COMPARE(entry->id, SILC_ID_GET_ID(id)))
+      silc_idlist_replace_channel_id(server->local_list, entry->id,
+                                    SILC_ID_GET_ID(id));
 
     entry->disabled = FALSE;
 
@@ -956,23 +1100,53 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
     founder_key = NULL;
   }
 
-  if (entry->hmac_name && hmac) {
+  if (entry->hmac_name && (hmac || (!hmac && entry->hmac))) {
     silc_free(entry->hmac_name);
-    entry->hmac_name = strdup(silc_hmac_get_name(hmac));
+    entry->hmac_name = strdup(silc_hmac_get_name(hmac ? hmac : entry->hmac));
   }
 
   /* Get the ban list */
   tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
-  if (tmp) {
-    silc_free(entry->ban_list);
-    entry->ban_list = silc_memdup(tmp, len);
+  if (tmp && len > 2) {
+    SilcArgumentPayload iargs;
+    SilcUInt16 iargc;
+    SILC_GET16_MSB(iargc, tmp);
+    iargs = silc_argument_payload_parse(tmp + 2, len - 2, iargc);
+    if (iargs) {
+      /* Delete old ban list */
+      if (entry->ban_list)
+       silc_hash_table_free(entry->ban_list);
+      entry->ban_list =
+       silc_hash_table_alloc(0, silc_hash_ptr,
+                             NULL, NULL, NULL,
+                             silc_server_inviteban_destruct, entry, TRUE);
+
+      /* Add new ban list */
+      silc_server_inviteban_process(server, entry->ban_list, 0, iargs);
+      silc_argument_payload_free(iargs);
+    }
   }
 
   /* Get the invite list */
   tmp = silc_argument_get_arg_type(cmd->args, 9, &len);
-  if (tmp) {
-    silc_free(entry->invite_list);
-    entry->invite_list = silc_memdup(tmp, len);
+  if (tmp && len > 2) {
+    SilcArgumentPayload iargs;
+    SilcUInt16 iargc;
+    SILC_GET16_MSB(iargc, tmp);
+    iargs = silc_argument_payload_parse(tmp + 2, len - 2, iargc);
+    if (iargs) {
+      /* Delete old invite list */
+      if (entry->invite_list)
+       silc_hash_table_free(entry->invite_list);
+      entry->invite_list =
+       silc_hash_table_alloc(0, silc_hash_ptr,
+                             NULL, NULL, NULL,
+                             silc_server_inviteban_destruct, entry, TRUE);
+
+      /* Add new invite list */
+      silc_server_inviteban_process(server, entry->invite_list, 0, iargs);
+      silc_argument_payload_free(iargs);
+    }
   }
 
   /* Get the topic */
@@ -982,16 +1156,23 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
     entry->topic = strdup(tmp);
   }
 
-  /* If channel was not created we know there is global users on the 
+  /* Get channel public key list */
+  tmp = silc_argument_get_arg_type(cmd->args, 16, &len);
+  if (tmp && server->server_type != SILC_ROUTER)
+    silc_server_set_channel_pk_list(server, NULL, entry, tmp, len);
+
+  /* The the user limit */
+  tmp = silc_argument_get_arg_type(cmd->args, 17, &len);
+  if (tmp && len == 4)
+    SILC_GET32_MSB(entry->user_limit, tmp);
+
+  /* If channel was not created we know there is global users on the
      channel. */
   entry->global_users = (created == 0 ? TRUE : FALSE);
 
   /* If channel was just created the mask must be zero */
-  if (!entry->global_users && mode) {
-    SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
-                   "new channel, forcing it to zero", cmd->sock->hostname));
+  if (!entry->global_users && mode)
     mode = 0;
-  }
 
   /* Save channel mode */
   entry->mode = mode;
@@ -1004,18 +1185,21 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
   }
 
   /* Save the users to the channel */
-  silc_server_save_users_on_channel(server, cmd->sock, entry, 
-                                   client_id, client_id_list,
+  silc_server_save_users_on_channel(server, cmd->sock, entry,
+                                   SILC_ID_GET_ID(id2), client_id_list,
                                    client_mode_list, list_count);
   entry->users_resolved = TRUE;
 
  out:
   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
  err:
-  silc_free(client_id);
+  silc_free(channel_namec);
+  if (hmac)
+    silc_hmac_free(hmac);
   silc_server_command_reply_free(cmd);
 
-  silc_pkcs_public_key_free(founder_key);
+  if (founder_key)
+    silc_pkcs_public_key_free(founder_key);
   if (client_id_list)
     silc_buffer_free(client_id_list);
   if (client_mode_list)
@@ -1037,7 +1221,7 @@ SILC_SERVER_CMD_REPLY_FUNC(stats)
 
   /* Get statistics structure */
   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
-  if (server->server_type == SILC_SERVER && tmp) {
+  if (server->server_type != SILC_ROUTER && tmp) {
     silc_buffer_set(&buf, tmp, tmp_len);
     silc_buffer_unformat(&buf,
                         SILC_STR_UI_INT(NULL),
@@ -1070,7 +1254,7 @@ SILC_SERVER_CMD_REPLY_FUNC(users)
   SilcServer server = cmd->server;
   SilcStatus status, error;
   SilcChannelEntry channel;
-  SilcChannelID *channel_id = NULL;
+  SilcID id;
   SilcBuffer client_id_list;
   SilcBuffer client_mode_list;
   unsigned char *tmp;
@@ -1080,35 +1264,31 @@ SILC_SERVER_CMD_REPLY_FUNC(users)
   COMMAND_CHECK_STATUS;
 
   /* Get channel ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (!tmp)
-    goto out;
-  channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-  if (!channel_id)
+  if (!silc_argument_get_decoded(cmd->args, 2, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
   /* Get channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                          channel_id, NULL);
+  channel = silc_idlist_find_channel_by_id(server->local_list,
+                                          SILC_ID_GET_ID(id), NULL);
   if (!channel) {
-    channel = silc_idlist_find_channel_by_id(server->global_list, 
-                                            channel_id, NULL);
+    channel = silc_idlist_find_channel_by_id(server->global_list,
+                                            SILC_ID_GET_ID(id), NULL);
     if (!channel) {
       SilcBuffer idp;
 
       if (server->server_type != SILC_SERVER)
        goto out;
 
-      idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
+      idp = silc_id_payload_encode(SILC_ID_GET_ID(id), SILC_ID_CHANNEL);
       silc_server_send_command(server, SILC_PRIMARY_ROUTE(server),
                               SILC_COMMAND_IDENTIFY, ++server->cmd_ident,
-                              1, 5, idp->data, idp->len);
+                              1, 5, idp->data, silc_buffer_len(idp));
       silc_buffer_free(idp);
 
       /* Register pending command callback. After we've received the channel
         information we will reprocess this command reply by re-calling this
         USERS command reply callback. */
-      silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
+      silc_server_command_pending(server, SILC_COMMAND_IDENTIFY,
                                  server->cmd_ident,
                                  silc_server_command_reply_users, cmd);
       return;
@@ -1141,7 +1321,7 @@ SILC_SERVER_CMD_REPLY_FUNC(users)
 
   /* Save the users to the channel */
   silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
-                                   client_id_list, client_mode_list, 
+                                   client_id_list, client_mode_list,
                                    list_count);
 
   channel->global_users = silc_server_channel_has_global(channel);
@@ -1152,7 +1332,6 @@ SILC_SERVER_CMD_REPLY_FUNC(users)
 
  out:
   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
-  silc_free(channel_id);
  err:
   silc_server_command_reply_free(cmd);
 }
@@ -1164,12 +1343,10 @@ SILC_SERVER_CMD_REPLY_FUNC(getkey)
   SilcStatus status, error;
   SilcClientEntry client = NULL;
   SilcServerEntry server_entry = NULL;
-  SilcClientID *client_id = NULL;
-  SilcServerID *server_id = NULL;
-  SilcSKEPKType type;
-  unsigned char *tmp, *pk;
+  SilcClientID client_id;
+  SilcServerID server_id;
+  unsigned char *tmp;
   SilcUInt32 len;
-  SilcUInt16 pk_len;
   SilcIDPayload idp = NULL;
   SilcIdType id_type;
   SilcPublicKey public_key = NULL;
@@ -1188,41 +1365,41 @@ SILC_SERVER_CMD_REPLY_FUNC(getkey)
   if (!tmp)
     goto out;
 
-  /* Decode the public key */
-
-  SILC_GET16_MSB(pk_len, tmp);
-  SILC_GET16_MSB(type, tmp + 2);
-  pk = tmp + 4;
-
-  if (type != SILC_SKE_PK_TYPE_SILC)
-    goto out;
-
-  if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key))
+  /* Decode the public key payload */
+  if (!silc_public_key_payload_decode(tmp, len, &public_key))
     goto out;
 
   id_type = silc_id_payload_get_type(idp);
   if (id_type == SILC_ID_CLIENT) {
-    client_id = silc_id_payload_get_id(idp);
+    silc_id_payload_get_id(idp, &client_id, sizeof(client_id));
 
-    client = silc_idlist_find_client_by_id(server->local_list, client_id,
+    client = silc_idlist_find_client_by_id(server->local_list, &client_id,
                                           TRUE, NULL);
     if (!client) {
-      client = silc_idlist_find_client_by_id(server->global_list, 
-                                            client_id, TRUE, NULL);
+      client = silc_idlist_find_client_by_id(server->global_list,
+                                            &client_id, TRUE, NULL);
       if (!client)
        goto out;
     }
 
-    client->data.public_key = public_key;
-    public_key = NULL;
+    if (!client->data.public_key) {
+      /* Add client's public key to repository */
+      if (!silc_server_get_public_key_by_client(server, client, NULL))
+       silc_skr_add_public_key_simple(server->repository,
+                                      public_key,
+                                      SILC_SKR_USAGE_IDENTIFICATION,
+                                      client, NULL);
+      client->data.public_key = public_key;
+      public_key = NULL;
+    }
   } else if (id_type == SILC_ID_SERVER) {
-    server_id = silc_id_payload_get_id(idp);
+    silc_id_payload_get_id(idp, &server_id, sizeof(server_id));
 
-    server_entry = silc_idlist_find_server_by_id(server->local_list, server_id,
-                                                TRUE, NULL);
+    server_entry = silc_idlist_find_server_by_id(server->local_list,
+                                                &server_id, TRUE, NULL);
     if (!server_entry) {
-      server_entry = silc_idlist_find_server_by_id(server->global_list, 
-                                                  server_id, TRUE, NULL);
+      server_entry = silc_idlist_find_server_by_id(server->global_list,
+                                                  &server_id, TRUE, NULL);
       if (!server_entry)
        goto out;
     }
@@ -1237,8 +1414,6 @@ SILC_SERVER_CMD_REPLY_FUNC(getkey)
   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
   if (idp)
     silc_id_payload_free(idp);
-  silc_free(client_id);
-  silc_free(server_id);
   if (public_key)
     silc_pkcs_public_key_free(public_key);
  err:
@@ -1250,19 +1425,16 @@ SILC_SERVER_CMD_REPLY_FUNC(list)
   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
   SilcServer server = cmd->server;
   SilcStatus status, error;
-  SilcChannelID *channel_id = NULL;
+  SilcID id;
   SilcChannelEntry channel;
   SilcIDCacheEntry cache;
-  SilcUInt32 len;
-  unsigned char *tmp, *name, *topic;
+  unsigned char *tmp, *name, *namec = NULL, *topic;
   SilcUInt32 usercount = 0;
-  bool global_list = FALSE;
+  SilcBool global_list = FALSE;
 
   COMMAND_CHECK_STATUS;
 
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
-  channel_id = silc_id_payload_parse_id(tmp, len, NULL);
-  if (!channel_id)
+  if (!silc_argument_get_decoded(cmd->args, 2, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
   name = silc_argument_get_arg_type(cmd->args, 3, NULL);
@@ -1271,12 +1443,17 @@ SILC_SERVER_CMD_REPLY_FUNC(list)
   if (tmp)
     SILC_GET32_MSB(usercount, tmp);
 
+  namec = silc_channel_name_check(name, strlen(name), SILC_STRING_UTF8,
+                                 256, NULL);
+  if (!namec)
+    goto out;
+
   /* Add the channel entry if we do not have it already */
-  channel = silc_idlist_find_channel_by_name(server->local_list, 
-                                            name, &cache);
+  channel = silc_idlist_find_channel_by_name(server->local_list,
+                                            namec, &cache);
   if (!channel) {
-    channel = silc_idlist_find_channel_by_name(server->global_list, 
-                                              name, &cache);
+    channel = silc_idlist_find_channel_by_name(server->global_list,
+                                              namec, &cache);
     global_list = TRUE;
   }
   if (!channel) {
@@ -1284,18 +1461,14 @@ SILC_SERVER_CMD_REPLY_FUNC(list)
        be bogus channel or some router in the net is buggy. */
     if (server->server_type != SILC_SERVER)
       goto out;
-    
+
     channel = silc_idlist_add_channel(server->global_list, strdup(name),
-                                     SILC_CHANNEL_MODE_NONE, channel_id, 
-                                     server->router, NULL, NULL, 
-                                     time(NULL) + 60);
+                                     SILC_CHANNEL_MODE_NONE,
+                                     silc_id_dup(SILC_ID_GET_ID(id),
+                                                 SILC_ID_CHANNEL),
+                                     server->router, NULL, NULL, NULL);
     if (!channel)
       goto out;
-    channel_id = NULL;
-  } else {
-    /* Found, update expiry */
-    if (global_list && server->server_type == SILC_SERVER)
-      cache->expire = time(NULL) + 60;
   }
 
   channel->user_count = usercount;
@@ -1312,14 +1485,10 @@ SILC_SERVER_CMD_REPLY_FUNC(list)
     return;
   }
 
-  /* Now purge all old entries from the global list, otherwise we'll might
-     have non-existent entries for long periods of time in the cache. */
-  silc_idcache_purge(server->global_list->channels);
-
  out:
   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
-  silc_free(channel_id);
  err:
+  silc_free(namec);
   silc_server_command_reply_free(cmd);
 }
 
@@ -1335,3 +1504,16 @@ SILC_SERVER_CMD_REPLY_FUNC(watch)
  err:
   silc_server_command_reply_free(cmd);
 }
+
+SILC_SERVER_CMD_REPLY_FUNC(ping)
+{
+  SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
+  SilcStatus status, error;
+
+  COMMAND_CHECK_STATUS;
+
+ out:
+  SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_PING);
+ err:
+  silc_server_command_reply_free(cmd);
+}