updates.
[silc.git] / lib / silcclient / command.c
index cf3c0c4e7ab06262d03168ac9b773f17a03588e7..781607e3ae676af4807153be70907c11804b9638 100644 (file)
@@ -48,8 +48,7 @@ SilcClientCommand silc_command_list[] =
   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(restart, RESTART, "RESTART",
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
+  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",
@@ -58,13 +57,13 @@ SilcClientCommand silc_command_list[] =
                  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(ban, BAN, "BAN", SILC_CF_LAG | SILC_CF_REG, 3),
+  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), \
+  x->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. 
@@ -80,8 +79,8 @@ SilcClientCommand silc_command_list[] =
    encoded into correct form and in correct order. */
 
 void silc_client_send_command(SilcClient client, SilcClientConnection conn,
-                             SilcCommand command, unsigned short ident,
-                             unsigned int argc, ...)
+                             SilcCommand command, uint16 ident,
+                             uint32 argc, ...)
 {
   SilcBuffer packet;
   va_list ap;
@@ -118,7 +117,7 @@ SilcClientCommand *silc_client_command_find(const char *name)
 
 void silc_client_command_pending(SilcClientConnection conn,
                                 SilcCommand reply_cmd,
-                                unsigned short ident,
+                                uint16 ident,
                                 SilcClientPendingDestructor destructor,
                                 SilcCommandCb callback,
                                 void *context)
@@ -138,7 +137,7 @@ void silc_client_command_pending(SilcClientConnection conn,
 
 void silc_client_command_pending_del(SilcClientConnection conn,
                                     SilcCommand reply_cmd,
-                                    unsigned short ident)
+                                    uint16 ident)
 {
   SilcClientCommandPending *r;
 
@@ -157,7 +156,7 @@ void silc_client_command_pending_del(SilcClientConnection conn,
 int silc_client_command_pending_check(SilcClientConnection conn,
                                      SilcClientCommandReplyContext ctx,
                                      SilcCommand command, 
-                                     unsigned short ident)
+                                     uint16 ident)
 {
   SilcClientCommandPending *r;
 
@@ -196,6 +195,8 @@ void silc_client_command_free(SilcClientCommandContext ctx)
 
     for (i = 0; i < ctx->argc; i++)
       silc_free(ctx->argv[i]);
+    silc_free(ctx->argv_lens);
+    silc_free(ctx->argv_types);
     silc_free(ctx);
   }
 }
@@ -218,16 +219,6 @@ static void silc_client_command_destructor(void *context)
   silc_client_command_free((SilcClientCommandContext)context);
 }
 
-/* silc_client_get_client completion callback */
-void silc_client_command_completion(SilcClient client,
-                                   SilcClientConnection conn,
-                                   SilcClientEntry clients,
-                                   unsigned int clients_count,
-                                   void *context)
-{
-
-}
-
 /* Command WHOIS. This command is used to query information about 
    specific user. */
 
@@ -243,10 +234,13 @@ SILC_CLIENT_CMD_FUNC(whois)
     goto out;
   }
 
-  if (cmd->argc < 2 || cmd->argc > 3) {
-    cmd->client->ops->say(cmd->client, conn, 
-            "Usage: /WHOIS <nickname>[@<server>] [<count>]");
-    COMMAND_ERROR;
+  /* 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, 
+                            ++conn->cmd_ident,
+                            1, 3, buffer->data, buffer->len);
+    silc_buffer_free(buffer);
     goto out;
   }
 
@@ -285,7 +279,7 @@ SILC_CLIENT_CMD_FUNC(whowas)
   }
 
   if (cmd->argc < 2 || cmd->argc > 3) {
-    cmd->client->ops->say(cmd->client, conn, 
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
             "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
     COMMAND_ERROR;
     goto out;
@@ -326,23 +320,27 @@ SILC_CLIENT_CMD_FUNC(identify)
   }
 
   if (cmd->argc < 2 || cmd->argc > 3) {
-    cmd->client->ops->say(cmd->client, conn,
-            "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
     COMMAND_ERROR;
     goto out;
   }
 
-  buffer = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
-                                      cmd->argc - 1, ++cmd->argv,
-                                      ++cmd->argv_lens, ++cmd->argv_types,
-                                      ++conn->cmd_ident);
+  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
+    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]);
+
   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;
@@ -366,21 +364,27 @@ SILC_CLIENT_CMD_FUNC(nick)
     goto out;
   }
 
+  if (cmd->argc < 2) {
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                         "Usage: /NICK <nickname>");
+    COMMAND_ERROR;
+    goto out;
+  }
+
   if (!strcmp(conn->nickname, cmd->argv[1]))
     goto out;
 
   /* Show current nickname */
   if (cmd->argc < 2) {
     if (cmd->conn) {
-      cmd->client->ops->say(cmd->client, conn, 
+      cmd->client->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, 
+      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "Your nickname is %s", conn->nickname);
     }
 
-    /* XXX Notify application */
     COMMAND;
     goto out;
   }
@@ -429,7 +433,7 @@ SILC_CLIENT_CMD_FUNC(list)
     name = cmd->argv[1];
 
     /* Get the Channel ID of the channel */
-    if (silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
+    if (silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
       channel = (SilcChannelEntry)id_cache->context;
       idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
     }
@@ -475,7 +479,7 @@ SILC_CLIENT_CMD_FUNC(topic)
   }
 
   if (cmd->argc < 2 || cmd->argc > 3) {
-    cmd->client->ops->say(cmd->client, conn,
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
                          "Usage: /TOPIC <channel> [<topic>]");
     COMMAND_ERROR;
     goto out;
@@ -483,7 +487,8 @@ SILC_CLIENT_CMD_FUNC(topic)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
+      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                           "You are not on any channel");
       COMMAND_ERROR;
       goto out;
     }
