Code auditing weekend results and fixes committing.
[silc.git] / lib / silcclient / command_reply.c
index a4e98d55d8180d51fdb9c1bb4cb5498abbecf177..b75c78b98da809bc64b9e4c6cd4d387192c696a8 100644 (file)
@@ -134,8 +134,11 @@ void silc_client_command_reply_process(SilcClient client,
                                       SilcPacketContext *packet)
 {
   SilcBuffer buffer = packet->buffer;
+  SilcClientCommandReply *cmd;
   SilcClientCommandReplyContext ctx;
   SilcCommandPayload payload;
+  SilcCommand command;
+  unsigned short ident;
 
   /* Get command reply payload from packet */
   payload = silc_command_payload_parse(buffer);
@@ -153,12 +156,24 @@ void silc_client_command_reply_process(SilcClient client,
   ctx->payload = payload;
   ctx->args = silc_command_get_args(ctx->payload);
   ctx->packet = packet;
+  ident = silc_command_get_ident(ctx->payload);
       
   /* Check for pending commands and mark to be exeucted */
-  SILC_CLIENT_COMMAND_CHECK_PENDING(ctx);
-  
+  silc_client_command_pending_check(sock->user_data, ctx, 
+                                   silc_command_get(ctx->payload), ident);
+
   /* Execute command reply */
-  SILC_CLIENT_COMMAND_REPLY_EXEC(ctx);
+  command = silc_command_get(ctx->payload);
+  for (cmd = silc_command_reply_list; cmd->cb; cmd++)
+    if (cmd->cmd == command)
+      break;
+
+  if (cmd == NULL || !cmd->cb) {
+    silc_free(ctx);
+    return;
+  }
+
+  cmd->cb(ctx);
 }
 
 /* Returns status message string */
@@ -214,6 +229,10 @@ silc_client_command_reply_whois_print(SilcClientCommandReplyContext cmd,
   }
   
   client_id = silc_id_payload_parse_id(id_data, len);
+  if (!client_id) {
+    COMMAND_REPLY_ERROR;
+    return;
+  }
   
   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
   if (nickname) {
@@ -332,6 +351,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(whois)
     silc_client_command_reply_whois_print(cmd, status);
   }
 
+  /* Execute any pending command callbacks */
   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
 
  out:
@@ -394,6 +414,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(identify)
     if (!id_data)
       goto out;
     client_id = silc_id_payload_parse_id(id_data, len);
+    if (!client_id)
+      goto out;
 
     nickname = silc_argument_get_arg_type(cmd->args, 3, NULL);
     username = silc_argument_get_arg_type(cmd->args, 4, NULL);
@@ -439,6 +461,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(identify)
 
   }
 
+  /* Execute any pending command callbacks */
   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
 
  out:
@@ -478,11 +501,18 @@ SILC_CLIENT_CMD_REPLY_FUNC(nick)
   /* Take received Client ID */
   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
   idp = silc_id_payload_parse_data(tmp, len);
+  if (!idp) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
   silc_client_receive_new_id(cmd->client, cmd->sock, idp);
     
   /* Notify application */
   COMMAND_REPLY((ARGS, conn->local_entry));
 
+  /* Execute any pending command callbacks */
+  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_NICK);
+
  out:
   silc_client_command_reply_free(cmd);
 }
@@ -531,6 +561,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(topic)
     goto out;
 
   channel_id = silc_id_payload_parse_id(tmp, len);
+  if (!channel_id)
+    goto out;
 
   /* Get the channel name */
   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
@@ -549,6 +581,9 @@ SILC_CLIENT_CMD_REPLY_FUNC(topic)
   /* Notify application */
   COMMAND_REPLY((ARGS, channel, topic));
 
+  /* Execute any pending command callbacks */
+  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_TOPIC);
+
  out:
   silc_client_command_reply_free(cmd);
 }
@@ -575,6 +610,9 @@ SILC_CLIENT_CMD_REPLY_FUNC(invite)
   /* Notify application */
   COMMAND_REPLY((ARGS));
 
+  /* Execute any pending command callbacks */
+  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_INVITE);
+
   silc_client_command_reply_free(cmd);
 }
  
