Changed the version string variables to #defines.
[silc.git] / lib / silcclient / command.c
index 88f5ac40b3addbcbcfd2b7113ce30b1c9016ec2e..f033dc25d0dd53770b8c7d983472c9ca827dccda 100644 (file)
@@ -103,16 +103,6 @@ void silc_client_command_pending(SilcClientConnection conn,
 {
   SilcClientCommandPending *reply;
 
-  /* Check whether identical pending already exists for same command,
-     ident, callback and callback context. If it does then it would be
-     error to register it again. */
-  silc_dlist_start(conn->pending_commands);
-  while ((reply = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
-    if (reply->reply_cmd == reply_cmd && reply->ident == ident &&
-       reply->callback == callback && reply->context == context)
-      return;
-  }
-
   reply = silc_calloc(1, sizeof(*reply));
   reply->reply_cmd = reply_cmd;
   reply->ident = ident;
@@ -134,9 +124,11 @@ void silc_client_command_pending_del(SilcClientConnection conn,
 
   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) {
+    if ((r->reply_cmd == reply_cmd || (r->reply_cmd == SILC_COMMAND_NONE &&
+                                      r->reply_check))
+       && r->ident == ident) {
       silc_dlist_del(conn->pending_commands, r);
-      break;
+      silc_free(r);
     }
   }
 }
@@ -162,6 +154,7 @@ silc_client_command_pending_check(SilcClientConnection conn,
       callbacks = silc_realloc(callbacks, sizeof(*callbacks) * (i + 1));
       callbacks[i].context = r->context;
       callbacks[i].callback = r->callback;
+      r->reply_check = TRUE;
       ctx->ident = ident;
       i++;
     }
@@ -216,8 +209,8 @@ SILC_CLIENT_CMD_FUNC(whois)
 {
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer;
-  unsigned char count[4];
+  SilcBuffer buffer, attrs = NULL;
+  unsigned char count[4], *tmp = NULL;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
@@ -241,13 +234,21 @@ SILC_CLIENT_CMD_FUNC(whois)
                                            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);
+    if (!strcasecmp(cmd->argv[2], "-details"))
+      attrs = silc_client_attributes_request(0);
+
+    if (!attrs || cmd->argc > 3) {
+      int c = atoi(cmd->argc > 3 ? cmd->argv[3] : cmd->argv[2]);
+      SILC_PUT32_MSB(c, count);
+      tmp = count;
+    }
+
     buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
-                                           ++conn->cmd_ident, 2,
+                                           ++conn->cmd_ident, 3,
                                            1, cmd->argv[1], cmd->argv_lens[1],
-                                           2, count, sizeof(count));
+                                           2, tmp ? tmp : NULL, tmp ? 4 : 0,
+                                           3, attrs ? attrs->data : NULL,
+                                           attrs ? attrs->len : 0);
   }
   silc_client_packet_send(cmd->client, cmd->conn->sock,
                          SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
@@ -492,8 +493,6 @@ SILC_CLIENT_CMD_FUNC(topic)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-         "You are not on any channel");
       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
@@ -503,16 +502,12 @@ SILC_CLIENT_CMD_FUNC(topic)
   }
 
   if (!conn->current_channel) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-       "You are not on that channel");
     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)) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-       "You are not on that channel");
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
   }
@@ -574,8 +569,6 @@ SILC_CLIENT_CMD_FUNC(invite)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-         "You are not on any channel");
       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
@@ -586,8 +579,6 @@ SILC_CLIENT_CMD_FUNC(invite)
 
     channel = silc_client_get_channel(cmd->client, conn, name);
     if (!channel) {
-      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-         "You are on that channel");
       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
@@ -671,7 +662,7 @@ SILC_TASK_CALLBACK(silc_client_command_quit_cb)
   QuitInternal q = (QuitInternal)context;
 
   /* Close connection */
-  q->client->internal->ops->disconnect(q->client, q->conn);
+  q->client->internal->ops->disconnected(q->client, q->conn, 0, NULL);
   silc_client_close_connection(q->client, q->conn->sock->user_data);
 
   silc_free(q);
@@ -891,6 +882,38 @@ SILC_CLIENT_CMD_FUNC(info)
   silc_client_command_free(cmd);
 }
 
