# HG changeset patch
# Node ID 284c57319cf7
# Parent  7197410767bf
diff --git a/ibis-cli/ibiscli.c b/ibis-cli/ibiscli.c
new file mode 100644
--- /dev/null
+++ b/ibis-cli/ibiscli.c
@@ -0,0 +1,140 @@
+/*
+ * Ibis - IRCv3 Library
+ * Copyright (C) 2022-2024 Ibis Developers <devel@pidgin.im>
+ *
+ * Ibis is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#ifndef _WIN32
+#include <signal.h>
+#include <glib-unix.h>
+#endif /* _WIN32 */
+
+#include <ibis.h>
+
+/******************************************************************************
+ * Command line options
+ *****************************************************************************/
+static char *nick = NULL;
+static char *server = NULL;
+static int port = 6667;
+static gboolean tls = FALSE;
+
+static GOptionEntry cli_options[] = {
+	{
+		.long_name = "nick",
+		.short_name = 'n',
+		.arg = G_OPTION_ARG_STRING,
+		.arg_data = &nick,
+		.description = N_("The nickname to use."),
+		.arg_description = N_("NICK"),
+	}, {
+		.long_name = "server",
+		.short_name = 's',
+		.arg = G_OPTION_ARG_STRING,
+		.arg_data = &server,
+		.description = N_("The ip or hostname for the server to connect to."),
+		.arg_description = N_("HOSTNAME"),
+	}, {
+		.long_name = "port",
+		.short_name = 'p',
+		.arg = G_OPTION_ARG_INT,
+		.arg_data = &port,
+		.description = N_("The port to connect to on the server."),
+		.arg_description = N_("PORT"),
+	}, {
+		.long_name = "tls",
+		.short_name = 't',
+		.arg = G_OPTION_ARG_NONE,
+		.arg_data = &tls,
+		.description = N_("Attempt a TLS connection."),
+	},
+	G_OPTION_ENTRY_NULL
+};
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+ibiscli_activate_cb(GApplication *application, gpointer data) {
+	IbisClient *client = data;
+
+	/* Hold the application so we stick around. */
+	g_application_hold(application);
+
+	ibis_client_set_nick(client, nick);
+	ibis_client_connect(client, server, (guint16)port, tls, NULL);
+}
+
+static void
+ibiscli_client_connected_cb(GObject *obj, G_GNUC_UNUSED GParamSpec *pspec,
+                            gpointer data)
+{
+	IbisClient *client = IBIS_CLIENT(obj);
+	GApplication *application = data;
+
+	if(!ibis_client_get_connected(client)) {
+		g_application_release(application);
+	}
+}
+
+#ifndef _WIN32
+static gboolean
+ibiscli_sigint_cb(gpointer data) {
+	IbisClient *client = data;
+
+	ibis_client_disconnect(client, NULL, "I'm out of here!");
+
+	return G_SOURCE_REMOVE;
+}
+#endif /* _WIN32 */
+
+/******************************************************************************
+ * Main
+ *****************************************************************************/
+int
+main(int argc, char *argv[]) {
+	IbisClient *client = NULL;
+	GApplication *application = NULL;
+	gint ret = 0;
+
+	client = ibis_client_new();
+
+	application = g_application_new("org.imfreedom.ibis.Ibis-Cli",
+	                                G_APPLICATION_NON_UNIQUE);
+	g_application_add_main_option_entries(application, cli_options);
+	g_signal_connect(application, "activate",
+	                 G_CALLBACK(ibiscli_activate_cb), client);
+
+	g_signal_connect(client, "notify::connected",
+	                 G_CALLBACK(ibiscli_client_connected_cb), application);
+
+#ifndef _WIN32
+	g_unix_signal_add(SIGINT, ibiscli_sigint_cb, client);
+#endif /* _WIN32 */
+
+	ret = g_application_run(application, argc, argv);
+
+	g_clear_object(&application);
+	g_clear_object(&client);
+
+	return ret;
+}
diff --git a/ibis-cli/meson.build b/ibis-cli/meson.build
new file mode 100644
--- /dev/null
+++ b/ibis-cli/meson.build
@@ -0,0 +1,9 @@
+IBIS_CLI_SOURCES = [
+  'ibiscli.c',
+]
+
+executable(
+  'ibis-cli',
+  IBIS_CLI_SOURCES,
+  dependencies: [gio_dep, glib_dep, ibis_dep],
+  install: false)
diff --git a/ibis/ibisclient.c b/ibis/ibisclient.c
--- a/ibis/ibisclient.c
+++ b/ibis/ibisclient.c
@@ -110,6 +110,26 @@
 }
 
 static void
+ibis_client_register(IbisClient *client) {
+	IbisMessage *message = NULL;
+
+	message = ibis_message_new("USER");
+	ibis_message_set_params(message, ibis_client_get_username(client), "0",
+	                        "*", ibis_client_get_realname(client), NULL);
+	ibis_client_write(client, message);
+
+	message = ibis_message_new("NICK");
+	ibis_message_set_params(message, client->nick, NULL);
+	ibis_client_write(client, message);
+
+	/* Set our connected property. */
+	ibis_client_set_connected(client, TRUE);
+}
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
 ibis_client_write_cb(GObject *source, GAsyncResult *result, gpointer data) {
 	IbisClient *client = data;
 	BirbQueuedOutputStream *stream = BIRB_QUEUED_OUTPUT_STREAM(source);
@@ -218,6 +238,37 @@
 }
 
 /******************************************************************************
+ * Default handlers
+ *****************************************************************************/
+static gboolean
+ibis_client_handle_ping(IbisClient *client, IbisMessage *message) {
+	IbisMessage *pong = NULL;
+	GStrv params = NULL;
+
+	pong = ibis_message_new("PONG");
+
+	params = ibis_message_get_params(message);
+	if(g_strv_length(params) >= 1) {
+		ibis_message_set_params(pong, params[0], NULL);
+	}
+
+	ibis_client_write(client, pong);
+
+	return TRUE;
+}
+
+static gboolean
+ibis_client_default_message_handler(IbisClient *client, const char *command,
+                                    IbisMessage *message)
+{
+	if(ibis_str_equal(command, "PING")) {
+		return ibis_client_handle_ping(client, message);
+	}
+
+	return FALSE;
+}
+
+/******************************************************************************
  * GObject Implementation
  *****************************************************************************/
 G_DEFINE_FINAL_TYPE(IbisClient, ibis_client, G_TYPE_OBJECT)
@@ -450,7 +501,7 @@
 		"message",
 		G_OBJECT_CLASS_TYPE(klass),
 		G_SIGNAL_DETAILED | G_SIGNAL_RUN_LAST,
-		NULL,
+		G_CALLBACK(ibis_client_default_message_handler),
 		g_signal_accumulator_true_handled,
 		NULL,
 		NULL,
@@ -677,8 +728,8 @@
 	                                    ibis_client_read_cb,
 	                                    client);
 
-	/* Set our connected property. */
-	ibis_client_set_connected(client, TRUE);
+	ibis_capabilities_start(client->capabilities);
+	ibis_client_register(client);
 }
 
 void
diff --git a/meson.build b/meson.build
--- a/meson.build
+++ b/meson.build
@@ -75,6 +75,7 @@
 # Sub directories
 ###############################################################################
 subdir('ibis')
+subdir('ibis-cli')
 subdir('po')
 
 ###############################################################################
