Memory leak fixes.
[silc.git] / lib / silcclient / command.c
index bdb818f5f0ca2a0997910c70a2ab331288abeec4..167d787830a6d1acf25fde54cbbf5f2561212e97 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
+  Copyright (C) 1997 - 2002 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
 */
 /* $Id$ */
 
-#include "clientlibincludes.h"
+#include "silcincludes.h"
+#include "silcclient.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, \
-  cmd, TRUE, cmd->command->cmd)
+   Usage: COMMAND(status); */
+#define COMMAND(status) cmd->client->internal->ops->command(cmd->client, \
+  cmd->conn, cmd, TRUE, cmd->command->cmd, (status))
+
+/* Error to application. Usage: COMMAND_ERROR(status); */
+#define COMMAND_ERROR(status)                          \
+  cmd->client->internal->ops->command(cmd->client,     \
+  cmd->conn, cmd, FALSE, cmd->command->cmd, (status))
 
-/* Error to application. Usage: COMMAND_ERROR; */
-#define COMMAND_ERROR cmd->client->ops->command(cmd->client, cmd->conn, \
-  cmd, FALSE, cmd->command->cmd)
+#define SAY cmd->client->internal->ops->say
 
 /* 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,
-                             SilcCommand command, uint16 ident,
-                             uint32 argc, ...)
+void silc_client_command_send(SilcClient client, SilcClientConnection conn,
+                             SilcCommand command, SilcUInt16 ident,
+                             SilcUInt32 argc, ...)
 {
   SilcBuffer packet;
   va_list ap;
@@ -97,28 +60,44 @@ 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
-   the `callback' will be executed when received reply with command 
-   identifier `ident'. */
+   received. The `reply_cmd' is the command that will call the `callback'
+   with `context' when reply has been received.  It can be SILC_COMMAND_NONE
+   to match any command with the `ident'.  If `ident' is non-zero
+   the `callback' will be executed when received reply with command
+   identifier `ident'. If there already exists pending command for the
+   specified command, ident, callback and context this function has no
+   effect. */
 
 void silc_client_command_pending(SilcClientConnection conn,
                                 SilcCommand reply_cmd,
-                                uint16 ident,
-                                SilcClientPendingDestructor destructor,
+                                SilcUInt16 ident,
                                 SilcCommandCb callback,
                                 void *context)
 {
@@ -129,7 +108,6 @@ void silc_client_command_pending(SilcClientConnection conn,
   reply->ident = ident;
   reply->context = context;
   reply->callback = callback;
-  reply->destructor = destructor;
   silc_dlist_add(conn->pending_commands, reply);
 }
 
@@ -137,46 +115,56 @@ void silc_client_command_pending(SilcClientConnection conn,
 
 void silc_client_command_pending_del(SilcClientConnection conn,
                                     SilcCommand reply_cmd,
-                                    uint16 ident)
+                                    SilcUInt16 ident)
 {
   SilcClientCommandPending *r;
 
+  if (!conn->pending_commands)
+    return;
+
   silc_dlist_start(conn->pending_commands);
   while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
     if (r->reply_cmd == reply_cmd && r->ident == ident) {
       silc_dlist_del(conn->pending_commands, r);
+      silc_free(r);
       break;
     }
   }
 }
 
 /* Checks for pending commands and marks callbacks to be called from
-   the command reply function. Returns TRUE if there were pending command. */
-
-int silc_client_command_pending_check(SilcClientConnection conn,
-                                     SilcClientCommandReplyContext ctx,
-                                     SilcCommand command, 
-                                     uint16 ident)
+   the command reply function. */
+
+SilcClientCommandPendingCallbacks
+silc_client_command_pending_check(SilcClientConnection conn,
+                                 SilcClientCommandReplyContext ctx,
+                                 SilcCommand command, 
+                                 SilcUInt16 ident,
+                                 SilcUInt32 *callbacks_count)
 {
   SilcClientCommandPending *r;
+  SilcClientCommandPendingCallbacks callbacks = NULL;
+  int i = 0;
 
   silc_dlist_start(conn->pending_commands);
   while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
-    if (r->reply_cmd == command && r->ident == ident) {
-      ctx->context = r->context;
-      ctx->callback = r->callback;
-      ctx->destructor = r->destructor;
+    if ((r->reply_cmd == command || r->reply_cmd == SILC_COMMAND_NONE)
+       && r->ident == ident) {
+      callbacks = silc_realloc(callbacks, sizeof(*callbacks) * (i + 1));
+      callbacks[i].context = r->context;
+      callbacks[i].callback = r->callback;
       ctx->ident = ident;
-      return TRUE;
+      i++;
     }
   }
 
-  return FALSE;
+  *callbacks_count = i;
+  return callbacks;
 }
 
 /* Allocate Command Context */
 
-SilcClientCommandContext silc_client_command_alloc()
+SilcClientCommandContext silc_client_command_alloc(void)
 {
   SilcClientCommandContext ctx = silc_calloc(1, sizeof(*ctx));
   ctx->users++;
@@ -212,13 +200,6 @@ SilcClientCommandContext silc_client_command_dup(SilcClientCommandContext ctx)
   return ctx;
 }
 
-/* Pending command destructor. */
-
-static void silc_client_command_destructor(void *context)
-{
-  silc_client_command_free((SilcClientCommandContext)context);
-}
-
 /* Command WHOIS. This command is used to query information about 
    specific user. */
 
@@ -227,37 +208,45 @@ SILC_CLIENT_CMD_FUNC(whois)
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
   SilcBuffer buffer;
