Added STATS command. Patch by Ville Räsänen.
[silc.git] / lib / silcclient / command_reply.c
index ddb4b0d34ff8a8928818719b8d0b7c52cb9b0523..5772d41493df95e08f45791e0f42a8daaaa40f76 100644 (file)
 #define SAY cmd->client->internal->ops->say
 
 /* All functions that call the COMMAND_CHECK_STATUS macro must have 
-   out: goto label. */
+   out: and err: goto labels. out label should call the pending
+   command replies, and the err label just handle error condition. */
 
 #define COMMAND_CHECK_STATUS                                   \
 do {                                                           \
   SILC_LOG_DEBUG(("Start"));                                   \
   if (!silc_command_get_status(cmd->payload, NULL, NULL)) {    \
+    if (SILC_STATUS_IS_ERROR(cmd->status)) {                   \
+      /* Single error */                                       \
+      COMMAND_REPLY_ERROR;                                     \
+      goto out;                                                        \
+    }                                                          \
+    /* List of errors */                                       \
     COMMAND_REPLY_ERROR;                                       \
-    goto out;                                                  \
+    if (cmd->status == SILC_STATUS_LIST_END)                   \
+      goto out;                                                        \
+    goto err;                                                  \
+  }                                                            \
+} while(0)
+
+/* Same as COMMAND_CHECK_STATUS but doesn't call client operation */
+#define COMMAND_CHECK_STATUS_I                                 \
+do {                                                           \
+  SILC_LOG_DEBUG(("Start"));                                   \
+  if (!silc_command_get_status(cmd->payload, NULL, NULL)) {    \
+    if (SILC_STATUS_IS_ERROR(cmd->status))                     \
+      goto out;                                                        \
+    if (cmd->status == SILC_STATUS_LIST_END)                   \
+      goto out;                                                        \
+    goto err;                                                  \
   }                                                            \
 } while(0)
 
@@ -74,6 +96,7 @@ void silc_client_command_reply_process(SilcClient client,
   /* Allocate command reply context. This must be free'd by the
      command reply routine receiving it. */
   ctx = silc_calloc(1, sizeof(*ctx));
+  ctx->users++;
   ctx->client = client;
   ctx->sock = sock;
   ctx->payload = payload;
@@ -112,11 +135,26 @@ void silc_client_command_reply_process(SilcClient client,
   }
 }
 
+/* Duplicate Command Reply Context by adding reference counter. The context
+   won't be free'd untill it hits zero. */
+
+SilcClientCommandReplyContext 
+silc_client_command_reply_dup(SilcClientCommandReplyContext cmd)
+{
+  cmd->users++;
+  SILC_LOG_DEBUG(("Command reply context %p refcnt %d->%d", cmd, 
+                 cmd->users - 1, cmd->users));
+  return cmd;
+}
+
 /* Free command reply context and its internals. */
 
 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
 {
-  if (cmd) {
+  cmd->users--;
+  SILC_LOG_DEBUG(("Command reply context %p refcnt %d->%d", cmd, 
+                 cmd->users + 1, cmd->users));
+  if (cmd->users < 1) {
     silc_command_payload_free(cmd->payload);
     silc_free(cmd);
   }
@@ -203,8 +241,7 @@ silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
     client_entry->fingerprint_len = fingerprint_len;
   }
 
-  if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING)
-    client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
+  client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
 
   /* Notify application */
   if (!cmd->callbacks_count && notify)
@@ -236,6 +273,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(whois)
  out:
   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
 
+ err:
   /* If we received notify for invalid ID we'll remove the ID if we
      have it cached. */
   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
@@ -312,6 +350,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(whowas)
 
  out:
   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS);
+ err:
   silc_client_command_reply_free(cmd);
 }
 
@@ -370,8 +409,7 @@ silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
                                name, info, NULL, 0);
     }
 
-    if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING)
-      client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
+    client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
 
     /* Notify application */
     if (notify)
@@ -394,8 +432,12 @@ silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
          COMMAND_REPLY_ERROR;
        return;
       }
+    } else {
+      silc_client_update_server(client, conn, server_entry, name, info);
     }
 
+    server_entry->resolve_cmd_ident = 0;
+
     /* Notify application */
     if (notify)
       COMMAND_REPLY((ARGS, server_entry, name, info));
@@ -454,6 +496,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(identify)
  out:
   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
 
+ err:
   /* If we received notify for invalid ID we'll remove the ID if we
      have it cached. */
   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
@@ -599,6 +642,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(list)
  out:
   silc_free(channel_id);
   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
+ err:
   silc_client_command_reply_free(cmd);
 }
 