+/* Command STATS. Shows server and network statistics. */
+
+SILC_CLIENT_CMD_FUNC(stats)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer, idp = NULL;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
+    goto out;
+  }
+
+  idp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER); 
+  
+  /* Send the command */
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_STATS,
+                                         ++conn->cmd_ident, 1,
+                                         SILC_ID_SERVER, 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);
+
+  /* Notify application */
+  COMMAND(SILC_STATUS_OK);
+
+ out:
+  silc_client_command_free(cmd);
+}
+
 /* Command PING. Sends ping to server. This is used to test the 
    communication channel. */
 
@@ -995,8 +1018,7 @@ SILC_CLIENT_CMD_FUNC(join)
       auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
                                                cmd->client->private_key,
                                                cmd->client->rng, 
-                                               cmd->client->internal->
-                                               sha1hash,
+                                               cmd->client->sha1hash,
                                                conn->local_id,
                                                SILC_ID_CLIENT);
       i++;
@@ -1261,8 +1283,6 @@ SILC_CLIENT_CMD_FUNC(cmode)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-         "You are not on any channel");
       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
@@ -1273,8 +1293,6 @@ SILC_CLIENT_CMD_FUNC(cmode)
 
     channel = silc_client_get_channel(cmd->client, conn, name);
     if (!channel) {
-      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-         "You are on that channel");
       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
@@ -1412,8 +1430,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
        auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
                                                  cmd->client->private_key,
                                                  cmd->client->rng, 
-                                                 cmd->client->internal->
-                                                 sha1hash,
+                                                 cmd->client->sha1hash,
                                                  conn->local_id,
                                                  SILC_ID_CLIENT);
        arg = auth->data;
@@ -1492,8 +1509,6 @@ SILC_CLIENT_CMD_FUNC(cumode)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-         "You are not on any channel");
       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
@@ -1504,8 +1519,6 @@ SILC_CLIENT_CMD_FUNC(cumode)
 
     channel = silc_client_get_channel(cmd->client, conn, name);
     if (!channel) {
-      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-         "You are on that channel");
       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
@@ -1568,8 +1581,7 @@ SILC_CLIENT_CMD_FUNC(cumode)
        auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
                                                  cmd->client->private_key,
                                                  cmd->client->rng,
-                                                 cmd->client->internal->
-                                                 sha1hash,
+                                                 cmd->client->sha1hash,
                                                  conn->local_id,
                                                  SILC_ID_CLIENT);
        mode |= SILC_CHANNEL_UMODE_CHANFO;
@@ -1601,6 +1613,12 @@ SILC_CLIENT_CMD_FUNC(cumode)
       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(SILC_STATUS_ERR_UNKNOWN_MODE);
       goto out;
@@ -1667,8 +1685,6 @@ SILC_CLIENT_CMD_FUNC(kick)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-         "You are not on any channel");
       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
@@ -1678,16 +1694,12 @@ SILC_CLIENT_CMD_FUNC(kick)
   }
 
   if (!conn->current_channel) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-       "You are not on that channel");
     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)) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-       "You are not on that channel");
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
   }
@@ -1760,7 +1772,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);
 
@@ -1829,7 +1842,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);
 
@@ -1901,8 +1915,6 @@ SILC_CLIENT_CMD_FUNC(ban)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-         "You are not on any channel");
       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
@@ -1913,8 +1925,6 @@ SILC_CLIENT_CMD_FUNC(ban)
 
     channel = silc_client_get_channel(cmd->client, conn, name);
     if (!channel) {
-      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-         "You are noton that channel");
       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
@@ -2051,8 +2061,6 @@ SILC_CLIENT_CMD_FUNC(leave)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-         "You are not on any channel");
       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
@@ -2064,8 +2072,6 @@ SILC_CLIENT_CMD_FUNC(leave)
   /* Get the channel entry */
   channel = silc_client_get_channel(cmd->client, conn, name);
   if (!channel) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-       "You are not on that channel");
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
   }
@@ -2124,8 +2130,6 @@ SILC_CLIENT_CMD_FUNC(users)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-         "You are not on any channel");
       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