+  unsigned char count[4];
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
   /* 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);
+                            1, 4, buffer->data, buffer->len);
     silc_buffer_free(buffer);
     goto out;
   }
 
-  buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS,
-                                      cmd->argc - 1, ++cmd->argv,
-                                      ++cmd->argv_lens, ++cmd->argv_types,
-                                      0);
+  if (cmd->argc == 2) {
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
+                                           ++conn->cmd_ident, 1,
+                                           1, cmd->argv[1], 
+                                           cmd->argv_lens[1]);
+  } else {
+    int c = atoi(cmd->argv[2]);
+    memset(count, 0, sizeof(count));
+    SILC_PUT32_MSB(c, count);
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
+                                           ++conn->cmd_ident, 2,
+                                           1, cmd->argv[1], cmd->argv_lens[1],
+                                           2, count, sizeof(count));
+  }
   silc_client_packet_send(cmd->client, cmd->conn->sock,
                          SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
                          buffer->data, buffer->len, TRUE);
   silc_buffer_free(buffer);
-  cmd->argv--;
-  cmd->argv_lens--;
-  cmd->argv_types--;
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
  out:
   silc_client_command_free(cmd);
@@ -271,80 +260,91 @@ SILC_CLIENT_CMD_FUNC(whowas)
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
   SilcBuffer buffer;
+  unsigned char count[4];
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
   if (cmd->argc < 2 || cmd->argc > 3) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-            "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
-    COMMAND_ERROR;
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
+    COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
+                  SILC_STATUS_ERR_TOO_MANY_PARAMS));
     goto out;
   }
 
-  buffer = silc_command_payload_encode(SILC_COMMAND_WHOWAS,
-                                      cmd->argc - 1, ++cmd->argv,
-                                      ++cmd->argv_lens, ++cmd->argv_types,
-                                      0);
+  if (cmd->argc == 2) {
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOWAS,
+                                           ++conn->cmd_ident, 1,
+                                           1, cmd->argv[1], 
+                                           cmd->argv_lens[1]);
+  } else {
+    int c = atoi(cmd->argv[2]);
+    memset(count, 0, sizeof(count));
+    SILC_PUT32_MSB(c, count);
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOWAS,
+                                           ++conn->cmd_ident, 2,
+                                           1, cmd->argv[1], cmd->argv_lens[1],
+                                           2, count, sizeof(count));
+  }
   silc_client_packet_send(cmd->client, cmd->conn->sock,
                          SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
                          buffer->data, buffer->len, TRUE);
   silc_buffer_free(buffer);
-  cmd->argv--;
-  cmd->argv_lens--;
-  cmd->argv_types--;
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
  out:
   silc_client_command_free(cmd);
 }
 
 /* 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)
 {
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
   SilcBuffer buffer;
+  unsigned char count[4];
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
-  if (cmd->argc < 2 || cmd->argc > 3) {
-    COMMAND_ERROR;
+  if (cmd->argc < 2 || cmd->argc > 3)
     goto out;
-  }
 
-  if (cmd->argc == 2)
+  if (cmd->argc == 2) {
     buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY, 
                                            ++conn->cmd_ident, 1,
                                            1, cmd->argv[1],
                                            cmd->argv_lens[1]);
-  else
+  } else {
+    int c = atoi(cmd->argv[2]);
+    memset(count, 0, sizeof(count));
+    SILC_PUT32_MSB(c, count);
     buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY, 
                                            ++conn->cmd_ident, 2,
                                            1, cmd->argv[1],
                                            cmd->argv_lens[1],
-                                           4, cmd->argv[2],
-                                           cmd->argv_lens[2]);
+                                           4, count, sizeof(count));
+  }
 
   silc_client_packet_send(cmd->client, cmd->conn->sock,
                          SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
                          buffer->data, buffer->len, TRUE);
   silc_buffer_free(buffer);
 
-  /* Notify application */
-  COMMAND;
-
  out:
   silc_client_command_free(cmd);
 }
@@ -360,14 +360,14 @@ SILC_CLIENT_CMD_FUNC(nick)
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                         "Usage: /NICK <nickname>");
-    COMMAND_ERROR;
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /NICK <nickname>");
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
@@ -377,22 +377,22 @@ 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, 
-                           "Your nickname is %s on server %s", 
-                           conn->nickname, conn->remote_host);
+      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, 
-                           "Your nickname is %s", conn->nickname);
+      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         "Your nickname is %s", conn->nickname);
     }
 
-    COMMAND;
+    COMMAND(SILC_STATUS_OK);
     goto out;
   }
 
   if (cmd->argv_lens[1] > 128)
     cmd->argv_lens[1] = 128;
 
-  /* Set new nickname */
+  /* Send the NICK command */
   buffer = silc_command_payload_encode(SILC_COMMAND_NICK, 1,
                                       &cmd->argv[1],
                                       &cmd->argv_lens[1], 
@@ -402,12 +402,6 @@ SILC_CLIENT_CMD_FUNC(nick)
                          SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
                          buffer->data, buffer->len, TRUE);
   silc_buffer_free(buffer);
-  if (conn->nickname)
-    silc_free(conn->nickname);
-  conn->nickname = strdup(cmd->argv[1]);
-
-  /* Notify application */
-  COMMAND;
 
  out:
   silc_client_command_free(cmd);
@@ -426,7 +420,7 @@ SILC_CLIENT_CMD_FUNC(list)
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
@@ -456,7 +450,7 @@ SILC_CLIENT_CMD_FUNC(list)
     silc_buffer_free(idp);
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
  out:
   silc_client_command_free(cmd);
@@ -475,22 +469,21 @@ SILC_CLIENT_CMD_FUNC(topic)
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
   if (cmd->argc < 2 || cmd->argc > 3) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
-                         "Usage: /TOPIC <channel> [<topic>]");
-    COMMAND_ERROR;
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+       "Usage: /TOPIC <channel> [<topic>]");
+    COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
+                  SILC_STATUS_ERR_TOO_MANY_PARAMS));
     goto out;
   }
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                           "You are not on any channel");
-      COMMAND_ERROR;
+      COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
     name = conn->current_channel->channel_name;
