mirror of
https://github.com/unrealircd/unrealircd.git
synced 2026-07-02 22:43:13 +02:00
dbb2d1a5c8
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.
231 lines
6.1 KiB
C
231 lines
6.1 KiB
C
/*
|
|
* IRC - Internet Relay Chat, src/modules/isupport.c
|
|
* (C) 2025 Valware & The UnrealIRCd Team
|
|
*
|
|
* See file AUTHORS in IRC package for additional names of
|
|
* the programmers.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 1, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
/* One include for all */
|
|
#include "unrealircd.h"
|
|
|
|
#define CMD_ISUPPORT "ISUPPORT"
|
|
|
|
/* Forward declarations */
|
|
void _send_isupport(Client *client);
|
|
void _isupport_check_for_changes(void);
|
|
|
|
ModuleHeader MOD_HEADER
|
|
={
|
|
"isupport", /* Name of module */
|
|
"5.0", /* Version */
|
|
"Implement ISUPPORT (numeric 005) sending", /* Short description of module */
|
|
"UnrealIRCd Team", /* Author */
|
|
"unrealircd-6", /* Version of UnrealIRCd */
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
MOD_INIT()
|
|
{
|
|
MARK_AS_OFFICIAL_MODULE(modinfo);
|
|
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_LOAD()
|
|
{
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_UNLOAD()
|
|
{
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
/* Command 'ISUPPORT' (no params)
|
|
* This command is used to send ISUPPORT information to the client.
|
|
* Clients who have the 'draft/extended-isupport' capability will be
|
|
* able to use this command to view ISUPPORT tokens before sending
|
|
* NICK/USER[/PASS].
|
|
* I've left this open to other users as well as it doesn't make sense
|
|
* to gatekeep it beyond what would be considered normal usage anyway.
|
|
* -- Valware
|
|
*/
|
|
void _send_isupport(Client *client)
|
|
{
|
|
char batch[BATCHLEN+1];
|
|
int i;
|
|
MessageTag *mtags = NULL, *m;
|
|
|
|
*batch = '\0';
|
|
|
|
if (HasCapability(client, "draft/extended-isupport") && HasCapability(client, "batch"))
|
|
{
|
|
generate_batch_id(batch);
|
|
new_message(client, NULL, &mtags);
|
|
m = safe_alloc(sizeof(MessageTag));
|
|
safe_strdup(m->name, "batch");
|
|
safe_strdup(m->value, batch);
|
|
AddListItem(m, mtags);
|
|
}
|
|
|
|
if (*batch)
|
|
sendto_one(client, NULL, ":%s BATCH +%s draft/isupport", me.name, batch);
|
|
|
|
for (i = 0; ISupportStrings[i]; i++)
|
|
{
|
|
if (*batch)
|
|
sendtaggednumericfmt(client, mtags, RPL_ISUPPORT, "%s :are supported by this server", ISupportStrings[i]);
|
|
else
|
|
sendnumeric(client, RPL_ISUPPORT, ISupportStrings[i]);
|
|
}
|
|
|
|
if (*batch)
|
|
{
|
|
sendto_one(client, NULL, ":%s BATCH -%s", me.name, batch);
|
|
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);
|
|
}
|