diff --git a/doc/conf/modules.default.conf b/doc/conf/modules.default.conf index 0e2452ca6..8c64de367 100644 --- a/doc/conf/modules.default.conf +++ b/doc/conf/modules.default.conf @@ -74,6 +74,7 @@ loadmodule "starttls"; loadmodule "time"; loadmodule "userip"; loadmodule "vhost"; +loadmodule "history"; // IRC Operator commands // Note: several of these like kill are also server-to-server commands diff --git a/include/h.h b/include/h.h index 0c70ba309..a31729d4d 100644 --- a/include/h.h +++ b/include/h.h @@ -922,9 +922,9 @@ extern MessageTag *find_mtag(MessageTag *mtags, const char *token); extern MessageTag *duplicate_mtag(MessageTag *mtag); extern void free_message_tags(MessageTag *m); extern time_t server_time_to_unix_time(const char *tbuf); +extern int history_set_limit(char *object, int max_lines, long max_t); extern int history_add(char *object, MessageTag *mtags, char *line); extern int history_request(Client *acptr, char *object, HistoryFilter *filter); -extern int history_del(char *object, int max_lines, long max_time); extern int history_destroy(char *object); extern void special_delayed_unloading(void); extern int write_int64(FILE *fd, uint64_t t); diff --git a/include/modules.h b/include/modules.h index f26f6e11b..86383a607 100644 --- a/include/modules.h +++ b/include/modules.h @@ -490,8 +490,8 @@ typedef struct HistoryBackend HistoryBackend; struct HistoryBackend { HistoryBackend *prev, *next; char *name; /**< The name of the history backend (eg: "mem") */ + int (*history_set_limit)(char *object, int max_lines, long max_time); /**< Impose a limit on a history object */ int (*history_add)(char *object, MessageTag *mtags, char *line); /**< Add to history */ - int (*history_del)(char *object, int max_lines, long max_time); /**< Delete history, based on lines/time */ int (*history_request)(Client *acptr, char *object, HistoryFilter *filter); /**< Request history */ int (*history_destroy)(char *object); /**< Destroy history of this object completely */ Module *owner; /**< Module introducing this */ @@ -503,8 +503,8 @@ struct HistoryBackend { */ typedef struct { char *name; + int (*history_set_limit)(char *object, int max_lines, long max_time); int (*history_add)(char *object, MessageTag *mtags, char *line); - int (*history_del)(char *object, int max_lines, long max_time); int (*history_request)(Client *acptr, char *object, HistoryFilter *filter); int (*history_destroy)(char *object); } HistoryBackendInfo; diff --git a/src/api-history-backend.c b/src/api-history-backend.c index d7c7c8c45..f6eba608f 100644 --- a/src/api-history-backend.c +++ b/src/api-history-backend.c @@ -61,11 +61,12 @@ HistoryBackend *HistoryBackendAdd(Module *module, HistoryBackendInfo *mreq) HistoryBackend *m; int exists = 0; - if (!mreq->history_add || !mreq->history_del || !mreq->history_request || !mreq->history_destroy) + if (!mreq->history_add || !mreq->history_request || + !mreq->history_destroy || !mreq->history_set_limit) { if (module) module->errorcode = MODERR_INVALID; - ircd_log(LOG_ERROR, "HistoryBackendAdd(): missing a handler for add/del/request/destroy"); + ircd_log(LOG_ERROR, "HistoryBackendAdd(): missing a handler for add/del/request/destroy/set_limit"); return NULL; } m = HistoryBackendFind(mreq->name); @@ -89,9 +90,9 @@ HistoryBackend *HistoryBackendAdd(Module *module, HistoryBackendInfo *mreq) /* Add or update the following fields: */ m->owner = module; m->history_add = mreq->history_add; - m->history_del = mreq->history_del; m->history_request = mreq->history_request; m->history_destroy = mreq->history_destroy; + m->history_set_limit = mreq->history_set_limit; if (!exists) AddListItem(m, historybackends); @@ -165,16 +166,6 @@ int history_add(char *object, MessageTag *mtags, char *line) return 1; } -int history_del(char *object, int max_lines, long max_time) -{ - HistoryBackend *hb; - - for (hb = historybackends; hb; hb=hb->next) - hb->history_del(object, max_lines, max_time); - - return 1; -} - int history_request(Client *client, char *object, HistoryFilter *filter) { HistoryBackend *hb; @@ -194,3 +185,13 @@ int history_destroy(char *object) return 1; } + +int history_set_limit(char *object, int max_lines, long max_t) +{ + HistoryBackend *hb; + + for (hb = historybackends; hb; hb=hb->next) + hb->history_set_limit(object, max_lines, max_t); + + return 1; +} diff --git a/src/modules/Makefile.in b/src/modules/Makefile.in index d7f5fa3db..375698d79 100644 --- a/src/modules/Makefile.in +++ b/src/modules/Makefile.in @@ -69,7 +69,8 @@ R_MODULES= \ message-tags.so batch.so \ account-tag.so labeled-response.so link-security.so \ message-ids.so plaintext-policy.so server-time.so sts.so \ - echo-message.so ident_lookup.so + echo-message.so \ + ident_lookup.so history.so MODULES=cloak.so $(R_MODULES) MODULEFLAGS=@MODULEFLAGS@ @@ -608,6 +609,10 @@ ident_lookup.so: ident_lookup.c $(INCLUDES) $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ -o ident_lookup.so ident_lookup.c +history.so: history.c $(INCLUDES) + $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ + -o history.so history.c + ############################################################################# # capabilities ############################################################################# diff --git a/src/modules/chanmodes/history.c b/src/modules/chanmodes/history.c index f44afeb78..d948713f8 100644 --- a/src/modules/chanmodes/history.c +++ b/src/modules/chanmodes/history.c @@ -33,26 +33,11 @@ struct HistoryChanMode { Cmode_t EXTMODE_HISTORY = 0L; #define HistoryEnabled(channel) (channel->mode.extmode & EXTMODE_HISTORY) -/* The regular history cleaning (by timer) is spread out - * a bit, rather than doing ALL channels every T time. - * HISTORY_SPREAD: how much to spread the "cleaning", eg 1 would be - * to clean everything in 1 go, 2 would mean the first event would - * clean half of the channels, and the 2nd event would clean the rest. - * Obviously more = better to spread the load, but doing a reasonable - * amount of work is also benefitial for performance (think: CPU cache). - * HISTORY_MAX_OFF_SECS: how many seconds may the history be 'off', - * that is: how much may we store the history longer than required. - * The other 2 macros are calculated based on that target. - */ -#define HISTORY_SPREAD 16 -#define HISTORY_MAX_OFF_SECS 128 -#define HISTORY_CLEAN_PER_LOOP (CHAN_HASH_TABLE_SIZE/HISTORY_SPREAD) -#define HISTORY_TIMER_EVERY (HISTORY_MAX_OFF_SECS/HISTORY_SPREAD) - /* Forward declarations */ static void init_config(void); int history_config_test(ConfigFile *, ConfigEntry *, int, int *); int history_config_run(ConfigFile *, ConfigEntry *, int); +int history_chanmode_change(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode); static int compare_history_modes(HistoryChanMode *a, HistoryChanMode *b); int history_chanmode_is_ok(Client *client, Channel *channel, char mode, char *para, int type, int what); void *history_chanmode_put_param(void *r_in, char *param); @@ -64,7 +49,6 @@ int history_chanmode_sjoin_check(Channel *channel, void *ourx, void *theirx); int history_channel_destroy(Channel *channel, int *should_destroy); int history_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, char *target, MessageTag *mtags, char *text, int notice); int history_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]); -EVENT(history_clean); MOD_TEST() { @@ -94,16 +78,16 @@ MOD_INIT() init_config(); HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, history_config_run); + HookAdd(modinfo->handle, HOOKTYPE_LOCAL_CHANMODE, 0, history_chanmode_change); + HookAdd(modinfo->handle, HOOKTYPE_REMOTE_CHANMODE, 0, history_chanmode_change); HookAdd(modinfo->handle, HOOKTYPE_LOCAL_JOIN, 0, history_join); HookAdd(modinfo->handle, HOOKTYPE_CHANMSG, 0, history_chanmsg); - HookAdd(modinfo->handle, HOOKTYPE_CHANNEL_DESTROY, 1000000, history_channel_destroy); return MOD_SUCCESS; } MOD_LOAD() { - EventAdd(modinfo->handle, "history_clean", history_clean, NULL, HISTORY_TIMER_EVERY*1000, 0); return MOD_SUCCESS; } @@ -476,6 +460,25 @@ int history_chanmode_sjoin_check(Channel *channel, void *ourx, void *theirx) return EXSJ_MERGE; } +/** On channel mode change, communicate the +H limits to the history backend layer */ +int history_chanmode_change(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode) +{ + HistoryChanMode *settings; + + /* Did anything change, with regards to channel mode H ? */ + if (!strchr(modebuf, 'H')) + return 0; + + /* If so, grab the settings, and communicate them */ + settings = (HistoryChanMode *)GETPARASTRUCT(channel, 'H'); + if (settings) + history_set_limit(channel->chname, settings->max_lines, settings->max_time); + else + history_destroy(channel->chname); + + return 0; +} + /** Channel is destroyed (or is it?) */ int history_channel_destroy(Channel *channel, int *should_destroy) { @@ -518,8 +521,6 @@ int history_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, text); history_add(channel->chname, mtags, buf); - settings = (HistoryChanMode *)GETPARASTRUCT(channel, 'H'); - history_del(channel->chname, settings->max_lines, settings->max_time); return 0; } @@ -530,39 +531,7 @@ int history_join(Client *client, Channel *channel, MessageTag *mtags, char *parv return 0; if (MyUser(client)) - { - HistoryChanMode *settings = (HistoryChanMode *)GETPARASTRUCT(channel, 'H'); - history_del(channel->chname, settings->max_lines, settings->max_time); history_request(client, channel->chname, NULL); - } return 0; } - -/** Periodically clean the history. - * Instead of doing all channels in 1 go, we do a limited number - * of channels each call, hence the 'static int' and the do { } while - * rather than a regular for loop. - */ -EVENT(history_clean) -{ - static int hashnum = 0; - int loopcnt = 0; - Channel *channel; - - do - { - for (channel = hash_get_chan_bucket(hashnum); channel; channel = channel->hnextch) - { - if (HistoryEnabled(channel)) - { - HistoryChanMode *settings = (HistoryChanMode *)GETPARASTRUCT(channel, 'H'); - if (settings) - history_del(channel->chname, settings->max_lines, settings->max_time); - } - } - hashnum++; - if (hashnum >= CHAN_HASH_TABLE_SIZE) - hashnum = 0; - } while(loopcnt++ < HISTORY_CLEAN_PER_LOOP); -} diff --git a/src/modules/history.c b/src/modules/history.c new file mode 100644 index 000000000..c9d7ea31b --- /dev/null +++ b/src/modules/history.c @@ -0,0 +1,112 @@ +/* src/modules/history.c - (C) 2020 Bram Matthys (Syzop) & The UnrealIRCd Team + * + * Simple HISTORY command to fetch channel history. + * This is one of the interfaces to the channel history backend. + * The other, most prominent, being history-on-join at the moment. + * In the future there will likely be an IRCv3 standard which + * will allow clients to fetch history automatically, which will + * be implemented under a different command and not really meant + * for end users. However, it will take a while until such a spec is + * finalized, let alone when major clients will finally support it. + * + * See doc/Authors and git history 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. + */ + +#include "unrealircd.h" + +ModuleHeader MOD_HEADER + = { + "history", + "5.0", + "Simple history command for end-users", + "UnrealIRCd Team", + "unrealircd-5", + }; + + +CMD_FUNC(cmd_history); + +MOD_INIT() +{ + CommandAdd(NULL, "HISTORY", cmd_history, MAXPARA, CMD_USER); + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +void history_usage(Client *client) +{ + sendnotice(client, " Use: /HISTORY #channel [lines-to-display]"); + sendnotice(client, " Ex: /HISTORY #lobby"); + sendnotice(client, " Ex: /HISTORY #lobby 50"); + sendnotice(client, "The lines-to-display value must be 1-100, the default is 100"); + sendnotice(client, "Naturally, the line count and time limits in channel mode +H are obeyed"); +} + +CMD_FUNC(cmd_history) +{ + HistoryFilter filter; + Channel *channel; + int lines; + + if ((parc < 3) || BadPtr(parv[2])) + { + history_usage(client); + return; + } + + channel = find_channel(parv[1], NULL); + if (!channel) + { + sendnumeric(client, ERR_NOSUCHCHANNEL, parv[1]); + return; + } + + if (!IsMember(client, channel)) + { + sendnumeric(client, ERR_NOTONCHANNEL, channel->chname); + return; + } + + lines = atoi(parv[2]); + if (lines < 1) + { + history_usage(client); + return; + } + if (lines > 100) + lines = 100; + + if (!HasCapability(client, "server-time")) + { + sendnotice(client, "Your IRC client does not support the 'server-time' capability"); + sendnotice(client, "https://ircv3.net/specs/extensions/server-time-3.2.html"); + sendnotice(client, "History request refused."); + return; + } + + filter.last_lines = lines; + history_request(client, channel->chname, &filter); +} diff --git a/src/modules/history_backend_mem.c b/src/modules/history_backend_mem.c index ba24fc957..f7e3bddf7 100644 --- a/src/modules/history_backend_mem.c +++ b/src/modules/history_backend_mem.c @@ -14,7 +14,7 @@ ModuleHeader MOD_HEADER = { "history_backend_mem", - "1.0", + "2.0", "History backend: memory", "UnrealIRCd Team", "unrealircd-5", @@ -24,6 +24,22 @@ ModuleHeader MOD_HEADER #define OBJECTLEN ((NICKLEN > CHANNELLEN) ? NICKLEN : CHANNELLEN) #define HISTORY_BACKEND_MEM_HASH_TABLE_SIZE 1019 +/* The regular history cleaning (by timer) is spread out + * a bit, rather than doing ALL channels every T time. + * HISTORY_SPREAD: how much to spread the "cleaning", eg 1 would be + * to clean everything in 1 go, 2 would mean the first event would + * clean half of the channels, and the 2nd event would clean the rest. + * Obviously more = better to spread the load, but doing a reasonable + * amount of work is also benefitial for performance (think: CPU cache). + * HISTORY_MAX_OFF_SECS: how many seconds may the history be 'off', + * that is: how much may we store the history longer than required. + * The other 2 macros are calculated based on that target. + */ +#define HISTORY_SPREAD 16 +#define HISTORY_MAX_OFF_SECS 128 +#define HISTORY_CLEAN_PER_LOOP (HISTORY_BACKEND_MEM_HASH_TABLE_SIZE/HISTORY_SPREAD) +#define HISTORY_TIMER_EVERY (HISTORY_MAX_OFF_SECS/HISTORY_SPREAD) + /* Definitions (structs, etc.) */ typedef struct HistoryLogLine HistoryLogLine; struct HistoryLogLine { @@ -40,6 +56,8 @@ struct HistoryLogObject { HistoryLogLine *tail; /**< End of the log (the latest entry) */ int num_lines; /**< Number of lines of log */ time_t oldest_t; /**< Oldest time in log */ + int max_lines; /**< Maximum number of lines permitted */ + long max_time; /**< Maximum number of seconds to retain history */ char name[OBJECTLEN+1]; }; @@ -49,9 +67,11 @@ HistoryLogObject *history_hash_table[HISTORY_BACKEND_MEM_HASH_TABLE_SIZE]; /* Forward declarations */ int hbm_history_add(char *object, MessageTag *mtags, char *line); -int hbm_history_del(char *object, int max_lines, long max_time); +int hbm_history_cleanup(HistoryLogObject *h); int hbm_history_request(Client *client, char *object, HistoryFilter *filter); int hbm_history_destroy(char *object); +int hbm_history_set_limit(char *object, int max_lines, long max_time); +EVENT(history_mem_clean); MOD_INIT() { @@ -66,9 +86,9 @@ MOD_INIT() memset(&hbi, 0, sizeof(hbi)); hbi.name = "mem"; hbi.history_add = hbm_history_add; - hbi.history_del = hbm_history_del; hbi.history_request = hbm_history_request; hbi.history_destroy = hbm_history_destroy; + hbi.history_set_limit = hbm_history_set_limit; if (!HistoryBackendAdd(modinfo->handle, &hbi)) return MOD_FAILED; @@ -77,6 +97,7 @@ MOD_INIT() MOD_LOAD() { + EventAdd(modinfo->handle, "history_mem_clean", history_mem_clean, NULL, HISTORY_TIMER_EVERY*1000, 0); return MOD_SUCCESS; } @@ -223,6 +244,21 @@ void hbm_history_del_line(HistoryLogObject *h, HistoryLogLine *l) int hbm_history_add(char *object, MessageTag *mtags, char *line) { HistoryLogObject *h = hbm_find_or_add_object(object); + if (!h->max_lines) + { + sendto_realops("hbm_history_add() for '%s', which has no limit", h->name); +#ifdef DEBUGMODE + abort(); +#else + h->max_lines = 50; + h->max_t = 86400; +#endif + } + if (h->num_lines >= h->max_lines) + { + /* Delete previous line */ + hbm_history_del_line(h, h->head); + } hbm_history_add_line(h, mtags, line); return 0; } @@ -260,6 +296,8 @@ int hbm_history_request(Client *client, char *object, HistoryFilter *filter) HistoryLogObject *h = hbm_find_object(object); HistoryLogLine *l; char batch[BATCHLEN+1]; + long redline; /* Imaginary timestamp. Before the red line, history is too old. */ + int lines_sendable = 0, lines_to_skip = 0, cnt = 0; if (!h || !can_receive_history(client)) return 0; @@ -273,8 +311,27 @@ int hbm_history_request(Client *client, char *object, HistoryFilter *filter) sendto_one(client, NULL, ":%s BATCH +%s chathistory %s", me.name, batch, object); } + redline = TStime() - h->max_time; + + /* Once the filter API expands, the following will change too. + * For now, this is sufficient, since requests are only about lines: + */ + lines_sendable = 0; for (l = h->head; l; l = l->next) - hbm_send_line(client, l, batch); + if (l->t >= redline) + lines_sendable++; + if (filter && (lines_sendable > filter->last_lines)) + lines_to_skip = lines_sendable - filter->last_lines; + + for (l = h->head; l; l = l->next) + { + /* Make sure we don't send too old entries: + * We only have to check for time here, as line count is already + * taken into account in hbm_history_add. + */ + if (l->t >= redline && (++cnt > lines_to_skip)) + hbm_send_line(client, l, batch); + } /* End of batch */ if (*batch) @@ -282,16 +339,13 @@ int hbm_history_request(Client *client, char *object, HistoryFilter *filter) return 1; } -int hbm_history_del(char *object, int max_lines, long max_time) +/** Clean up expired entries */ +int hbm_history_cleanup(HistoryLogObject *h) { - HistoryLogObject *h = hbm_find_object(object); HistoryLogLine *l, *l_next = NULL; - long redline = TStime() - max_time; + long redline = TStime() - h->max_time; - if (!h) - return 0; - - /* First enforce 'max_time', after that enforce 'max_lines' */ + /* First enforce 'h->max_time', after that enforce 'h->max_lines' */ /* Checking for time */ if (h->oldest_t < redline) @@ -311,14 +365,14 @@ int hbm_history_del(char *object, int max_lines, long max_time) } } - if (h->num_lines > max_lines) + if (h->num_lines > h->max_lines) { h->oldest_t = 0; /* recalculate in next loop */ for (l = h->head; l; l = l_next) { l_next = l->next; - if (h->num_lines > max_lines) + if (h->num_lines > h->max_lines) { hbm_history_del_line(h, l); continue; @@ -354,3 +408,39 @@ int hbm_history_destroy(char *object) hbm_delete_object_hlo(h); return 1; } + +/** Set new limit on history object */ +int hbm_history_set_limit(char *object, int max_lines, long max_time) +{ + HistoryLogObject *h = hbm_find_or_add_object(object); + h->max_lines = max_lines; + h->max_time = max_time; + hbm_history_cleanup(h); /* impose new restrictions */ + return 1; +} + +/** Periodically clean the history. + * Instead of doing all channels in 1 go, we do a limited number + * of channels each call, hence the 'static int' and the do { } while + * rather than a regular for loop. + * Note that we already impose the line limit in hbm_history_add, + * so this history_mem_clean is for removals due to max_time limits. + */ +EVENT(history_mem_clean) +{ + static int hashnum = 0; + int loopcnt = 0; + Channel *channel; + HistoryLogObject *h; + + do + { + for (h = history_hash_table[hashnum++]; h; h = h->next) + hbm_history_cleanup(h); + + hashnum++; + + if (hashnum >= HISTORY_BACKEND_MEM_HASH_TABLE_SIZE) + hashnum = 0; + } while(loopcnt++ < HISTORY_CLEAN_PER_LOOP); +} diff --git a/src/modules/history_backend_null.c b/src/modules/history_backend_null.c index 7819e9b59..3f62371dc 100644 --- a/src/modules/history_backend_null.c +++ b/src/modules/history_backend_null.c @@ -13,15 +13,15 @@ ModuleHeader MOD_HEADER = { "history_backend_null", - "1.0", + "2.0", "History backend: null/none", "UnrealIRCd Team", "unrealircd-5", }; /* Forward declarations */ +int hbn_history_set_limit(char *object, int max_lines, long max_time); int hbn_history_add(char *object, MessageTag *mtags, char *line); -int hbn_history_del(char *object, int max_lines, long max_time); int hbn_history_request(Client *client, char *object, HistoryFilter *filter); int hbn_history_destroy(char *object); @@ -34,8 +34,8 @@ MOD_INIT() memset(&hbi, 0, sizeof(hbi)); hbi.name = "mem"; + hbi.history_set_limit = hbn_history_set_limit; hbi.history_add = hbn_history_add; - hbi.history_del = hbn_history_del; hbi.history_request = hbn_history_request; hbi.history_destroy = hbn_history_destroy; if (!HistoryBackendAdd(modinfo->handle, &hbi)) @@ -64,7 +64,7 @@ int hbn_history_request(Client *client, char *object, HistoryFilter *filter) return 0; } -int hbn_history_del(char *object, int max_lines, long max_time) +int hbn_history_set_limit(char *object, int max_lines, long max_time) { return 1; } diff --git a/src/modules/sjoin.c b/src/modules/sjoin.c index 063f72a80..6c2a54aaa 100644 --- a/src/modules/sjoin.c +++ b/src/modules/sjoin.c @@ -78,6 +78,20 @@ aParv *mp2parv(char *xmbuf, char *parmbuf) return (&pparv); } +void send_local_chan_mode(MessageTag *recv_mtags, Client *client, Channel *channel, char *modebuf, char *parabuf) +{ + MessageTag *mtags = NULL; + + new_message_special(client, recv_mtags, &mtags, ":%s MODE %s %s %s", client->name, channel->chname, modebuf, parabuf); + sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags, + ":%s MODE %s %s %s", client->name, channel->chname, modebuf, parabuf); + if (MyConnect(client)) + RunHook7(HOOKTYPE_LOCAL_CHANMODE, client, channel, mtags, modebuf, parabuf, 0, 0); + else + RunHook7(HOOKTYPE_REMOTE_CHANMODE, client, channel, mtags, modebuf, parabuf, 0, 0); + free_message_tags(mtags); +} + /** SJOIN: Synchronize channel modes, +beI lists and users (server-to-server command) * Extensive technical documentation is available at: * https://www.unrealircd.org/docs/Server_protocol:SJOIN_command @@ -114,11 +128,7 @@ aParv *mp2parv(char *xmbuf, char *parmbuf) modebuf[b] = 0;\ }\ else {\ - MessageTag *mtags = NULL; \ - new_message_special(client, recv_mtags, &mtags, ":%s MODE %s %s %s", client->name, channel->chname, modebuf, parabuf); \ - sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags, \ - ":%s MODE %s %s %s", client->name, channel->chname, modebuf, parabuf); \ - free_message_tags(mtags); \ + send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf); \ strcpy(parabuf,param);\ /* modebuf[0] should stay what it was ('+' or '-') */ \ modebuf[1] = mode;\ @@ -227,13 +237,7 @@ CMD_FUNC(cmd_sjoin) MessageTag *mtags = NULL; ap = mp2parv(modebuf, parabuf); set_mode(channel, client, ap->parc, ap->parv, &pcount, pvar, 0); - - /* send to all local users: */ - new_message_special(client, recv_mtags, &mtags, ":%s MODE %s %s %s", client->name, channel->chname, modebuf, parabuf); - sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags, - ":%s MODE %s %s %s", - client->name, channel->chname, modebuf, parabuf); - free_message_tags(mtags); + send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf); } /* remove bans */ /* reset the buffers */ @@ -306,16 +310,8 @@ CMD_FUNC(cmd_sjoin) } if (b > 1) { - MessageTag *mtags = NULL; - modebuf[b] = '\0'; - - /* send to all local users: */ - new_message_special(client, recv_mtags, &mtags, ":%s MODE %s %s %s", client->name, channel->chname, modebuf, parabuf); - sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags, - ":%s MODE %s %s %s", - client->name, channel->chname, modebuf, parabuf); - free_message_tags(mtags); + send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf); } /* since we're dropping our modes, we want to clear the mlock as well. --nenolod */ @@ -644,15 +640,8 @@ getnick: if (modebuf[1]) { - MessageTag *mtags = NULL; - modebuf[b] = '\0'; - - new_message_special(client, recv_mtags, &mtags, ":%s MODE %s %s %s", client->name, channel->chname, modebuf, parabuf); - sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags, - ":%s MODE %s %s %s", - client->name, channel->chname, modebuf, parabuf); - free_message_tags(mtags); + send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf); } if (!merge && !removetheirs && !nomode) @@ -674,12 +663,7 @@ getnick: ap = mp2parv(modebuf, parabuf); set_mode(channel, client, ap->parc, ap->parv, &pcount, pvar, 0); - - new_message_special(client, recv_mtags, &mtags, ":%s MODE %s %s %s", client->name, channel->chname, modebuf, parabuf); - sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags, - ":%s MODE %s %s %s", - client->name, channel->chname, modebuf, parabuf); - free_message_tags(mtags); + send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf); } if (merge && !nomode) @@ -883,13 +867,7 @@ getnick: if (modebuf[1]) { - MessageTag *mtags = NULL; - - new_message_special(client, recv_mtags, &mtags, ":%s MODE %s %s %s", client->name, channel->chname, modebuf, parabuf); - sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags, - ":%s MODE %s %s %s", - client->name, channel->chname, modebuf, parabuf); - free_message_tags(mtags); + send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf); } /* free the oldmode.* crap :( */