@@ -499,17 +492,13 @@ SILC_CLIENT_CMD_FUNC(topic)
   }
 
   if (!conn->current_channel) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                         "You are not on that channel");
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
   }
 
   /* 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, 
-                         "You are not on that channel");
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
   }
 
@@ -533,7 +522,7 @@ SILC_CLIENT_CMD_FUNC(topic)
   silc_buffer_free(idp);
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
  out:
   silc_client_command_free(cmd);
@@ -550,29 +539,27 @@ SILC_CLIENT_CMD_FUNC(invite)
   SilcClientEntry client_entry = NULL;
   SilcChannelEntry channel;
   SilcBuffer buffer, clidp, chidp;
-  uint32 type = 0;
+  SilcUInt32 type = 0;
   char *nickname = NULL, *name;
   char *invite = NULL;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
-                  "Usage: /INVITE <channel> [<nickname>[@server>]"
-                  "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
-    COMMAND_ERROR;
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+       "Usage: /INVITE <channel> [<nickname>[@server>]"
+       "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                           "You are not on any channel");
-      COMMAND_ERROR;
+      COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
 
@@ -582,9 +569,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, 
-                           "You are on that channel");
-      COMMAND_ERROR;
+      COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
   }
@@ -592,8 +577,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]);
 
@@ -602,20 +587,18 @@ SILC_CLIENT_CMD_FUNC(invite)
                                            cmd->argv[2], TRUE);
       if (!client_entry) {
        if (cmd->pending) {
-         COMMAND_ERROR;
+         COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
          goto out;
        }
-       silc_free(nickname);
       
        /* Client entry not found, it was requested thus mark this to be
           pending command. */
        silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
                                    conn->cmd_ident,
-                                   silc_client_command_destructor,
                                    silc_client_command_invite, 
                                    silc_client_command_dup(cmd));
        cmd->pending = 1;
-       return;
+       goto out;
       }
     } else {
       invite = cmd->argv[2];
@@ -652,7 +635,7 @@ SILC_CLIENT_CMD_FUNC(invite)
   silc_buffer_free(chidp);
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
  out:
   silc_free(nickname);
@@ -669,8 +652,8 @@ SILC_TASK_CALLBACK(silc_client_command_quit_cb)
   QuitInternal q = (QuitInternal)context;
 
   /* Close connection */
-  q->client->ops->disconnect(q->client, q->conn);
-  silc_client_close_connection(q->client, NULL, q->conn->sock->user_data);
+  q->client->internal->ops->disconnect(q->client, q->conn, 0, NULL);
+  silc_client_close_connection(q->client, q->conn->sock->user_data);
 
   silc_free(q);
 }
@@ -685,7 +668,7 @@ SILC_CLIENT_CMD_FUNC(quit)
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
@@ -714,7 +697,7 @@ SILC_CLIENT_CMD_FUNC(quit)
                         1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
  out:
   silc_client_command_free(cmd);
@@ -731,18 +714,17 @@ 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]);
 
   /* Get the target client */
   target = silc_idlist_get_client(cmd->client, conn, nickname, 
                                  cmd->argv[1], FALSE);
-  if (target) {
-    silc_client_remove_from_channels(client, conn, target);
+  if (target)
+    /* Remove the client from all channels and free it */
     silc_client_del_client(client, conn, target);
-  }
 
   silc_free(nickname);
   silc_client_command_free(cmd);
@@ -756,9 +738,9 @@ SILC_CLIENT_CMD_FUNC(kill_remove)
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientCommandReplyContext reply = 
     (SilcClientCommandReplyContext)context2;
-  SilcCommandStatus status;
+  SilcStatus status;
 
-  SILC_GET16_MSB(status, silc_argument_get_arg_type(reply->args, 1, NULL));
+  silc_command_get_status(reply->payload, &status, NULL);
   if (status == SILC_STATUS_OK) {
     /* Remove with timeout */
     silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
@@ -784,20 +766,20 @@ SILC_CLIENT_CMD_FUNC(kill)
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                         "Usage: /KILL <nickname> [<comment>]");
-    COMMAND_ERROR;
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /KILL <nickname> [<comment>]");
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     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]);
 
@@ -806,21 +788,18 @@ SILC_CLIENT_CMD_FUNC(kill)
                                  cmd->argv[1], TRUE);
   if (!target) {
     if (cmd->pending) {
-      COMMAND_ERROR;
+      COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
       goto out;
     }
 
-    silc_free(nickname);
-
     /* Client entry not found, it was requested thus mark this to be
        pending command. */
     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
                                conn->cmd_ident,  
-                               silc_client_command_destructor,
                                silc_client_command_kill, 
                                silc_client_command_dup(cmd));
     cmd->pending = 1;
-    return;
+    goto out;
   }
 
   /* Send the KILL command to the server */
@@ -841,12 +820,12 @@ SILC_CLIENT_CMD_FUNC(kill)
   silc_buffer_free(idp);
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
   /* Register a pending callback that will actually remove the killed
      client from our cache. */
   silc_client_command_pending(conn, SILC_COMMAND_KILL, conn->cmd_ident,
-                             NULL, silc_client_command_kill_remove,
+                             silc_client_command_kill_remove,
                              silc_client_command_dup(cmd));
 
  out:
@@ -866,7 +845,7 @@ SILC_CLIENT_CMD_FUNC(info)
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
@@ -887,7 +866,7 @@ SILC_CLIENT_CMD_FUNC(info)
     silc_free(name);
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
  out:
   silc_client_command_free(cmd);
@@ -906,7 +885,7 @@ SILC_CLIENT_CMD_FUNC(ping)
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
@@ -923,7 +902,7 @@ SILC_CLIENT_CMD_FUNC(ping)
                      SILC_ID_SERVER);
   if (!id) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
@@ -933,7 +912,6 @@ SILC_CLIENT_CMD_FUNC(ping)
       conn->ping[i].start_time = time(NULL);
       conn->ping[i].dest_id = id;
       conn->ping[i].dest_name = strdup(conn->remote_host);
