updates.
[silc.git] / lib / silcclient / command.c
index 5257bf450ef6f306f11eec2a37932d7a4c117b89..cf3fc46e4f34072ba8f34e0018929882d81a3586 100644 (file)
@@ -1,16 +1,15 @@
 /*
 
-  command.c
+  command.c 
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
   Copyright (C) 1997 - 2001 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
 #include "clientlibincludes.h"
 #include "client_internal.h"
 
-/* Client command list. */
-SilcClientCommand silc_command_list[] =
-{
-  SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
-  SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
-  SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 
-                 SILC_CF_LAG | SILC_CF_REG, 3),
-  SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 3),
-  SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 3),
-  SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(kill, KILL, "KILL", 
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
-  SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
-  SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(oper, OPER, "OPER",
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
-  SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 5),
-  SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 4),
-  SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", SILC_CF_LAG | SILC_CF_REG, 5),
-  SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 4),
-  SILC_CLIENT_CMD(ban, BAN, "BAN", SILC_CF_LAG | SILC_CF_REG, 3),
-  SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
-  SILC_CLIENT_CMD(shutdown, SHUTDOWN, "SHUTDOWN",
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 1),
-  SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER",
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 3),
-  SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(users, USERS, "USERS", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", SILC_CF_LAG | SILC_CF_REG, 2),
-
-  { NULL, 0, NULL, 0, 0 },
-};
-
 #define SILC_NOT_CONNECTED(x, c) \
-  x->ops->say((x), (c), SILC_CLIENT_MESSAGE_ERROR, \
+  x->internal->ops->say((x), (c), SILC_CLIENT_MESSAGE_ERROR, \
           "You are not connected to a server, use /SERVER to connect");
 
 /* Command operation that is called at the end of all commands. 
    Usage: COMMAND; */
-#define COMMAND cmd->client->ops->command(cmd->client, cmd->conn, \
+#define COMMAND cmd->client->internal->ops->command(cmd->client, cmd->conn, \
   cmd, TRUE, cmd->command->cmd)
 
 /* Error to application. Usage: COMMAND_ERROR; */
-#define COMMAND_ERROR cmd->client->ops->command(cmd->client, cmd->conn, \
-  cmd, FALSE, cmd->command->cmd)
+#define COMMAND_ERROR cmd->client->internal->ops->command(cmd->client, \
+  cmd->conn, cmd, FALSE, cmd->command->cmd)
 
 /* Generic function to send any command. The arguments must be sent already
    encoded into correct form and in correct order. */
 