@@ -493,14 +498,16 @@ SILC_CLIENT_CMD_FUNC(topic)
   }
 
   if (!conn->current_channel) {
-    cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                         "You are not on that channel");
     COMMAND_ERROR;
     goto out;
   }
 
   /* Get the Channel ID of the channel */
-  if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
-    cmd->client->ops->say(cmd->client, conn, "You are not on that 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;
     goto out;
   }
@@ -510,14 +517,15 @@ SILC_CLIENT_CMD_FUNC(topic)
   /* Send TOPIC command to the server */
   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
   if (cmd->argc > 2)
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 0, 2, 
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 
+                                           ++conn->cmd_ident, 2, 
                                            1, idp->data, idp->len,
                                            2, cmd->argv[2], 
                                            strlen(cmd->argv[2]));
   else
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 1, 
-                                           1, idp->data, idp->len,
-                                           0);
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 
+                                           ++conn->cmd_ident, 1,
+                                           1, idp->data, idp->len);
   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
                          0, NULL, NULL, buffer->data, buffer->len, TRUE);
   silc_buffer_free(buffer);
@@ -541,8 +549,8 @@ SILC_CLIENT_CMD_FUNC(invite)
   SilcClientEntry client_entry = NULL;
   SilcChannelEntry channel;
   SilcBuffer buffer, clidp, chidp;
-  unsigned int num = 0, type = 0;
-  char *nickname = NULL, *server = NULL, *name;
+  uint32 type = 0;
+  char *nickname = NULL, *name;
   char *invite = NULL;
 
   if (!cmd->conn) {
@@ -552,7 +560,7 @@ SILC_CLIENT_CMD_FUNC(invite)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn,
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
                   "Usage: /INVITE <channel> [<nickname>[@server>]"
                   "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
     COMMAND_ERROR;
@@ -561,7 +569,8 @@ SILC_CLIENT_CMD_FUNC(invite)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
+      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                           "You are not on any channel");
       COMMAND_ERROR;
       goto out;
     }
@@ -572,7 +581,8 @@ SILC_CLIENT_CMD_FUNC(invite)
 
     channel = silc_client_get_channel(cmd->client, conn, name);
     if (!channel) {
-      cmd->client->ops->say(cmd->client, conn, "You are on that channel");
+      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                           "You are on that channel");
       COMMAND_ERROR;
       goto out;
     }
@@ -581,25 +591,20 @@ SILC_CLIENT_CMD_FUNC(invite)
   /* Parse the typed nickname. */
   if (cmd->argc == 3) {
     if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
-      if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
-       cmd->client->ops->say(cmd->client, conn, "Bad nickname");
-       COMMAND_ERROR;
-       goto out;
-      }
-      
+      if (client->params->nickname_parse)
+       client->params->nickname_parse(cmd->argv[2], &nickname);
+      else
+       nickname = strdup(cmd->argv[2]);
+
       /* Find client entry */
       client_entry = silc_idlist_get_client(client, conn, nickname, 
-                                           server, num, TRUE);
+                                           cmd->argv[2], TRUE);
       if (!client_entry) {
-       if (nickname)
-         silc_free(nickname);
-       if (server)
-         silc_free(server);
-       
        if (cmd->pending) {
          COMMAND_ERROR;
          goto out;
        }
+       silc_free(nickname);
       
        /* Client entry not found, it was requested thus mark this to be
           pending command. */
@@ -611,10 +616,6 @@ SILC_CLIENT_CMD_FUNC(invite)
        cmd->pending = 1;
        return;
       }
-      
-      cmd->client->ops->say(cmd->client, conn, 
-                           "Inviting %s to channel %s", cmd->argv[2], 
-                           channel->channel_name);
     } else {
       invite = cmd->argv[2];
       invite++;
@@ -653,10 +654,7 @@ SILC_CLIENT_CMD_FUNC(invite)
   COMMAND;
 
  out:
-  if (nickname)
-    silc_free(nickname);
-  if (server)
-    silc_free(server);
+  silc_free(nickname);
   silc_client_command_free(cmd);
 }
 
@@ -671,7 +669,7 @@ SILC_TASK_CALLBACK(silc_client_command_quit_cb)
 
   /* Close connection */
   q->client->ops->disconnect(q->client, q->conn);
-  silc_client_close_connection(q->client, q->conn->sock->user_data);
+  silc_client_close_connection(q->client, NULL, q->conn->sock->user_data);
 
   silc_free(q);
 }
@@ -706,10 +704,13 @@ SILC_CLIENT_CMD_FUNC(quit)
   q->client = cmd->client;
   q->conn = cmd->conn;
 
+  /* Sleep for a while */
+  sleep(2);
+
   /* We quit the connection with little timeout */
-  silc_task_register(cmd->client->timeout_queue, cmd->conn->sock->sock,
-                    silc_client_command_quit_cb, (void *)q,
-                    1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+  silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
+                        silc_client_command_quit_cb, (void *)q,
+                        1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 
   /* Notify application */
   COMMAND;
@@ -718,17 +719,67 @@ SILC_CLIENT_CMD_FUNC(quit)
   silc_client_command_free(cmd);
 }
 
