# HG changeset patch
# Node ID 79754f6a153a6045fbae8f24859171cb1a624f23
# Parent  d0997355b89dcf273ea73ef96f0976605fdcd11f
diff --git a/ibis-cli/ibiscli.c b/ibis-cli/ibiscli.c
--- a/ibis-cli/ibiscli.c
+++ b/ibis-cli/ibiscli.c
@@ -230,16 +230,18 @@
 {
 	ibiscli_log(message, "< ");
 
-	if(ibis_str_equal(command, IBIS_MSG_PING) ||
-	   ibis_str_equal(command, IBIS_MSG_PONG) ||
-	   ibis_str_equal(command, IBIS_MSG_CAP) ||
-	   ibis_str_equal(command, IBIS_MSG_AUTHENTICATE) ||
-	   ibis_str_equal(command, IBIS_ERR_SASLABORTED) ||
+	if(ibis_str_equal(command, IBIS_ERR_SASLABORTED) ||
 	   ibis_str_equal(command, IBIS_ERR_SASLALREADY) ||
 	   ibis_str_equal(command, IBIS_ERR_SASLFAIL) ||
 	   ibis_str_equal(command, IBIS_ERR_SASLTOOLONG) ||
+	   ibis_str_equal(command, IBIS_MSG_AUTHENTICATE) ||
+	   ibis_str_equal(command, IBIS_MSG_CAP) ||
+	   ibis_str_equal(command, IBIS_MSG_NICK) ||
+	   ibis_str_equal(command, IBIS_MSG_PING) ||
+	   ibis_str_equal(command, IBIS_MSG_PONG) ||
 	   ibis_str_equal(command, IBIS_RPL_SASLMECHS) ||
-	   ibis_str_equal(command, IBIS_RPL_SASLSUCCESS))
+	   ibis_str_equal(command, IBIS_RPL_SASLSUCCESS) ||
+	   ibis_str_equal(command, IBIS_RPL_WELCOME))
 	{
 		return FALSE;
 	}
diff --git a/ibis/ibisclient.c b/ibis/ibisclient.c
--- a/ibis/ibisclient.c
+++ b/ibis/ibisclient.c
@@ -33,6 +33,7 @@
 
 enum {
 	PROP_0,
+	PROP_ACTIVE_NICK,
 	PROP_ALT_NICK,
 	PROP_CANCELLABLE,
 	PROP_CAPABILITIES,
@@ -88,11 +89,38 @@
 	char *server_name;
 
 	HaslContext *hasl_context;
+
+	/* The nick name that we have on the server. This is separate from nick and
+	 * alt_nick above because the user might want to restore their nick later.
+	 */
+	char *active_nick;
 };
 
 /******************************************************************************
  * Helpers
  *****************************************************************************/
+
+/*
+ * ibis_client_set_active_nick:
+ * @active_nick: the new active nick.
+ *
+ * Sets active nick.
+ *
+ * This is a helper function to set the active nick since it can come from a
+ * few places.
+ *
+ * Since: 0.11
+ */
+static void
+ibis_client_set_active_nick(IbisClient *client, const char *active_nick) {
+	g_return_if_fail(IBIS_IS_CLIENT(client));
+
+	if(g_set_str(&client->active_nick, active_nick)) {
+		g_object_notify_by_pspec(G_OBJECT(client),
+		                         properties[PROP_ACTIVE_NICK]);
+	}
+}
+
 static void
 ibis_client_set_connected(IbisClient *client, gboolean connected) {
 	g_return_if_fail(IBIS_IS_CLIENT(client));
@@ -330,6 +358,30 @@
  * Default handlers
  *****************************************************************************/
 static gboolean
+ibis_client_handle_nick(IbisClient *client, IbisMessage *message) {
+	const char *source = NULL;
+	char *nick = NULL;
+
+	source = ibis_message_get_source(message);
+	ibis_source_parse(source, &nick, NULL, NULL);
+
+	/* The server tells us of our nick changing by sending a nick message from
+	 * our old nick with a parameter of the new nick.
+	 */
+	if(ibis_str_equal(client->active_nick, nick)) {
+		GStrv params = ibis_message_get_params(message);
+
+		if(g_strv_length(params) == 1) {
+			ibis_client_set_active_nick(client, params[0]);
+		}
+	}
+
+	g_free(nick);
+
+	return TRUE;
+}
+
+static gboolean
 ibis_client_handle_ping(IbisClient *client, IbisMessage *message) {
 	IbisMessage *pong = NULL;
 	GStrv params = NULL;
@@ -354,11 +406,30 @@
 }
 
 static gboolean
+ibis_client_handle_rpl_welcome(IbisClient *client, IbisMessage *message) {
+	GStrv params = NULL;
+
+	/* Per https://modern.ircdocs.horse/#connection-registration the first
+	 * parameter of RPL_WELCOME is the nick that the server assigned us.
+	 */
+	params = ibis_message_get_params(message);
+	if(params != NULL && g_strv_length(params) > 0) {
+		ibis_client_set_active_nick(client, params[0]);
+	}
+
+	return TRUE;
+}
+
+static gboolean
 ibis_client_default_message_handler(IbisClient *client, const char *command,
                                     IbisMessage *message)
 {
 	if(ibis_str_equal(command, IBIS_MSG_PING)) {
 		return ibis_client_handle_ping(client, message);
+	} else if(ibis_str_equal(command, IBIS_MSG_NICK)) {
+		return ibis_client_handle_nick(client, message);
+	} else if(ibis_str_equal(command, IBIS_RPL_WELCOME)) {
+		return ibis_client_handle_rpl_welcome(client, message);
 	} else if(ibis_str_equal(command, IBIS_RPL_ISUPPORT)) {
 		return ibis_client_handle_rpl_isupport(client, message);
 	}
@@ -435,6 +506,8 @@
 
 	g_clear_pointer(&client->server_name, g_free);
 
+	g_clear_pointer(&client->active_nick, g_free);
+
 	G_OBJECT_CLASS(ibis_client_parent_class)->finalize(obj);
 }
 
@@ -445,6 +518,9 @@
 	IbisClient *client = IBIS_CLIENT(obj);
 
 	switch(param_id) {
+	case PROP_ACTIVE_NICK:
+		g_value_set_string(value, ibis_client_get_active_nick(client));
+		break;
 	case PROP_ALT_NICK:
 		g_value_set_string(value, ibis_client_get_alt_nick(client));
 		break;
@@ -540,6 +616,18 @@
 	obj_class->set_property = ibis_client_set_property;
 
 	/**
+	 * IbisClient:active-nick:
+	 *
+	 * This the nick that the server has acknowledged.
+	 *
+	 * Since: 0.11
+	 */
+	properties[PROP_ACTIVE_NICK] = g_param_spec_string(
+		"active-nick", NULL, NULL,
+		NULL,
+		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	/**
 	 * IbisClient:alt-nick:
 	 *
 	 * The alternative nick to use if [property@Client:nick] is already in use.
@@ -834,6 +922,13 @@
 }
 
 const char *
+ibis_client_get_active_nick(IbisClient *client) {
+	g_return_val_if_fail(IBIS_IS_CLIENT(client), NULL);
+
+	return client->active_nick;
+}
+
+const char *
 ibis_client_get_alt_nick(IbisClient *client) {
 	g_return_val_if_fail(IBIS_IS_CLIENT(client), NULL);
 
diff --git a/ibis/ibisclient.h b/ibis/ibisclient.h
--- a/ibis/ibisclient.h
+++ b/ibis/ibisclient.h
@@ -58,6 +58,20 @@
  *
  * A representation of an IRCv3 client.
  *
+ * The client has a bit of functionality built-in but there are some cases
+ * where you may have to let the default handler run if you listening to the
+ * generic [signal@Client::message] signal.
+ *
+ * The client uses the [const@Ibis.RPL_WELCOME] message to update
+ * [property@Client:active-nick]. This means your handler will need to return
+ * %FALSE to let the default handler implement this feature.
+ *
+ * Likewise, the client also uses [const@Ibis.MSG_NICK] to update the
+ * [property@Client:active-nick] property if the nick name of the user has been
+ * changed via a bouncer or just via a `NICK` command. So again, your handler
+ * will need to return %FALSE to let the default handler implement this
+ * feature.
+ *
  * Since: 0.1
  */
 
@@ -102,6 +116,20 @@
 void ibis_client_disconnect(IbisClient *client, GError *error, const char *reason);
 
 /**
+ * ibis_client_get_active_nick:
+ *
+ * Gets the active nick for the client.
+ *
+ * The active nick is the nick that the server has accepted.
+ *
+ * Returns: (nullable): The active nick if it is known.
+ *
+ * Since: 0.11
+ */
+IBIS_AVAILABLE_IN_0_11
+const char *ibis_client_get_active_nick(IbisClient *client);
+
+/**
  * ibis_client_get_alt_nick:
  *
  * Gets the alternative nick to use in the event that [property@Client:nick] is
diff --git a/ibis/ibisversion.h b/ibis/ibisversion.h
--- a/ibis/ibisversion.h
+++ b/ibis/ibisversion.h
@@ -365,6 +365,30 @@
 #define IBIS_AVAILABLE_TYPE_IN_0_10
 #endif
 
+/**
+ * IBIS_VERSION_0_11:
+ *
+ * A macro that evaluates to the 0.10 version of ibis, in a format that can be
+ * used by the C pre-processor.
+ *
+ * Since: 0.11
+ */
+#define IBIS_VERSION_0_11 (G_ENCODE_VERSION(0, 11))
+
+#if IBIS_VERSION_MAX_ALLOWED < IBIS_VERSION_0_11
+#define IBIS_AVAILABLE_IN_0_11 IBIS_UNAVAILABLE(0, 11)
+#define IBIS_AVAILABLE_STATIC_INLINE_IN_0_11 IBIS_UNAVAILABLE_STATIC_INLINE(0, 11)
+#define IBIS_AVAILABLE_MACRO_IN_0_11 IBIS_UNAVAILABLE_MACRO(0, 11)
+#define IBIS_AVAILABLE_ENUMERATOR_IN_0_11 IBIS_UNAVAILABLE_ENUMERATOR(0, 11)
+#define IBIS_AVAILABLE_TYPE_IN_0_11 IBIS_UNAVAILABLE_TYPE(0, 11)
+#else
+#define IBIS_AVAILABLE_IN_0_11 _IBIS_EXTERN
+#define IBIS_AVAILABLE_STATIC_INLINE_IN_0_11
+#define IBIS_AVAILABLE_MACRO_IN_0_11
+#define IBIS_AVAILABLE_ENUMERATOR_IN_0_11
+#define IBIS_AVAILABLE_TYPE_IN_0_11
+#endif
+
 G_BEGIN_DECLS
 
 /**