-void silc_client_send_command(SilcClient client, SilcClientConnection conn,
+void silc_client_command_send(SilcClient client, SilcClientConnection conn,
                              SilcCommand command, uint16 ident,
                              uint32 argc, ...)
 {
@@ -97,18 +56,32 @@ void silc_client_send_command(SilcClient client, SilcClientConnection conn,
 /* Finds and returns a pointer to the command list. Return NULL if the
    command is not found. */
 
-SilcClientCommand *silc_client_command_find(const char *name)
+SilcClientCommand silc_client_command_find(SilcClient client,
+                                          const char *name)
 {
-  SilcClientCommand *cmd;
+  SilcClientCommand cmd;
 
-  for (cmd = silc_command_list; cmd->name; cmd++) {
-    if (!strcmp(cmd->name, name))
+  silc_list_start(client->internal->commands);
+  while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
+    if (cmd->name && !strcmp(cmd->name, name))
       return cmd;
   }
 
   return NULL;
 }
 
+/* Calls the command (executes it).  Application can call this after
+   it has allocated the SilcClientCommandContext with the function
+   silc_client_command_alloc and found the command from the client
+   library by calling silc_client_command_find.  This will execute
+   the command. */
+
+void silc_client_command_call(SilcClientCommand command, 
+                             SilcClientCommandContext cmd)
+{
+  (*command->command)((void *)cmd, NULL);
+}
+
 /* Add new pending command to be executed when reply to a command has been
    received.  The `reply_cmd' is the command that will call the `callback'
    with `context' when reply has been received.  If `ident is non-zero
@@ -176,7 +149,7 @@ int silc_client_command_pending_check(SilcClientConnection conn,
 
 /* Allocate Command Context */
 
-SilcClientCommandContext silc_client_command_alloc()
+SilcClientCommandContext silc_client_command_alloc(void)
 {
   SilcClientCommandContext ctx = silc_calloc(1, sizeof(*ctx));
   ctx->users++;
@@ -237,7 +210,7 @@ SILC_CLIENT_CMD_FUNC(whois)
   /* Given without arguments fetches client's own information */
   if (cmd->argc < 2) {
     buffer = silc_id_payload_encode(cmd->conn->local_id, SILC_ID_CLIENT);
-    silc_client_send_command(cmd->client, cmd->conn, SILC_COMMAND_WHOIS, 
+    silc_client_command_send(cmd->client, cmd->conn, SILC_COMMAND_WHOIS, 
                             ++conn->cmd_ident,
                             1, 3, buffer->data, buffer->len);
     silc_buffer_free(buffer);
@@ -279,7 +252,7 @@ SILC_CLIENT_CMD_FUNC(whowas)
   }
 
   if (cmd->argc < 2 || cmd->argc > 3) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
             "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
     COMMAND_ERROR;
     goto out;
@@ -305,7 +278,10 @@ SILC_CLIENT_CMD_FUNC(whowas)
 }
 
 /* Command IDENTIFY. This command is used to query information about 
-   specific user, especially ID's. */
+   specific user, especially ID's. 
+
+   NOTE: This command is used only internally by the client library
+   and application MUST NOT call this command directly. */
 
 SILC_CLIENT_CMD_FUNC(identify)
 {
@@ -315,14 +291,11 @@ SILC_CLIENT_CMD_FUNC(identify)
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
     goto out;
   }
 
-  if (cmd->argc < 2 || cmd->argc > 3) {
-    COMMAND_ERROR;
+  if (cmd->argc < 2 || cmd->argc > 3)
     goto out;
-  }
 
   if (cmd->argc == 2)
     buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY, 
@@ -342,9 +315,6 @@ SILC_CLIENT_CMD_FUNC(identify)
                          buffer->data, buffer->len, TRUE);
   silc_buffer_free(buffer);
 
-  /* Notify application */
-  COMMAND;
-
  out:
   silc_client_command_free(cmd);
 }
@@ -364,13 +334,14 @@ SILC_CLIENT_CMD_FUNC(nick_change)
   SILC_GET16_MSB(status, silc_argument_get_arg_type(reply->args, 1, NULL));
   if (status == SILC_STATUS_OK) {
     /* Set the nickname */
+    silc_idcache_del_by_context(conn->client_cache, conn->local_entry);
     if (conn->nickname)
       silc_free(conn->nickname);
     conn->nickname = strdup(cmd->argv[1]);
     conn->local_entry->nickname = conn->nickname;
-    silc_idcache_del_by_context(conn->client_cache, conn->local_entry);
+    silc_client_nickname_format(cmd->client, conn, conn->local_entry);
     silc_idcache_add(conn->client_cache, strdup(cmd->argv[1]), 
-                    conn->local_entry->id, conn->local_entry, FALSE);
+                    conn->local_entry->id, conn->local_entry, 0, NULL);
     COMMAND;
   } else {
     COMMAND_ERROR;
@@ -395,7 +366,7 @@ SILC_CLIENT_CMD_FUNC(nick)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /NICK <nickname>");
     COMMAND_ERROR;
     goto out;
@@ -407,11 +378,11 @@ SILC_CLIENT_CMD_FUNC(nick)
   /* Show current nickname */
   if (cmd->argc < 2) {
     if (cmd->conn) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "Your nickname is %s on server %s", 
                            conn->nickname, conn->remote_host);
     } else {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "Your nickname is %s", conn->nickname);
     }
 
@@ -514,7 +485,7 @@ SILC_CLIENT_CMD_FUNC(topic)
   }
 
   if (cmd->argc < 2 || cmd->argc > 3) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
                          "Usage: /TOPIC <channel> [<topic>]");
     COMMAND_ERROR;
     goto out;
@@ -522,7 +493,7 @@ SILC_CLIENT_CMD_FUNC(topic)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are not on any channel");
       COMMAND_ERROR;
       goto out;
@@ -533,7 +504,7 @@ SILC_CLIENT_CMD_FUNC(topic)
   }
 
   if (!conn->current_channel) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "You are not on that channel");
     COMMAND_ERROR;
     goto out;
