diff --git a/ibis/ibiscapabilities.h b/ibis/ibiscapabilities.h
--- a/ibis/ibiscapabilities.h
+++ b/ibis/ibiscapabilities.h
@@ -87,42 +87,29 @@
 
 /**
  * ibis_capabilities_new:
- * @client: The client instance.
  *
- * Creates a new capabilities object for @client.
+ * Creates a new capabilities object.
  *
  * Returns: (transfer full): The new instance.
  *
  * Since: 0.1
  */
 IBIS_AVAILABLE_IN_0_1
-IbisCapabilities *ibis_capabilities_new(IbisClient *client);
+IbisCapabilities *ibis_capabilities_new(void);
 
 /**
  * ibis_capabilities_start:
  * @capabilities: The instance.
+ * @client: The client instance.
  *
- * Starts capability negotiation.
+ * Starts capability negotiation on @client.
  *
  * This will send the `CAP LS` command.
  *
  * Since: 0.1
  */
 IBIS_AVAILABLE_IN_0_1
-void ibis_capabilities_start(IbisCapabilities *capabilities);
-
-/**
- * ibis_capabilities_get_client:
- * @capabilities: The instance.
- *
- * Gets the [class@Client] instance that @capabilities was created with.
- *
- * Returns: (transfer none): The client instance.
- *
- * Since: 0.1
- */
-IBIS_AVAILABLE_IN_0_1
-IbisClient *ibis_capabilities_get_client(IbisCapabilities *capabilities);
+void ibis_capabilities_start(IbisCapabilities *capabilities, IbisClient *client);
 
 /**
  * ibis_capabilities_request:
@@ -204,6 +191,20 @@
 IBIS_AVAILABLE_IN_0_1
 void ibis_capabilities_remove_wait(IbisCapabilities *capabilities);
 
+/**
+ * ibis_capabilities_stop:
+ * @capabilities: The instance.
+ *
+ * Stops handling capability commands.
+ *
+ * This is the opposite of [method@Capabilities.start] and should not need to
+ * be called outside of Ibis.
+ *
+ * Since: 0.1
+ */
+IBIS_AVAILABLE_IN_0_1
+void ibis_capabilities_stop(IbisCapabilities *capabilities);
+
 G_END_DECLS
 
 #endif /* IBIS_CAPABILITIES_H */
# HG changeset patch
# Node ID ac20cf32929a
# Parent  2cb6d26ea428
diff --git a/ibis/ibiscapabilities.c b/ibis/ibiscapabilities.c
--- a/ibis/ibiscapabilities.c
+++ b/ibis/ibiscapabilities.c
@@ -25,13 +25,6 @@
 #include "ibiscore.h"
 #include "ibisstring.h"
 
-enum {
-	PROP_0,
-	PROP_CLIENT,
-	N_PROPERTIES,
-};
-static GParamSpec *properties[N_PROPERTIES] = {NULL, };
-
 /* Windows is does something weird with signal handling that includes defining
  * SIG_ACK. We don't care about that here, so we undef it if we find it.
  * See https://learn.microsoft.com/en-us/cpp/c-runtime-library/signal-action-constants?view=msvc-170
@@ -70,24 +63,6 @@
  * Helpers
  *****************************************************************************/
 static void