@@ -624,6 +662,9 @@ SILC_CLIENT_CMD_REPLY_FUNC(info)
   /* Notify application */
   COMMAND_REPLY((ARGS, NULL, (char *)tmp));
 
+  /* Execute any pending command callbacks */
+  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_INFO);
+
  out:
   silc_client_command_reply_free(cmd);
 }
@@ -652,7 +693,12 @@ SILC_CLIENT_CMD_REPLY_FUNC(ping)
   }
 
   curtime = time(NULL);
-  id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
+  id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
+                     cmd->packet->src_id_type);
+  if (!id) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
 
   for (i = 0; i < conn->ping_count; i++) {
     if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
@@ -676,6 +722,9 @@ SILC_CLIENT_CMD_REPLY_FUNC(ping)
 
   silc_free(id);
 
+  /* Execute any pending command callbacks */
+  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_PING);
+
  out:
   silc_client_command_reply_free(cmd);
 }
@@ -735,6 +784,11 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
     goto out;
   }
   idp = silc_id_payload_parse_data(tmp, len);
+  if (!idp) {
+    COMMAND_REPLY_ERROR;
+    silc_free(channel_name);
+    goto out;
+  }
 
   /* Get channel mode */
   tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
@@ -774,6 +828,9 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
   COMMAND_REPLY((ARGS, channel_name, conn->current_channel, mode,
                 NULL, NULL, topic));
 
+  /* Execute any pending command callbacks */
+  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_JOIN);
+
  out:
   silc_client_command_reply_free(cmd);
 }
@@ -834,6 +891,9 @@ SILC_CLIENT_CMD_REPLY_FUNC(motd)
   /* Notify application */
   COMMAND_REPLY((ARGS, motd));
 
+  /* Execute any pending command callbacks */
+  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_MOTD);
+
  out:
   silc_client_command_reply_free(cmd);
 }
@@ -869,6 +929,9 @@ SILC_CLIENT_CMD_REPLY_FUNC(cmode)
   /* Notify application */
   COMMAND_REPLY((ARGS, tmp));
 
+  /* Execute any pending command callbacks */
+  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_CMODE);
+
  out:
   silc_client_command_reply_free(cmd);
 }
@@ -907,6 +970,10 @@ SILC_CLIENT_CMD_REPLY_FUNC(cumode)
     goto out;
   }
   client_id = silc_id_payload_parse_id(id, len);
+  if (!client_id) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
   
   /* Get client entry */
   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
@@ -919,6 +986,9 @@ SILC_CLIENT_CMD_REPLY_FUNC(cumode)
   COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
   silc_free(client_id);
   
+  /* Execute any pending command callbacks */
+  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_CUMODE);
+
  out:
   silc_client_command_reply_free(cmd);
 }
@@ -964,6 +1034,9 @@ SILC_CLIENT_CMD_REPLY_FUNC(leave)
   /* Notify application */
   COMMAND_REPLY((ARGS));
 
+  /* Execute any pending command callbacks */
+  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_LEAVE);
+
   silc_client_command_reply_free(cmd);
 }
 
@@ -982,11 +1055,10 @@ SILC_CLIENT_CMD_REPLY_FUNC(users)
   SilcBuffer client_id_list;
   SilcBuffer client_mode_list;
   unsigned char *tmp;
-  unsigned int tmp_len;
-  int i, k, len1, len2, list_count;
+  unsigned int tmp_len, list_count;
+  int i;
   unsigned char **res_argv = NULL;
   unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
-  char *name_list, *cp;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -1004,6 +1076,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(users)
   if (!tmp)
     goto out;
   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+  if (!channel_id)
+    goto out;
 
   /* Get the list count */
   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
@@ -1057,11 +1131,11 @@ SILC_CLIENT_CMD_REPLY_FUNC(users)
     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
     idp_len += 4;
     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
-    silc_buffer_pull(client_id_list, idp_len);
+    if (!client_id)
+      continue;
 
     /* Mode */
     SILC_GET32_MSB(mode, client_mode_list->data);
-    silc_buffer_pull(client_mode_list, 4);
 
     /* Check if we have this client cached already. */
     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
@@ -1089,106 +1163,49 @@ SILC_CLIENT_CMD_REPLY_FUNC(users)
       silc_free(client_id);
       id_cache = NULL;
     }