+/* Timeout callback to remove the killed client from cache */
+
+SILC_TASK_CALLBACK(silc_client_command_kill_remove_later)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClient client = cmd->client;
+  SilcClientConnection conn = cmd->conn;
+  SilcClientEntry target;
+  char *nickname = NULL;
+  
+  /* Parse the typed nickname. */
+  if (client->params->nickname_parse)
+    client->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);
+    silc_client_del_client(client, conn, target);
+  }
+
+  silc_free(nickname);
+  silc_client_command_free(cmd);
+}
+
+/* Kill command's pending command callback to actually remove the killed
+   client from our local cache. */
+
+SILC_CLIENT_CMD_FUNC(kill_remove)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandReplyContext reply = 
+    (SilcClientCommandReplyContext)context2;
+  SilcCommandStatus status;
+
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(reply->args, 1, NULL));
+  if (status == SILC_STATUS_OK) {
+    /* Remove with timeout */
+    silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
+                          silc_client_command_kill_remove_later, context,
+                          1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+    return;
+  }
+
+  silc_client_command_free(cmd);
+}
+
 /* Command KILL. Router operator can use this command to remove an client
    fromthe SILC Network. */
 
 SILC_CLIENT_CMD_FUNC(kill)
 {
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClient client = cmd->client;
   SilcClientConnection conn = cmd->conn;
   SilcBuffer buffer, idp;
   SilcClientEntry target;
-  unsigned int num = 0;
-  char *nickname = NULL, *server = NULL;
+  char *nickname = NULL;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
@@ -737,32 +788,29 @@ SILC_CLIENT_CMD_FUNC(kill)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, 
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /KILL <nickname> [<comment>]");
     COMMAND_ERROR;
     goto out;
   }
 
   /* Parse the typed nickname. */
-  if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
-    cmd->client->ops->say(cmd->client, conn, "Bad nickname");
-    COMMAND_ERROR;
-    goto out;
-  }
+  if (client->params->nickname_parse)
+    client->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, 
-                                 server, num, TRUE);
+                                 cmd->argv[1], TRUE);
   if (!target) {
-    silc_free(nickname);
-    if (server)
-      silc_free(server);
-
     if (cmd->pending) {
       COMMAND_ERROR;
       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, 
@@ -777,10 +825,12 @@ SILC_CLIENT_CMD_FUNC(kill)
   /* Send the KILL command to the server */
   idp = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
   if (cmd->argc == 2)
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 0, 1, 
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 
+                                           ++conn->cmd_ident, 1, 
                                            1, idp->data, idp->len);
   else
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 0, 2, 
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 
+                                           ++conn->cmd_ident, 2, 
                                            1, idp->data, idp->len,
                                            2, cmd->argv[2], 
                                            strlen(cmd->argv[2]));
@@ -792,11 +842,14 @@ SILC_CLIENT_CMD_FUNC(kill)
   /* Notify application */
   COMMAND;
 
+  /* 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_dup(cmd));
+
  out:
-  if (nickname)
-    silc_free(nickname);
-  if (server)
-    silc_free(server);
+  silc_free(nickname);
   silc_client_command_free(cmd);
 }
 
@@ -808,7 +861,7 @@ SILC_CLIENT_CMD_FUNC(info)
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
   SilcBuffer buffer;
-  char *name;
+  char *name = NULL;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
@@ -816,17 +869,21 @@ SILC_CLIENT_CMD_FUNC(info)
     goto out;
   }
 
-  if (cmd->argc < 2)
-    name = strdup(conn->remote_host);
-  else
+  if (cmd->argc == 2)
     name = strdup(cmd->argv[1]);
 
   /* Send the command */
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1, 
-                                         1, name, strlen(name));
+  if (name)
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1, 
+                                           1, name, strlen(name));
+  else
+    buffer = silc_command_payload_encode(SILC_COMMAND_INFO, 0,
+                                        NULL, NULL, NULL, 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);
+  if (name)
+    silc_free(name);
 
   /* Notify application */
   COMMAND;
@@ -845,7 +902,6 @@ SILC_CLIENT_CMD_FUNC(ping)
   SilcBuffer buffer;
   void *id;
   int i;
-  char *name = NULL;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
@@ -853,13 +909,11 @@ SILC_CLIENT_CMD_FUNC(ping)
     goto out;
   }
 
-  if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
-    name = strdup(conn->remote_host);
-
   /* Send the command */
   buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1, 
                                          1, conn->remote_id_data, 
-                                         SILC_ID_SERVER_LEN);
+                                         silc_id_get_len(conn->remote_id,
+                                                         SILC_ID_SERVER));
   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
                          0, NULL, NULL, buffer->data, buffer->len, TRUE);
   silc_buffer_free(buffer);
@@ -877,7 +931,7 @@ SILC_CLIENT_CMD_FUNC(ping)
     if (conn->ping[i].dest_id == NULL) {
       conn->ping[i].start_time = time(NULL);
       conn->ping[i].dest_id = id;
-      conn->ping[i].dest_name = name;
+      conn->ping[i].dest_name = strdup(conn->remote_host);
       conn->ping_count++;
       break;
     }
@@ -887,7 +941,7 @@ SILC_CLIENT_CMD_FUNC(ping)
     conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
     conn->ping[i].start_time = time(NULL);
     conn->ping[i].dest_id = id;
-    conn->ping[i].dest_name = name;
+    conn->ping[i].dest_name = strdup(conn->remote_host);
     conn->ping_count++;
   }
   
@@ -898,10 +952,6 @@ SILC_CLIENT_CMD_FUNC(ping)
   silc_client_command_free(cmd);
 }
 
