diff --git a/doc/ircd.conf.example b/doc/ircd.conf.example index 6c19c8e4..fe1bbd7a 100644 --- a/doc/ircd.conf.example +++ b/doc/ircd.conf.example @@ -374,6 +374,7 @@ channel { autochanmodes = "+nt"; displayed_usercount = 3; strip_topic_colors = no; + opmod_send_statusmsg = no; }; serverhide { diff --git a/doc/reference.conf b/doc/reference.conf index 22438ff5..9c169bb9 100644 --- a/doc/reference.conf +++ b/doc/reference.conf @@ -837,6 +837,11 @@ channel { /* strip_topic_colors: whether or not color codes in TOPIC should be stripped. */ strip_topic_colors = no; + + /* opmod_send_statusmsg: format messages sent to ops due to +z + * as PRIVMSG @#channel when sent to clients. + */ + opmod_send_statusmsg = no; }; diff --git a/include/s_conf.h b/include/s_conf.h index a192a615..da6797b5 100644 --- a/include/s_conf.h +++ b/include/s_conf.h @@ -274,6 +274,7 @@ struct config_channel_entry unsigned int autochanmodes; int displayed_usercount; int strip_topic_colors; + int opmod_send_statusmsg; }; struct config_server_hide diff --git a/ircd/newconf.c b/ircd/newconf.c index a7e4b48e..54579103 100644 --- a/ircd/newconf.c +++ b/ircd/newconf.c @@ -2831,6 +2831,7 @@ static struct ConfEntry conf_channel_table[] = { "autochanmodes", CF_QSTRING, conf_set_channel_autochanmodes, 0, NULL }, { "displayed_usercount", CF_INT, NULL, 0, &ConfigChannel.displayed_usercount }, { "strip_topic_colors", CF_YESNO, NULL, 0, &ConfigChannel.strip_topic_colors }, + { "opmod_send_statusmsg", CF_YESNO, NULL, 0, &ConfigChannel.opmod_send_statusmsg }, { "\0", 0, NULL, 0, NULL } }; diff --git a/ircd/s_conf.c b/ircd/s_conf.c index c2ee0164..073ee792 100644 --- a/ircd/s_conf.c +++ b/ircd/s_conf.c @@ -805,6 +805,7 @@ set_default_conf(void) ConfigChannel.channel_target_change = true; ConfigChannel.disable_local_channels = false; ConfigChannel.displayed_usercount = 3; + ConfigChannel.opmod_send_statusmsg = false; ConfigChannel.autochanmodes = MODE_TOPICLIMIT | MODE_NOPRIVMSGS; diff --git a/ircd/send.c b/ircd/send.c index 7b036996..d2a92d16 100644 --- a/ircd/send.c +++ b/ircd/send.c @@ -598,22 +598,23 @@ sendto_channel_opmod(struct Client *one, struct Client *source_p, build_msgbuf_tags(&msgbuf, source_p); current_serial++; + const char *statusmsg_prefix = (ConfigChannel.opmod_send_statusmsg ? "@" : ""); if(IsServer(source_p)) { msgbuf_cache_initf(&msgbuf_cache, &msgbuf, &strings, - ":%s %s %s :", - source_p->name, command, chptr->chname); + ":%s %s %s%s :", + source_p->name, command, statusmsg_prefix, chptr->chname); } else { msgbuf_cache_initf(&msgbuf_cache, &msgbuf, &strings, - ":%s!%s@%s %s %s :", + ":%s!%s@%s %s %s%s :", source_p->name, source_p->username, - source_p->host, command, chptr->chname); + source_p->host, command, statusmsg_prefix, chptr->chname); } if (chptr->mode.mode & MODE_MODERATED) { linebuf_put_msgf(&rb_linebuf_old, &strings, - ":%s %s %s :", - use_id(source_p), command, chptr->chname, text); + ":%s %s %s%s :", + use_id(source_p), command, statusmsg_prefix, chptr->chname, text); } else { linebuf_put_msgf(&rb_linebuf_old, &strings, ":%s NOTICE @%s :<%s:%s> ", @@ -623,7 +624,6 @@ sendto_channel_opmod(struct Client *one, struct Client *source_p, linebuf_put_msgf(&rb_linebuf_new, &strings, ":%s %s =%s :", use_id(source_p), command, chptr->chname); - RB_DLINK_FOREACH_SAFE(ptr, next_ptr, chptr->members.head) { msptr = ptr->data; diff --git a/modules/m_info.c b/modules/m_info.c index f7deda17..4ffd23b6 100644 --- a/modules/m_info.c +++ b/modules/m_info.c @@ -638,6 +638,12 @@ static struct InfoStruct info_table[] = { &ConfigChannel.resv_forcepart, "Force-part local users on channel RESV" }, + { + "opmod_send_statusmsg", + OUTPUT_BOOLEAN_YN, + &ConfigChannel.opmod_send_statusmsg, + "Send messages to @#channel if affected by +z" + }, { "disable_hidden", OUTPUT_BOOLEAN_YN, diff --git a/tests/send1.c b/tests/send1.c index a1671ac3..13d4a218 100644 --- a/tests/send1.c +++ b/tests/send1.c @@ -1182,7 +1182,7 @@ static void sendto_channel_opmod__local(void) is_client_sendq_empty(local_chan_v, "Not +o; " MSG); is_client_sendq_empty(local_chan_p, "Message source; " MSG); is_client_sendq_empty(local_chan_d, "Deaf; " MSG); - is_client_sendq(":" TEST_ME_ID " NOTICE @" TEST_CHANNEL " : Hello World!" CRLF, server, MSG); + is_client_sendq(":" TEST_ME_ID " NOTICE @" TEST_CHANNEL " : Hello World!" CRLF, server, MSG); is_client_sendq_empty(server2, "No users to receive message; " MSG); // Moderated channel @@ -1211,6 +1211,73 @@ static void sendto_channel_opmod__local(void) is_client_sendq_empty(local_chan_d, "Deaf; " MSG); is_client_sendq(":" TEST_ME_ID "90004 TEST =" TEST_CHANNEL " :Hello World!" CRLF, server, MSG); is_client_sendq_empty(server2, "No users to receive message; " MSG); + + standard_free(); +} + +static void sendto_channel_opmod_statusmsg__local(void) +{ + standard_init(); + ConfigChannel.opmod_send_statusmsg = true; + + // This function does not support TS5... + standard_ids(); + + // Without CAP_CHW | CAP_EOPMOD + standard_server_caps(0, CAP_CHW | CAP_EOPMOD); + + sendto_channel_opmod(local_chan_p, local_chan_p, channel, "TEST", "Hello World!"); + is_client_sendq_empty(user, "Not on channel; " MSG); + is_client_sendq(":LChanPeon" TEST_ID_SUFFIX " TEST @" TEST_CHANNEL " :Hello World!" CRLF, local_chan_o, "On channel; " MSG); + is_client_sendq(":LChanPeon" TEST_ID_SUFFIX " TEST @" TEST_CHANNEL " :Hello World!" CRLF, local_chan_ov, "On channel; " MSG); + is_client_sendq_empty(local_chan_v, "Not +o; " MSG); + is_client_sendq_empty(local_chan_p, "Message source; " MSG); + is_client_sendq_empty(local_chan_d, "Deaf; " MSG); + is_client_sendq_empty(server, "No users to receive message; " MSG); + is_client_sendq_empty(server2, "No users to receive message; " MSG); + + // With CAP_CHW, without CAP_EOPMOD + standard_server_caps(CAP_CHW, CAP_EOPMOD); + + sendto_channel_opmod(local_chan_p, local_chan_p, channel, "TEST", "Hello World!"); + is_client_sendq_empty(user, "Not on channel; " MSG); + is_client_sendq(":LChanPeon" TEST_ID_SUFFIX " TEST @" TEST_CHANNEL " :Hello World!" CRLF, local_chan_o, "On channel; " MSG); + is_client_sendq(":LChanPeon" TEST_ID_SUFFIX " TEST @" TEST_CHANNEL " :Hello World!" CRLF, local_chan_ov, "On channel; " MSG); + is_client_sendq_empty(local_chan_v, "Not +o; " MSG); + is_client_sendq_empty(local_chan_p, "Message source; " MSG); + is_client_sendq_empty(local_chan_d, "Deaf; " MSG); + is_client_sendq(":" TEST_ME_ID " NOTICE @" TEST_CHANNEL " : Hello World!" CRLF, server, MSG); + is_client_sendq_empty(server2, "No users to receive message; " MSG); + + // Moderated channel + channel->mode.mode |= MODE_MODERATED; + + sendto_channel_opmod(local_chan_p, local_chan_p, channel, "TEST", "Hello World!"); + is_client_sendq_empty(user, "Not on channel; " MSG); + is_client_sendq(":LChanPeon" TEST_ID_SUFFIX " TEST @" TEST_CHANNEL " :Hello World!" CRLF, local_chan_o, "On channel; " MSG); + is_client_sendq(":LChanPeon" TEST_ID_SUFFIX " TEST @" TEST_CHANNEL " :Hello World!" CRLF, local_chan_ov, "On channel; " MSG); + is_client_sendq_empty(local_chan_v, "Not +o; " MSG); + is_client_sendq_empty(local_chan_p, "Message source; " MSG); + is_client_sendq_empty(local_chan_d, "Deaf; " MSG); + is_client_sendq(":" TEST_ME_ID "90004 TEST @" TEST_CHANNEL " :Hello World!" CRLF, server, MSG); + is_client_sendq_empty(server2, "No users to receive message; " MSG); + + // With CAP_CHW | CAP_EOPMOD + channel->mode.mode &= ~MODE_MODERATED; + standard_server_caps(CAP_CHW | CAP_EOPMOD, 0); + + sendto_channel_opmod(local_chan_p, local_chan_p, channel, "TEST", "Hello World!"); + is_client_sendq_empty(user, "Not on channel; " MSG); + is_client_sendq(":LChanPeon" TEST_ID_SUFFIX " TEST @" TEST_CHANNEL " :Hello World!" CRLF, local_chan_o, "On channel; " MSG); + is_client_sendq(":LChanPeon" TEST_ID_SUFFIX " TEST @" TEST_CHANNEL " :Hello World!" CRLF, local_chan_ov, "On channel; " MSG); + is_client_sendq_empty(local_chan_v, "Not +o; " MSG); + is_client_sendq_empty(local_chan_p, "Message source; " MSG); + is_client_sendq_empty(local_chan_d, "Deaf; " MSG); + is_client_sendq(":" TEST_ME_ID "90004 TEST =" TEST_CHANNEL " :Hello World!" CRLF, server, MSG); + is_client_sendq_empty(server2, "No users to receive message; " MSG); + + ConfigChannel.opmod_send_statusmsg = false; + standard_free(); } static void sendto_channel_opmod__local__tags(void) @@ -1248,7 +1315,7 @@ static void sendto_channel_opmod__local__tags(void) is_client_sendq_empty(local_chan_v, "Not +o; " MSG); is_client_sendq_empty(local_chan_p, "Message source; " MSG); is_client_sendq_empty(local_chan_d, "Deaf; " MSG); - is_client_sendq(":" TEST_ME_ID " NOTICE @" TEST_CHANNEL " : Hello World!" CRLF, server, MSG); + is_client_sendq(":" TEST_ME_ID " NOTICE @" TEST_CHANNEL " : Hello World!" CRLF, server, MSG); is_client_sendq_empty(server2, "No users to receive message; " MSG); // Moderated channel @@ -1282,6 +1349,8 @@ static void sendto_channel_opmod__local__tags(void) is_client_sendq_empty(local_chan_d, "Deaf; " MSG); is_client_sendq(":" TEST_ME_ID "90004 TEST =" TEST_CHANNEL " :Hello World!" CRLF, server, MSG); is_client_sendq_empty(server2, "No users to receive message; " MSG); + + standard_free(); } static void sendto_channel_opmod__remote(void) @@ -1310,7 +1379,7 @@ static void sendto_channel_opmod__remote(void) is_client_sendq(":R2ChanDeaf" TEST_ID_SUFFIX " TEST " TEST_CHANNEL " :Hello World!" CRLF, local_chan_ov, "On channel; " MSG); is_client_sendq_empty(local_chan_v, "Not +o; " MSG); is_client_sendq_empty(local_chan_d, "Deaf; " MSG); - is_client_sendq(":" TEST_SERVER2_ID " NOTICE @" TEST_CHANNEL " : Hello World!" CRLF, server, MSG); + is_client_sendq(":" TEST_SERVER2_ID " NOTICE @" TEST_CHANNEL " : Hello World!" CRLF, server, MSG); is_client_sendq_empty(server2, "Message source; " MSG); // Moderated channel @@ -1339,6 +1408,63 @@ static void sendto_channel_opmod__remote(void) standard_free(); } +static void sendto_channel_opmod_statusmsg__remote(void) +{ + standard_init(); + ConfigChannel.opmod_send_statusmsg = true; + + // This function does not support TS5... + standard_ids(); + + // Without CAP_CHW | CAP_EOPMOD + standard_server_caps(0, CAP_CHW | CAP_EOPMOD); + + sendto_channel_opmod(server2, remote2_chan_d, channel, "TEST", "Hello World!"); + is_client_sendq(":R2ChanDeaf" TEST_ID_SUFFIX " TEST @" TEST_CHANNEL " :Hello World!" CRLF, local_chan_o, "On channel; " MSG); + is_client_sendq(":R2ChanDeaf" TEST_ID_SUFFIX " TEST @" TEST_CHANNEL " :Hello World!" CRLF, local_chan_ov, "On channel; " MSG); + is_client_sendq_empty(local_chan_v, "Not +o; " MSG); + is_client_sendq_empty(local_chan_d, "Deaf; " MSG); + is_client_sendq_empty(server, "Message source; " MSG); + is_client_sendq_empty(server2, "No users to receive message; " MSG); + + // With CAP_CHW, without CAP_EOPMOD + standard_server_caps(CAP_CHW, CAP_EOPMOD); + + sendto_channel_opmod(server2, remote2_chan_d, channel, "TEST", "Hello World!"); + is_client_sendq(":R2ChanDeaf" TEST_ID_SUFFIX " TEST @" TEST_CHANNEL " :Hello World!" CRLF, local_chan_o, "On channel; " MSG); + is_client_sendq(":R2ChanDeaf" TEST_ID_SUFFIX " TEST @" TEST_CHANNEL " :Hello World!" CRLF, local_chan_ov, "On channel; " MSG); + is_client_sendq_empty(local_chan_v, "Not +o; " MSG); + is_client_sendq_empty(local_chan_d, "Deaf; " MSG); + is_client_sendq(":" TEST_SERVER2_ID " NOTICE @" TEST_CHANNEL " : Hello World!" CRLF, server, MSG); + is_client_sendq_empty(server2, "Message source; " MSG); + + // Moderated channel + channel->mode.mode |= MODE_MODERATED; + + sendto_channel_opmod(server2, remote2_chan_d, channel, "TEST", "Hello World!"); + is_client_sendq(":R2ChanDeaf" TEST_ID_SUFFIX " TEST @" TEST_CHANNEL " :Hello World!" CRLF, local_chan_o, "On channel; " MSG); + is_client_sendq(":R2ChanDeaf" TEST_ID_SUFFIX " TEST @" TEST_CHANNEL " :Hello World!" CRLF, local_chan_ov, "On channel; " MSG); + is_client_sendq_empty(local_chan_v, "Not +o; " MSG); + is_client_sendq_empty(local_chan_d, "Deaf; " MSG); + is_client_sendq(":" TEST_SERVER2_ID "90205 TEST @" TEST_CHANNEL " :Hello World!" CRLF, server, MSG); + is_client_sendq_empty(server2, "Message source; " MSG); + + // With CAP_CHW | CAP_EOPMOD + channel->mode.mode &= ~MODE_MODERATED; + standard_server_caps(CAP_CHW | CAP_EOPMOD, 0); + + sendto_channel_opmod(server2, remote2_chan_d, channel, "TEST", "Hello World!"); + is_client_sendq(":R2ChanDeaf" TEST_ID_SUFFIX " TEST @" TEST_CHANNEL " :Hello World!" CRLF, local_chan_o, "On channel; " MSG); + is_client_sendq(":R2ChanDeaf" TEST_ID_SUFFIX " TEST @" TEST_CHANNEL " :Hello World!" CRLF, local_chan_ov, "On channel; " MSG); + is_client_sendq_empty(local_chan_v, "Not +o; " MSG); + is_client_sendq_empty(local_chan_d, "Deaf; " MSG); + is_client_sendq(":" TEST_SERVER2_ID "90205 TEST =" TEST_CHANNEL " :Hello World!" CRLF, server, MSG); + is_client_sendq_empty(server2, "Message source; " MSG); + + ConfigChannel.opmod_send_statusmsg = false; + standard_free(); +} + static void sendto_channel_opmod__remote__tags(void) { standard_init(); @@ -1370,7 +1496,7 @@ static void sendto_channel_opmod__remote__tags(void) is_client_sendq("@time=" ADVENTURE_TIME " :R2ChanDeaf" TEST_ID_SUFFIX " TEST " TEST_CHANNEL " :Hello World!" CRLF, local_chan_ov, "On channel; " MSG); is_client_sendq_empty(local_chan_v, "Not +o; " MSG); is_client_sendq_empty(local_chan_d, "Deaf; " MSG); - is_client_sendq(":" TEST_SERVER2_ID " NOTICE @" TEST_CHANNEL " : Hello World!" CRLF, server, MSG); + is_client_sendq(":" TEST_SERVER2_ID " NOTICE @" TEST_CHANNEL " : Hello World!" CRLF, server, MSG); is_client_sendq_empty(server2, "Message source; " MSG); // Moderated channel @@ -4939,8 +5065,10 @@ int main(int argc, char *argv[]) sendto_channel_flags__remote__chanop_voice(); sendto_channel_opmod__local(); + sendto_channel_opmod_statusmsg__local(); sendto_channel_opmod__local__tags(); sendto_channel_opmod__remote(); + sendto_channel_opmod_statusmsg__remote(); sendto_channel_opmod__remote__tags(); sendto_channel_local1(); sendto_channel_local1__tags();