+
+    silc_buffer_pull(client_id_list, idp_len);
+    silc_buffer_pull(client_mode_list, 4);
   }
 
   /* Query the client information from server if the list included clients
      that we don't know about. */
   if (res_argc) {
-#if 0
     SilcBuffer res_cmd;
 
+    /* Send the IDENTIFY command to server */
     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
                                          res_argc, res_argv, res_argv_lens,
-                                         res_argv_types, 0);
-    silc_client_packet_send(cmd->client, cmd->conn->sock,
-                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
-                           buffer->data, buffer->len, TRUE);
-    goto out;
-#endif
-  }
-
-  name_list = cp;
-  for (i = 0; i < list_count; i++) {
-    int c = 0;
-    int nick_len = strcspn(name_list, " ");
-    char *nickname = silc_calloc(nick_len + 1, sizeof(*nickname));
-    memcpy(nickname, name_list, nick_len);
-
-    silc_list_start(channel->clients);
-    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
-      if (!strncmp(chu->client->nickname, nickname, 
-                  strlen(chu->client->nickname))) {
-       char t[8];
-       
-       if (!c) {
-         c++;
-         continue;
-       }
-       
-       memset(t, 0, sizeof(t));
-       chu->client->nickname = silc_calloc(strlen(nickname) + 8, 1);
-       snprintf(t, sizeof(t), "[%d]", c++);
-       strncat(chu->client->nickname, t, strlen(t));
-       strncat(chu->client->nickname, nickname, strlen(nickname));
-      }
-    }
-
-    silc_free(nickname);
+                                         res_argv_types, ++conn->cmd_ident);
+    silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
+                           NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
+                           TRUE);
+
+    /* Register pending command callback. After we've received the IDENTIFY
+       command reply we will reprocess this command reply by re-calling this
+       USERS command reply callback. */
+    silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
+                               silc_client_command_reply_users, cmd);
+
+    silc_buffer_free(res_cmd);
+    if (channel_id)
+      silc_free(channel_id);
+
+    silc_free(res_argv);
+    silc_free(res_argv_lens);
+    silc_free(res_argv_types);
+    return;
   }
 
-  /* XXX hmm... actually it is applications business to display this
-     information. We should just pass (as we do) the data to application and
-     let it to parse it and display it the way it wants. */
-  if (cmd->callback) {
-    cmd->client->ops->say(cmd->client, conn, "Users on %s", 
-                         channel->channel_name);
-    
-    silc_list_start(channel->clients);
-    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
-      SilcClientEntry e = chu->client;
-      char *m, tmp[80], line[80];
-
-      memset(line, 0, sizeof(line));
-      memset(tmp, 0, sizeof(tmp));
-      m = silc_client_chumode_char(chu->mode);
-
-      strcat(line, " ");
-      strcat(line, e->nickname);
-      strcat(line, e->server ? "@" : "");
-
-      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, " ");
-      }
-
-      strcat(line, "  H");
-      strcat(tmp, m ? m : "");
-      strcat(line, tmp);
+  /* We have all the clients on the channel cached now. Create a nice
+     output for user interface and notify application. */
 
-      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);
-    }
-
-  } else {
-    name_list = NULL;
-    len1 = 0;
-    k = 0;
+  if (!cmd->callback) {
+    /* Server has sent us USERS reply even when we haven't actually sent
+       USERS command. This is normal behaviour when joining to a channel.
+       Display some nice information on the user interface. */
+    int k = 0, len1 = 0, len2 = 0;
+    char *name_list = NULL;
 
     silc_list_start(channel->clients);
     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
@@ -1220,12 +1237,13 @@ SILC_CLIENT_CMD_REPLY_FUNC(users)
     silc_free(name_list);
   }
 
-  name_list = silc_argument_get_arg_type(cmd->args, 3, &len1);
-
   /* Notify application */
-  COMMAND_REPLY((ARGS, channel, name_list, client_id_list->head,
+  COMMAND_REPLY((ARGS, channel, client_id_list->head,
                 client_mode_list->head));
 
+  /* Execute any pending command callbacks */
+  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_USERS);
+
   silc_buffer_free(client_id_list);
   silc_buffer_free(client_mode_list);