Merged silc_1_0_branch to trunk.
[silc.git] / apps / silcd / command_reply.c
index 55d62bb5921a59b8ca4e93471e23147387df0040..b4862c61221ed3d38de4bd46891c810400405ceb 100644 (file)
@@ -4,13 +4,12 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2002 Pekka Riikonen
+  Copyright (C) 1997 - 2005 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 },
 };
@@ -76,7 +77,7 @@ void silc_server_command_reply_process(SilcServer server,
     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));
@@ -85,14 +86,21 @@ void silc_server_command_reply_process(SilcServer server,
   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);
+
+  /* Client is not allowed to send reply to all commands */
+  if (sock->type == SILC_SOCKET_TYPE_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;
@@ -118,7 +126,7 @@ void silc_server_command_reply_free(SilcServerCommandReplyContext cmd)
   }
 }
 
-static void 
+static void
 silc_server_command_process_error(SilcServerCommandReplyContext cmd,
                                  SilcStatus error)
 {
@@ -136,11 +144,17 @@ silc_server_command_process_error(SilcServerCommandReplyContext cmd,
       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 = 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);
+
+         if (client->data.public_key)
+           silc_hash_table_del_by_context(server->pk_hash,
+                                           client->data.public_key,
+                                           client);
+
+         silc_server_remove_from_channels(server, NULL, client, TRUE,
+                                          NULL, TRUE, FALSE);
          silc_idlist_del_data(client);
          silc_idlist_del_client(server->global_list, client);
        }
@@ -156,14 +170,14 @@ 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, *servername = NULL;
   unsigned char *fingerprint;
   SilcClientID *client_id;
   SilcClientEntry client;
   SilcIDCacheEntry cache = NULL;
   char global = FALSE;
-  char *nick;
+  char *nick = NULL;
   SilcUInt32 mode = 0, len, len2, id_len, flen;
 
   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
@@ -185,10 +199,10 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
 
   /* 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, 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;
   }
@@ -203,18 +217,19 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
     silc_parse_userfqdn(nickname, &nick, &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, 
+       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, nick, username,
+                                   strdup(realname), client_id,
                                    cmd->sock->user_data, NULL, 0);
     if (!client) {
       SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
+      silc_free(nick);
+      silc_free(servername);
       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;
@@ -224,8 +239,31 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
 
     SILC_LOG_DEBUG(("Updating client data"));
 
-    /* Take hostname out of nick string if it includes it. */
+    /* Check nickname */
     silc_parse_userfqdn(nickname, &nick, &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",
+                     cmd->sock->hostname ? cmd->sock->hostname : "", nick));
+      silc_free(nick);
+      silc_free(servername);
+      return FALSE;
+    }
+
+    /* Check username */
+    silc_parse_userfqdn(username, &tmp, NULL);
+    if (!silc_identifier_verify(tmp, strlen(tmp), SILC_STRING_UTF8, 128)) {
+      silc_free(tmp);
+      silc_free(nick);
+      silc_free(servername);
+      SILC_LOG_ERROR(("Malformed username '%s' received in WHOIS reply "
+                     "from %s",
+                     cmd->sock->hostname ? cmd->sock->hostname : "", tmp));
+      return FALSE;
+    }
+    silc_free(tmp);
 
     /* Remove the old cache entry  */
     silc_idcache_del_by_context(global ? server->global_list->clients :
@@ -235,7 +273,7 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
     silc_free(client->username);
     silc_free(client->userinfo);
     silc_free(client->servername);
-    
+
     client->nickname = nick;
     client->username = strdup(username);
     client->userinfo = strdup(realname);
@@ -246,8 +284,8 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
 
     /* Create new cache entry */
     silc_idcache_add(global ? server->global_list->clients :
-                    server->local_list->clients, nick, client->id, 
-                    client, 0, NULL); 
+                    server->local_list->clients, nickname, client->id,
+                    client, 0, NULL);
     silc_free(client_id);
   }
 