@@ -786,6 +830,38 @@ SILC_CLIENT_CMD_REPLY_FUNC(info)
   silc_client_command_reply_free(cmd);
 }
 
+/* Received reply to STATS command. */
+
+SILC_CLIENT_CMD_REPLY_FUNC(stats)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  unsigned char *tmp, *buf = NULL;
+  SilcUInt32 len, buf_len = 0;
+
+  if (cmd->error != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_get_status_message(cmd->error));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Get server ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+  if (!tmp)
+    goto out;
+
+  /* Get statistics structure */
+  buf = silc_argument_get_arg_type(cmd->args, 3, &buf_len);
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS, buf, buf_len));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_STATS);
+  silc_client_command_reply_free(cmd);
+}
+
 /* Received reply to PING command. The reply time is shown to user. */
 
 SILC_CLIENT_CMD_REPLY_FUNC(ping)
@@ -865,7 +941,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
   }
 
   argc = silc_argument_get_arg_num(cmd->args);
-  if (argc < 7 || argc > 14) {
+  if (argc < 7) {
     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
        "Cannot join channel: Bad reply packet");
     COMMAND_REPLY_ERROR;
@@ -1568,22 +1644,6 @@ silc_client_command_reply_users_save(SilcClientCommandReplyContext cmd,
     /* Check if we have this client cached already. */
     client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
     if (!client_entry || !client_entry->username || !client_entry->realname) {
-      if (client_entry) {
-       if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
-         /* Attach to this resolving and wait until it finishes */
-         silc_client_command_pending(conn, SILC_COMMAND_NONE, 
-                                     client_entry->resolve_cmd_ident,
-                                     get_clients, cmd);
-         wait_res = TRUE;
-
-         silc_buffer_pull(&client_id_list, idp_len);
-         silc_buffer_pull(&client_mode_list, 4);
-         continue;
-       }
-       client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
-       client_entry->resolve_cmd_ident = conn->cmd_ident + 1;
-      }
-
       /* No we don't have it (or it is incomplete in information), query
         it from the server. Assemble argument table that will be sent
         for the WHOIS command later. */
@@ -1595,7 +1655,7 @@ silc_client_command_reply_users_save(SilcClientCommandReplyContext cmd,
                                    (res_argc + 1));
       res_argv[res_argc] = client_id_list.data;
       res_argv_lens[res_argc] = idp_len;
-      res_argv_types[res_argc] = res_argc + 3;
+      res_argv_types[res_argc] = res_argc + 4;
       res_argc++;
     } else {
       if (!silc_client_on_channel(channel, client_entry)) {
@@ -1793,10 +1853,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(whois_i)
   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
 
-  SILC_LOG_DEBUG(("Start"));
-
-  if (cmd->error != SILC_STATUS_OK)
-    goto out;
+  COMMAND_CHECK_STATUS_I;
 
   /* Save WHOIS info */
   silc_client_command_reply_whois_save(cmd, cmd->status, FALSE);
@@ -1811,6 +1868,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(whois_i)
  out:
   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
 
+ err:
   /* If we received notify for invalid ID we'll remove the ID if we
      have it cached. */
   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
@@ -1844,10 +1902,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(identify_i)
   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
 
-  SILC_LOG_DEBUG(("Start"));
-
-  if (cmd->error != SILC_STATUS_OK)
-    goto out;
+  COMMAND_CHECK_STATUS_I;
 
   /* Save IDENTIFY info */
   silc_client_command_reply_identify_save(cmd, cmd->status, FALSE);
@@ -1862,6 +1917,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(identify_i)
  out:
   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
 
+ err:
   /* If we received notify for invalid ID we'll remove the ID if we
      have it cached. */
   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
@@ -1900,10 +1956,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(info_i)
   char *server_name, *server_info;
   SilcUInt32 len;
 
-  SILC_LOG_DEBUG(("Start"));
-
-  if (cmd->error != SILC_STATUS_OK)
-    goto out;
+  COMMAND_CHECK_STATUS_I;
 
   /* Get server ID */
   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
@@ -1935,6 +1988,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(info_i)
  out:
   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
   silc_free(server_id);
+ err:
   silc_client_command_reply_free(cmd);
 }
 
@@ -1964,10 +2018,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(users_i)
 {
   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
 
-  SILC_LOG_DEBUG(("Start"));
-
-  if (cmd->error != SILC_STATUS_OK)
-    goto out;
+  COMMAND_CHECK_STATUS_I;
 
   /* Save USERS info */
   if (silc_client_command_reply_users_save(
@@ -1979,6 +2030,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(users_i)
  out:
   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
 
+ err:
   /* Unregister this command reply */
   silc_client_command_unregister(cmd->client, SILC_COMMAND_USERS,
                                 NULL, silc_client_command_reply_users_i,