-      conn->ping_count++;
       break;
     }
   }
@@ -947,7 +925,7 @@ SILC_CLIENT_CMD_FUNC(ping)
   }
   
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
  out:
   silc_client_command_free(cmd);
@@ -959,56 +937,86 @@ SILC_CLIENT_CMD_FUNC(join)
 {
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
-  SilcIDCacheEntry id_cache = NULL;
-  SilcBuffer buffer, idp;
+  SilcChannelEntry channel;
+  SilcBuffer buffer, idp, auth = NULL;
+  char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
+  int i, passphrase_len = 0;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     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)) {
-    SilcChannelEntry channel = (SilcChannelEntry)id_cache->context;
-    if (channel->on_channel)
-      goto out;
+  if (cmd->argc < 2) {
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
   }
+  
+  /* See if we have joined to the requested channel already */
+  channel = silc_client_get_channel(cmd->client, conn, cmd->argv[1]);
+  if (channel && silc_client_on_channel(channel, conn->local_entry))
+    goto out;
 
   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
 
   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")) {
+      auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
+                                               cmd->client->private_key,
+                                               cmd->client->rng, 
+                                               cmd->client->internal->
+                                               sha1hash,
+                                               conn->local_id,
+                                               SILC_ID_CLIENT);
+      i++;
+    } else {
+      /* Passphrases must be UTF-8 encoded, so encode if it is not */
+      if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
+       passphrase_len = silc_utf8_encoded_len(cmd->argv[i], 
+                                              cmd->argv_lens[i], 0);
+       pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
+       passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
+                                         0, pu8, passphrase_len);
+       passphrase = pu8;
+      } else {
+       passphrase = strdup(cmd->argv[i]);
+       passphrase_len = cmd->argv_lens[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_len,
+                                  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);
+  silc_free(passphrase);
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
  out:
   silc_client_command_free(cmd);
@@ -1024,14 +1032,15 @@ SILC_CLIENT_CMD_FUNC(motd)
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
   if (cmd->argc < 1 || cmd->argc > 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
-                         "Usage: /MOTD [<server>]");
-    COMMAND_ERROR;
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+       "Usage: /MOTD [<server>]");
+    COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
+                  SILC_STATUS_ERR_TOO_MANY_PARAMS));
     goto out;
   }
 
@@ -1049,7 +1058,7 @@ SILC_CLIENT_CMD_FUNC(motd)
   silc_buffer_free(buffer);
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
  out:
   silc_client_command_free(cmd);
@@ -1064,19 +1073,19 @@ SILC_CLIENT_CMD_FUNC(umode)
   SilcClientConnection conn = cmd->conn;
   SilcBuffer buffer, idp;
   unsigned char *cp, modebuf[4];
-  uint32 mode, add, len;
+  SilcUInt32 mode, add, len;
   int i;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                 "Usage: /UMODE +|-<modes>");
-    COMMAND_ERROR;
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /UMODE +|-<modes>");
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
@@ -1098,6 +1107,14 @@ SILC_CLIENT_CMD_FUNC(umode)
        mode = 0;
        mode |= SILC_UMODE_SERVER_OPERATOR;
        mode |= SILC_UMODE_ROUTER_OPERATOR;
+       mode |= SILC_UMODE_GONE;
+       mode |= SILC_UMODE_INDISPOSED;
+       mode |= SILC_UMODE_BUSY;
+       mode |= SILC_UMODE_PAGE;
+       mode |= SILC_UMODE_HYPER;
+       mode |= SILC_UMODE_ROBOT;
+       mode |= SILC_UMODE_BLOCK_PRIVMSG;
+       mode |= SILC_UMODE_REJECT_WATCHING;
       } else {
        mode = SILC_UMODE_NONE;
       }
@@ -1120,8 +1137,56 @@ SILC_CLIENT_CMD_FUNC(umode)
       else
        mode &= ~SILC_UMODE_GONE;
       break;
+    case 'i':
+      if (add)
+       mode |= SILC_UMODE_INDISPOSED;
+      else
+       mode &= ~SILC_UMODE_INDISPOSED;
+      break;
+    case 'b':
+      if (add)
+       mode |= SILC_UMODE_BUSY;
+      else
+       mode &= ~SILC_UMODE_BUSY;
+      break;
+    case 'p':
+      if (add)
+       mode |= SILC_UMODE_PAGE;
+      else
+       mode &= ~SILC_UMODE_PAGE;
+      break;
+    case 'h':
+      if (add)
+       mode |= SILC_UMODE_HYPER;
+      else
+       mode &= ~SILC_UMODE_HYPER;
+      break;
+    case 't':
+      if (add)
+       mode |= SILC_UMODE_ROBOT;
+      else
+       mode &= ~SILC_UMODE_ROBOT;
+      break;
+    case 'P':
+      if (add)
+       mode |= SILC_UMODE_BLOCK_PRIVMSG;
+      else
+       mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
+      break;
+    case 'w':
+      if (add)
+       mode |= SILC_UMODE_REJECT_WATCHING;
+      else
+       mode &= ~SILC_UMODE_REJECT_WATCHING;
+      break;
+    case 'I':
+      if (add)
+       mode |= SILC_UMODE_BLOCK_INVITE;
+      else
+       mode &= ~SILC_UMODE_BLOCK_INVITE;
+      break;
     default:
-      COMMAND_ERROR;
+      COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
       goto out;
       break;
     }
@@ -1142,7 +1207,7 @@ SILC_CLIENT_CMD_FUNC(umode)
   silc_buffer_free(idp);
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
  out:
   silc_client_command_free(cmd);
@@ -1159,27 +1224,25 @@ SILC_CLIENT_CMD_FUNC(cmode)
   SilcChannelEntry channel;
   SilcBuffer buffer, chidp, auth = NULL;
   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