-SILC_CLIENT_CMD_FUNC(notice)
-{
-}
-
 /* Command JOIN. Joins to a channel. */
 
 SILC_CLIENT_CMD_FUNC(join)
@@ -917,23 +967,12 @@ SILC_CLIENT_CMD_FUNC(join)
     goto out;
   }
 
-  if (cmd->argc < 2) {
-    /* Show channels currently joined to */
-
-    goto out;
-  }
-
   /* See if we have joined to the requested channel already */
-  if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
+  if (silc_idcache_find_by_name_one(conn->channel_cache, cmd->argv[1],
                                    &id_cache)) {
-    cmd->client->ops->say(cmd->client, conn, 
-                         "You are talking to channel %s", cmd->argv[1]);
-    conn->current_channel = (SilcChannelEntry)id_cache->context;
-#if 0
-    cmd->client->screen->bottom_line->channel = cmd->argv[1];
-    silc_screen_print_bottom_line(cmd->client->screen, 0);
-#endif
-    goto out;
+    SilcChannelEntry channel = (SilcChannelEntry)id_cache->context;
+    if (channel->on_channel)
+      goto out;
   }
 
   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
@@ -986,7 +1025,7 @@ SILC_CLIENT_CMD_FUNC(motd)
   }
 
   if (cmd->argc < 1 || cmd->argc > 2) {
-    cmd->client->ops->say(cmd->client, conn,
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
                          "Usage: /MOTD [<server>]");
     COMMAND_ERROR;
     goto out;
@@ -1021,7 +1060,7 @@ SILC_CLIENT_CMD_FUNC(umode)
   SilcClientConnection conn = cmd->conn;
   SilcBuffer buffer, idp;
   unsigned char *cp, modebuf[4];
-  unsigned int mode, add, len;
+  uint32 mode, add, len;
   int i;
 
   if (!cmd->conn) {
@@ -1031,7 +1070,7 @@ SILC_CLIENT_CMD_FUNC(umode)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, 
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                  "Usage: /UMODE +|-<modes>");
     COMMAND_ERROR;
     goto out;
@@ -1071,6 +1110,12 @@ SILC_CLIENT_CMD_FUNC(umode)
       else
        mode &= ~SILC_UMODE_ROUTER_OPERATOR;
       break;
+    case 'g':
+      if (add)
+       mode |= SILC_UMODE_GONE;
+      else
+       mode &= ~SILC_UMODE_GONE;
+      break;
     default:
       COMMAND_ERROR;
       goto out;
@@ -1108,9 +1153,9 @@ SILC_CLIENT_CMD_FUNC(cmode)
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
   SilcChannelEntry channel;
-  SilcBuffer buffer, chidp;
+  SilcBuffer buffer, chidp, auth = NULL;
   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
-  unsigned int mode, add, type, len, arg_len = 0;
+  uint32 mode, add, type, len, arg_len = 0;
   int i;
 
   if (!cmd->conn) {
@@ -1120,7 +1165,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
   }
 
   if (cmd->argc < 3) {
-    cmd->client->ops->say(cmd->client, conn, 
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                  "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
     COMMAND_ERROR;
     goto out;
@@ -1128,7 +1173,8 @@ SILC_CLIENT_CMD_FUNC(cmode)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
+      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                           "You are not on any channel");
       COMMAND_ERROR;
       goto out;
     }
@@ -1139,7 +1185,8 @@ SILC_CLIENT_CMD_FUNC(cmode)
 
     channel = silc_client_get_channel(cmd->client, conn, name);
     if (!channel) {
-      cmd->client->ops->say(cmd->client, conn, "You are on that channel");
+      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                           "You are on that channel");
       COMMAND_ERROR;
       goto out;
     }
@@ -1196,6 +1243,12 @@ SILC_CLIENT_CMD_FUNC(cmode)
        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;
+         goto out;
+       }
        ll = atoi(cmd->argv[3]);
        SILC_PUT32_MSB(ll, tmp);
        arg = tmp;
@@ -1208,6 +1261,12 @@ SILC_CLIENT_CMD_FUNC(cmode)
       if (add) {
        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;
+         goto out;
+       }
        arg = cmd->argv[3];
        arg_len = cmd->argv_lens[3];
       } else {
@@ -1217,13 +1276,64 @@ SILC_CLIENT_CMD_FUNC(cmode)
     case 'c':
       if (add) {
        mode |= SILC_CHANNEL_MODE_CIPHER;
-       type = 8;
+       type = 5;
+       if (cmd->argc < 4) {
+         cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+              "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
+         COMMAND_ERROR;
+         goto out;
+       }
        arg = cmd->argv[3];
        arg_len = cmd->argv_lens[3];
       } else {
        mode &= ~SILC_CHANNEL_MODE_CIPHER;
       }
       break;
+    case 'h':
+      if (add) {
+       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;
+         goto out;
+       }
+       arg = cmd->argv[3];
+       arg_len = cmd->argv_lens[3];
+      } else {
+       mode &= ~SILC_CHANNEL_MODE_HMAC;
+      }
+      break;
+    case 'f':
+      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]);
+       }
+
+       arg = auth->data;
+       arg_len = auth->len;
+      } else {
+       mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
+      }
+      break;
     default:
       COMMAND_ERROR;
       goto out;
@@ -1231,11 +1341,6 @@ SILC_CLIENT_CMD_FUNC(cmode)
     }
   }
 
-  if (type && cmd->argc < 3) {
-    COMMAND_ERROR;
-    goto out;
-  }
-
   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
   SILC_PUT32_MSB(mode, modebuf);
 
