1
0
mirror of https://github.com/unrealircd/unrealircd.git synced 2026-07-10 12:23:13 +02:00

Add new command: HISTORY #chan [lines]. This is meant for end-users

so they can fetch more history than the standard on-join history.

In the future we are also likely to implement IRCv3 CHATHISTORY
once that becomes an official specification. However, until it is
specified and until most major clients support it, several years
are likely to pass. It would be a shame to withhold channel
history to many end-users in the meantime when it takes so little
effort from us to provide an easy command.

See also
https://www.unrealircd.org/docs/Channel_history
And in particular the new section:
https://www.unrealircd.org/docs/Channel_history#Playback_frontends
which explains the relationship between on-join playback,
HISTORY and CHATHISTORY.
This commit is contained in:
Bram Matthys
2020-02-02 11:57:51 +01:00
parent b5a205f4f3
commit a8c191b291
10 changed files with 285 additions and 129 deletions
+1
View File
@@ -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
+1 -1
View File
@@ -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);
+2 -2
View File
@@ -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;
+14 -13
View File
@@ -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;
}
+6 -1
View File
@@ -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
#############################################################################
+22 -53
View File
@@ -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);
}
+112
View File
@@ -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);
}
+103 -13
View File
@@ -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);
}
+4 -4
View File
@@ -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;
}
+20 -42
View File
@@ -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 :( */