@@ -285,11 +323,96 @@ 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[20];
+      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_decode(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 to public key hash table */
+           if (!silc_hash_table_find_by_context(server->pk_hash,
+                                                client->data.public_key,
+                                                client, NULL))
+             silc_hash_table_add(server->pk_hash,
+                                 client->data.public_key, client);
+
+           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 = cmd->sock->user_data;
+
+  /* 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
@@ -303,8 +426,13 @@ SILC_SERVER_CMD_REPLY_FUNC(whois)
 
   COMMAND_CHECK_STATUS;
 
-  if (!silc_server_command_reply_whois_save(cmd))
-    goto out;
+  if (cmd->sock->type != SILC_SOCKET_TYPE_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,11 +460,11 @@ 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;
+  char *nickname, *username, *realname, *servername = NULL, *tmp;
   SilcClientID *client_id;
   SilcClientEntry client;
   SilcIDCacheEntry cache = NULL;
-  char *nick;
+  char *nick = NULL;
   int global = FALSE;
 
   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
@@ -356,7 +484,7 @@ silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
   client = silc_idlist_find_client_by_id(server->local_list, client_id,
                                         FALSE, &cache);
   if (!client) {
-    client = silc_idlist_find_client_by_id(server->global_list, 
+    client = silc_idlist_find_client_by_id(server->global_list,
                                           client_id, FALSE, &cache);
     global = TRUE;
   }
@@ -371,32 +499,57 @@ silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
     silc_parse_userfqdn(nickname, &nick, &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), 
+    client = silc_idlist_add_client(server->global_list, nick, username,
+                                   strdup(realname),
+                                   silc_id_dup(client_id, SILC_ID_CLIENT),
                                    cmd->sock->user_data, NULL,
                                    SILC_ID_CACHE_EXPIRE_DEF);
     if (!client) {
       SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
+      silc_free(nick);
+      silc_free(servername);
       return FALSE;
     }
 
     client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
     client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
-    client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED; 
+    client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
     client->servername = servername;
   } else {
     /* We have the client already, update the data */
 
-    /* Take hostname out of nick string if it includes it. */
+    /* Check nickname */
     silc_parse_userfqdn(nickname, &nick, &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",
+                     cmd->sock->hostname ? cmd->sock->hostname : "", nick));
+      silc_free(nick);
+      silc_free(servername);
+      return FALSE;
+    }
+
+    /* Check username */
+    silc_parse_userfqdn(username, &tmp, NULL);
+    if (!silc_identifier_verify(tmp, strlen(tmp), SILC_STRING_UTF8, 128)) {
+      silc_free(tmp);
+      silc_free(nick);
+      silc_free(servername);
+      SILC_LOG_ERROR(("Malformed username '%s' received in WHOWAS reply "
+                     "from %s",
+                     cmd->sock->hostname ? cmd->sock->hostname : "", tmp));
+      return FALSE;
+    }
+    silc_free(tmp);
 
     silc_free(client->nickname);
     silc_free(client->username);
     silc_free(client->servername);
-    
+
     client->nickname = nick;
     client->username = strdup(username);
     client->servername = servername;
@@ -407,7 +560,7 @@ silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
     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, 
+                    server->local_list->clients, nickname, client->id,
                     client, 0, NULL);
   }
 
@@ -499,7 +652,7 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
 
     SILC_LOG_DEBUG(("Received client information"));
 
-    client = silc_idlist_find_client_by_id(server->local_list, 
+    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,
@@ -517,36 +670,53 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
        silc_parse_userfqdn(name, &nick, NULL);
 
       /* 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 = silc_idlist_add_client(server->global_list, nick, info, NULL,
                                      client_id, cmd->sock->user_data,
                                      NULL, time(NULL) + 300);
       if (!client) {
        SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
+       silc_free(nick);
        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);
 
+       /* 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 "
+                         "from %s",
+                         cmd->sock->hostname ?
+                         cmd->sock->hostname : "", nick));
+         return FALSE;
+       }
+
        /* Remove the old cache entry */
        silc_idcache_del_by_context(global ? server->global_list->clients :
                                    server->local_list->clients, client);
 
        silc_free(client->nickname);
        client->nickname = nick;
+
+       /* Add new cache entry */
+       silc_idcache_add(global ? server->global_list->clients :
+                        server->local_list->clients, name, client->id,
+                        client, expire, NULL);
       }
-      
+
       if (info) {
        silc_free(client->username);
        client->username = strdup(info);
@@ -554,13 +724,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. */
@@ -589,19 +752,19 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
 
     SILC_LOG_DEBUG(("Received server information"));
 
-    server_entry = silc_idlist_find_server_by_id(server->local_list, 
+    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_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_PRIMARY_ROUTE(server));
@@ -628,25 +791,35 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
 
     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, channel_id,
                                        server->router, NULL, NULL, 0);
       if (!channel) {
        silc_free(channel_id);
+       silc_free(info);
        goto error;
       }
+      silc_free(info);
       channel_id = NULL;
     }
 
