updates.
[silc.git] / lib / silcclient / command.c
index 0388f29a082476385b276d1ab4312beeba3b174c..5257bf450ef6f306f11eec2a37932d7a4c117b89 100644 (file)
@@ -349,6 +349,36 @@ SILC_CLIENT_CMD_FUNC(identify)
   silc_client_command_free(cmd);
 }
 
+/* Pending callbcak that will be called after the NICK command was
+   replied by the server.  This sets the nickname if there were no
+   errors. */
+
+SILC_CLIENT_CMD_FUNC(nick_change)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClientCommandReplyContext reply = 
+    (SilcClientCommandReplyContext)context2;
+  SilcCommandStatus status;
+
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(reply->args, 1, NULL));
+  if (status == SILC_STATUS_OK) {
+    /* Set the nickname */
+    if (conn->nickname)
+      silc_free(conn->nickname);
+    conn->nickname = strdup(cmd->argv[1]);
+    conn->local_entry->nickname = conn->nickname;
+    silc_idcache_del_by_context(conn->client_cache, conn->local_entry);
+    silc_idcache_add(conn->client_cache, strdup(cmd->argv[1]), 
+                    conn->local_entry->id, conn->local_entry, FALSE);
+    COMMAND;
+  } else {
+    COMMAND_ERROR;
+  }
+
+  silc_client_command_free(cmd);
+}
+
 /* Command NICK. Shows current nickname/sets new nickname on current
    window. */
 
@@ -389,24 +419,29 @@ SILC_CLIENT_CMD_FUNC(nick)
     goto out;
   }
 
-  /* Set new nickname */
-  buffer = silc_command_payload_encode(SILC_COMMAND_NICK,
-                                      cmd->argc - 1, ++cmd->argv,
-                                      ++cmd->argv_lens, ++cmd->argv_types,
+  if (cmd->argv_lens[1] > 128)
+    cmd->argv_lens[1] = 128;
+
+  /* Send the NICK command */
+  buffer = silc_command_payload_encode(SILC_COMMAND_NICK, 1,
+                                      &cmd->argv[1],
+                                      &cmd->argv_lens[1], 
+                                      &cmd->argv_types[1],
                                       ++cmd->conn->cmd_ident);
   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--;
-  if (conn->nickname)
-    silc_free(conn->nickname);
-  conn->nickname = strdup(cmd->argv[1]);
 
-  /* Notify application */
-  COMMAND;
+  /* Register pending callback that will actually set the new nickname
+     if there were no errors returned by the server. */
+  silc_client_command_pending(conn, SILC_COMMAND_NICK, 
+                             cmd->conn->cmd_ident,
+                             silc_client_command_destructor,
+                             silc_client_command_nick_change,
+                             silc_client_command_dup(cmd));
+  cmd->pending = TRUE;
+  return;
 
  out:
   silc_client_command_free(cmd);
@@ -600,12 +635,11 @@ SILC_CLIENT_CMD_FUNC(invite)
       client_entry = silc_idlist_get_client(client, conn, nickname, 
                                            cmd->argv[2], TRUE);
       if (!client_entry) {
-       silc_free(nickname);
-       
        if (cmd->pending) {
          COMMAND_ERROR;
          goto out;
        }
+       silc_free(nickname);
       
        /* Client entry not found, it was requested thus mark this to be
           pending command. */
@@ -705,6 +739,9 @@ 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_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
                         silc_client_command_quit_cb, (void *)q,
@@ -717,6 +754,56 @@ 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. */
 
@@ -752,13 +839,13 @@ SILC_CLIENT_CMD_FUNC(kill)
   target = silc_idlist_get_client(cmd->client, conn, nickname, 
                                  cmd->argv[1], TRUE);
   if (!target) {
-    silc_free(nickname);
-
     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, 
@@ -773,10 +860,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]));
@@ -788,6 +877,12 @@ 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:
   silc_free(nickname);
   silc_client_command_free(cmd);
@@ -872,7 +967,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;
     }
   }
@@ -909,11 +1003,17 @@ SILC_CLIENT_CMD_FUNC(join)
 
   /* 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))
-    goto out;
+                                   &id_cache)) {
+    SilcChannelEntry channel = (SilcChannelEntry)id_cache->context;
+    if (channel->on_channel)
+      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 = 
@@ -1370,13 +1470,13 @@ SILC_CLIENT_CMD_FUNC(cumode)
   client_entry = silc_idlist_get_client(cmd->client, conn, nickname,
                                        cmd->argv[3], TRUE);
   if (!client_entry) {
-    silc_free(nickname);
-
     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, 
@@ -1389,6 +1489,7 @@ SILC_CLIENT_CMD_FUNC(cumode)
   }
   
   /* 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;
@@ -1453,7 +1554,8 @@ 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, 4, 
+  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,
@@ -1955,6 +2057,7 @@ SILC_CLIENT_CMD_FUNC(leave)
   }
 
   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);
@@ -2083,7 +2186,12 @@ SILC_CLIENT_CMD_FUNC(getkey)
     /* Check whether user requested server actually */
     server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
 
-    if (!server_entry && !cmd->pending) {
+    if (!server_entry) {
+      if (cmd->pending) {
+       COMMAND_ERROR;
+       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. */