Fixed entry resolving while processing incoming notify packets,
[silc.git] / lib / silcclient / command.c
index b1d233fc8c1a7f3f0ac6d4ec48104ed7366e8ae1..72a9c38fc08e1d6a5e9107d9392faab905fa3b4c 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2006 Pekka Riikonen
+  Copyright (C) 1997 - 2007 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -119,9 +119,6 @@ static void silc_client_command_resolve_continue(SilcClient client,
   if (status != SILC_STATUS_OK)
     silc_fsm_next(&cmd->thread, silc_client_command_continue_error);
 
-  if (clients)
-    silc_dlist_uninit(clients);
-
   /* Continue with the command */
   SILC_FSM_CALL_CONTINUE(&cmd->thread);
 }
@@ -203,7 +200,14 @@ static void silc_client_command_destructor(SilcFSMThread thread,
                                           void *fsm_context,
                                           void *destructor_context)
 {
-  silc_client_command_free(fsm_context);
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+
+  /* Removes commands that aren't waiting for reply but are waiting
+     for something.  They may not have been removed yet. */
+  silc_list_del(conn->internal->pending_commands, cmd);
+
+  silc_client_command_free(cmd);
 }
 
 /* Add a command pending a command reply.  Used internally by the library. */
@@ -254,6 +258,9 @@ static SilcUInt16 silc_client_command_send_vap(SilcClient client,
 
   SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
 
+  if (conn->internal->disconnected)
+    return 0;
+
   if (!cmd->cmd_ident)
     cmd->cmd_ident = silc_client_cmd_ident(conn);
 
@@ -297,6 +304,9 @@ silc_client_command_send_arg_array(SilcClient client,
 
   SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
 
+  if (conn->internal->disconnected)
+    return 0;
+
   if (!cmd->cmd_ident)
     cmd->cmd_ident = silc_client_cmd_ident(conn);
 
@@ -787,7 +797,7 @@ SILC_FSM_STATE(silc_client_command_identify)
 
 SILC_FSM_STATE(silc_client_command_nick)
 {
-  SilcClientCommandContext cmd = fsm_context;
+  SilcClientCommandContext cmd2, cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
 
   if (cmd->argc < 2) {
@@ -815,6 +825,19 @@ SILC_FSM_STATE(silc_client_command_nick)
     goto out;
   }
 
+  /* If JOIN command is active, wait for it to finish before sending NICK.
+     To avoid problems locally with changing IDs while joining, we do this. */
+  silc_mutex_lock(conn->internal->lock);
+  silc_list_start(conn->internal->pending_commands);
+  while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
+    if (cmd2->cmd == SILC_COMMAND_JOIN) {
+      silc_mutex_unlock(conn->internal->lock);
+      silc_fsm_next_later(fsm, silc_client_command_nick, 0, 300000);
+      return SILC_FSM_WAIT;
+    }
+  }
+  silc_mutex_unlock(conn->internal->lock);
+
   if (cmd->argv_lens[1] > 128)
     cmd->argv_lens[1] = 128;
 
@@ -841,7 +864,8 @@ SILC_FSM_STATE(silc_client_command_list)
 {
   SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-  SilcChannelEntry channel;
+  SilcClient client = conn->client;
+  SilcChannelEntry channel = NULL;
   SilcBuffer idp = NULL;
 
   if (cmd->argc == 2) {
@@ -858,6 +882,7 @@ SILC_FSM_STATE(silc_client_command_list)
                                1, 1, silc_buffer_datalen(idp));
 
   silc_buffer_free(idp);
+  silc_client_unref_channel(client, conn, channel);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
@@ -875,6 +900,7 @@ SILC_FSM_STATE(silc_client_command_topic)
 {
   SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
   SilcChannelEntry channel;
   SilcBuffer idp;
   char *name;
@@ -921,6 +947,7 @@ SILC_FSM_STATE(silc_client_command_topic)
                                1, silc_buffer_datalen(idp));
 
   silc_buffer_free(idp);
+  silc_client_unref_channel(client, conn, channel);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
@@ -944,7 +971,7 @@ SILC_FSM_STATE(silc_client_command_invite)
   SilcClientConnection conn = cmd->conn;
   SilcClient client = conn->client;
   SilcClientEntry client_entry = NULL;
-  SilcChannelEntry channel;
+  SilcChannelEntry channel = NULL;
   SilcBuffer clidp, chidp, args = NULL;
   SilcPublicKey pubkey = NULL;
   SilcDList clients = NULL;
@@ -967,6 +994,7 @@ SILC_FSM_STATE(silc_client_command_invite)
     }
 
     channel = conn->current_channel;
+    silc_client_ref_channel(client, conn, channel);
   } else {
     name = cmd->argv[1];
 
@@ -980,10 +1008,7 @@ SILC_FSM_STATE(silc_client_command_invite)
   /* Parse the typed nickname. */
   if (cmd->argc == 3) {
     if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
-      if (client->internal->params->nickname_parse)
-       client->internal->params->nickname_parse(cmd->argv[2], &nickname);
-      else
-       nickname = strdup(cmd->argv[2]);
+      silc_client_nickname_parse(client, conn, cmd->argv[2], &nickname);
 
       /* Find client entry */
       clients = silc_client_get_clients_local(client, conn, nickname,
@@ -1048,6 +1073,7 @@ SILC_FSM_STATE(silc_client_command_invite)
   silc_buffer_free(args);
   silc_free(nickname);
   silc_client_list_free(client, conn, clients);
+  silc_client_unref_channel(client, conn, channel);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
@@ -1069,19 +1095,17 @@ SILC_FSM_STATE(silc_client_command_quit_final)
 {
   SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-  SilcClient client = conn->client;
+
+  SILC_LOG_DEBUG(("Quitting"));
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
-  /* Call connection callback */
-  conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED,
-                0, NULL, conn->callback_context);
-
   /* Signal to close connection */
+  conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
   if (!conn->internal->disconnected) {
     conn->internal->disconnected = TRUE;
-    SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+    SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
   }
 
   return SILC_FSM_FINISH;
@@ -1110,7 +1134,6 @@ SILC_FSM_STATE(silc_client_command_quit)
 
 /********************************** KILL ************************************/
 
-
 /* Command KILL. Router operator can use this command to remove an client
    fromthe SILC Network. */
 
@@ -1132,11 +1155,7 @@ SILC_FSM_STATE(silc_client_command_kill)
   }
 
   /* Parse the typed nickname. */
-  if (client->internal->params->nickname_parse)
-    client->internal->params->nickname_parse(cmd->argv[1], &nickname);
-  else
-    nickname = strdup(cmd->argv[1]);
-  if (!nickname)
+  if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname))
     return SILC_FSM_FINISH;
 
   /* Get the target client */
@@ -1268,9 +1287,10 @@ SILC_FSM_STATE(silc_client_command_ping)
 
 SILC_FSM_STATE(silc_client_command_join)
 {
-  SilcClientCommandContext cmd = fsm_context;
+  SilcClientCommandContext cmd2, cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-  SilcChannelEntry channel;
+  SilcClient client = conn->client;
+  SilcChannelEntry channel = NULL;
   SilcBuffer auth = NULL, cauth = NULL;
   char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
   int i, passphrase_len = 0;
@@ -1285,6 +1305,19 @@ SILC_FSM_STATE(silc_client_command_join)
   if (channel && silc_client_on_channel(channel, conn->local_entry))
     goto out;
 
+  /* If NICK command is active, wait for it to finish before sending JOIN.
+     To avoid problems locally with changing IDs while joining, we do this. */
+  silc_mutex_lock(conn->internal->lock);
+  silc_list_start(conn->internal->pending_commands);
+  while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
+    if (cmd2->cmd == SILC_COMMAND_NICK) {
+      silc_mutex_unlock(conn->internal->lock);
+      silc_fsm_next_later(fsm, silc_client_command_join, 0, 300000);
+      return SILC_FSM_WAIT;
+    }
+  }
+  silc_mutex_unlock(conn->internal->lock);
+
   if (cmd->argv_lens[1] > 256)
     cmd->argv_lens[1] = 256;
 
@@ -1368,6 +1401,7 @@ SILC_FSM_STATE(silc_client_command_join)
   if (passphrase)
     memset(passphrase, 0, strlen(passphrase));
   silc_free(passphrase);
+  silc_client_unref_channel(client, conn, channel);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
@@ -1377,6 +1411,7 @@ SILC_FSM_STATE(silc_client_command_join)
   return SILC_FSM_CONTINUE;
 
  out:
+  silc_client_unref_channel(client, conn, channel);
   return SILC_FSM_FINISH;
 }
 