@@ -719,18 +892,18 @@ SILC_SERVER_CMD_REPLY_FUNC(info)
 
   /* 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, server_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, server_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,
+      entry = silc_idlist_add_server(server->global_list, strdup(name), 0,
                                     server_id, cmd->sock->user_data,
                                     cmd->sock);
       if (!entry) {
@@ -776,13 +949,37 @@ SILC_SERVER_CMD_REPLY_FUNC(motd)
   if (!server_id)
     goto out;
 
-  entry = silc_idlist_find_server_by_id(server->local_list, server_id, 
+  entry = silc_idlist_find_server_by_id(server->local_list, server_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, server_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;
+
+      /* 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 */
+      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,
+                             buffer->len, TRUE);
+      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 */
@@ -817,7 +1014,7 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
   SilcHmac hmac = NULL;
   SilcUInt32 id_len, len, list_count;
   unsigned char *id_string;
-  char *channel_name, *tmp;
+  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;
@@ -903,22 +1100,26 @@ 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_pkcs_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)));
 
     /* 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);
 
@@ -930,6 +1131,7 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
       silc_free(id);
       goto out;
     }
+    hmac = NULL;
     server->stat.my_channels++;
     server->stat.channels++;
   } else {
@@ -956,23 +1158,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,7 +1214,17 @@ 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_SERVER)
+    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);
 
@@ -1004,7 +1246,7 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
   }
 
   /* Save the users to the channel */
-  silc_server_save_users_on_channel(server, cmd->sock, entry, 
+  silc_server_save_users_on_channel(server, cmd->sock, entry,
                                    client_id, client_id_list,
                                    client_mode_list, list_count);
   entry->users_resolved = TRUE;
@@ -1012,6 +1254,9 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
  out:
   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
  err:
+  silc_free(channel_namec);
+  if (hmac)
+    silc_hmac_free(hmac);
   silc_free(client_id);
   silc_server_command_reply_free(cmd);
 
@@ -1037,7 +1282,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),
@@ -1088,10 +1333,10 @@ SILC_SERVER_CMD_REPLY_FUNC(users)
     goto out;
 
   /* Get channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, 
+  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 = silc_idlist_find_channel_by_id(server->global_list,
                                             channel_id, NULL);
     if (!channel) {
       SilcBuffer idp;
@@ -1108,7 +1353,7 @@ SILC_SERVER_CMD_REPLY_FUNC(users)
       /* 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 +1386,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);
@@ -1166,10 +1411,8 @@ SILC_SERVER_CMD_REPLY_FUNC(getkey)
   SilcServerEntry server_entry = NULL;
   SilcClientID *client_id = NULL;
   SilcServerID *server_id = NULL;
-  SilcSKEPKType type;
-  unsigned char *tmp, *pk;
+  unsigned char *tmp;
   SilcUInt32 len;
-  SilcUInt16 pk_len;
   SilcIDPayload idp = NULL;
   SilcIdType id_type;
   SilcPublicKey public_key = NULL;
@@ -1188,16 +1431,8 @@ 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_pkcs_public_key_payload_decode(tmp, len, &public_key))
     goto out;
 
   id_type = silc_id_payload_get_type(idp);
@@ -1207,21 +1442,27 @@ SILC_SERVER_CMD_REPLY_FUNC(getkey)
     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 = 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) {
+      if (!silc_hash_table_find_by_context(server->pk_hash, public_key,
+                                          client, NULL))
+       silc_hash_table_add(server->pk_hash, public_key, client);
+
+      client->data.public_key = public_key;
+      public_key = NULL;
+    }
   } else if (id_type == SILC_ID_SERVER) {
     server_id = silc_id_payload_get_id(idp);
 
     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_entry = silc_idlist_find_server_by_id(server->global_list,
                                                   server_id, TRUE, NULL);
       if (!server_entry)
        goto out;
@@ -1254,13 +1495,15 @@ SILC_SERVER_CMD_REPLY_FUNC(list)
   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;
 
   COMMAND_CHECK_STATUS;
 
   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+  if (!tmp)
+    goto out;
   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
   if (!channel_id)
     goto out;
@@ -1271,12 +1514,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,10 +1532,10 @@ 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, 
+                                     SILC_CHANNEL_MODE_NONE, channel_id,
+                                     server->router, NULL, NULL,
                                      time(NULL) + 60);
     if (!channel)
       goto out;
@@ -1320,6 +1568,7 @@ SILC_SERVER_CMD_REPLY_FUNC(list)
   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
   silc_free(channel_id);
  err:
+  silc_free(namec);
   silc_server_command_reply_free(cmd);
 }
 
@@ -1335,3 +1584,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);
+}