@@ -541,7 +512,7 @@ SILC_CLIENT_CMD_FUNC(topic)
 
   /* Get the Channel ID of the channel */
   if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "You are not on that channel");
     COMMAND_ERROR;
     goto out;
@@ -595,7 +566,7 @@ SILC_CLIENT_CMD_FUNC(invite)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
                   "Usage: /INVITE <channel> [<nickname>[@server>]"
                   "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
     COMMAND_ERROR;
@@ -604,7 +575,7 @@ SILC_CLIENT_CMD_FUNC(invite)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are not on any channel");
       COMMAND_ERROR;
       goto out;
@@ -616,7 +587,7 @@ SILC_CLIENT_CMD_FUNC(invite)
 
     channel = silc_client_get_channel(cmd->client, conn, name);
     if (!channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are on that channel");
       COMMAND_ERROR;
       goto out;
@@ -626,8 +597,8 @@ SILC_CLIENT_CMD_FUNC(invite)
   /* Parse the typed nickname. */
   if (cmd->argc == 3) {
     if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
-      if (client->params->nickname_parse)
-       client->params->nickname_parse(cmd->argv[2], &nickname);
+      if (client->internal->params->nickname_parse)
+       client->internal->params->nickname_parse(cmd->argv[2], &nickname);
       else
        nickname = strdup(cmd->argv[2]);
 
@@ -703,7 +674,7 @@ SILC_TASK_CALLBACK(silc_client_command_quit_cb)
   QuitInternal q = (QuitInternal)context;
 
   /* Close connection */
-  q->client->ops->disconnect(q->client, q->conn);
+  q->client->internal->ops->disconnect(q->client, q->conn);
   silc_client_close_connection(q->client, NULL, q->conn->sock->user_data);
 
   silc_free(q);
@@ -765,8 +736,8 @@ SILC_TASK_CALLBACK(silc_client_command_kill_remove_later)
   char *nickname = NULL;
   
   /* Parse the typed nickname. */
-  if (client->params->nickname_parse)
-    client->params->nickname_parse(cmd->argv[1], &nickname);
+  if (client->internal->params->nickname_parse)
+    client->internal->params->nickname_parse(cmd->argv[1], &nickname);
   else
     nickname = strdup(cmd->argv[1]);
 
@@ -823,15 +794,15 @@ SILC_CLIENT_CMD_FUNC(kill)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /KILL <nickname> [<comment>]");
     COMMAND_ERROR;
     goto out;
   }
 
   /* Parse the typed nickname. */
-  if (client->params->nickname_parse)
-    client->params->nickname_parse(cmd->argv[1], &nickname);
+  if (client->internal->params->nickname_parse)
+    client->internal->params->nickname_parse(cmd->argv[1], &nickname);
   else
     nickname = strdup(cmd->argv[1]);
 
@@ -993,7 +964,9 @@ SILC_CLIENT_CMD_FUNC(join)
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
   SilcIDCacheEntry id_cache = NULL;
-  SilcBuffer buffer, idp;
+  SilcBuffer buffer, idp, auth = NULL;
+  char *name, *passphrase = NULL, *cipher = NULL, *hmac = NULL;
+  int i;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
@@ -1001,6 +974,11 @@ SILC_CLIENT_CMD_FUNC(join)
     goto out;
   }
 
