From dbb2d1a5c8e1596bdbca07134291b81a4cca2be2 Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Sat, 20 Sep 2025 15:02:42 +0200 Subject: [PATCH] Move isupport_check_for_changes() to the 'isupport' module. This function was added a short while ago, and well it seems to be able to be possible in a module. Since the 'isupport' module is mandatory and this is ISUPPORT related, it is the right place. Can't move isupport_snapshot() because modules might not be loaded yet or things are currently unloading, i think. Not important anyway. Also, make things work if there are more changes than would fit on one isupport line. Although I didn't really test this.. Ended up splitting things in 3 helper functions to avoid some goto and/or duplicate code and stuff. The alternative was, surprisingly, even more ugly. --- include/h.h | 4 +- include/modules.h | 1 + include/struct.h | 2 + src/api-efunctions.c | 2 + src/api-isupport.c | 80 --------------------------- src/modules/isupport.c | 121 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 129 insertions(+), 81 deletions(-) diff --git a/include/h.h b/include/h.h index e842c59f7..b9f2b6e7e 100644 --- a/include/h.h +++ b/include/h.h @@ -944,6 +944,7 @@ extern MODVAR char *(*utf8_convert_confusables)(const char *i, char *obuf, int o extern MODVAR const char *(*utf8_get_block_name)(int i); extern MODVAR int (*utf8_get_block_number)(const char *name); extern MODVAR void (*send_isupport)(Client *client); +extern MODVAR void (*isupport_check_for_changes)(void); /* /Efuncs */ /* TLS functions */ @@ -1061,8 +1062,9 @@ extern PendingNet *find_pending_net_by_sid_butone(const char *sid, Client *exemp extern Client *find_pending_net_duplicates(Client *cptr, Client **srv, char **sid); extern MODVAR char serveropts[]; extern MODVAR char *ISupportStrings[]; +extern MODVAR ISupport *ISupports; +extern MODVAR ISupport *ISupports_old; extern void isupport_snapshot(void); -extern void isupport_check_for_changes(void); extern void read_packet(int fd, int revents, void *data); extern int process_packet(Client *cptr, char *readbuf, int length, int killsafely); extern int parse_chanmode(ParseMode *pm, const char *modebuf_in, const char *parabuf_in); diff --git a/include/modules.h b/include/modules.h index 516e5840b..5ccb13bc1 100644 --- a/include/modules.h +++ b/include/modules.h @@ -2781,6 +2781,7 @@ enum EfunctionType { EFUNC_UTF8_GET_BLOCK_NAME, EFUNC_UTF8_GET_BLOCK_NUMBER, EFUNC_SEND_ISUPPORT, + EFUNC_ISUPPORT_CHECK_FOR_CHANGES, }; /* Module flags */ diff --git a/include/struct.h b/include/struct.h index f46623ff0..dd0003414 100644 --- a/include/struct.h +++ b/include/struct.h @@ -209,6 +209,8 @@ typedef OperPermission (*OperClassEntryEvalCallback)(OperClassACLEntryVar* varia #define USERHOST_REPLYLEN (NICKLEN+HOSTLEN+USERLEN+5) +#define ISUPPORTLEN BUFSIZE-HOSTLEN-NICKLEN-39 + /* NOTE: this must be down here so the stuff from struct.h IT uses works */ #include "whowas.h" diff --git a/src/api-efunctions.c b/src/api-efunctions.c index 893980c24..94ac3f0ac 100644 --- a/src/api-efunctions.c +++ b/src/api-efunctions.c @@ -189,6 +189,7 @@ char *(*utf8_convert_confusables)(const char *i, char *obuf, int olen); const char *(*utf8_get_block_name)(int i); int (*utf8_get_block_number)(const char *name); void (*send_isupport)(Client *client); +void (*isupport_check_for_changes)(void); Efunction *EfunctionAddMain(Module *module, EfunctionType eftype, int (*func)(), void (*vfunc)(), void *(*pvfunc)(), char *(*stringfunc)(), const char *(*conststringfunc)()) { @@ -520,4 +521,5 @@ void efunctions_init(void) efunc_init_function(EFUNC_UTF8_GET_BLOCK_NAME, utf8_get_block_name, utf8_get_block_name_default_handler, 0); efunc_init_function(EFUNC_UTF8_GET_BLOCK_NUMBER, utf8_get_block_number, utf8_get_block_number_default_handler, 0); efunc_init_function(EFUNC_SEND_ISUPPORT, send_isupport, NULL, 0); + efunc_init_function(EFUNC_ISUPPORT_CHECK_FOR_CHANGES, isupport_check_for_changes, NULL, 0); } diff --git a/src/api-isupport.c b/src/api-isupport.c index 5c87abf3b..deb3560fc 100644 --- a/src/api-isupport.c +++ b/src/api-isupport.c @@ -256,7 +256,6 @@ void ISupportDel(ISupport *isupport) void make_isupportstrings(void) { int i; -#define ISUPPORTLEN BUFSIZE-HOSTLEN-NICKLEN-39 int bufsize = ISUPPORTLEN; int tokcnt = 0; ISupport *isupport; @@ -352,82 +351,3 @@ void isupport_snapshot(void) AppendListItem(f, ISupports_old); } } - -ISupport *isupport_find(ISupport *list, const char *name) -{ - for (; list; list = list->next) - if (!strcmp(list->token, name)) - return list; - return NULL; -} - -void isupport_check_for_changes(void) -{ - Client *acptr; - ISupport *n; // iterator for "new isupports" - ISupport *o; // iterator for "old isupports" - char buf[512], addstr[512]; - - if (!iConf.send_isupport_updates) - return; - - buf[0] = '\0'; - - /* New tokens and changed values */ - for (n = ISupports; n; n = n->next) - { - o = isupport_find(ISupports_old, n->token); - if (!o || - (!o->value && n->value) || - (n->value && !o->value) || - (n->value && o->value && strcmp(n->value, o->value))) - { - /* New or changed */ - if (n->value) - { - snprintf(addstr, sizeof(addstr), "%s=%s", - n->token, n->value); - } else { - strlcpy(addstr, n->token, sizeof(addstr)); - } - if (strlen(buf) + strlen(addstr) < 400) - { - if (*buf) - strlcat(buf, " ", sizeof(buf)); - strlcat(buf, addstr, sizeof(buf)); - } else { - abort(); - } - } - } - - /* Removed tokens */ - for (o = ISupports_old; o; o = o->next) - { - n = isupport_find(ISupports, o->token); - if (!n) - { - /* Removed */ - strlcpy(addstr, "-", sizeof(addstr)); - strlcpy(addstr, o->token, sizeof(addstr)); - if (strlen(buf) + strlen(addstr) < 400) - { - if (*buf) - strlcat(buf, " ", sizeof(buf)); - strlcat(buf, addstr, sizeof(buf)); - } else { - abort(); - } - } - } - - if (*buf) - { - // batch! well, for some :D - list_for_each_entry(acptr, &lclient_list, lclient_node) - { - sendto_one(acptr, NULL, ":%s 005 %s %s", - me.name, acptr->name, buf); - } - } -} diff --git a/src/modules/isupport.c b/src/modules/isupport.c index 7678882b6..8c96d8915 100644 --- a/src/modules/isupport.c +++ b/src/modules/isupport.c @@ -27,6 +27,7 @@ /* Forward declarations */ void _send_isupport(Client *client); +void _isupport_check_for_changes(void); ModuleHeader MOD_HEADER ={ @@ -42,6 +43,7 @@ MOD_TEST() MARK_AS_OFFICIAL_MODULE(modinfo); EfunctionAddVoid(modinfo->handle, EFUNC_SEND_ISUPPORT, _send_isupport); + EfunctionAddVoid(modinfo->handle, EFUNC_ISUPPORT_CHECK_FOR_CHANGES, _isupport_check_for_changes); return MOD_SUCCESS; } @@ -107,3 +109,122 @@ void _send_isupport(Client *client) safe_free_message_tags(mtags); } } + +ISupport *isupport_find_ex(ISupport *list, const char *name) +{ + for (; list; list = list->next) + if (!strcmp(list->token, name)) + return list; + return NULL; +} + +void isupport_check_for_changes_send(const char *addstr, char *buf, size_t buflen, char *batch, MessageTag *mtags, int *changes, int *buffered_changes) +{ + Client *acptr; + + if (!*buf) + return; + + list_for_each_entry(acptr, &lclient_list, lclient_node) + { + if (HasCapability(acptr, "draft/extended-isupport") && HasCapability(acptr, "batch")) + { + sendtaggednumericfmt(acptr, mtags, RPL_ISUPPORT, "%s :are supported by this server", buf); + } else { + sendnumeric(acptr, RPL_ISUPPORT, buf); + } + } + *buf = '\0'; + *buffered_changes = 0; +} + +void isupport_check_for_changes_one(const char *addstr, char *buf, size_t buflen, char *batch, MessageTag *mtags, int *changes, int *buffered_changes) +{ + Client *acptr; + + if (*changes == 0) + { + /* First change, need to start the batch */ + list_for_each_entry(acptr, &lclient_list, lclient_node) + if (HasCapability(acptr, "draft/extended-isupport") && HasCapability(acptr, "batch")) + sendto_one(acptr, NULL, ":%s BATCH +%s draft/isupport", me.name, batch); + } + + *changes = *changes + 1; + *buffered_changes = *buffered_changes + 1; + + if ((strlen(buf) + strlen(addstr) >= ISUPPORTLEN) || (*buffered_changes == 13)) + isupport_check_for_changes_send(addstr, buf, buflen, batch, mtags, changes, buffered_changes); + + /* Append */ + if (*buf) + strlcat(buf, " ", buflen); + strlcat(buf, addstr, buflen); +} + +void _isupport_check_for_changes(void) +{ + Client *acptr; + MessageTag *mtags = NULL; + char batch[BATCHLEN+1]; + ISupport *n; // iterator for "new isupports" + ISupport *o; // iterator for "old isupports" + char buf[512], addstr[512]; + int changes = 0, bc = 0; + + if (!iConf.send_isupport_updates) + return; + + buf[0] = '\0'; + + generate_batch_id(batch); + mtags = safe_alloc(sizeof(MessageTag)); + safe_strdup(mtags->name, "batch"); + safe_strdup(mtags->value, batch); + + /* New tokens and changed values */ + for (n = ISupports; n; n = n->next) + { + o = isupport_find_ex(ISupports_old, n->token); + if (!o || + (!o->value && n->value) || + (n->value && !o->value) || + (n->value && o->value && strcmp(n->value, o->value))) + { + /* New or changed */ + if (n->value) + { + snprintf(addstr, sizeof(addstr), "%s=%s", + n->token, n->value); + } else { + strlcpy(addstr, n->token, sizeof(addstr)); + } + isupport_check_for_changes_one(addstr, buf, sizeof(buf), batch, mtags, &changes, &bc); + } + } + + /* Removed tokens */ + for (o = ISupports_old; o; o = o->next) + { + n = isupport_find_ex(ISupports, o->token); + if (!n) + { + /* Removed */ + strlcpy(addstr, "-", sizeof(addstr)); + strlcpy(addstr, o->token, sizeof(addstr)); + isupport_check_for_changes_one(addstr, buf, sizeof(buf), batch, mtags, &changes, &bc); + } + } + + isupport_check_for_changes_send(NULL, buf, sizeof(buf), batch, mtags, &changes, &bc); + + if (changes) + { + /* End the batch (for those clients who received a batch, that is) */ + list_for_each_entry(acptr, &lclient_list, lclient_node) + if (HasCapability(acptr, "draft/extended-isupport") && HasCapability(acptr, "batch")) + sendto_one(acptr, NULL, ":%s BATCH -%s", me.name, batch); + } + + safe_free_message_tags(mtags); +}