Memory leak fixes.
[silc.git] / lib / silcclient / client.c
index d46f081837a6bf03fa0f575c85e504f697cda547..1d7884553696564c1dd8bca8477034cfc25760ce 100644 (file)
@@ -132,17 +132,11 @@ static void silc_client_packet_eos(SilcPacketEngine engine,
                                   void *stream_context)
 {
   SilcClientConnection conn = stream_context;
-  SilcClient client = conn->client;
 
   SILC_LOG_DEBUG(("Remote disconnected connection"));
 
-  /* Call connection callback */
-  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->status = SILC_CLIENT_CONN_DISCONNECTED;
   if (!conn->internal->disconnected) {
     conn->internal->disconnected = TRUE;
     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
@@ -184,6 +178,9 @@ static void silc_client_connect_abort(SilcAsyncOperation op, void *context)
 
   SILC_LOG_DEBUG(("Connection %p aborted by application", conn));
 
+  /* Connection callback will not be called after user aborted connecting */
+  conn->callback = NULL;
+
   /* Signal to close connection */
   if (!conn->internal->disconnected) {
     conn->internal->disconnected = TRUE;
@@ -218,7 +215,7 @@ SILC_FSM_STATE(silc_client_connection_st_start)
     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
 
   /* Wait until this thread is terminated from the machine destructor */
-  SILC_FSM_WAIT;
+  return SILC_FSM_WAIT;
 }
 
 /* Connection machine main state.  This handles various connection related
@@ -239,46 +236,49 @@ SILC_FSM_STATE(silc_client_connection_st_run)
   if (conn->internal->connect) {
     SILC_LOG_DEBUG(("Event: connect"));
     conn->internal->connect = FALSE;
+    SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
 
     /*** Event: connect */
     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
                         NULL, NULL, FALSE);
     silc_fsm_start_sync(thread, silc_client_st_connect);
-    SILC_FSM_CONTINUE;
+    return SILC_FSM_CONTINUE;
   }
 
   if (conn->internal->key_exchange) {
     SILC_LOG_DEBUG(("Event: key exchange"));
     conn->internal->key_exchange = FALSE;
+    SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
 
     /*** Event: key exchange */
     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
                         NULL, NULL, FALSE);
     silc_fsm_start_sync(thread, silc_client_st_connect_set_stream);
-    SILC_FSM_CONTINUE;
+    return SILC_FSM_CONTINUE;
   }
 
   if (conn->internal->rekeying) {
     SILC_LOG_DEBUG(("Event: rekey"));
     conn->internal->rekeying = FALSE;
+    SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
 
     /*** Event: rekey */
     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
                         NULL, NULL, FALSE);
     silc_fsm_start_sync(thread, silc_client_st_rekey);
-    SILC_FSM_CONTINUE;
+    return SILC_FSM_CONTINUE;
   }
 
   if (conn->internal->disconnected) {
     /** Event: disconnected */
     SILC_LOG_DEBUG(("Event: disconnected"));
     silc_fsm_next(fsm, silc_client_connection_st_close);
-    SILC_FSM_YIELD;
+    return SILC_FSM_YIELD;
   }
 
   /* NOT REACHED */
   SILC_ASSERT(FALSE);
-  SILC_FSM_CONTINUE;
+  return SILC_FSM_CONTINUE;
 }
 
 /* Packet processor thread.  Each incoming packet is processed in FSM
@@ -305,7 +305,7 @@ SILC_FSM_STATE(silc_client_connection_st_packet)
 
   case SILC_PACKET_FTP:
     /* File transfer packet */
-//    silc_fsm_next(fsm, silc_client_ftp);
+    silc_fsm_next(fsm, silc_client_ftp);
     break;
 
   case SILC_PACKET_CHANNEL_KEY:
@@ -365,22 +365,22 @@ SILC_FSM_STATE(silc_client_connection_st_packet)
     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
 
     silc_packet_free(packet);
-    SILC_FSM_FINISH;
+    return SILC_FSM_FINISH;
     break;
 
   default:
     silc_packet_free(packet);