-  uint32 mode, add, type, len, arg_len = 0;
+  SilcUInt32 mode, add, type, len, arg_len = 0;
   int i;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
   if (cmd->argc < 3) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
-    COMMAND_ERROR;
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                           "You are not on any channel");
-      COMMAND_ERROR;
+      COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
 
@@ -1189,9 +1252,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, 
-                           "You are on that channel");
-      COMMAND_ERROR;
+      COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
   }
@@ -1242,15 +1303,27 @@ SILC_CLIENT_CMD_FUNC(cmode)
       else
        mode &= ~SILC_CHANNEL_MODE_TOPIC;
       break;
+    case 'm':
+      if (add)
+       mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
+      else
+       mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
+      break;
+    case 'M':
+      if (add)
+       mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
+      else
+       mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
+      break;
     case 'l':
       if (add) {
        int ll;
        mode |= SILC_CHANNEL_MODE_ULIMIT;
        type = 3;
        if (cmd->argc < 4) {
-         cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-              "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
-         COMMAND_ERROR;
+         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+             "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
+         COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
          goto out;
        }
        ll = atoi(cmd->argv[3]);
@@ -1266,9 +1339,9 @@ 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, 
-              "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
-         COMMAND_ERROR;
+         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+             "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
+         COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
          goto out;
        }
        arg = cmd->argv[3];
@@ -1282,9 +1355,9 @@ 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, 
-              "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
-         COMMAND_ERROR;
+         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+             "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
+         COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
          goto out;
        }
        arg = cmd->argv[3];
@@ -1298,9 +1371,9 @@ 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, 
-              "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
-         COMMAND_ERROR;
+         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+             "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
+         COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
          goto out;
        }
        arg = cmd->argv[3];
@@ -1313,25 +1386,13 @@ SILC_CLIENT_CMD_FUNC(cmode)
       if (add) {
        mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
        type = 7;
-
-       if (cmd->argc < 4) {
-         cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-              "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
-         COMMAND_ERROR;
-         goto out;
-       }
-
-       if (!strcasecmp(cmd->argv[3], "-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[3], cmd->argv_lens[3]);
-       }
-
+       auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
+                                                 cmd->client->private_key,
+                                                 cmd->client->rng, 
+                                                 cmd->client->internal->
+                                                 sha1hash,
+                                                 conn->local_id,
+                                                 SILC_ID_CLIENT);
        arg = auth->data;
        arg_len = auth->len;
       } else {
@@ -1339,7 +1400,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
       }
       break;
     default:
-      COMMAND_ERROR;
+      COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
       goto out;
       break;
     }
@@ -1371,7 +1432,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
     silc_buffer_free(auth);
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
  out:
   silc_client_command_free(cmd);
@@ -1389,28 +1450,26 @@ SILC_CLIENT_CMD_FUNC(cumode)
   SilcClientEntry client_entry;
   SilcBuffer buffer, clidp, chidp, auth = NULL;
   unsigned char *name, *cp, modebuf[4];
-  uint32 mode = 0, add, len;
+  SilcUInt32 mode = 0, add, len;
   char *nickname = NULL;
   int i;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
   if (cmd->argc < 4) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
-    COMMAND_ERROR;
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                           "You are not on any channel");
-      COMMAND_ERROR;
+      COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
 
@@ -1420,16 +1479,14 @@ 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, 
-                           "You are on that channel");
-      COMMAND_ERROR;
+      COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
   }
 
   /* 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]);
 
@@ -1438,31 +1495,24 @@ SILC_CLIENT_CMD_FUNC(cumode)
                                        cmd->argv[3], TRUE);
   if (!client_entry) {
     if (cmd->pending) {
-      COMMAND_ERROR;
+      COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
       goto out;
     }
 
-    silc_free(nickname);
-
     /* Client entry not found, it was requested thus mark this to be
        pending command. */
     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
                                conn->cmd_ident,  
-                               silc_client_command_destructor,
                                silc_client_command_cumode, 
                                silc_client_command_dup(cmd));
     cmd->pending = 1;
-    return;
+    goto out;
   }
   
   /* Get the current mode */
-  silc_list_start(channel->clients);
-  while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
-    if (chu->client == client_entry) {
-      mode = chu->mode;
-      break;
-    }
-  }
+  chu = silc_client_on_channel(channel, client_entry);
+  if (chu)
+    mode = chu->mode;
 
   /* Are we adding or removing mode */
   if (cmd->argv[2][0] == '-')
@@ -1479,24 +1529,22 @@ SILC_CLIENT_CMD_FUNC(cumode)
       if (add) {
        mode |= SILC_CHANNEL_UMODE_CHANFO;
        mode |= SILC_CHANNEL_UMODE_CHANOP;
+       mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
+       mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
+       mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
       } else {
        mode = SILC_CHANNEL_UMODE_NONE;
       }
       break;
     case 'f':
       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);
-         } else {
-           auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
-                                           cmd->argv[4], cmd->argv_lens[4]);
-         }
-       }
+       auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
+                                                 cmd->client->private_key,
+                                                 cmd->client->rng,
+                                                 cmd->client->internal->
+                                                 sha1hash,
+                                                 conn->local_id,
+                                                 SILC_ID_CLIENT);
        mode |= SILC_CHANNEL_UMODE_CHANFO;
       } else {
        mode &= ~SILC_CHANNEL_UMODE_CHANFO;
@@ -1508,8 +1556,32 @@ SILC_CLIENT_CMD_FUNC(cumode)
       else
        mode &= ~SILC_CHANNEL_UMODE_CHANOP;
       break;
+    case 'b':
+      if (add)
+       mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
+      else
+       mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
+      break;
+    case 'u':
+      if (add)
+       mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
+      else
+       mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
+      break;
+    case 'r':
+      if (add)
+       mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
+      else
+       mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
+      break;
+    case 'q':
+      if (add)
+       mode |= SILC_CHANNEL_UMODE_QUIET;
+      else
+       mode &= ~SILC_CHANNEL_UMODE_QUIET;
+      break;
     default:
-      COMMAND_ERROR;
+      COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
       goto out;
       break;
     }