@@ -1564,7 +1599,7 @@ SILC_FSM_STATE(silc_client_command_cmode)
   SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
   SilcClient client = conn->client;
-  SilcChannelEntry channel;
+  SilcChannelEntry channel = NULL;
   SilcBuffer chidp, auth = NULL, pk = NULL;
   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
   SilcUInt32 mode, add, type, len, arg_len = 0;
@@ -1584,6 +1619,7 @@ SILC_FSM_STATE(silc_client_command_cmode)
     }
 
     channel = conn->current_channel;
+    silc_client_ref_channel(client, conn, channel);
   } else {
     name = cmd->argv[1];
 
@@ -1837,6 +1873,7 @@ SILC_FSM_STATE(silc_client_command_cmode)
   silc_buffer_free(chidp);
   silc_buffer_free(auth);
   silc_buffer_free(pk);
+  silc_client_unref_channel(client, conn, channel);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
@@ -1846,6 +1883,7 @@ SILC_FSM_STATE(silc_client_command_cmode)
   return SILC_FSM_CONTINUE;
 
  out:
+  silc_client_unref_channel(client, conn, channel);
   return SILC_FSM_FINISH;
 }
 
@@ -1858,7 +1896,7 @@ SILC_FSM_STATE(silc_client_command_cumode)
   SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
   SilcClient client = conn->client;
-  SilcChannelEntry channel;
+  SilcChannelEntry channel = NULL;
   SilcChannelUser chu;
   SilcClientEntry client_entry;
   SilcBuffer clidp, chidp, auth = NULL;