@@ -1258,6 +1363,8 @@ SILC_CLIENT_CMD_FUNC(cmode)
                          0, NULL, NULL, buffer->data, buffer->len, TRUE);
   silc_buffer_free(buffer);
   silc_buffer_free(chidp);
+  if (auth)
+    silc_buffer_free(auth);
 
   /* Notify application */
   COMMAND;
@@ -1271,15 +1378,15 @@ SILC_CLIENT_CMD_FUNC(cmode)
 SILC_CLIENT_CMD_FUNC(cumode)
 {
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClient client = cmd->client;
   SilcClientConnection conn = cmd->conn;
   SilcChannelEntry channel;
   SilcChannelUser chu;
   SilcClientEntry client_entry;
-  SilcBuffer buffer, clidp, chidp;
+  SilcBuffer buffer, clidp, chidp, auth = NULL;
   unsigned char *name, *cp, modebuf[4];
-  unsigned int mode = 0, add, len;
-  char *nickname = NULL, *server = NULL;
-  unsigned int num = 0;
+  uint32 mode = 0, add, len;
+  char *nickname = NULL;
   int i;
 
   if (!cmd->conn) {
@@ -1289,7 +1396,7 @@ SILC_CLIENT_CMD_FUNC(cumode)
   }
 
   if (cmd->argc < 4) {
-    cmd->client->ops->say(cmd->client, conn, 
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                  "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
     COMMAND_ERROR;
     goto out;
@@ -1297,7 +1404,8 @@ SILC_CLIENT_CMD_FUNC(cumode)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
+      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                           "You are not on any channel");
       COMMAND_ERROR;
       goto out;
     }
@@ -1308,28 +1416,30 @@ SILC_CLIENT_CMD_FUNC(cumode)
 
     channel = silc_client_get_channel(cmd->client, conn, name);
     if (!channel) {
-      cmd->client->ops->say(cmd->client, conn, "You are on that channel");
+      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                           "You are on that channel");
       COMMAND_ERROR;
       goto out;
     }
   }
 
   /* Parse the typed nickname. */
-  if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
-    cmd->client->ops->say(cmd->client, conn, "Bad nickname");
-    COMMAND_ERROR;
-    goto out;
-  }
+  if (client->params->nickname_parse)
+    client->params->nickname_parse(cmd->argv[3], &nickname);
+  else
+    nickname = strdup(cmd->argv[3]);
 
   /* Find client entry */
-  client_entry = silc_idlist_get_client(cmd->client, conn, 
-                                       nickname, server, num, TRUE);
+  client_entry = silc_idlist_get_client(cmd->client, conn, nickname,
+                                       cmd->argv[3], TRUE);
   if (!client_entry) {
     if (cmd->pending) {
       COMMAND_ERROR;
       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, 
@@ -1341,9 +1451,11 @@ SILC_CLIENT_CMD_FUNC(cumode)
     return;
   }
   
+  /* Get the current mode */
+  silc_list_start(channel->clients);
   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
     if (chu->client == client_entry) {
-      chu->mode = mode;
+      mode = chu->mode;
       break;
     }
   }
@@ -1368,10 +1480,23 @@ SILC_CLIENT_CMD_FUNC(cumode)
       }
       break;
     case 'f':
-      if (add)
+      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]);
+         }
+       }
        mode |= SILC_CHANNEL_UMODE_CHANFO;
-      else
+      } else {
        mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+      }
       break;
     case 'o':
       if (add)
@@ -1392,25 +1517,27 @@ SILC_CLIENT_CMD_FUNC(cumode)
 
   /* Send the command packet. We support sending only one mode at once
      that requires an argument. */
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3, 
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 
+                                         auth ? 4 : 3, 
                                          1, chidp->data, chidp->len, 
                                          2, modebuf, 4,
-                                         3, clidp->data, clidp->len);
-
+                                         3, clidp->data, clidp->len,
+                                         4, 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(chidp);
   silc_buffer_free(clidp);
+  if (auth)
+    silc_buffer_free(auth);
   
   /* Notify application */
   COMMAND;
 
  out:
-  if (nickname)
-    silc_free(nickname);
-  if (server)
-    silc_free(server);
+  silc_free(nickname);
   silc_client_command_free(cmd);
 }
 
@@ -1419,14 +1546,14 @@ SILC_CLIENT_CMD_FUNC(cumode)
 SILC_CLIENT_CMD_FUNC(kick)
 {
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClient client = cmd->client;
   SilcClientConnection conn = cmd->conn;
   SilcIDCacheEntry id_cache = NULL;
   SilcChannelEntry channel;
   SilcBuffer buffer, idp, idp2;
   SilcClientEntry target;
   char *name;
-  unsigned int num = 0;
-  char *nickname = NULL, *server = NULL;
+  char *nickname = NULL;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
@@ -1435,7 +1562,7 @@ SILC_CLIENT_CMD_FUNC(kick)
   }
 
   if (cmd->argc < 3) {
-    cmd->client->ops->say(cmd->client, conn, 
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /KICK <channel> <nickname> [<comment>]");
     COMMAND_ERROR;
     goto out;
@@ -1443,7 +1570,8 @@ SILC_CLIENT_CMD_FUNC(kick)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
+      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                           "You are not on any channel");
       COMMAND_ERROR;
       goto out;
     }