@@ -1538,7 +1610,7 @@ SILC_CLIENT_CMD_FUNC(cumode)
     silc_buffer_free(auth);
   
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
  out:
   silc_free(nickname);
@@ -1561,22 +1633,20 @@ SILC_CLIENT_CMD_FUNC(kick)
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
   if (cmd->argc < 3) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                         "Usage: /KICK <channel> <nickname> [<comment>]");
-    COMMAND_ERROR;
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /KICK <channel> <nickname> [<comment>]");
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                           "You are not on any channel");
-      COMMAND_ERROR;
+      COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
     name = conn->current_channel->channel_name;
@@ -1585,25 +1655,21 @@ SILC_CLIENT_CMD_FUNC(kick)
   }
 
   if (!conn->current_channel) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                         "You are not on that channel");
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
   }
 
   /* 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, 
-                         "You are not on that channel");
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
   }
 
   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]);
 
@@ -1611,10 +1677,9 @@ 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, 
-                         "No such client: %s",
-                         cmd->argv[2]);
-    COMMAND_ERROR;
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "No such client: %s", cmd->argv[2]);
+    COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
     goto out;
   }
 
@@ -1638,7 +1703,7 @@ SILC_CLIENT_CMD_FUNC(kick)
   silc_buffer_free(idp2);
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
  out:
   silc_free(nickname);
@@ -1646,7 +1711,7 @@ SILC_CLIENT_CMD_FUNC(kick)
 }
 
 static void silc_client_command_oper_send(unsigned char *data,
-                                         uint32 data_len, void *context)
+                                         SilcUInt32 data_len, void *context)
 {
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
@@ -1656,7 +1721,7 @@ static void silc_client_command_oper_send(unsigned char *data,
     /* Encode the public key authentication payload */
     auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
                                              cmd->client->private_key,
-                                             conn->hash,
+                                             cmd->client->rng, conn->hash,
                                              conn->local_id,
                                              SILC_ID_CLIENT);
   } else {
@@ -1668,7 +1733,8 @@ static void silc_client_command_oper_send(unsigned char *data,
   buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
                                          1, cmd->argv[1], 
                                          strlen(cmd->argv[1]),
-                                         2, auth->data, auth->len);
+                                         2, 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);
 
@@ -1676,7 +1742,7 @@ static void silc_client_command_oper_send(unsigned char *data,
   silc_buffer_free(auth);
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 }
 
 /* OPER command. Used to obtain server operator privileges. */
@@ -1688,20 +1754,20 @@ SILC_CLIENT_CMD_FUNC(oper)
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                         "Usage: /OPER <username> [-pubkey]");
-    COMMAND_ERROR;
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /OPER <username> [-pubkey]");
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
   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;
@@ -1714,7 +1780,8 @@ SILC_CLIENT_CMD_FUNC(oper)
 }
 
 static void silc_client_command_silcoper_send(unsigned char *data,
-                                             uint32 data_len, void *context)
+                                             SilcUInt32 data_len, 
+                                             void *context)
 {
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
@@ -1724,7 +1791,7 @@ static void silc_client_command_silcoper_send(unsigned char *data,
     /* Encode the public key authentication payload */
     auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
                                              cmd->client->private_key,
-                                             conn->hash,
+                                             cmd->client->rng, conn->hash,
                                              conn->local_id,
                                              SILC_ID_CLIENT);
   } else {
@@ -1736,7 +1803,8 @@ static void silc_client_command_silcoper_send(unsigned char *data,
   buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
                                          1, cmd->argv[1], 
                                          strlen(cmd->argv[1]),
-                                         2, auth->data, auth->len);
+                                         2, 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);
 
@@ -1744,7 +1812,7 @@ static void silc_client_command_silcoper_send(unsigned char *data,
   silc_buffer_free(auth);
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 }
 
 /* SILCOPER command. Used to obtain router operator privileges. */
@@ -1756,20 +1824,20 @@ SILC_CLIENT_CMD_FUNC(silcoper)
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                         "Usage: /SILCOPER <username> [-pubkey]");
-    COMMAND_ERROR;
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /SILCOPER <username> [-pubkey]");
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
   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;
@@ -1781,54 +1849,6 @@ SILC_CLIENT_CMD_FUNC(silcoper)
   silc_client_command_free(cmd);
 }
 
-/* CONNECT command. Connects the server to another server. */
-
-SILC_CLIENT_CMD_FUNC(connect)
-{
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-  SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer;
-  unsigned char port[4];
-  uint32 tmp;
-
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
-    goto out;
-  }
-
-  if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                         "Usage: /CONNECT <server> [<port>]");
-    COMMAND_ERROR;
-    goto out;
-  }
-
-  if (cmd->argc == 3) {
-    tmp = atoi(cmd->argv[2]);
-    SILC_PUT32_MSB(tmp, port);
-  }
-
-  if (cmd->argc == 3)
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2, 
-                                           1, cmd->argv[1], 
-                                           strlen(cmd->argv[1]),
-                                           2, port, 4);
-  else
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
-                                           1, cmd->argv[1], 
-                                           strlen(cmd->argv[1]));
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
-
-  /* Notify application */
-  COMMAND;
-
- out:
-  silc_client_command_free(cmd);
-}
-
 /* Command BAN. This is used to manage the ban list of the channel. */
 
 SILC_CLIENT_CMD_FUNC(ban)
@@ -1842,23 +1862,21 @@ SILC_CLIENT_CMD_FUNC(ban)
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                  "Usage: /BAN <channel> "
-                  "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
-    COMMAND_ERROR;
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /BAN <channel> "
+       "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                           "You are not on any channel");
-      COMMAND_ERROR;
+      COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
 