-    SILC_FSM_FINISH;
+    return SILC_FSM_FINISH;
     break;
   }
 
-  SILC_FSM_CONTINUE;
+  return SILC_FSM_CONTINUE;
 }
 
 /* Disconnection event to close remote connection.  We close the connection
    and finish the connection machine in this state.  The connection context
-   is deleted in the machine destructor.  The connection callback must be
-   already called back to application before getting here. */
+   is deleted in the machine destructor.  The connection callback is called
+   in this state if it is set. */
 
 SILC_FSM_STATE(silc_client_connection_st_close)
 {
@@ -401,7 +401,7 @@ SILC_FSM_STATE(silc_client_connection_st_close)
     }
 
     /* Give threads time to finish */
-    SILC_FSM_YIELD;
+    return SILC_FSM_YIELD;
   }
 
   /* Abort ongoing event */
@@ -415,16 +415,23 @@ SILC_FSM_STATE(silc_client_connection_st_close)
   if (silc_fsm_is_started(&conn->internal->event_thread)) {
     SILC_LOG_DEBUG(("Finish event thread"));
     silc_fsm_continue_sync(&conn->internal->event_thread);
-    SILC_FSM_YIELD;
+    return SILC_FSM_YIELD;
   }
 
+  /* Call the connection callback */
+  if (conn->callback)
+    conn->callback(conn->client, conn, conn->internal->status,
+                  conn->internal->error, conn->internal->disconnect_message,
+                  conn->callback_context);
+  silc_free(conn->internal->disconnect_message);
+
   SILC_LOG_DEBUG(("Closing remote connection"));
 
   /* Close connection */
   silc_packet_stream_destroy(conn->stream);
 
   SILC_LOG_DEBUG(("Finishing connection machine"));
-  SILC_FSM_FINISH;
+  return SILC_FSM_FINISH;
 }
 
 /* Received error packet from server.  Send it to application. */
@@ -444,7 +451,7 @@ SILC_FSM_STATE(silc_client_error)
   silc_free(msg);
   silc_packet_free(packet);
 
-  SILC_FSM_FINISH;
+  return SILC_FSM_FINISH;
 }
 
 /* Received disconnect packet from server.  We close the connection and
@@ -453,7 +460,6 @@ SILC_FSM_STATE(silc_client_error)
 SILC_FSM_STATE(silc_client_disconnect)
 {
   SilcClientConnection conn = fsm_context;
-  SilcClient client = conn->client;
   SilcPacket packet = state_context;
   SilcStatus status;
   char *message = NULL;
@@ -462,7 +468,7 @@ SILC_FSM_STATE(silc_client_disconnect)
 
   if (silc_buffer_len(&packet->buffer) < 1) {
     silc_packet_free(packet);
-    SILC_FSM_FINISH;
+    return SILC_FSM_FINISH;
   }
 
   status = (SilcStatus)packet->buffer.data[0];
@@ -475,13 +481,9 @@ SILC_FSM_STATE(silc_client_disconnect)
                          silc_buffer_len(&packet->buffer));
 
   /* Call connection callback */
-  if (!conn->internal->callback_called)
-    conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, status,
-                  message, conn->callback_context);
-  conn->internal->callback_called = TRUE;
-
-  silc_free(message);
-  silc_packet_free(packet);
+  conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
+  conn->internal->error = status;
+  conn->internal->disconnect_message = message;
 
   /* Signal to close connection */
   if (!conn->internal->disconnected) {
@@ -489,7 +491,9 @@ SILC_FSM_STATE(silc_client_disconnect)
     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
   }
 
-  SILC_FSM_FINISH;
+  silc_packet_free(packet);
+
+  return SILC_FSM_FINISH;
 }
 
 /*************************** Main client machine ****************************/
@@ -510,7 +514,7 @@ SILC_FSM_STATE(silc_client_st_run)
     SILC_LOG_DEBUG(("We are up, call running callback"));
     client->internal->run_callback = FALSE;
     client->internal->running(client, client->internal->running_context);