-ibis_capabilities_set_client(IbisCapabilities *capabilities,
-                             IbisClient *client)
-{
-	g_return_if_fail(IBIS_IS_CLIENT(client));
-
-	if(g_set_object(&capabilities->client, client)) {
-		g_object_notify_by_pspec(G_OBJECT(capabilities),
-		                         properties[PROP_CLIENT]);
-
-		if(IBIS_IS_CLIENT(capabilities->client)) {
-			g_signal_connect_object(capabilities->client, "message::CAP",
-			                        G_CALLBACK(ibis_capabilities_message_cb),
-			                        capabilities, G_CONNECT_DEFAULT);
-		}
-	}
-}
-
-static void
 ibis_capabilities_finish(IbisCapabilities *capabilities) {
 	IbisMessage *message = NULL;
 
@@ -157,7 +132,8 @@
 	guint index = 0;
 	gboolean found = FALSE;
 
-	g_signal_emit(capabilities, sig, g_quark_from_string(caps), caps);
+	g_signal_emit(capabilities, sig, g_quark_from_string(caps),
+	              capabilities->client, caps);
 
 	found = g_ptr_array_find_with_equal_func(capabilities->requests, caps,
 	                                         g_str_equal, &index);
@@ -201,11 +177,15 @@
  *****************************************************************************/
 static void
 ibis_capabilities_message_tags_ack_cb(IbisCapabilities *capabilities,
+                                      G_GNUC_UNUSED IbisClient *client,
                                       G_GNUC_UNUSED const char *capability,
                                       G_GNUC_UNUSED gpointer data)
 {
-	/* We have message tags so add the stuff we support that depends on it. */
-	ibis_capabilities_lookup_and_request(capabilities, "msgid");
+	/* The server-time capability just tells the server to send a tag on
+	 * messages, so we just need to request it.
+	 */
+	ibis_capabilities_lookup_and_request(capabilities,
+	                                     IBIS_CAPABILITY_SERVER_TIME);
 }
 
 static gboolean
@@ -297,13 +277,6 @@
 		                 G_CALLBACK(ibis_capabilities_message_tags_ack_cb),
 		                 NULL);
 	}