@@ -1868,9 +1886,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, 
-                           "You are on that channel");
-      COMMAND_ERROR;
+      COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
   }
@@ -1888,94 +1904,95 @@ SILC_CLIENT_CMD_FUNC(ban)
   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
 
   /* Send the command */
-  if (ban)
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 2, 
-                                           1, chidp->data, chidp->len,
-                                           type, ban, strlen(ban));
-  else
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 1, 
-                                           1, chidp->data, chidp->len);
-
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 
+                                         ++conn->cmd_ident, 2,
+                                         1, chidp->data, chidp->len,
+                                         type, ban, ban ? strlen(ban) : 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(chidp);
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
  out:
   silc_client_command_free(cmd);
 }
 
-/* CLOSE command. Close server connection to the remote server */
-SILC_CLIENT_CMD_FUNC(close)
+/* Command DETACH. This is used to detach from the server */
+
+SILC_CLIENT_CMD_FUNC(detach)
 {
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
   SilcBuffer buffer;
-  unsigned char port[4];
-  uint32 tmp;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
-    goto out;
-  }
-
-  if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                         "Usage: /CLOSE <server> [<port>]");
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
-  if (cmd->argc == 3) {
-    tmp = atoi(cmd->argv[2]);
-    SILC_PUT32_MSB(tmp, port);
-  }
-
-  if (cmd->argc == 3)
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2, 
-                                           1, cmd->argv[1], 
-                                           strlen(cmd->argv[1]),
-                                           2, port, 4);
-  else
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
-                                           1, cmd->argv[1], 
-                                           strlen(cmd->argv[1]));
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_DETACH,
+                                         ++conn->cmd_ident, 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);
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
  out:
   silc_client_command_free(cmd);
 }
-/* SHUTDOWN command. Shutdowns the server. */
 
-SILC_CLIENT_CMD_FUNC(shutdown)
+/* Command WATCH. */
+
+SILC_CLIENT_CMD_FUNC(watch)
 {
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer, idp = NULL;
+  int type = 0;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
-  /* Send the command */
-  silc_client_send_command(cmd->client, cmd->conn, 
-                          SILC_COMMAND_SHUTDOWN, 0, 0);
+  if (cmd->argc < 3) {
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
+
+  if (!strcasecmp(cmd->argv[1], "-add")) {
+    type = 2;
+  } else if (!strcasecmp(cmd->argv[1], "-del")) {
+    type = 3;
+  } else {
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_WATCH, 
+                                         ++conn->cmd_ident, 2,
+                                         1, idp->data, idp->len,
+                                         type, cmd->argv[2],
+                                         cmd->argv_lens[2]);
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
  out:
+  if (idp)
+    silc_buffer_free(idp);
   silc_client_command_free(cmd);
 }
 
@@ -1985,29 +2002,27 @@ SILC_CLIENT_CMD_FUNC(leave)
 {
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
-  SilcIDCacheEntry id_cache = NULL;
   SilcChannelEntry channel;
+  SilcChannelUser chu;
   SilcBuffer buffer, idp;
   char *name;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
   if (cmd->argc != 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                         "Usage: /LEAVE <channel>");
-    COMMAND_ERROR;
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /LEAVE <channel>");
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                           "You are not on any channel");
-      COMMAND_ERROR;
+      COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
     name = conn->current_channel->channel_name;
@@ -2015,19 +2030,23 @@ SILC_CLIENT_CMD_FUNC(leave)
     name = cmd->argv[1];
   }
 
-  /* 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, 
-                         "You are not on that channel");
-    COMMAND_ERROR;
+  /* Get the channel entry */
+  channel = silc_client_get_channel(cmd->client, conn, name);
+  if (!channel) {
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
   }
 
-  channel = (SilcChannelEntry)id_cache->context;
-  channel->on_channel = FALSE;
+  /* Remove us from channel */
+  chu = silc_client_on_channel(channel, conn->local_entry);
+  if (chu) {
+    silc_hash_table_del(chu->client->channels, chu->channel);
+    silc_hash_table_del(chu->channel->user_list, chu->client);
+    silc_free(chu);
+  }
 
   /* Send LEAVE command to the server */
-  idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
+  idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
                                          1, idp->data, idp->len);
   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
@@ -2036,7 +2055,7 @@ SILC_CLIENT_CMD_FUNC(leave)
   silc_buffer_free(idp);
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
   if (conn->current_channel == channel)
     conn->current_channel = NULL;
@@ -2059,22 +2078,20 @@ SILC_CLIENT_CMD_FUNC(users)
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
   if (cmd->argc != 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                         "Usage: /USERS <channel>");
-    COMMAND_ERROR;
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /USERS <channel>");
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                           "You are not on any channel");
-      COMMAND_ERROR;
+      COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
     name = conn->current_channel->channel_name;
@@ -2092,7 +2109,7 @@ SILC_CLIENT_CMD_FUNC(users)
   silc_buffer_free(buffer);
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
  out:
   silc_client_command_free(cmd);
@@ -2110,39 +2127,24 @@ SILC_CLIENT_CMD_FUNC(getkey)
   char *nickname = NULL;
   SilcBuffer idp, buffer;
 
+  SILC_LOG_DEBUG(("Start"));
+
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
     goto out;
   }
 
   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;
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
-  if (cmd->pending) {
-    SilcClientCommandReplyContext reply = 
-      (SilcClientCommandReplyContext)context2;
-    SilcCommandStatus status;
-    unsigned char *tmp = silc_argument_get_arg_type(reply->args, 1, NULL);
-    SILC_GET16_MSB(status, tmp);
-    
-    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,
-                           "%s", 
-                           silc_client_command_status_message(status));
-      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]);
 