-    SILC_FSM_CONTINUE;
+    return SILC_FSM_CONTINUE;
   }
 
   if (client->internal->connection_closed) {
@@ -520,7 +524,7 @@ SILC_FSM_STATE(silc_client_st_run)
     if (silc_atomic_get_int16(&client->internal->conns) == 0 &&
        client->internal->stop)
       SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
-    SILC_FSM_CONTINUE;
+    return SILC_FSM_CONTINUE;
   }
 
   if (client->internal->stop) {
@@ -529,12 +533,12 @@ SILC_FSM_STATE(silc_client_st_run)
     SILC_LOG_DEBUG(("Event: stop"));
     if (silc_atomic_get_int16(&client->internal->conns) == 0)
       silc_fsm_next(fsm, silc_client_st_stop);
-    SILC_FSM_CONTINUE;
+    return SILC_FSM_CONTINUE;
   }
 
   /* NOT REACHED */
   SILC_ASSERT(FALSE);
-  SILC_FSM_CONTINUE;
+  return SILC_FSM_CONTINUE;
 }
 
 /* Stop event.  Stops the client library. */
@@ -553,16 +557,17 @@ SILC_FSM_STATE(silc_client_st_stop)
   if (client->internal->running)
     client->internal->running(client, client->internal->running_context);
 
-  SILC_FSM_FINISH;
+  return SILC_FSM_FINISH;
 }
 
 /******************************* Private API ********************************/
 
 /* Adds new connection.  Creates the connection context and returns it. */
 
-static SilcClientConnection
+SilcClientConnection
 silc_client_add_connection(SilcClient client,
                           SilcConnectionType conn_type,
+                          SilcBool connect,
                           SilcClientConnectionParams *params,
                           SilcPublicKey public_key,
                           SilcPrivateKey private_key,
@@ -622,31 +627,33 @@ silc_client_add_connection(SilcClient client,
   silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
 
   /* Allocate client, channel and serve caches */
-  conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
-                                                   NULL, NULL);
-  conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
-                                                    NULL, NULL);
-  conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
-                                                   NULL, NULL);
-  if (!conn->internal->client_cache || !conn->internal->channel_cache ||
-      !conn->internal->server_cache) {
-    silc_client_del_connection(client, conn);
-    return NULL;
+  if (conn_type != SILC_CONN_CLIENT) {
+    conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
+                                                     NULL, NULL);
+    conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
+                                                      NULL, NULL);
+    conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
+                                                     NULL, NULL);
+    if (!conn->internal->client_cache || !conn->internal->channel_cache ||
+       !conn->internal->server_cache) {
+      silc_client_del_connection(client, conn);
+      return NULL;
+    }
   }
 
-  conn->internal->ftp_sessions = silc_dlist_init();
-
-  /* Initialize our async operation so that application may abort us
-     while we're connecting. */
-  conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
-                                        NULL, conn);
-  if (!conn->internal->cop) {
-    silc_client_del_connection(client, conn);
-    return NULL;
+  if (connect) {
+    /* Initialize our async operation so that application may abort us
+       while we're connecting. */
+    conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
+                                          NULL, conn);
+    if (!conn->internal->cop) {
+      silc_client_del_connection(client, conn);
+      return NULL;
+    }
   }
 
-  /* Run the connection state machine.  If threads are in use the machine
-     is always run in a real thread. */
+  /* Run the connection state machine.  If threads are in use the connection
+     machine is always run in a real thread. */
   thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
                                 silc_client_connection_finished, NULL,
                                 client->internal->params->threads);
@@ -677,22 +684,28 @@ void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
   silc_schedule_task_del_by_context(conn->internal->schedule, conn);
 
   /* Free all cache entries */
-  if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
-    silc_list_start(list);
-    while ((entry = silc_list_get(list)))
-      silc_client_del_server(client, conn, entry->context);
+  if (conn->internal->server_cache) {
+    if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
+      silc_list_start(list);
+      while ((entry = silc_list_get(list)))
+       silc_client_del_server(client, conn, entry->context);
+    }
   }