-
-	/* The server-time capability just tells the server to send a tag to
-	 * messages, so we just need to request it and then handle the tag when
-	 * we're processing messages if it exists.
-	 */
-	ibis_capabilities_lookup_and_request(capabilities,
-	                                     IBIS_CAPABILITY_SERVER_TIME);
 }
 
 /******************************************************************************
@@ -312,38 +285,6 @@
 G_DEFINE_FINAL_TYPE(IbisCapabilities, ibis_capabilities, G_TYPE_OBJECT)
 
 static void
-ibis_capabilities_get_property(GObject *obj, guint param_id, GValue *value,
-                               GParamSpec *pspec)
-{
-	IbisCapabilities *capabilities = IBIS_CAPABILITIES(obj);
-
-	switch(param_id) {
-	case PROP_CLIENT:
-		g_value_set_object(value, ibis_capabilities_get_client(capabilities));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
-		break;
-	}
-}
-
-static void
-ibis_capabilities_set_property(GObject *obj, guint param_id,
-                               const GValue *value, GParamSpec *pspec)
-{
-	IbisCapabilities *capabilities = IBIS_CAPABILITIES(obj);
-
-	switch(param_id) {
-	case PROP_CLIENT:
-		ibis_capabilities_set_client(capabilities, g_value_get_object(value));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
-		break;
-	}
-}
-
-static void
 ibis_capabilities_finalize(GObject *obj) {
 	IbisCapabilities *capabilities = IBIS_CAPABILITIES(obj);
 
@@ -369,24 +310,6 @@
 	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
 
 	obj_class->finalize = ibis_capabilities_finalize;
-	obj_class->get_property = ibis_capabilities_get_property;
-	obj_class->set_property = ibis_capabilities_set_property;
-
-	/**
-	 * IbisCapabilities:client:
-	 *
-	 * The [class@Client] that this instance was created with.
-	 *
-	 * Since: 0.1
-	 */
-	properties[PROP_CLIENT] = g_param_spec_object(
-		"client", "client",
-		"The client this capabilities was created with.",
-		IBIS_TYPE_CLIENT,
-		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
-
-	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
-
 	/**
 	 * IbisCapabilities::ready:
 	 * @capabilities: The instance.
@@ -413,10 +336,11 @@
 	/**
 	 * IbisCapabilities::ack:
 	 * @capabilities: The instance.
+	 * @client: The client instance.
 	 * @capability: The capability string.
 	 *
 	 * Emitted when the server has acknowledged a `CAP REQ` call from
-	 * ibis_capabilities_request.
+	 * [method@Capabilities.request].
 	 *
 	 * The value of @capability will be the same as the one that was requested.
 	 *
@@ -431,16 +355,18 @@
 		NULL,
 		NULL,
 		G_TYPE_NONE,
-		1,
+		2,
+		IBIS_TYPE_CLIENT,
 		G_TYPE_STRING);
 
 	/**
 	 * IbisCapabilities::nak:
 	 * @capabilities: The instance.
+	 * @client: The client instance.
 	 * @capability: The capability string.
 	 *
 	 * Emitted when the server has nacked a `CAP REQ` call from
-	 * ibis_capabilities_request.
+	 * [method@Capabilities.request].
 	 *
 	 * The value of @capability will be the same as the one that was requested.
 	 *
@@ -455,7 +381,8 @@
 		NULL,
 		NULL,
 		G_TYPE_NONE,
-		1,
+		2,
+		IBIS_TYPE_CLIENT,
 		G_TYPE_STRING);
 
 	/**
@@ -488,8 +415,8 @@
 	 *
 	 * There are two approaches to how you can use this signal. You can check
 	 * each item in @added for the values you need and parsing their values, or
-	 * you can call #ibis_capabilities_lookup to see if the
-	 * capabilities you're interested in have been added.
+	 * you can call [method@Capabilities.lookup] to see if the capabilities
+	 * you're interested in have been added.
 	 *
 	 * Since: 0.1
 	 */
@@ -515,7 +442,7 @@
 	 *
 	 * There are two approaches to how you can use this signal. You can check
 	 * each item in @removed for the values you care about, or you can call
-	 * #ibis_capabilities_lookup to see if the capabilities you're
+	 * [method@Capabilities.lookup] to see if the capabilities you're
 	 * interested in have been removed.
 	 *
 	 * Since: 0.1
@@ -537,28 +464,47 @@
  * Public API
  *****************************************************************************/
 IbisCapabilities *
-ibis_capabilities_new(IbisClient *client) {
-	return g_object_new(
-		IBIS_TYPE_CAPABILITIES,
-		"client", client,
-		NULL);
+ibis_capabilities_new(void) {
+	return g_object_new(IBIS_TYPE_CAPABILITIES, NULL);
 }
 
 void
-ibis_capabilities_start(IbisCapabilities *capabilities) {
+ibis_capabilities_start(IbisCapabilities *capabilities, IbisClient *client) {
 	IbisMessage *message = NULL;
 
+	g_return_if_fail(IBIS_IS_CAPABILITIES(capabilities));
+	g_return_if_fail(IBIS_IS_CLIENT(client));
+
+	if(IBIS_IS_CLIENT(capabilities->client)) {
+		g_warning("capabilities %p is already started.", capabilities);
+
+		return;
+	}
+
+	capabilities->client = g_object_ref(client);
+	g_signal_connect_object(capabilities->client, "message::CAP",
+	                        G_CALLBACK(ibis_capabilities_message_cb),
+	                        capabilities, 0);
+
 	message = ibis_message_new("CAP");
 	ibis_message_set_params(message, "LS", IBIS_CAPABILITY_CAP_LS_VERSION,
 	                        NULL);
 	ibis_client_write(capabilities->client, message);
 }
 
-IbisClient *
-ibis_capabilities_get_client(IbisCapabilities *capabilities) {
-	g_return_val_if_fail(IBIS_IS_CAPABILITIES(capabilities), NULL);
+void
+ibis_capabilities_stop(IbisCapabilities *capabilities) {
+	g_return_if_fail(IBIS_IS_CAPABILITIES(capabilities));
 
-	return capabilities->client;
+	if(!IBIS_IS_CLIENT(capabilities->client)) {
+		return;
+	}
+
+	g_signal_handlers_disconnect_by_func(capabilities->client,
+	                                     G_CALLBACK(ibis_capabilities_message_cb),
+	                                     capabilities);
+
+	g_clear_object(&capabilities->client);
 }
 
 void
diff --git a/ibis/ibisclient.c b/ibis/ibisclient.c
--- a/ibis/ibisclient.c
+++ b/ibis/ibisclient.c
@@ -375,7 +375,7 @@
 
 static void
 ibis_client_init(IbisClient *client) {
-	client->capabilities = ibis_capabilities_new(client);
+	client->capabilities = ibis_capabilities_new();
 }
 
 static void
@@ -566,6 +566,9 @@
 	g_return_if_fail(IBIS_IS_CLIENT(client));
 	g_return_if_fail(client->connected == TRUE);
 
+	/* Disable the capabilities handler. */
+	ibis_capabilities_stop(client->capabilities);
+
 	if(error == NULL) {
 		IbisMessage *msg = NULL;
 
@@ -753,7 +756,7 @@
 	                                    ibis_client_read_cb,
 	                                    client);
 
-	ibis_capabilities_start(client->capabilities);
+	ibis_capabilities_start(client->capabilities, client);
 	ibis_client_register(client, password);
 }
 
diff --git a/ibis/tests/test_client.c b/ibis/tests/test_client.c
--- a/ibis/tests/test_client.c
+++ b/ibis/tests/test_client.c
@@ -76,10 +76,7 @@
 	g_assert_cmpstr(username, ==, "username");
 	g_clear_pointer(&username, g_free);
 
-	/* We can't use g_assert_finalize_object because there is a circular
-	 * reference between the client and its capabilities object.
-	 */
-	g_clear_object(&client);
+	g_assert_finalize_object(client);
 }
 
 static void
@@ -97,7 +94,7 @@
 	alt_nick = ibis_client_get_alt_nick(client);
 	g_assert_null(alt_nick);
 
-	g_clear_object(&client);
+	g_assert_finalize_object(client);
 }
 
 static void
@@ -108,7 +105,7 @@
 		client = ibis_client_new();
 		ibis_client_set_alt_nick(client, "");
 
-		g_clear_object(&client);
+		g_assert_finalize_object(client);
 	}
 
 	g_test_trap_subprocess(NULL, 0, G_TEST_SUBPROCESS_DEFAULT);
