From 94613c78b6314673aaf058b334ac912a8e80b66c Mon Sep 17 00:00:00 2001
From: Ed Kellett <e@kellett.im>
Date: Fri, 16 Oct 2020 00:24:46 +0100
Subject: [PATCH] Implement the solanum.chat/identify-msg vendor cap

---
 extensions/Makefile.am                |  1 +
 extensions/identify_msg.c             | 29 ++++++++++++
 include/client.h                      |  4 ++
 ircd/modules.c                        |  1 +
 modules/Makefile.am                   |  3 +-
 modules/core/m_identified.c           | 64 +++++++++++++++++++++++++++
 tests/runtime/modules/m_identified.so |  1 +
 7 files changed, 102 insertions(+), 1 deletion(-)
 create mode 100644 extensions/identify_msg.c
 create mode 100644 modules/core/m_identified.c
 create mode 120000 tests/runtime/modules/m_identified.so

diff --git a/extensions/Makefile.am b/extensions/Makefile.am
index e4460d1c..cf44a370 100644
--- a/extensions/Makefile.am
+++ b/extensions/Makefile.am
@@ -78,6 +78,7 @@ extension_LTLIBRARIES =		\
   spy_stats_p_notice.la		\
   spy_trace_notice.la		\
   drain.la			\
+  identify_msg.la		\
   example_module.la
 
 if HAVE_HYPERSCAN
diff --git a/extensions/identify_msg.c b/extensions/identify_msg.c
new file mode 100644
index 00000000..d6b10fcc
--- /dev/null
+++ b/extensions/identify_msg.c
@@ -0,0 +1,29 @@
+#include <stdinc.h>
+#include <modules.h>
+#include <msgbuf.h>
+
+static const char identify_msg_desc[] = "Provides the solanum.chat/identify-msg client capability";
+
+static void identmsg_outbound(void *);
+unsigned int CLICAP_IDENTIFY_MSG = 0;
+
+mapi_cap_list_av2 identmsg_cap_list[] = {
+	{ MAPI_CAP_CLIENT, "solanum.chat/identify-msg", NULL, &CLICAP_IDENTIFY_MSG },
+	{ 0, NULL, NULL, NULL }
+};
+
+static mapi_hfn_list_av1 identmsg_hfnlist[] = {
+	{ "outbound_msgbuf", identmsg_outbound },
+	{ NULL, NULL }
+};
+
+static void identmsg_outbound(void *data_)
+{
+	hook_data *data = data_;
+	struct MsgBuf *msgbuf = data->arg1;
+
+	if (IsIdentified(data->client))
+		msgbuf_append_tag(msgbuf, "solanum.chat/identified", NULL, CLICAP_IDENTIFY_MSG);
+}
+
+DECLARE_MODULE_AV2(identify_msg, NULL, NULL, NULL, NULL, identmsg_hfnlist, identmsg_cap_list, NULL, identify_msg_desc);
diff --git a/include/client.h b/include/client.h
index 894d82fd..cb5ec43d 100644
--- a/include/client.h
+++ b/include/client.h
@@ -429,6 +429,7 @@ struct ListClient
 #define FLAGS_EXEMPTSPAMBOT	0x02000000
 #define FLAGS_EXEMPTSHIDE	0x04000000
 #define FLAGS_EXEMPTJUPE	0x08000000
+#define FLAGS_IDENTIFIED	0x10000000	/* owns their current nick */
 
 
 /* flags for local clients, this needs stuff moved from above to here at some point */
@@ -494,6 +495,9 @@ struct ListClient
 #define IsTGExcessive(x)	((x)->flags & FLAGS_TGEXCESSIVE)
 #define SetTGExcessive(x)	((x)->flags |= FLAGS_TGEXCESSIVE)
 #define ClearTGExcessive(x)	((x)->flags &= ~FLAGS_TGEXCESSIVE)