@@ -2154,35 +2156,56 @@ SILC_CLIENT_CMD_FUNC(getkey)
     server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
 
     if (!server_entry) {
-      if (cmd->pending) {
-       COMMAND_ERROR;
+      /* No. what ever user wants we don't have it, so resolve it. We
+        will first try to resolve the client, and if that fails then
+        we'll try to resolve the server. */
+
+      if (!cmd->pending) {
+       /* This will send the IDENTIFY command for nickname */
+       silc_idlist_get_client(client, conn, nickname, cmd->argv[1], TRUE);
+       silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
+                                   conn->cmd_ident,  
+                                   silc_client_command_getkey, 
+                                   silc_client_command_dup(cmd));
+       cmd->pending = 1;
        goto out;
-      }
+      } else {
+       SilcClientCommandReplyContext reply = 
+         (SilcClientCommandReplyContext)context2;
+       SilcStatus error;
+
+       /* If nickname was not found, then resolve the server. */
+       silc_command_get_status(reply->payload, NULL, &error);
+       if (error == SILC_STATUS_ERR_NO_SUCH_NICK) {
+         /* This sends the IDENTIFY command to resolve the server. */
+         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, 
+                                     silc_client_command_getkey, 
+                                     silc_client_command_dup(cmd));
+         goto out;
+       }
 
-      /* No. what ever user wants we don't have it, so resolve it. We
-        will try to resolve both client and server, one of them is
-        bound to be wrong. */
-
-      /* This will send the IDENTIFY command */
-      silc_idlist_get_client(client, conn, nickname, cmd->argv[1], TRUE);
-      silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
-                                 conn->cmd_ident,  
-                                 silc_client_command_destructor,
-                                 silc_client_command_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, 
-                              2, cmd->argv[1], cmd->argv_lens[1]);
-      silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
-                                 conn->cmd_ident, NULL,
-                                 silc_client_command_getkey, 
-                                 silc_client_command_dup(cmd));
-
-      cmd->pending = 1;
-      silc_free(nickname);
-      return;
+       /* If server was not found, then we've resolved both nickname and
+          server and did not find anybody. */
+       if (error == SILC_STATUS_ERR_NO_SUCH_SERVER) {
+         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", 
+            silc_get_status_message(SILC_STATUS_ERR_NO_SUCH_NICK));
+         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", 
+           silc_get_status_message(error));
+         COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
+         goto out;
+       }
+
+       COMMAND_ERROR(error);
+       goto out;
+      }
     }
 
     idp = silc_id_payload_encode(server_entry->server_id, SILC_ID_SERVER);
@@ -2198,9 +2221,268 @@ SILC_CLIENT_CMD_FUNC(getkey)
   silc_buffer_free(idp);
 
   /* Notify application */
-  COMMAND;
+  COMMAND(SILC_STATUS_OK);
 
  out:
   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,
+                                 SilcUInt8 max_args,
+                                 SilcUInt16 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,
+                                   SilcUInt16 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;
+}
+
+/* Private range commands, specific to this implementation (and compatible
+   with SILC Server). */
+
+/* CONNECT command. Connects the server to another server. */
+
+SILC_CLIENT_CMD_FUNC(connect)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer;
+  unsigned char port[4];
+  SilcUInt32 tmp;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
+    goto out;
+  }
+
+  if (cmd->argc < 2) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /CONNECT <server> [<port>]");
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  if (cmd->argc == 3) {
+    tmp = atoi(cmd->argv[2]);
+    SILC_PUT32_MSB(tmp, port);
+  }
+
+  if (cmd->argc == 3)
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 2, 
+                                           1, cmd->argv[1], 
+                                           strlen(cmd->argv[1]),
+                                           2, port, 4);
+  else
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 1,
+                                           1, cmd->argv[1], 
+                                           strlen(cmd->argv[1]));
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+
+  /* Notify application */
+  COMMAND(SILC_STATUS_OK);
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+
+/* CLOSE command. Close server connection to the remote server */
+SILC_CLIENT_CMD_FUNC(close)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer;
+  unsigned char port[4];
+  SilcUInt32 tmp;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
+    goto out;
+  }
+
+  if (cmd->argc < 2) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /CLOSE <server> [<port>]");
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  if (cmd->argc == 3) {
+    tmp = atoi(cmd->argv[2]);
+    SILC_PUT32_MSB(tmp, port);
+  }
+
+  if (cmd->argc == 3)
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 2, 
+                                           1, cmd->argv[1], 
+                                           strlen(cmd->argv[1]),
+                                           2, port, 4);
+  else
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 1,
+                                           1, cmd->argv[1], 
+                                           strlen(cmd->argv[1]));
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+
+  /* Notify application */
+  COMMAND(SILC_STATUS_OK);
+
+ out:
+  silc_client_command_free(cmd);
+}
+/* SHUTDOWN command. Shutdowns the server. */
+
+SILC_CLIENT_CMD_FUNC(shutdown)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
+    goto out;
+  }
+
+  /* Send the command */
+  silc_client_command_send(cmd->client, cmd->conn, 
+                          SILC_COMMAND_PRIV_SHUTDOWN, 0, 0);
+
+  /* Notify application */
+  COMMAND(SILC_STATUS_OK);
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* 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(ping, PING, "PING", 2);
+  SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
+  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(detach, DETACH, "DETACH", 0);
+  SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
+  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);
+
+  SILC_CLIENT_CMD(connect, PRIV_CONNECT, "CONNECT", 3);
+  SILC_CLIENT_CMD(close, PRIV_CLOSE, "CLOSE", 3);
+  SILC_CLIENT_CMD(shutdown, PRIV_SHUTDOWN, "SHUTDOWN", 1);
+}
+
+/* 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(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(detach, DETACH, "DETACH");
+  SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
+  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_CLIENT_CMDU(connect, PRIV_CONNECT, "CONNECT");
+  SILC_CLIENT_CMDU(close, PRIV_CLOSE, "CLOSE");
+  SILC_CLIENT_CMDU(shutdown, PRIV_SHUTDOWN, "SHUTDOWN");
+}