The silc_client_connect_to_[server|client] and
[silc.git] / lib / silcclient / command.c
index 4accee1a8ffa903f81bf9ae95278847dc99bff3a..602c527805f3c90f23a7ecd769a256e9e1c6f8e3 100644 (file)
@@ -139,11 +139,17 @@ silc_client_command_register(SilcClient client,
   SilcClientCommand cmd;
 
   cmd = silc_calloc(1, sizeof(*cmd));
+  if (!cmd)
+    return FALSE;
   cmd->cmd = command;
   cmd->command = command_func;
   cmd->reply = command_reply_func;
-  cmd->name = name ? strdup(name) : NULL;
   cmd->max_args = max_args;
+  cmd->name = name ? strdup(name) : NULL;
+  if (!cmd->name) {
+    silc_free(cmd);
+    return FALSE;
+  }
 
   silc_list_add(client->internal->commands, cmd);
 
@@ -191,27 +197,20 @@ static SilcClientCommand silc_client_command_find(SilcClient client,
   return NULL;
 }
 
-/* Free command context and its internals */
-
-static void silc_client_command_free(SilcClientCommandContext cmd)
-{
-  int i;
-
-  for (i = 0; i < cmd->argc; i++)
-    silc_free(cmd->argv[i]);
-  silc_free(cmd->argv);
-  silc_free(cmd->argv_lens);
-  silc_free(cmd->argv_types);
-  silc_free(cmd);
-}
-
 /* Command thread destructor */
 
 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. */
@@ -262,6 +261,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);
 
@@ -305,6 +307,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);
 
@@ -353,6 +358,26 @@ static SilcUInt16 silc_client_command_send_va(SilcClientConnection conn,
 
 /****************************** Command API *********************************/
 
+/* Free command context and its internals */
+
+void silc_client_command_free(SilcClientCommandContext cmd)
+{
+  SilcClientCommandReplyCallback cb;
+  int i;
+
+  for (i = 0; i < cmd->argc; i++)
+    silc_free(cmd->argv[i]);
+  silc_free(cmd->argv);
+  silc_free(cmd->argv_lens);
+  silc_free(cmd->argv_types);
+
+  silc_list_start(cmd->reply_callbacks);
+  while ((cb = silc_list_get(cmd->reply_callbacks)))
+    silc_free(cb);
+
+  silc_free(cmd);
+}
+
 /* Executes a command */
 
 SilcUInt16 silc_client_command_call(SilcClient client,
@@ -435,6 +460,8 @@ SilcUInt16 silc_client_command_call(SilcClient client,
   cmd->cmd_ident = silc_client_cmd_ident(conn);
   cmd->called = TRUE;
   cmd->verbose = TRUE;
+  silc_list_init(cmd->reply_callbacks,
+                struct SilcClientCommandReplyCallbackStruct, next);
 
   /*** Call command */
   SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
@@ -467,6 +494,8 @@ SilcUInt16 silc_client_command_send(SilcClient client,
     return 0;
   cmd->conn = conn;
   cmd->cmd = command;
+  silc_list_init(cmd->reply_callbacks,
+                struct SilcClientCommandReplyCallbackStruct, next);
 
   /* Send the command */
   va_start(ap, argc);
@@ -1055,16 +1084,22 @@ SILC_FSM_STATE(silc_client_command_quit_final)
   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);
+  if (!conn->internal->callback_called)
+    conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED,
+                  0, NULL, conn->callback_context);
+  conn->internal->callback_called = TRUE;
 
   /* Signal to close connection */
-  conn->internal->disconnected = TRUE;
-  SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+  if (!conn->internal->disconnected) {
+    conn->internal->disconnected = TRUE;
+    SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+  }
 
   return SILC_FSM_FINISH;
 }
@@ -2577,6 +2612,7 @@ SILC_FSM_STATE(silc_client_command_getkey)
       /* NOT REACHED */
     }
     idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
+    silc_client_unref_server(client, conn, server_entry);
   } else {
     client_entry = silc_dlist_get(clients);
     idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);