summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2020-02-27 02:36:32 -0500
committerJune McEnroe <june@causal.agency>2020-02-27 02:36:32 -0500
commit793f564ffd9622e2c879b1ee3591748d602d6118 (patch)
treecc556825581fa1e81ccd4246ad84ae1f2d972970
parentSupport cap-notify (diff)
downloadpounce-793f564ffd9622e2c879b1ee3591748d602d6118.tar.gz
pounce-793f564ffd9622e2c879b1ee3591748d602d6118.zip
Support CAP LS 302 from clients
-rw-r--r--bounce.h10
-rw-r--r--client.c16
-rw-r--r--pounce.113
-rw-r--r--state.c4
4 files changed, 32 insertions, 11 deletions
diff --git a/bounce.h b/bounce.h
index 1655772..5aff027 100644
--- a/bounce.h
+++ b/bounce.h
@@ -87,7 +87,9 @@ enum Cap {
 #define X(name, id) BIT(id),
 	ENUM_CAP
 #undef X
-	TagCaps = CapAccountTag
+	CapBits,
+	TagCaps = 0
+		| CapAccountTag
 		| CapBatch
 		| CapLabeledResponse
 		| CapMessageTags
@@ -118,13 +120,17 @@ static inline enum Cap capParse(const char *list) {
 	return caps;
 }
 
-static inline const char *capList(enum Cap caps) {
+static inline const char *capList(enum Cap caps, const char *values[CapBits]) {
 	static char buf[1024];
 	buf[0] = '\0';
 	for (size_t i = 0; i < ARRAY_LEN(CapNames); ++i) {
 		if (caps & (1 << i)) {
 			if (buf[0]) strlcat(buf, " ", sizeof(buf));
 			strlcat(buf, CapNames[i], sizeof(buf));
+			if (values && values[i]) {
+				strlcat(buf, "=", sizeof(buf));
+				strlcat(buf, values[i], sizeof(buf));
+			}
 		}
 	}
 	return buf;
diff --git a/client.c b/client.c
index 2d5b8e0..f66ea0e 100644
--- a/client.c
+++ b/client.c
@@ -166,6 +166,7 @@ static void handleCap(struct Client *client, struct Message *msg) {
 	if (!msg->params[0]) msg->params[0] = "";
 
 	enum Cap avail = CapServerTime | CapPassive | (stateCaps & ~CapSASL);
+	const char *values[CapBits] = { [CapSASLBit] = "EXTERNAL" };
 	if (clientCA) avail |= CapSASL;
 
 	if (!strcmp(msg->params[0], "END")) {
@@ -175,7 +176,18 @@ static void handleCap(struct Client *client, struct Message *msg) {
 
 	} else if (!strcmp(msg->params[0], "LS")) {
 		if (client->need) client->need |= NeedCapEnd;
-		clientFormat(client, ":%s CAP * LS :%s\r\n", ORIGIN, capList(avail));
+		if (msg->params[1] && !strcmp(msg->params[1], "302")) {
+			if (avail & CapCapNotify) client->caps |= CapCapNotify;
+			clientFormat(
+				client, ":%s CAP * LS :%s\r\n",
+				ORIGIN, capList(avail, values)
+			);
+		} else {
+			clientFormat(
+				client, ":%s CAP * LS :%s\r\n",
+				ORIGIN, capList(avail, NULL)
+			);
+		}
 
 	} else if (!strcmp(msg->params[0], "REQ") && msg->params[1]) {
 		if (client->need) client->need |= NeedCapEnd;
@@ -190,7 +202,7 @@ static void handleCap(struct Client *client, struct Message *msg) {
 	} else if (!strcmp(msg->params[0], "LIST")) {
 		clientFormat(
 			client, ":%s CAP * LIST :%s\r\n",
-			ORIGIN, capList(client->caps)
+			ORIGIN, capList(client->caps, NULL)
 		);
 
 	} else {
diff --git a/pounce.1 b/pounce.1
index ef28279..0ab6210 100644
--- a/pounce.1
+++ b/pounce.1
@@ -525,9 +525,12 @@ daemon implements the following:
 .%A Perry Lorier
 .%A Kevin L. Mitchell
 .%A William Pitcock
-.%T IRCv3.1 Client Capability Negotiation
+.%A Attila Molnar
+.%A Daniel Oakley
+.%A James Wheare
+.%T IRCv3 Client Capability Negotiation
 .%I IRCv3 Working Group
-.%U https://ircv3.net/specs/core/capability-negotiation-3.1.html
+.%U https://ircv3.net/specs/core/capability-negotiation
 .Re
 .
 .It
@@ -554,10 +557,10 @@ daemon implements the following:
 .It
 .Rs
 .%A William Pitcock
-.%A Jilles Tjoelker
-.%T IRCv3.1 SASL Authentication
+.%A Attila Molnar
+.%T IRCv3.2 SASL Authentication
 .%I IRCv3 Working Group
-.%U https://ircv3.net/specs/extensions/sasl-3.1.html
+.%U https://ircv3.net/specs/extensions/sasl-3.2
 .Re
 .
 .It
diff --git a/state.c b/state.c
index d840018..71c2763 100644
--- a/state.c
+++ b/state.c
@@ -51,7 +51,7 @@ void stateLogin(
 	serverFormat("CAP LS\r\n");
 	if (pass) serverFormat("PASS :%s\r\n", pass);
 	if (sasl) {
-		serverFormat("CAP REQ :%s\r\n", capList(CapSASL));
+		serverFormat("CAP REQ :%s\r\n", capList(CapSASL, NULL));
 		if (plain) {
 			byte buf[AuthLen];
 			size_t len = 1 + strlen(plain);
@@ -76,7 +76,7 @@ static void handleCap(struct Message *msg) {
 
 	if (!strcmp(msg->params[1], "LS") || !strcmp(msg->params[1], "NEW")) {
 		caps &= ~(CapSASL | CapUnsupported);
-		if (caps) serverFormat("CAP REQ :%s\r\n", capList(caps));
+		if (caps) serverFormat("CAP REQ :%s\r\n", capList(caps, NULL));
 
 	} else if (!strcmp(msg->params[1], "ACK")) {
 		stateCaps |= caps;