From 684f6515d4ff49a8334ebb07d5ef01735fed29c7 Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Mon, 22 Jun 2026 09:00:30 +0200 Subject: [PATCH] "CAP LS" may only respond 1 line, we now advertise less. "CAP LS 302" unaffected. When not using version 302, such as with "CAP LS", the specification does not allow us to use continuation lines. This means all advertised caps must fit into one line. That is no longer always the case, especially if you load 3rd party capabilities. So we need to scratch advertising some capabilities to <302 clients. "CAP LS 302" is unaffected. Note that version 302 in the specification exists since at least November 2017, so most clients use that one. According to https://ircv3.net/software/clients the following clients are affected by this change: Desktop Clients * KVIrc * Circe * catgirl * BitchX * Pidgin * LimeChat Mobile Clients * IRC for Android * LimeChat And various older versions of other clients (obviously). NOTE: The source is only that IRCv3 page. I did not check manually. For this particular commit. We filter out various unrealircd.org informative CAPs and the vendor specific json-log. So that isn't much of a problem. However, in the future we may be forced to filter out more capabilities to make room. It would be much better if all clients are on >=302. Also, I should mention we are not the only IRCd out there, so I can't vouch on what other IRCds (will) do when hitting this non-302-limit. Reported by ProgVal in https://bugs.unrealircd.org/view.php?id=6630 --- doc/RELEASE-NOTES.md | 6 ++++++ include/modules.h | 2 ++ src/api-clicap.c | 1 + src/modules/cap.c | 3 +++ src/modules/history_backend_mem.c | 1 + src/modules/json-log-tag.c | 1 + src/modules/link-security.c | 1 + src/modules/plaintext-policy.c | 1 + 8 files changed, 16 insertions(+) diff --git a/doc/RELEASE-NOTES.md b/doc/RELEASE-NOTES.md index 7099bcdc3..d5ddbb7b3 100644 --- a/doc/RELEASE-NOTES.md +++ b/doc/RELEASE-NOTES.md @@ -120,6 +120,12 @@ has been improved. telling the server "does not support remote JSON-RPC". ### Developers and protocol: +* If you use `CAP LS` instead of `CAP LS 302` then you will now miss various + capabilities. We had to trim it down because only 302 and later allow + responses that span multiple lines. If your client is still not using 302 + then please do so soon (the IRCv3 spec is from Nov'2017). For this first + change, you won't miss out much, but somewhere in the future this can + become a real problem for you. * URL API: The OutgoingWebRequest `max_size` (introduced last release) now also caps file-backed downloads. Default for file-backed when left at 0 is 50MB (`DOWNLOAD_MAX_SIZE_FILE_BACKED`). For memory-backed, it stays diff --git a/include/modules.h b/include/modules.h index a466c73c1..8a5e1dfac 100644 --- a/include/modules.h +++ b/include/modules.h @@ -517,6 +517,7 @@ struct ClientCapability { MessageTagHandler *mtag_handler; /**< For reverse dependency */ Module *owner; /**< Module introducing this CAP. */ char unloaded; /**< Internal flag to indicate module is being unloaded */ + int minimum_cap_version; /**< Minimum CAP version to show this CAP */ }; typedef struct { @@ -524,6 +525,7 @@ typedef struct { int flags; int (*visible)(Client *); const char *(*parameter)(Client *); + int minimum_cap_version; } ClientCapabilityInfo; /** @defgroup MessagetagAPI Message tag API diff --git a/src/api-clicap.c b/src/api-clicap.c index bb6991c97..5c6060cf2 100644 --- a/src/api-clicap.c +++ b/src/api-clicap.c @@ -204,6 +204,7 @@ ClientCapability *ClientCapabilityAdd(Module *module, ClientCapabilityInfo *clic /* Add or update the following fields: */ clicap->owner = module; clicap->flags = clicap_request->flags; + clicap->minimum_cap_version = clicap_request->minimum_cap_version; clicap->visible = clicap_request->visible; clicap->parameter = clicap_request->parameter; diff --git a/src/modules/cap.c b/src/modules/cap.c index 0b4a5d01c..f989de702 100644 --- a/src/modules/cap.c +++ b/src/modules/cap.c @@ -165,6 +165,9 @@ static void clicap_generate(Client *client, const char *subcmd, int flags) if (cap->visible && !cap->visible(client)) continue; /* hidden */ + if (cap->minimum_cap_version && (client->local->cap_protocol < cap->minimum_cap_version)) + continue; /* skip: doesn't meet minimum CAP version */ + if (flags) { if (!cap->cap || !(client->local->caps & cap->cap)) diff --git a/src/modules/history_backend_mem.c b/src/modules/history_backend_mem.c index 74f9e928a..6cbbe1c3c 100644 --- a/src/modules/history_backend_mem.c +++ b/src/modules/history_backend_mem.c @@ -428,6 +428,7 @@ static void init_history_storage(ModuleInfo *modinfo) cap.name = "unrealircd.org/history-storage"; cap.flags = CLICAP_FLAGS_ADVERTISE_ONLY; cap.parameter = history_storage_capability_parameter; + cap.minimum_cap_version = 302; ClientCapabilityAdd(modinfo->handle, &cap, NULL); } diff --git a/src/modules/json-log-tag.c b/src/modules/json-log-tag.c index 89d86106b..92e758a01 100644 --- a/src/modules/json-log-tag.c +++ b/src/modules/json-log-tag.c @@ -54,6 +54,7 @@ MOD_INIT() memset(&cap, 0, sizeof(cap)); cap.name = "unrealircd.org/json-log"; + cap.minimum_cap_version = 302; c = ClientCapabilityAdd(modinfo->handle, &cap, &CAP_JSON_LOG); memset(&mtag, 0, sizeof(mtag)); diff --git a/src/modules/link-security.c b/src/modules/link-security.c index 76e8bed11..30fac0fba 100644 --- a/src/modules/link-security.c +++ b/src/modules/link-security.c @@ -80,6 +80,7 @@ MOD_LOAD() cap.name = "unrealircd.org/link-security"; cap.flags = CLICAP_FLAGS_ADVERTISE_ONLY; cap.parameter = link_security_capability_parameter; + cap.minimum_cap_version = 302; ClientCapabilityAdd(modinfo->handle, &cap, NULL); EventAdd(modinfo->handle, "checklinksec", checklinksec, NULL, 2000, 0); diff --git a/src/modules/plaintext-policy.c b/src/modules/plaintext-policy.c index d5159d682..dd82b655c 100644 --- a/src/modules/plaintext-policy.c +++ b/src/modules/plaintext-policy.c @@ -71,5 +71,6 @@ void init_plaintext_policy(ModuleInfo *modinfo) cap.name = "unrealircd.org/plaintext-policy"; cap.flags = CLICAP_FLAGS_ADVERTISE_ONLY; cap.parameter = plaintext_policy_capability_parameter; + cap.minimum_cap_version = 302; ClientCapabilityAdd(modinfo->handle, &cap, NULL); }