+  if (cmd->argc < 2) {
+    COMMAND_ERROR;
+    goto out;
+  }
+  
   /* See if we have joined to the requested channel already */
   if (silc_idcache_find_by_name_one(conn->channel_cache, cmd->argv[1],
                                    &id_cache)) {
@@ -1014,31 +992,50 @@ SILC_CLIENT_CMD_FUNC(join)
   if (cmd->argv_lens[1] > 256)
     cmd->argv_lens[1] = 256;
 
-  /* Send JOIN command to the server */
-  if (cmd->argc == 2)
-    buffer = 
-      silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
-                                    1, cmd->argv[1], cmd->argv_lens[1],
-                                    2, idp->data, idp->len);
-  else if (cmd->argc == 3)
-    /* XXX Buggy */
-    buffer = 
-      silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
-                                    1, cmd->argv[1], cmd->argv_lens[1],
-                                    2, idp->data, idp->len,
-                                    3, cmd->argv[2], cmd->argv_lens[2]);
-  else
-    buffer = 
-      silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
-                                    1, cmd->argv[1], cmd->argv_lens[1],
-                                    2, idp->data, idp->len,
-                                    3, cmd->argv[2], cmd->argv_lens[2],
-                                    4, cmd->argv[3], cmd->argv_lens[3]);
+  name = cmd->argv[1];
+
+  for (i = 2; i < cmd->argc; i++) {
+    if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc >= i + 1) {
+      cipher = cmd->argv[i + 1];
+      i++;
+    } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc >= i + 1) {
+      hmac = cmd->argv[i + 1];
+      i++;
+    } else if (!strcasecmp(cmd->argv[i], "-founder") && cmd->argc >= i + 1) {
+      if (!strcasecmp(cmd->argv[i + 1], "-pubkey")) {
+       auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
+                                                 cmd->client->private_key,
+                                                 conn->hash,
+                                                 conn->local_id,
+                                                 SILC_ID_CLIENT);
+      } else {
+       auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
+                                       cmd->argv[i + 1], 
+                                       cmd->argv_lens[i + 1]);
+      }
+      i++;
+    } else {
+      passphrase = cmd->argv[i];
+    }
+  }
 
+  /* Send JOIN command to the server */
+  buffer =
+    silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 6,
+                                  1, name, strlen(name),
+                                  2, idp->data, idp->len,
+                                  3, passphrase, 
+                                  passphrase ? strlen(passphrase) : 0,
+                                  4, cipher, cipher ? strlen(cipher) : 0,
+                                  5, hmac, hmac ? strlen(hmac) : 0,
+                                  6, auth ? auth->data : NULL,
+                                  auth ? auth->len : 0);
   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
                          0, NULL, NULL, buffer->data, buffer->len, TRUE);
   silc_buffer_free(buffer);
   silc_buffer_free(idp);
+  if (auth)
+    silc_buffer_free(auth);
 
   /* Notify application */
   COMMAND;
@@ -1062,7 +1059,7 @@ SILC_CLIENT_CMD_FUNC(motd)
   }
 
   if (cmd->argc < 1 || cmd->argc > 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
                          "Usage: /MOTD [<server>]");
     COMMAND_ERROR;
     goto out;
@@ -1107,7 +1104,7 @@ SILC_CLIENT_CMD_FUNC(umode)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                  "Usage: /UMODE +|-<modes>");
     COMMAND_ERROR;
     goto out;
@@ -1202,7 +1199,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
   }
 
   if (cmd->argc < 3) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                  "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
     COMMAND_ERROR;
     goto out;
@@ -1210,7 +1207,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are not on any channel");
       COMMAND_ERROR;
       goto out;