-  if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
-    silc_list_start(list);
-    while ((entry = silc_list_get(list))) {
-      silc_client_empty_channel(client, conn, entry->context);
-      silc_client_del_channel(client, conn, entry->context);
+  if (conn->internal->channel_cache) {
+    if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
+      silc_list_start(list);
+      while ((entry = silc_list_get(list))) {
+       silc_client_empty_channel(client, conn, entry->context);
+       silc_client_del_channel(client, conn, entry->context);
+      }
     }
   }
-  if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
-    silc_list_start(list);
-    while ((entry = silc_list_get(list)))
-      silc_client_del_client(client, conn, entry->context);
+  if (conn->internal->client_cache) {
+    if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
+      silc_list_start(list);
+      while ((entry = silc_list_get(list)))
+       silc_client_del_client(client, conn, entry->context);
+    }
   }
 
   /* Free ID caches */
@@ -717,6 +730,11 @@ void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
   if (conn->internal->sha1hash)
     silc_hash_free(conn->internal->sha1hash);
   silc_atomic_uninit16(&conn->internal->cmd_ident);
+  silc_free(conn->internal->away_message);
+  if (conn->internal->rekey)
+    silc_ske_free_rekey_material(conn->internal->rekey);
+  if (conn->internal->cop)
+    silc_async_free(conn->internal->cop);
 
   silc_free(conn->internal);
   memset(conn, 'F', sizeof(*conn));
@@ -746,7 +764,7 @@ silc_client_connect_to_server(SilcClient client,
     return NULL;
 
   /* Add new connection */
-  conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
+  conn = silc_client_add_connection(client, SILC_CONN_SERVER, TRUE, params,
                                    public_key, private_key, remote_host,
                                    port, callback, context);
   if (!conn) {
@@ -782,8 +800,11 @@ silc_client_connect_to_client(SilcClient client,
   if (!client || !remote_host)
     return NULL;
 
+  if (params)
+    params->no_authentication = TRUE;
+
   /* Add new connection */
-  conn = silc_client_add_connection(client, SILC_CONN_CLIENT, params,
+  conn = silc_client_add_connection(client, SILC_CONN_CLIENT, TRUE, params,
                                    public_key, private_key, remote_host,
                                    port, callback, context);
   if (!conn) {
@@ -825,7 +846,7 @@ silc_client_key_exchange(SilcClient client,
   }
 
   /* Add new connection */
-  conn = silc_client_add_connection(client, conn_type, params,
+  conn = silc_client_add_connection(client, conn_type, TRUE, params,
                                    public_key, private_key,
                                    (char *)host, port, callback, context);
   if (!conn) {
@@ -846,7 +867,15 @@ void silc_client_close_connection(SilcClient client,
 {
   SILC_LOG_DEBUG(("Closing connection %p", conn));
 
+  /* If connection machine is not running, we just delete the connection */
+  if (!silc_fsm_is_started(&conn->internal->fsm)) {
+    silc_packet_stream_destroy(conn->stream);
+    silc_client_del_connection(conn->client, conn);
+    return;
+  }
+
   /* Signal to close connection */
+  conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
   if (!conn->internal->disconnected) {
     conn->internal->disconnected = TRUE;
     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
@@ -910,7 +939,10 @@ void silc_client_free(SilcClient client)
     silc_hmac_unregister_all();
   }
 
+  silc_packet_engine_stop(client->internal->packet_engine);
+  silc_dlist_uninit(client->internal->ftp_sessions);
   silc_atomic_uninit16(&client->internal->conns);
+  silc_mutex_free(client->internal->lock);
   silc_free(client->username);
   silc_free(client->hostname);
   silc_free(client->realname);
@@ -967,6 +999,10 @@ SilcBool silc_client_init(SilcClient client, const char *username,
   if (!username || !hostname || !realname)
     return FALSE;
 
+  client->internal->ftp_sessions = silc_dlist_init();
+  if (!client->internal->ftp_sessions)
+    return FALSE;
+
   if (!client->internal->params->dont_register_crypto_library) {
     /* Initialize the crypto library.  If application has done this already
        this has no effect.  Also, we will not be overriding something