@@ -1453,14 +1581,16 @@ SILC_CLIENT_CMD_FUNC(kick)
   }
 
   if (!conn->current_channel) {
-    cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                         "You are not on that channel");
     COMMAND_ERROR;
     goto out;
   }
 
   /* Get the Channel ID of the channel */
-  if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
-    cmd->client->ops->say(cmd->client, conn, "You are not on that 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;
     goto out;
   }
@@ -1468,17 +1598,17 @@ SILC_CLIENT_CMD_FUNC(kick)
   channel = (SilcChannelEntry)id_cache->context;
 
   /* Parse the typed nickname. */
-  if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
-    cmd->client->ops->say(cmd->client, conn, "Bad nickname");
-    COMMAND_ERROR;
-    goto out;
-  }
+  if (client->params->nickname_parse)
+    client->params->nickname_parse(cmd->argv[2], &nickname);
+  else
+    nickname = strdup(cmd->argv[2]);
 
   /* Get the target client */
   target = silc_idlist_get_client(cmd->client, conn, nickname, 
-                                 server, num, FALSE);
+                                 cmd->argv[2], FALSE);
   if (!target) {
-    cmd->client->ops->say(cmd->client, conn, "No such client: %s",
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                         "No such client: %s",
                          cmd->argv[2]);
     COMMAND_ERROR;
     goto out;
@@ -1507,22 +1637,50 @@ SILC_CLIENT_CMD_FUNC(kick)
   COMMAND;
 
  out:
-  if (nickname)
-    silc_free(nickname);
-  if (server)
-    silc_free(server);
+  silc_free(nickname);
   silc_client_command_free(cmd);
 }
 
+static void silc_client_command_oper_send(unsigned char *data,
+                                         uint32 data_len, void *context)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer, auth;
+
+  if (cmd->argc >= 3) {
+    /* Encode the public key authentication payload */
+    auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
+                                             cmd->client->private_key,
+                                             conn->hash,
+                                             conn->local_id,
+                                             SILC_ID_CLIENT);
+  } else {
+    /* Encode the password authentication payload */
+    auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
+                                   data, data_len);
+  }
+
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
+                                         1, cmd->argv[1], 
+                                         strlen(cmd->argv[1]),
+                                         2, auth->data, auth->len);
+  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(auth);
+
+  /* Notify application */
+  COMMAND;
+}
+
 /* OPER command. Used to obtain server operator privileges. */
 
 SILC_CLIENT_CMD_FUNC(oper)
 {
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer;
-  unsigned char *auth_data;
-  SilcBuffer auth;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
@@ -1531,32 +1689,47 @@ SILC_CLIENT_CMD_FUNC(oper)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, 
-                         "Usage: /OPER <username> [<public key>]");
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                         "Usage: /OPER <username> [-pubkey]");
     COMMAND_ERROR;
     goto out;
   }
 
-  if (cmd->argc == 3) {
-    /* XXX Get public key */
-    auth_data = NULL;
-    COMMAND_ERROR;
-    goto out;
-  } else {
+  if (cmd->argc < 3) {
     /* Get passphrase */
+    cmd->client->ops->ask_passphrase(cmd->client, conn,
+                                    silc_client_command_oper_send,
+                                    context);
+    return;
+  }
 
-    auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
-    if (!auth_data) {
-      COMMAND_ERROR;
-      goto out;
-    }
+  silc_client_command_oper_send(NULL, 0, context);
+
+ out:
+  silc_client_command_free(cmd);
+}
 
-    /* Encode the authentication payload */
+static void silc_client_command_silcoper_send(unsigned char *data,
+                                             uint32 data_len, void *context)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer, auth;
+
+  if (cmd->argc >= 3) {
+    /* Encode the public key authentication payload */
+    auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
+                                             cmd->client->private_key,
+                                             conn->hash,
+                                             conn->local_id,
+                                             SILC_ID_CLIENT);
+  } else {
+    /* Encode the password authentication payload */
     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
-                                   auth_data, strlen(auth_data));
+                                   data, data_len);
   }
 
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
                                          1, cmd->argv[1], 
                                          strlen(cmd->argv[1]),
                                          2, auth->data, auth->len);
@@ -1565,14 +1738,9 @@ SILC_CLIENT_CMD_FUNC(oper)
 
   silc_buffer_free(buffer);
   silc_buffer_free(auth);
-  memset(auth_data, 0, strlen(auth_data));
-  silc_free(auth_data);
 
   /* Notify application */
   COMMAND;
-
- out:
-  silc_client_command_free(cmd);
 }
 
 /* SILCOPER command. Used to obtain router operator privileges. */
@@ -1581,9 +1749,6 @@ SILC_CLIENT_CMD_FUNC(silcoper)
 {
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer;
-  unsigned char *auth_data;
-  SilcBuffer auth;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
@@ -1592,45 +1757,21 @@ SILC_CLIENT_CMD_FUNC(silcoper)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, 
-                         "Usage: /SILCOPER <username> [<public key>]");
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                         "Usage: /SILCOPER <username> [-pubkey]");
     COMMAND_ERROR;
     goto out;
   }
 
-  if (cmd->argc == 3) {
-    /* XXX Get public key */
-    auth_data = NULL;
-    COMMAND_ERROR;
-    goto out;
-  } else {
+  if (cmd->argc < 3) {
     /* Get passphrase */
-
-    auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
-    if (!auth_data) {
-      COMMAND_ERROR;
-      goto out;
-    }
-
-    /* Encode the authentication payload */
-    auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
-                                   auth_data, strlen(auth_data));
+    cmd->client->ops->ask_passphrase(cmd->client, conn,
+                                    silc_client_command_silcoper_send,
+                                    context);
+    return;
   }
 
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
-                                         1, cmd->argv[1], 
-                                         strlen(cmd->argv[1]),
-                                         2, auth->data, auth->len);
-  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(auth);
-  memset(auth_data, 0, strlen(auth_data));
-  silc_free(auth_data);
-
-  /* Notify application */
-  COMMAND;
+  silc_client_command_silcoper_send(NULL, 0, context);
 
  out:
   silc_client_command_free(cmd);
