about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--bounce.h55
-rw-r--r--client.c18
2 files changed, 64 insertions, 9 deletions
diff --git a/bounce.h b/bounce.h
index aa7fda1..5dd2536 100644
--- a/bounce.h
+++ b/bounce.h
@@ -35,8 +35,6 @@
 
 typedef unsigned char byte;
 
-bool verbose;
-
 enum { ParamCap = 15 };
 struct Message {
 	char *origin;
@@ -58,6 +56,59 @@ static inline struct Message parse(char *line) {
 	return msg;
 }
 
+#define ENUM_CAP \
+	X("account-notify", CapAccountNotify) \
+	X("away-notify", CapAwayNotify) \
+	X("chghost", CapChghost) \
+	X("extended-join", CapExtendedJoin) \
+	X("invite-notify", CapInviteNotify) \
+	X("server-time", CapServerTime) \
+	X("", CapUnsupported)
+
+enum Cap {
+#define X(name, id) BIT(id),
+	ENUM_CAP
+#undef X
+};
+
+static const char *CapNames[] = {
+#define X(name, id) [id##Bit] = name,
+	ENUM_CAP
+#undef X
+};
+
+static inline enum Cap capParse(const char *list) {
+	enum Cap caps = 0;
+	while (*list) {
+		enum Cap cap = CapUnsupported;
+		size_t len = strcspn(list, " ");
+		for (size_t i = 0; i < ARRAY_LEN(CapNames); ++i) {
+			if (len != strlen(CapNames[i])) continue;
+			if (strncmp(list, CapNames[i], len)) continue;
+			cap = 1 << i;
+			break;
+		}
+		caps |= cap;
+		list += len;
+		if (*list) list++;
+	}
+	return caps;
+}
+
+static inline const char *capList(enum Cap caps) {
+	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));
+		}
+	}
+	return buf;
+}
+
+bool verbose;
+
 void ringAlloc(size_t len);
 void ringProduce(const char *line);
 size_t ringConsumer(const char *name);
diff --git a/client.c b/client.c
index 6ac0869..9bbc5d9 100644
--- a/client.c
+++ b/client.c
@@ -41,7 +41,7 @@ struct Client {
 	struct tls *tls;
 	enum Need need;
 	size_t consumer;
-	bool serverTime;
+	enum Cap caps;
 	char buf[1024];
 	size_t len;
 	bool error;
@@ -158,13 +158,17 @@ 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 :server-time\r\n", ORIGIN);
+		clientFormat(
+			client, ":%s CAP * LS :%s\r\n",
+			ORIGIN, capList(CapServerTime)
+		);
 
 	} else if (!strcmp(msg->params[0], "REQ") && msg->params[1]) {
 		if (client->need) client->need |= NeedCapEnd;
-		if (!strcmp(msg->params[1], "server-time")) {
-			client->serverTime = true;
-			clientFormat(client, ":%s CAP * ACK :server-time\r\n", ORIGIN);
+		enum Cap caps = capParse(msg->params[1]);
+		if (caps == CapServerTime) {
+			client->caps |= caps;
+			clientFormat(client, ":%s CAP * ACK :%s\r\n", ORIGIN, msg->params[1]);
 		} else {
 			clientFormat(client, ":%s CAP * NAK :%s\r\n", ORIGIN, msg->params[1]);
 		}
@@ -172,7 +176,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, (client->serverTime ? "server-time" : "")
+			ORIGIN, capList(client->caps)
 		);
 
 	} else {
@@ -277,7 +281,7 @@ void clientConsume(struct Client *client) {
 	time_t time;
 	const char *line = ringPeek(&time, client->consumer);
 	if (!line) return;
-	if (client->serverTime) {
+	if (client->caps & CapServerTime) {
 		char ts[sizeof("YYYY-MM-DDThh:mm:ss.sssZ")];
 		struct tm *tm = gmtime(&time);
 		strftime(ts, sizeof(ts), "%FT%T.000Z", tm);