@@ -1222,7 +1219,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
 
     channel = silc_client_get_channel(cmd->client, conn, name);
     if (!channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are on that channel");
       COMMAND_ERROR;
       goto out;
@@ -1281,7 +1278,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
        mode |= SILC_CHANNEL_MODE_ULIMIT;
        type = 3;
        if (cmd->argc < 4) {
-         cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
          COMMAND_ERROR;
          goto out;
@@ -1299,7 +1296,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
        mode |= SILC_CHANNEL_MODE_PASSPHRASE;
        type = 4;
        if (cmd->argc < 4) {
-         cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
          COMMAND_ERROR;
          goto out;
@@ -1315,7 +1312,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
        mode |= SILC_CHANNEL_MODE_CIPHER;
        type = 5;
        if (cmd->argc < 4) {
-         cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
          COMMAND_ERROR;
          goto out;
@@ -1331,7 +1328,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
        mode |= SILC_CHANNEL_MODE_HMAC;
        type = 6;
        if (cmd->argc < 4) {
-         cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
          COMMAND_ERROR;
          goto out;
@@ -1348,7 +1345,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
        type = 7;
 
        if (cmd->argc < 4) {
-         cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
          COMMAND_ERROR;
          goto out;
@@ -1433,7 +1430,7 @@ SILC_CLIENT_CMD_FUNC(cumode)
   }
 
   if (cmd->argc < 4) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                  "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
     COMMAND_ERROR;
     goto out;
@@ -1441,7 +1438,7 @@ SILC_CLIENT_CMD_FUNC(cumode)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are not on any channel");
       COMMAND_ERROR;
       goto out;
@@ -1453,7 +1450,7 @@ SILC_CLIENT_CMD_FUNC(cumode)
 
     channel = silc_client_get_channel(cmd->client, conn, name);
     if (!channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are on that channel");
       COMMAND_ERROR;
       goto out;
@@ -1461,8 +1458,8 @@ SILC_CLIENT_CMD_FUNC(cumode)
   }
 
   /* Parse the typed nickname. */
-  if (client->params->nickname_parse)
-    client->params->nickname_parse(cmd->argv[3], &nickname);
+  if (client->internal->params->nickname_parse)
+    client->internal->params->nickname_parse(cmd->argv[3], &nickname);
   else
     nickname = strdup(cmd->argv[3]);
 
@@ -1520,11 +1517,11 @@ SILC_CLIENT_CMD_FUNC(cumode)
       if (add) {
        if (cmd->argc == 5) {
          if (!strcasecmp(cmd->argv[4], "-pubkey")) {
-         auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
-                                                   cmd->client->private_key,
-                                                   conn->hash,
-                                                   conn->local_id,
-                                                   SILC_ID_CLIENT);
+           auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
+                                                     cmd->client->private_key,
+                                                     conn->hash,
+                                                     conn->local_id,
+                                                     SILC_ID_CLIENT);
          } else {
            auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
                                            cmd->argv[4], cmd->argv_lens[4]);
@@ -1599,7 +1596,7 @@ SILC_CLIENT_CMD_FUNC(kick)
   }
 
   if (cmd->argc < 3) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /KICK <channel> <nickname> [<comment>]");
     COMMAND_ERROR;
     goto out;
@@ -1607,7 +1604,7 @@ SILC_CLIENT_CMD_FUNC(kick)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are not on any channel");
       COMMAND_ERROR;
       goto out;
@@ -1618,7 +1615,7 @@ SILC_CLIENT_CMD_FUNC(kick)
   }
 
   if (!conn->current_channel) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "You are not on that channel");
     COMMAND_ERROR;
     goto out;
@@ -1626,7 +1623,7 @@ SILC_CLIENT_CMD_FUNC(kick)
 
   /* Get the Channel ID of the channel */
   if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "You are not on that channel");
     COMMAND_ERROR;
     goto out;
@@ -1635,8 +1632,8 @@ SILC_CLIENT_CMD_FUNC(kick)
   channel = (SilcChannelEntry)id_cache->context;
 
   /* Parse the typed nickname. */
-  if (client->params->nickname_parse)
-    client->params->nickname_parse(cmd->argv[2], &nickname);
+  if (client->internal->params->nickname_parse)
+    client->internal->params->nickname_parse(cmd->argv[2], &nickname);
   else
     nickname = strdup(cmd->argv[2]);
 
@@ -1644,7 +1641,7 @@ SILC_CLIENT_CMD_FUNC(kick)
   target = silc_idlist_get_client(cmd->client, conn, nickname, 
                                  cmd->argv[2], FALSE);
   if (!target) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "No such client: %s",
                          cmd->argv[2]);
     COMMAND_ERROR;
@@ -1726,7 +1723,7 @@ SILC_CLIENT_CMD_FUNC(oper)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /OPER <username> [-pubkey]");
     COMMAND_ERROR;
     goto out;