@@ -1644,7 +1785,7 @@ SILC_CLIENT_CMD_FUNC(connect)
   SilcClientConnection conn = cmd->conn;
   SilcBuffer buffer;
   unsigned char port[4];
-  unsigned int tmp;
+  uint32 tmp;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
@@ -1653,7 +1794,7 @@ SILC_CLIENT_CMD_FUNC(connect)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, 
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /CONNECT <server> [<port>]");
     COMMAND_ERROR;
     goto out;
@@ -1684,8 +1825,83 @@ SILC_CLIENT_CMD_FUNC(connect)
   silc_client_command_free(cmd);
 }
 
-SILC_CLIENT_CMD_FUNC(restart)
+/* Command BAN. This is used to manage the ban list of the channel. */
+
+SILC_CLIENT_CMD_FUNC(ban)
 {
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcChannelEntry channel;
+  SilcBuffer buffer, chidp;
+  int type = 0;
+  char *name, *ban = NULL;
+
+  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: /BAN <channel> "
+                  "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
+    COMMAND_ERROR;
+    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;
+      goto out;
+    }
+
+    channel = conn->current_channel;
+  } else {
+    name = cmd->argv[1];
+
+    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;
+      goto out;
+    }
+  }
+
+  if (cmd->argc == 3) {
+    if (cmd->argv[2][0] == '+')
+      type = 2;
+    else
+      type = 3;
+
+    ban = cmd->argv[2];
+    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);
+
+  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;
+
+ out:
+  silc_client_command_free(cmd);
 }
 
 /* CLOSE command. Close server connection to the remote server */
@@ -1696,7 +1912,7 @@ SILC_CLIENT_CMD_FUNC(close)
   SilcClientConnection conn = cmd->conn;
   SilcBuffer buffer;
   unsigned char port[4];
-  unsigned int tmp;
+  uint32 tmp;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
@@ -1705,7 +1921,7 @@ SILC_CLIENT_CMD_FUNC(close)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, 
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /CLOSE <server> [<port>]");
     COMMAND_ERROR;
     goto out;
@@ -1758,7 +1974,7 @@ SILC_CLIENT_CMD_FUNC(shutdown)
  out:
   silc_client_command_free(cmd);
 }
+
 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
 
 SILC_CLIENT_CMD_FUNC(leave)
@@ -1777,14 +1993,16 @@ SILC_CLIENT_CMD_FUNC(leave)
   }
 
   if (cmd->argc != 2) {
-    cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                         "Usage: /LEAVE <channel>");
     COMMAND_ERROR;
     goto out;
   }
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
+      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                           "You are not on any channel");
       COMMAND_ERROR;
       goto out;
     }
@@ -1793,20 +2011,16 @@ SILC_CLIENT_CMD_FUNC(leave)
     name = cmd->argv[1];
   }
 
-  if (!conn->current_channel) {
-    cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
-    COMMAND_ERROR;
-    goto out;
-  }
-
   /* Get the Channel ID of the channel */
-  if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
-    cmd->client->ops->say(cmd->client, conn, "You are not on that 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;
     goto out;
   }
 
   channel = (SilcChannelEntry)id_cache->context;
+  channel->on_channel = FALSE;
 
   /* Send LEAVE command to the server */
   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
@@ -1817,21 +2031,14 @@ SILC_CLIENT_CMD_FUNC(leave)
   silc_buffer_free(buffer);
   silc_buffer_free(idp);
 
-  /* We won't talk anymore on this channel */
-  cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
-
-  conn->current_channel = NULL;
-
-  silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
-  silc_free(channel->channel_name);
-  silc_free(channel->id);
-  silc_free(channel->key);
-  silc_cipher_free(channel->channel_key);
-  silc_free(channel);
-
   /* Notify application */
   COMMAND;
 
+  if (conn->current_channel == channel)
+    conn->current_channel = NULL;
+
+  silc_client_del_channel(cmd->client, cmd->conn, channel);
+
  out:
   silc_client_command_free(cmd);
 }
@@ -1843,11 +2050,8 @@ SILC_CLIENT_CMD_FUNC(users)
 {
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
-  SilcIDCacheEntry id_cache = NULL;
-  SilcChannelEntry channel;
-  SilcBuffer buffer, idp;
-  char *name, *line = NULL;
-  unsigned int line_len = 0;
+  SilcBuffer buffer;
+  char *name;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
@@ -1856,14 +2060,16 @@ SILC_CLIENT_CMD_FUNC(users)
   }
 
   if (cmd->argc != 2) {
-    cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                         "Usage: /USERS <channel>");
     COMMAND_ERROR;
     goto out;
   }
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
+      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                           "You are not on any channel");
       COMMAND_ERROR;
       goto out;
     }
@@ -1872,109 +2078,14 @@ SILC_CLIENT_CMD_FUNC(users)
     name = cmd->argv[1];
   }
 