+#define IsIdentified(x)		((x)->flags & FLAGS_IDENTIFIED)
+#define SetIdentified(x)	((x)->flags |= FLAGS_IDENTIFIED)
+#define ClearIdentified(x)	((x)->flags &= ~FLAGS_IDENTIFIED)
 
 /* local flags */
 
diff --git a/ircd/modules.c b/ircd/modules.c
index 7e466d65..e4dc728b 100644
--- a/ircd/modules.c
+++ b/ircd/modules.c
@@ -51,6 +51,7 @@ static const char *core_module_table[] = {
 	"m_ban",
 	"m_die",
 	"m_error",
+	"m_identified",
 	"m_join",
 	"m_kick",
 	"m_kill",
diff --git a/modules/Makefile.am b/modules/Makefile.am
index d7005577..3ebe628f 100644
--- a/modules/Makefile.am
+++ b/modules/Makefile.am
@@ -97,4 +97,5 @@ module_LTLIBRARIES = \
   core/m_part.la \
   core/m_quit.la \
   core/m_server.la \
-  core/m_squit.la
+  core/m_squit.la \
+  core/m_identified.la
diff --git a/modules/core/m_identified.c b/modules/core/m_identified.c
new file mode 100644
index 00000000..46d0918f
--- /dev/null
+++ b/modules/core/m_identified.c
@@ -0,0 +1,64 @@
+#include <stdinc.h>
+#include <modules.h>
+#include <messages.h>
+#include <send.h>
+
+static const char identified_desc[] = "Provides the IDENTIFIED server-to-server command";
+
+static void m_identified(struct MsgBuf *, struct Client *, struct Client *, int, const char *[]);
+
+static struct Message identified_msgtab = {
+	"IDENTIFIED", 0, 0, 0, 0,
+	{mg_ignore, mg_ignore, mg_ignore, mg_ignore, {m_identified, 3}, mg_ignore}
+};
+
+static mapi_clist_av1 identified_clist[] = {
+	&identified_msgtab,
+	NULL
+};
+
+static void identified_nick_change(void *);
+static void identified_burst_client(void *);
+
+static mapi_hfn_list_av1 identified_hfnlist[] = {
+	{ "local_nick_change", identified_nick_change, HOOK_MONITOR },
+	{ "remote_nick_change", identified_nick_change, HOOK_MONITOR },
+	{ "burst_client", identified_burst_client },
+	{ NULL, NULL }
+};
+
+static void m_identified(struct MsgBuf *msgbuf, struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
+{
+	struct Client *target_p = find_person(parv[1]);
+	const char *nick = parv[2];
+
+	if (target_p == NULL)
+		return;
+
+	if (irccmp(target_p->name, nick))
+		return;
+
+	if (parc > 3 && irccmp(parv[3], "OFF") == 0)
+		ClearIdentified(target_p);
+	else
+		SetIdentified(target_p);
+}
+
+static void identified_nick_change(void *data_)
+{
+	hook_cdata *data = data_;
+	const char *oldnick = data->arg1, *newnick = data->arg2;
+
+	if (irccmp(oldnick, newnick) != 0)
+		ClearIdentified(data->client);
+}
+
+static void identified_burst_client(void *data_)
+{
+	hook_data_client *data = data_;
+
+	if (IsIdentified(data->target))
+		sendto_one(data->client, ":%s ENCAP * IDENTIFIED %s :%s", me.id, use_id(data->target), data->target->name);
+}
+
+DECLARE_MODULE_AV2(identified, NULL, NULL, identified_clist, NULL, identified_hfnlist, NULL, NULL, identified_desc);
diff --git a/tests/runtime/modules/m_identified.so b/tests/runtime/modules/m_identified.so
new file mode 120000
index 00000000..e709ea09
--- /dev/null
+++ b/tests/runtime/modules/m_identified.so
@@ -0,0 +1 @@
+../../../modules/core/.libs/m_identified.so
\ No newline at end of file