@@ -1734,7 +1731,7 @@ SILC_CLIENT_CMD_FUNC(oper)
 
   if (cmd->argc < 3) {
     /* Get passphrase */
-    cmd->client->ops->ask_passphrase(cmd->client, conn,
+    cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
                                     silc_client_command_oper_send,
                                     context);
     return;
@@ -1794,7 +1791,7 @@ SILC_CLIENT_CMD_FUNC(silcoper)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /SILCOPER <username> [-pubkey]");
     COMMAND_ERROR;
     goto out;
@@ -1802,7 +1799,7 @@ SILC_CLIENT_CMD_FUNC(silcoper)
 
   if (cmd->argc < 3) {
     /* Get passphrase */
-    cmd->client->ops->ask_passphrase(cmd->client, conn,
+    cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
                                     silc_client_command_silcoper_send,
                                     context);
     return;
@@ -1831,7 +1828,7 @@ SILC_CLIENT_CMD_FUNC(connect)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /CONNECT <server> [<port>]");
     COMMAND_ERROR;
     goto out;
@@ -1880,7 +1877,7 @@ SILC_CLIENT_CMD_FUNC(ban)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                   "Usage: /BAN <channel> "
                   "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
     COMMAND_ERROR;
@@ -1889,7 +1886,7 @@ SILC_CLIENT_CMD_FUNC(ban)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are not on any channel");
       COMMAND_ERROR;
       goto out;
@@ -1901,7 +1898,7 @@ SILC_CLIENT_CMD_FUNC(ban)
 
     channel = silc_client_get_channel(cmd->client, conn, name);
     if (!channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are on that channel");
       COMMAND_ERROR;
       goto out;
@@ -1958,7 +1955,7 @@ SILC_CLIENT_CMD_FUNC(close)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /CLOSE <server> [<port>]");
     COMMAND_ERROR;
     goto out;
@@ -2002,7 +1999,7 @@ SILC_CLIENT_CMD_FUNC(shutdown)
   }
 
   /* Send the command */
-  silc_client_send_command(cmd->client, cmd->conn, 
+  silc_client_command_send(cmd->client, cmd->conn, 
                           SILC_COMMAND_SHUTDOWN, 0, 0);
 
   /* Notify application */
@@ -2030,7 +2027,7 @@ SILC_CLIENT_CMD_FUNC(leave)
   }
 
   if (cmd->argc != 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /LEAVE <channel>");
     COMMAND_ERROR;
     goto out;
@@ -2038,7 +2035,7 @@ SILC_CLIENT_CMD_FUNC(leave)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are not on any channel");
       COMMAND_ERROR;
       goto out;
@@ -2050,7 +2047,7 @@ SILC_CLIENT_CMD_FUNC(leave)
 
   /* Get the Channel ID of the channel */
   if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "You are not on that channel");
     COMMAND_ERROR;
     goto out;
@@ -2097,7 +2094,7 @@ SILC_CLIENT_CMD_FUNC(users)
   }
 
   if (cmd->argc != 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /USERS <channel>");
     COMMAND_ERROR;
     goto out;
@@ -2105,7 +2102,7 @@ SILC_CLIENT_CMD_FUNC(users)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are not on any channel");
       COMMAND_ERROR;
       goto out;
@@ -2150,7 +2147,7 @@ SILC_CLIENT_CMD_FUNC(getkey)
   }
 
   if (cmd->argc < 2) {
-    client->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, 
                     "Usage: /GETKEY <nickname or server name>");
     COMMAND_ERROR;
     goto out;
@@ -2165,7 +2162,7 @@ SILC_CLIENT_CMD_FUNC(getkey)
     
     if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
        status == SILC_STATUS_ERR_NO_SUCH_SERVER) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
                            "%s", 
                            silc_client_command_status_message(status));
       COMMAND_ERROR;
@@ -2174,8 +2171,8 @@ SILC_CLIENT_CMD_FUNC(getkey)
   }
 
   /* Parse the typed nickname. */
-  if (client->params->nickname_parse)
-    client->params->nickname_parse(cmd->argv[1], &nickname);
+  if (client->internal->params->nickname_parse)
+    client->internal->params->nickname_parse(cmd->argv[1], &nickname);
   else
     nickname = strdup(cmd->argv[1]);
 
@@ -2205,8 +2202,11 @@ SILC_CLIENT_CMD_FUNC(getkey)
                                  silc_client_command_dup(cmd));
 
       /* This sends the IDENTIFY command to resolve the server. */
-      silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY,
-                              ++conn->cmd_ident, 1, 
+      silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
+                                  silc_client_command_reply_identify_i, 0,
+                                  ++conn->cmd_ident);
+      silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
+                              conn->cmd_ident, 1, 
                               2, cmd->argv[1], cmd->argv_lens[1]);
       silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
                                  conn->cmd_ident, NULL,
@@ -2237,3 +2237,135 @@ SILC_CLIENT_CMD_FUNC(getkey)
   silc_free(nickname);
   silc_client_command_free(cmd);
 }