-  if (!conn->current_channel) {
-    cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
-    COMMAND_ERROR;
-    goto out;
-  }
-
-  /* Get the Channel ID of the channel */
-  if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
-    /* XXX should resolve the channel ID; LIST command */
-    cmd->client->ops->say(cmd->client, conn, 
-                         "You are not on that channel", name);
-    COMMAND_ERROR;
-    goto out;
-  }
-
-  channel = (SilcChannelEntry)id_cache->context;
-
-  if (!cmd->pending) {
-    /* Send USERS command to the server */
-    idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 
-                                           ++conn->cmd_ident, 1, 
-                                           1, idp->data, idp->len);
-    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);
-
-    /* Register pending callback which will recall this command callback with
-       same context and reprocesses the command. When reprocessing we actually
-       display the information on the screen. */
-    silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident, 
-                               silc_client_command_destructor,
-                               silc_client_command_users, 
-                               silc_client_command_dup(cmd));
-    cmd->pending = TRUE;
-    return;
-  }
-
-  if (cmd->pending) {
-    /* Pending command. Now we've resolved the information from server and
-       we are ready to display the information on screen. */
-    int i;
-    SilcChannelUser chu;
-
-    cmd->client->ops->say(cmd->client, conn, "Users on %s", 
-                         channel->channel_name);
-
-    line = silc_calloc(4096, sizeof(*line));
-    line_len = 4096;
-    silc_list_start(channel->clients);
-    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
-      SilcClientEntry e = chu->client;
-      char *m, tmp[80], len1;
-
-      memset(line, 0, sizeof(line_len));
-
-      if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
-       silc_free(line);
-       line_len += strlen(e->nickname) + strlen(e->server) + 100;
-       line = silc_calloc(line_len, sizeof(*line));
-      }
-
-      memset(tmp, 0, sizeof(tmp));
-      m = silc_client_chumode_char(chu->mode);
-
-      strncat(line, " ", 1);
-      strncat(line, e->nickname, strlen(e->nickname));
-      strncat(line, e->server ? "@" : "", 1);
-
-      len1 = 0;
-      if (e->server)
-       len1 = strlen(e->server);
-      strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
-
-      len1 = strlen(line);
-      if (len1 >= 30) {
-       memset(&line[29], 0, len1 - 29);
-      } else {
-       for (i = 0; i < 30 - len1 - 1; i++)
-         strcat(line, " ");
-      }
-
-      strncat(line, "  H", 3);
-      strcat(tmp, m ? m : "");
-      strncat(line, tmp, strlen(tmp));
-
-      if (strlen(tmp) < 5)
-       for (i = 0; i < 5 - strlen(tmp); i++)
-         strcat(line, " ");
-
-      strcat(line, e->username ? e->username : "");
-
-      cmd->client->ops->say(cmd->client, conn, "%s", line);
-
-      if (m)
-       silc_free(m);
-    }
-  }
-
-  if (line)
-    silc_free(line);
+  /* Send USERS command to the server */
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 
+                                         ++conn->cmd_ident, 1, 
+                                         2, name, strlen(name));
+  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;
@@ -1983,16 +2094,17 @@ SILC_CLIENT_CMD_FUNC(users)
   silc_client_command_free(cmd);
 }
 
-/* Command BAN. This is used to manage the ban list of the channel. */
+/* Command GETKEY. Used to fetch remote client's public key. */
 
-SILC_CLIENT_CMD_FUNC(ban)
+SILC_CLIENT_CMD_FUNC(getkey)
 {
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
-  SilcChannelEntry channel;
-  SilcBuffer buffer, chidp;
-  int type = 0;
-  char *name, *ban = NULL;
+  SilcClient client = cmd->client;
+  SilcClientEntry client_entry = NULL;
+  SilcServerEntry server_entry = NULL;
+  char *nickname = NULL;
+  SilcBuffer idp, buffer;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
@@ -2001,61 +2113,85 @@ SILC_CLIENT_CMD_FUNC(ban)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, 
-                  "Usage: /BAN <channel> "
-                  "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
+    client->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                    "Usage: /GETKEY <nickname or server name>");
     COMMAND_ERROR;
     goto out;
   }
 
-  if (cmd->argv[1][0] == '*') {
-    if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
+  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;
     }
+  }
 
-    channel = conn->current_channel;
-  } else {
-    name = cmd->argv[1];
+  /* Parse the typed nickname. */
+  if (client->params->nickname_parse)
+    client->params->nickname_parse(cmd->argv[1], &nickname);
+  else
+    nickname = strdup(cmd->argv[1]);
 
-    channel = silc_client_get_channel(cmd->client, conn, name);
-    if (!channel) {
-      cmd->client->ops->say(cmd->client, conn, "You are on that channel");
-      COMMAND_ERROR;
-      goto out;
+  /* Find client entry */
+  client_entry = silc_idlist_get_client(client, conn, nickname, cmd->argv[1],
+                                       FALSE);
+  if (!client_entry) {
+    /* Check whether user requested server actually */
+    server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
+
+    if (!server_entry && !cmd->pending) {
+      /* 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 (cmd->argc == 3) {
-    if (cmd->argv[2][0] == '+')
-      type = 2;
-    else
-      type = 3;
-
-    ban = cmd->argv[2];
-    ban++;
+    idp = silc_id_payload_encode(server_entry->server_id, SILC_ID_SERVER);
+  } else {
+    idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
   }
 
-  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_GETKEY, 0, 1, 
+                                         1, idp->data, idp->len);
   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);
+  silc_buffer_free(idp);
 
   /* Notify application */
   COMMAND;
 
  out:
+  silc_free(nickname);
   silc_client_command_free(cmd);
 }