@@ -2466,6 +2470,7 @@ void silc_client_commands_register(SilcClient client)
   SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
   SILC_CLIENT_CMD(kill, KILL, "KILL", 3);
   SILC_CLIENT_CMD(info, INFO, "INFO", 2);
+  SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
   SILC_CLIENT_CMD(ping, PING, "PING", 2);
   SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
   SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
@@ -2501,6 +2506,7 @@ void silc_client_commands_unregister(SilcClient client)
   SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
   SILC_CLIENT_CMDU(kill, KILL, "KILL");
   SILC_CLIENT_CMDU(info, INFO, "INFO");
+  SILC_CLIENT_CMDU(stats, STATS, "STATS");
   SILC_CLIENT_CMDU(ping, PING, "PING");
   SILC_CLIENT_CMDU(oper, OPER, "OPER");
   SILC_CLIENT_CMDU(join, JOIN, "JOIN");
@@ -2521,3 +2527,97 @@ void silc_client_commands_unregister(SilcClient client)
   SILC_CLIENT_CMDU(close, PRIV_CLOSE, "CLOSE");
   SILC_CLIENT_CMDU(shutdown, PRIV_SHUTDOWN, "SHUTDOWN");
 }
+
+/**** Client side incoming command handling **********************************/
+
+void silc_client_command_process_whois(SilcClient client,
+                                      SilcSocketConnection sock,
+                                      SilcCommandPayload payload,
+                                      SilcArgumentPayload args);
+
+/* Client is able to receive some command packets even though they are
+   special case.  Server may send WHOIS command to the client to retrieve
+   Requested Attributes information for WHOIS query the server is
+   processing. This function currently handles only the WHOIS command,
+   but if in the future for commands may arrive then this can be made
+   to support other commands too. */
+
+void silc_client_command_process(SilcClient client,
+                                SilcSocketConnection sock,
+                                SilcPacketContext *packet)
+{
+  SilcCommandPayload payload;
+  SilcCommand command;
+  SilcArgumentPayload args;
+
+  /* Get command payload from packet */
+  payload = silc_command_payload_parse(packet->buffer->data,
+                                      packet->buffer->len);
+  if (!payload) {
+    /* Silently ignore bad reply packet */
+    SILC_LOG_DEBUG(("Bad command packet"));
+    return;
+  }
+
+  /* Get arguments */
+  args = silc_command_get_args(payload);
+  
+  /* Get the command */
+  command = silc_command_get(payload);
+  switch (command) {
+
+  case SILC_COMMAND_WHOIS:
+    /* Ignore everything if requested by application */
+    if (client->internal->params->ignore_requested_attributes)
+      break;
+
+    silc_client_command_process_whois(client, sock, payload, args);
+    break;
+
+  default:
+    break;
+  }
+
+  silc_command_payload_free(payload);
+}
+
+void silc_client_command_process_whois(SilcClient client,
+                                      SilcSocketConnection sock,
+                                      SilcCommandPayload payload,
+                                      SilcArgumentPayload args)
+{
+  SilcDList attrs;
+  unsigned char *tmp;
+  SilcUInt32 tmp_len;
+  SilcBuffer buffer, packet;
+
+  SILC_LOG_DEBUG(("Received WHOIS command"));
+
+  /* Try to take the Requested Attributes */
+  tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+  if (!tmp)
+    return;
+
+  attrs = silc_attribute_payload_parse(tmp, tmp_len);
+  if (!attrs)
+    return;
+
+  /* Process requested attributes */
+  buffer = silc_client_attributes_process(client, sock, attrs);
+  if (!buffer) {
+    silc_attribute_payload_list_free(attrs);
+    return;
+  }
+
+  /* Send the attributes back */
+  packet =
+    silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
+                                        SILC_STATUS_OK, 0,
+                                        silc_command_get_ident(payload),
+                                        1, 11, buffer->data, buffer->len);
+  silc_client_packet_send(client, sock, SILC_PACKET_COMMAND_REPLY,
+                         NULL, 0, NULL, NULL, packet->data, 
+                         packet->len, TRUE);
+  silc_buffer_free(packet);
+  silc_buffer_free(buffer);
+}