Updates.
[silc.git] / apps / silcd / command_reply.c
index f72bed1e22db21cf04caa2f93976c11307079f4e..073bf822477dc3332e990347004644c83f8ef499 100644 (file)
@@ -25,6 +25,7 @@
 
 #define COMMAND_CHECK_STATUS                                             \
 do {                                                                     \
+  SILC_LOG_DEBUG(("Start"));                                             \
   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
   if (status != SILC_STATUS_OK) {                                        \
     silc_server_command_reply_free(cmd);                                 \
@@ -34,6 +35,7 @@ do {                                                                    \
 
 #define COMMAND_CHECK_STATUS_LIST                                        \
 do {                                                                     \
+  SILC_LOG_DEBUG(("Start"));                                             \
   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
   if (status != SILC_STATUS_OK &&                                        \
       status != SILC_STATUS_LIST_START &&                                \
@@ -49,8 +51,9 @@ do {                                                                    \
 SilcServerCommandReply silc_command_reply_list[] =
 {
   SILC_SERVER_CMD_REPLY(join, JOIN),
-  SILC_SERVER_CMD_REPLY(identify, WHOIS),
+  SILC_SERVER_CMD_REPLY(whois, WHOIS),
   SILC_SERVER_CMD_REPLY(identify, IDENTIFY),
+  SILC_SERVER_CMD_REPLY(users, USERS),
 
   { NULL, 0 },
 };
@@ -96,7 +99,7 @@ void silc_server_command_reply_process(SilcServer server,
     if (cmd->cmd == command)
       break;
 
-  if (cmd == NULL) {
+  if (cmd == NULL || !cmd->cb) {
     silc_free(ctx);
     return;
   }
@@ -115,18 +118,21 @@ void silc_server_command_reply_free(SilcServerCommandReplyContext cmd)
 }
 
 /* Caches the received WHOIS information. If we are normal server currently
-   we cache global information only for short period of time.  If we are
-   router we want to cache them a bit longer since we can receive information
-   if any of the information becomes invalid. Normal server cannot receive
-   that information. Returns FALSE if something was wrong with the reply. */
+   we cache global information only for short period of time.  */
+/* XXX cache expirying not implemented yet! */
 
 static char
 silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
 {
+  SilcServer server = cmd->server;
   int len, id_len;
   unsigned char *id_data;
   char *nickname, *username, *realname;
   SilcClientID *client_id;
+  SilcClientEntry client;
+  SilcIDCacheEntry cache = NULL;
+  char global = FALSE;
+  char *nick;
 
   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
@@ -137,6 +143,70 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
 
   client_id = silc_id_payload_parse_id(id_data, id_len);
 
+  /* Check if we have this client cached already. */
+
+  client = silc_idlist_find_client_by_id(server->local_list, client_id,
+                                        &cache);
+  if (!client) {
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, &cache);
+    global = TRUE;
+  }
+
+  if (!client) {
+    /* If router did not find such Client ID in its lists then this must
+       be bogus client or some router in the net is buggy. */
+    if (server->server_type == SILC_ROUTER)
+      return FALSE;
+
+    /* Take hostname out of nick string if it includes it. */
+    if (strchr(nickname, '@')) {
+      int len = strcspn(nickname, "@");
+      nick = silc_calloc(len + 1, sizeof(char));
+      memcpy(nick, nickname, len);
+    } else {
+      nick = strdup(nickname);
+    }
+
+    /* We don't have that client anywhere, add it. The client is added
+       to global list since server didn't have it in the lists so it must be 
+       global. */
+    silc_idlist_add_client(server->global_list, nick,
+                          strdup(username), 
+                          strdup(realname), client_id, NULL, NULL);
+  } else {
+    /* We have the client already, update the data */
+
+    SILC_LOG_DEBUG(("Updating client data"));
+
+    /* Take hostname out of nick string if it includes it. */
+    if (strchr(nickname, '@')) {
+      int len = strcspn(nickname, "@");
+      nick = silc_calloc(len + 1, sizeof(char));
+      memcpy(nick, nickname, len);
+    } else {
+      nick = strdup(nickname);
+    }
+
+    if (client->nickname)
+      silc_free(client->nickname);
+    if (client->username)
+      silc_free(client->username);
+    if (client->userinfo)
+      silc_free(client->userinfo);
+    
+    client->nickname = nick;
+    client->username = strdup(username);
+    client->userinfo = strdup(realname);
+
+    if (cache) {
+      cache->data = nick;
+      silc_idcache_sort_by_data(global ? server->global_list->clients : 
+                               server->local_list->clients);
+    }
+
+    silc_free(client_id);
+  }
 
   return TRUE;
 }
@@ -150,13 +220,15 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
 SILC_SERVER_CMD_REPLY_FUNC(whois)
 {
   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
-  SilcServer server = cmd->server;
   SilcCommandStatus status;
 
-  SILC_LOG_DEBUG(("Start"));
-
   COMMAND_CHECK_STATUS_LIST;
 
+  if (!silc_server_command_reply_whois_save(cmd))
+    goto out;
+
+  /* XXX */
+
   /* Process one identify reply */
   if (status == SILC_STATUS_OK) {
 
@@ -181,6 +253,100 @@ SILC_SERVER_CMD_REPLY_FUNC(whois)
   silc_server_command_reply_free(cmd);
 }
 
+/* Caches the received IDENTIFY information. */
+
+static char
+silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
+{
+  SilcServer server = cmd->server;
+  int len, id_len;
+  unsigned char *id_data;
+  char *nickname, *username;
+  SilcClientID *client_id;
+  SilcClientEntry client;
+  SilcIDCacheEntry cache = NULL;
+  char global = FALSE;
+  char *nick = NULL;
+
+  id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
+  nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
+  username = silc_argument_get_arg_type(cmd->args, 4, &len);
+  if (!id_data)
+    return FALSE;
+
+  client_id = silc_id_payload_parse_id(id_data, id_len);
+
+  /* Check if we have this client cached already. */
+
+  client = silc_idlist_find_client_by_id(server->local_list, client_id,
+                                        &cache);
+  if (!client) {
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, &cache);
+    global = TRUE;
+  }
+
+  if (!client) {
+    /* If router did not find such Client ID in its lists then this must
+       be bogus client or some router in the net is buggy. */
+    if (server->server_type == SILC_ROUTER)
+      return FALSE;
+
+    /* Take hostname out of nick string if it includes it. */
+    if (nickname) {
+      if (strchr(nickname, '@')) {
+       int len = strcspn(nickname, "@");
+       nick = silc_calloc(len + 1, sizeof(char));
+       memcpy(nick, nickname, len);
+      } else {
+       nick = strdup(nickname);
+      }
+    }
+
+    /* We don't have that client anywhere, add it. The client is added
+       to global list since server didn't have it in the lists so it must be 
+       global. */
+    silc_idlist_add_client(server->global_list, nick,
+                          username ? strdup(username) : NULL, NULL,
+                          client_id, NULL, NULL);
+  } else {
+    /* We have the client already, update the data */
+
+    SILC_LOG_DEBUG(("Updating client data"));
+
+    /* Take hostname out of nick string if it includes it. */
+    if (nickname) {
+      if (strchr(nickname, '@')) {
+       int len = strcspn(nickname, "@");
+       nick = silc_calloc(len + 1, sizeof(char));
+       memcpy(nick, nickname, len);
+      } else {
+       nick = strdup(nickname);
+      }
+    }
+
+    if (nickname && client->nickname) {
+      silc_free(client->nickname);
+      client->nickname = nick;
+    }
+
+    if (username && client->username) {
+      silc_free(client->username);
+      client->username = strdup(username);
+    }
+
+    if (nickname && cache) {
+      cache->data = nick;
+      silc_idcache_sort_by_data(global ? server->global_list->clients : 
+                               server->local_list->clients);
+    }
+
+    silc_free(client_id);
+  }
+
+  return TRUE;
+}
+
 /* Received reply for forwarded IDENTIFY command. We have received the
    requested identify information now and we will cache it. After this we
    will call the pending command so that the requestee gets the information
@@ -189,33 +355,17 @@ SILC_SERVER_CMD_REPLY_FUNC(whois)
 SILC_SERVER_CMD_REPLY_FUNC(identify)
 {
   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
-  SilcServer server = cmd->server;
   SilcCommandStatus status;
 
-  SILC_LOG_DEBUG(("Start"));
-
   COMMAND_CHECK_STATUS_LIST;
 
-  /* Process one identify reply */
-  if (status == SILC_STATUS_OK) {
-    SilcClientID *client_id;
-    unsigned int len;
-    unsigned char *id_data;
-    char *nickname, *username;
+  if (!silc_server_command_reply_identify_save(cmd))
+    goto out;
 
-    id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
-    nickname = silc_argument_get_arg_type(cmd->args, 3, NULL);
-    if (!id_data || !nickname)
-      goto out;
+  /* XXX */
 
-    username = silc_argument_get_arg_type(cmd->args, 4, NULL);
-    client_id = silc_id_payload_parse_id(id_data, len);
+  if (status == SILC_STATUS_OK) {
 
-    /* Add the client always to our global list. If normal or router server
-       ever gets here it means they don't have this client's information
-       in their cache. */
-    silc_idlist_add_client(server->global_list, strdup(nickname),
-                          username, NULL, client_id, NULL, NULL);
   }
 
   if (status == SILC_STATUS_LIST_START) {
@@ -251,14 +401,14 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
   unsigned int len;
   unsigned char *id_string;
   char *channel_name, *tmp;
-
-  SILC_LOG_DEBUG(("Start"));
+  unsigned int mode, created;
+  SilcBuffer keyp;
 
   COMMAND_CHECK_STATUS;
 
   /* Get channel name */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
-  if (!tmp)
+  channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
+  if (!channel_name)
     goto out;
 
   /* Get channel ID */
@@ -266,28 +416,66 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
   if (!id_string)
     goto out;
 
-  channel_name = strdup(tmp);
-  id = silc_id_payload_parse_id(id_string, len);
+  /* Get mode mask */
+  tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
+  if (!tmp)
+    goto out;
+  SILC_GET32_MSB(mode, tmp);
+
+  /* Get created boolean value */
+  tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
+  if (!tmp)
+    goto out;
+  SILC_GET32_MSB(created, tmp);
 
-  /* XXX We should check that we have sent JOIN command to the router
-     in the first place. Also should check that we don't have the channel
-     already in the cache. These checks must be made because of possible
-     buggy routers. */
+  /* Get channel key */
+  tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
+  if (!tmp)
+    goto out;
+  keyp = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
+  silc_buffer_put(keyp, tmp, len);
 
-  SILC_LOG_DEBUG(("Adding new channel %s id(%s)", channel_name,
-                 silc_id_render(id, SILC_ID_CHANNEL)));
+  id = silc_id_payload_parse_id(id_string, len);
 
-  /* Add the channel to our local list. */
-  entry = silc_idlist_add_channel(server->local_list, channel_name, 
-                                 SILC_CHANNEL_MODE_NONE, id, 
-                                 server->router, NULL);
+  /* See whether we already have the channel. */
+  entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
   if (!entry) {
-    silc_free(channel_name);
+    /* Add new channel */
+
+    SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)", 
+                   (created == 0 ? "existing" : "created"), channel_name,
+                   silc_id_render(id, SILC_ID_CHANNEL)));
+
+    /* Add the channel to our local list. */
+    entry = silc_idlist_add_channel(server->local_list, strdup(channel_name), 
+                                   SILC_CHANNEL_MODE_NONE, id, 
+                                   server->router, NULL);
+    if (!entry) {
+      silc_free(id);
+      goto out;
+    }
+  } else {
     silc_free(id);
-    goto out;
   }
 
-  //entry->global_users = TRUE;
+  /* If channel was not created we know there is global users on the 
+     channel. */
+  entry->global_users = (created == 0 ? TRUE : FALSE);
+
+  /* If channel was just created the mask must be zero */
+  if (!entry->global_users && mode) {
+    SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
+                   "new channel, forcing it to zero", cmd->sock->hostname));
+    mode = 0;
+  }
+
+  /* Save channel mode */
+  entry->mode = mode;
+
+  /* Save channel key */
+  silc_server_save_channel_key(server, keyp, entry);
+  silc_buffer_free(keyp);
 
   /* Execute any pending commands */
   SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_JOIN);
@@ -295,3 +483,112 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
  out:
   silc_server_command_reply_free(cmd);
 }
+
+SILC_SERVER_CMD_REPLY_FUNC(users)
+{
+  SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
+  SilcServer server = cmd->server;
+  SilcCommandStatus status;
+  SilcChannelEntry channel;
+  SilcChannelID *channel_id = NULL;
+  SilcBuffer client_id_list;
+  SilcBuffer client_mode_list;
+  unsigned char *tmp;
+  unsigned int tmp_len;
+  unsigned int list_count, i;
+
+  COMMAND_CHECK_STATUS;
+
+  /* Get channel ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (!tmp)
+    goto out;
+  channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+
+  /* Get the list count */
+  tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+  if (!tmp)
+    goto out;
+  SILC_GET32_MSB(list_count, tmp);
+
+  /* Get Client ID list */
+  tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
+  if (!tmp)
+    goto out;
+
+  client_id_list = silc_buffer_alloc(tmp_len);
+  silc_buffer_pull_tail(client_id_list, tmp_len);
+  silc_buffer_put(client_id_list, tmp, tmp_len);
+
+  /* Get client mode list */
+  tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
+  if (!tmp)
+    goto out;
+
+  client_mode_list = silc_buffer_alloc(tmp_len);
+  silc_buffer_pull_tail(client_mode_list, tmp_len);
+  silc_buffer_put(client_mode_list, tmp, tmp_len);
+
+  /* Get channel entry */
+  channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                          channel_id, NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel)
+      goto out;
+  }
+
+  /* Cache the received Client ID's and modes. This cache expires
+     whenever server sends notify message to channel. It means two things;
+     some user has joined or leaved the channel. XXX! */
+  for (i = 0; i < list_count; i++) {
+    unsigned short idp_len;
+    unsigned int mode;
+    SilcClientID *client_id;
+    SilcClientEntry client;
+
+    /* Client ID */
+    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);
+    
+    /* Mode */
+    SILC_GET32_MSB(mode, client_mode_list->data);
+    silc_buffer_pull(client_mode_list, 4);
+
+    /* Check if we have this client cached already. */
+    client = silc_idlist_find_client_by_id(server->local_list, client_id,
+                                          NULL);
+    if (!client)
+      client = silc_idlist_find_client_by_id(server->global_list, 
+                                            client_id, NULL);
+    if (!client) {
+      /* If router did not find such Client ID in its lists then this must
+        be bogus client or some router in the net is buggy. */
+      if (server->server_type == SILC_ROUTER)
+       goto out;
+
+      /* We don't have that client anywhere, add it. The client is added
+        to global list since server didn't have it in the lists so it must be 
+        global. */
+      silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
+                            client_id, NULL, NULL);
+    } else {
+      /* We have the client already. */
+      silc_free(client_id);
+    }
+  }
+
+  silc_buffer_free(client_id_list);
+  silc_buffer_free(client_mode_list);
+
+  /* Execute any pending commands */
+  SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_USERS);
+
+ out:
+  if (channel_id)
+    silc_free(channel_id);
+  silc_server_command_reply_free(cmd);
+}