+
+/* Register a new command indicated by the `command' to the SILC client.
+   The `name' is optional command name.  If provided the command may be
+   searched using the silc_client_command_find by that name.  The
+   `command_function' is the function to be called when the command is
+   executed, and the `command_reply_function' is the function to be
+   called after the server has sent reply back to the command. 
+
+   The `ident' is optional identifier for the command.  If non-zero
+   the `command_reply_function' for the command type `command' will be
+   called only if the command reply sent by server includes the 
+   command identifier `ident'. Application usually does not need it
+   and set it to zero value. */
+
+bool silc_client_command_register(SilcClient client,
+                                 SilcCommand command,
+                                 const char *name,
+                                 SilcCommandCb command_function,
+                                 SilcCommandCb command_reply_function,
+                                 uint8 max_args,
+                                 uint16 ident)
+{
+  SilcClientCommand cmd;
+
+  cmd = silc_calloc(1, sizeof(*cmd));
+  cmd->cmd = command;
+  cmd->command = command_function;
+  cmd->reply = command_reply_function;
+  cmd->name = name ? strdup(name) : NULL;
+  cmd->max_args = max_args;
+  cmd->ident = ident;
+
+  silc_list_add(client->internal->commands, cmd);
+
+  return TRUE;
+}
+
+/* Unregister a command indicated by the `command' with command function
+   `command_function' and command reply function `command_reply_function'.
+   Returns TRUE if the command was found and unregistered. */
+
+bool silc_client_command_unregister(SilcClient client,
+                                   SilcCommand command,
+                                   SilcCommandCb command_function,
+                                   SilcCommandCb command_reply_function,
+                                   uint16 ident)
+{
+  SilcClientCommand cmd;
+
+  silc_list_start(client->internal->commands);
+  while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
+    if (cmd->cmd == command && cmd->command == command_function &&
+       cmd->reply == command_reply_function && cmd->ident == ident) {
+      silc_list_del(client->internal->commands, cmd);
+      silc_free(cmd->name);
+      silc_free(cmd);
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+/* Register all default commands provided by the client library for the
+   application. */
+
+void silc_client_commands_register(SilcClient client)
+{
+  silc_list_init(client->internal->commands, struct SilcClientCommandStruct, 
+                next);
+
+  SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 3);
+  SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
+  SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
+  SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
+  SILC_CLIENT_CMD(list, LIST, "LIST", 2);
+  SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
+  SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
+  SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
+  SILC_CLIENT_CMD(kill, KILL, "KILL", 3);
+  SILC_CLIENT_CMD(info, INFO, "INFO", 2);
+  SILC_CLIENT_CMD(connect, CONNECT, "CONNECT", 3);
+  SILC_CLIENT_CMD(ping, PING, "PING", 2);
+  SILC_CLIENT_CMD(oper, OPER, "OPER", 2);
+  SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
+  SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
+  SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
+  SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 4);
+  SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 5);
+  SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
+  SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
+  SILC_CLIENT_CMD(close, CLOSE, "CLOSE", 3);
+  SILC_CLIENT_CMD(shutdown, SHUTDOWN, "SHUTDOWN", 1);
+  SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
+  SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
+  SILC_CLIENT_CMD(users, USERS, "USERS", 2);
+  SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
+}
+
+/* Unregister all commands. */
+
+void silc_client_commands_unregister(SilcClient client)
+{
+  SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
+  SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
+  SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
+  SILC_CLIENT_CMDU(nick, NICK, "NICK");
+  SILC_CLIENT_CMDU(list, LIST, "LIST");
+  SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
+  SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
+  SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
+  SILC_CLIENT_CMDU(kill, KILL, "KILL");
+  SILC_CLIENT_CMDU(info, INFO, "INFO");
+  SILC_CLIENT_CMDU(connect, CONNECT, "CONNECT");
+  SILC_CLIENT_CMDU(ping, PING, "PING");
+  SILC_CLIENT_CMDU(oper, OPER, "OPER");
+  SILC_CLIENT_CMDU(join, JOIN, "JOIN");
+  SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
+  SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
+  SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
+  SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
+  SILC_CLIENT_CMDU(kick, KICK, "KICK");
+  SILC_CLIENT_CMDU(ban, BAN, "BAN");
+  SILC_CLIENT_CMDU(close, CLOSE, "CLOSE");
+  SILC_CLIENT_CMDU(shutdown, SHUTDOWN, "SHUTDOWN");
+  SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
+  SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
+  SILC_CLIENT_CMDU(users, USERS, "USERS");
+  SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
+
+  silc_list_uninit(client->internal->commands);
+}