@@ -1882,6 +1920,7 @@ SILC_FSM_STATE(silc_client_command_cumode)
     }
 
     channel = conn->current_channel;
+    silc_client_ref_channel(client, conn, channel);
   } else {
     name = cmd->argv[1];
 
@@ -1893,10 +1932,7 @@ SILC_FSM_STATE(silc_client_command_cumode)
   }
 
   /* Parse the typed nickname. */
-  if (client->internal->params->nickname_parse)
-    client->internal->params->nickname_parse(cmd->argv[3], &nickname);
-  else
-    nickname = strdup(cmd->argv[3]);
+  silc_client_nickname_parse(client, conn, cmd->argv[3], &nickname);
 
   /* Find client entry */
   clients = silc_client_get_clients_local(client, conn, nickname,
@@ -2019,6 +2055,7 @@ SILC_FSM_STATE(silc_client_command_cumode)
     silc_buffer_free(auth);
   silc_free(nickname);
   silc_client_list_free(client, conn, clients);
+  silc_client_unref_channel(client, conn, channel);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
@@ -2028,6 +2065,7 @@ SILC_FSM_STATE(silc_client_command_cumode)
   return SILC_FSM_CONTINUE;
 
  out:
+  silc_client_unref_channel(client, conn, channel);
   silc_client_list_free(client, conn, clients);
   silc_free(nickname);
   return SILC_FSM_FINISH;
@@ -2042,7 +2080,7 @@ SILC_FSM_STATE(silc_client_command_kick)
   SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
   SilcClient client = conn->client;
-  SilcChannelEntry channel;
+  SilcChannelEntry channel = NULL;
   SilcBuffer idp, idp2;
   SilcClientEntry target;
   SilcDList clients = NULL;
@@ -2079,10 +2117,7 @@ SILC_FSM_STATE(silc_client_command_kick)
   }
 
   /* Parse the typed nickname. */
-  if (client->internal->params->nickname_parse)
-    client->internal->params->nickname_parse(cmd->argv[2], &nickname);
-  else
-    nickname = strdup(cmd->argv[2]);
+  silc_client_nickname_parse(client, conn, cmd->argv[2], &nickname);
 
   /* Get the target client */
   clients = silc_client_get_clients_local(client, conn, nickname,
@@ -2112,6 +2147,7 @@ SILC_FSM_STATE(silc_client_command_kick)
   silc_buffer_free(idp2);
   silc_free(nickname);
   silc_client_list_free(client, conn, clients);
+  silc_client_unref_channel(client, conn, channel);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
@@ -2121,6 +2157,7 @@ SILC_FSM_STATE(silc_client_command_kick)
   return SILC_FSM_CONTINUE;
 
  out:
+  silc_client_unref_channel(client, conn, channel);
   silc_free(nickname);
   return SILC_FSM_FINISH;
 }
@@ -2258,6 +2295,7 @@ SILC_FSM_STATE(silc_client_command_ban)
 {
   SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
   SilcChannelEntry channel;
   SilcBuffer chidp, args = NULL;
   char *name, *ban = NULL;
@@ -2279,6 +2317,7 @@ SILC_FSM_STATE(silc_client_command_ban)
     }
 
     channel = conn->current_channel;
+    silc_client_ref_channel(client, conn, channel);
   } else {
     name = cmd->argv[1];
 
@@ -2328,6 +2367,7 @@ SILC_FSM_STATE(silc_client_command_ban)
 
   silc_buffer_free(chidp);
   silc_buffer_free(args);
+  silc_client_unref_channel(client, conn, channel);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
@@ -2442,6 +2482,7 @@ SILC_FSM_STATE(silc_client_command_leave)
 {
   SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
   SilcChannelEntry channel;
   SilcBuffer idp;
   char *name;
@@ -2484,6 +2525,8 @@ SILC_FSM_STATE(silc_client_command_leave)
   if (conn->current_channel == channel)
     conn->current_channel = NULL;
 
+  silc_client_unref_channel(client, conn, channel);
+
   /** Wait for command reply */
   silc_fsm_next(fsm, silc_client_command_reply_wait);
   return SILC_FSM_CONTINUE;
@@ -2558,11 +2601,7 @@ SILC_FSM_STATE(silc_client_command_getkey)
   }
 
   /* Parse the typed nickname. */
-  if (client->internal->params->nickname_parse)
-    client->internal->params->nickname_parse(cmd->argv[1], &nickname);
-  else
-    nickname = strdup(cmd->argv[1]);
-  if (!nickname) {
+  if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname)) {
     COMMAND_ERROR(SILC_STATUS_ERR_RESOURCE_LIMIT);
     return SILC_FSM_FINISH;
   }
@@ -2807,7 +2846,7 @@ SILC_FSM_STATE(silc_client_command)
 
   case SILC_COMMAND_WHOIS:
     /* Ignore everything if requested by application */
-    if (client->internal->params->ignore_requested_attributes)
+    if (conn->internal->params.ignore_requested_attributes)
       break;
 
     silc_client_command_process_whois(client, conn, payload, args);