@@ -130,7 +127,7 @@
 	realname = ibis_client_get_realname(client);
 	g_assert_null(realname);
 
-	g_clear_object(&client);
+	g_assert_finalize_object(client);
 }
 
 static void
@@ -141,7 +138,7 @@
 		client = ibis_client_new();
 		ibis_client_set_realname(client, "");
 
-		g_clear_object(&client);
+		g_assert_finalize_object(client);
 	}
 
 	g_test_trap_subprocess(NULL, 0, G_TEST_SUBPROCESS_DEFAULT);
@@ -163,7 +160,7 @@
 	realname = ibis_client_get_realname(client);
 	g_assert_cmpstr(realname, ==, "nick");
 
-	g_clear_object(&client);
+	g_assert_finalize_object(client);
 }
 
 static void
@@ -181,7 +178,7 @@
 	username = ibis_client_get_username(client);
 	g_assert_null(username);
 
-	g_clear_object(&client);
+	g_assert_finalize_object(client);
 }
 
 static void
@@ -192,7 +189,7 @@
 		client = ibis_client_new();
 		ibis_client_set_username(client, "");
 
-		g_clear_object(&client);
+		g_assert_finalize_object(client);
 	}
 
 	g_test_trap_subprocess(NULL, 0, G_TEST_SUBPROCESS_DEFAULT);
@@ -214,7 +211,7 @@
 	username = ibis_client_get_username(client);
 	g_assert_cmpstr(username, ==, "nick");
 
-	g_clear_object(&client);
+	g_assert_finalize_object(client);
 }
 
 
