diff --git a/doc/technical/ts6-protocol.txt b/doc/technical/ts6-protocol.txt index 7604d248..48834201 100644 --- a/doc/technical/ts6-protocol.txt +++ b/doc/technical/ts6-protocol.txt @@ -544,6 +544,9 @@ The target can be: with that status or higher only. capab: CHW propagation: all servers with -D users with appropriate status +- '=' followed by a channel name, to send to chanops only, for cmode +z. + capab: CHW and EOPMOD + propagation: all servers with -D chanops - a user@server message, to send to users on a specific server. The exact meaning of the part before the '@' is not prescribed, except that "opers" allows IRC operators to send to all IRC operators on the server in an diff --git a/help/opers/cmode b/help/opers/cmode index 2716c1c8..ce084703 100644 --- a/help/opers/cmode +++ b/help/opers/cmode @@ -16,7 +16,8 @@ NO PARAMETERS: +c - No color. All color codes in messages are stripped. +g - Free invite. Everyone may invite users. Significantly weakens +i control. - +z - Op moderated. Messages blocked by +m are instead sent to ops. + +z - Op moderated. Messages blocked by +m, +b and +q are instead + sent to ops. +L - Large ban list. Increase maximum number of +beIq entries. Only settable by opers. +P - Permanent. Channel does not disappear when empty. Only diff --git a/include/s_serv.h b/include/s_serv.h index 6ad5eb22..1d4296cb 100644 --- a/include/s_serv.h +++ b/include/s_serv.h @@ -70,12 +70,13 @@ struct Capability #define CAP_RSFNC 0x20000 /* rserv FNC */ #define CAP_SAVE 0x40000 /* supports SAVE (nick collision FNC) */ #define CAP_EUID 0x80000 /* supports EUID (ext UID + nonencap CHGHOST) */ +#define CAP_EOPMOD 0x100000 /* supports EOPMOD (ext +z + ext topic) */ #define CAP_MASK (CAP_QS | CAP_EX | CAP_CHW | \ CAP_IE | CAP_KLN | CAP_SERVICE |\ CAP_CLUSTER | CAP_ENCAP | \ CAP_ZIP | CAP_KNOCK | CAP_UNKLN | \ - CAP_RSFNC | CAP_SAVE | CAP_EUID) + CAP_RSFNC | CAP_SAVE | CAP_EUID | CAP_EOPMOD) #ifdef HAVE_LIBZ #define CAP_ZIP_SUPPORTED CAP_ZIP diff --git a/include/send.h b/include/send.h index cc3e0112..9e5c16b6 100644 --- a/include/send.h +++ b/include/send.h @@ -57,6 +57,9 @@ extern void sendto_server(struct Client *one, struct Channel *chptr, extern void sendto_channel_flags(struct Client *one, int type, struct Client *source_p, struct Channel *chptr, const char *, ...) AFP(5, 6); +extern void sendto_channel_opmod(struct Client *one, struct Client *source_p, + struct Channel *chptr, const char *command, + const char *text); extern void sendto_channel_local(int type, struct Channel *, const char *, ...) AFP(3, 4); extern void sendto_channel_local_butone(struct Client *, int type, struct Channel *, const char *, ...) AFP(4, 5); diff --git a/modules/core/m_message.c b/modules/core/m_message.c index f22450db..a6ac60d3 100644 --- a/modules/core/m_message.c +++ b/modules/core/m_message.c @@ -97,8 +97,9 @@ static int flood_attack_channel(int p_or_n, struct Client *source_p, #define ENTITY_NONE 0 #define ENTITY_CHANNEL 1 -#define ENTITY_CHANOPS_ON_CHANNEL 2 -#define ENTITY_CLIENT 3 +#define ENTITY_CHANNEL_OPMOD 2 +#define ENTITY_CHANOPS_ON_CHANNEL 3 +#define ENTITY_CLIENT 4 static struct entity targets[512]; static int ntargets = 0; @@ -109,6 +110,11 @@ static void msg_channel(int p_or_n, const char *command, struct Client *client_p, struct Client *source_p, struct Channel *chptr, const char *text); +static void msg_channel_opmod(int p_or_n, const char *command, + struct Client *client_p, + struct Client *source_p, struct Channel *chptr, + const char *text); + static void msg_channel_flags(int p_or_n, const char *command, struct Client *client_p, struct Client *source_p, @@ -204,6 +210,11 @@ m_message(int p_or_n, (struct Channel *) targets[i].ptr, parv[2]); break; + case ENTITY_CHANNEL_OPMOD: + msg_channel_opmod(p_or_n, command, client_p, source_p, + (struct Channel *) targets[i].ptr, parv[2]); + break; + case ENTITY_CHANOPS_ON_CHANNEL: msg_channel_flags(p_or_n, command, client_p, source_p, (struct Channel *) targets[i].ptr, @@ -382,6 +393,32 @@ build_target_list(int p_or_n, const char *command, struct Client *client_p, continue; } + if(IsServer(client_p) && *nick == '=' && nick[1] == '#') + { + nick++; + if((chptr = find_channel(nick)) != NULL) + { + if(!duplicate_ptr(chptr)) + { + if(ntargets >= ConfigFileEntry.max_targets) + { + sendto_one(source_p, form_str(ERR_TOOMANYTARGETS), + me.name, source_p->name, nick); + return (1); + } + targets[ntargets].ptr = (void *) chptr; + targets[ntargets++].type = ENTITY_CHANNEL_OPMOD; + } + } + + /* non existant channel */ + else if(p_or_n != NOTICE) + sendto_one_numeric(source_p, ERR_NOSUCHNICK, + form_str(ERR_NOSUCHNICK), nick); + + continue; + } + /* no matching anything found - error if not NOTICE */ if(p_or_n != NOTICE) { @@ -476,16 +513,65 @@ msg_channel(int p_or_n, const char *command, } } else if(chptr->mode.mode & MODE_OPMODERATE && - chptr->mode.mode & MODE_MODERATED && (!(chptr->mode.mode & MODE_NOPRIVMSGS) || IsMember(source_p, chptr))) { - /* only do +z for +m channels for now, as bans/quiets - * aren't tested for remote clients -- jilles */ if(!flood_attack_channel(p_or_n, source_p, chptr, chptr->chname)) { - sendto_channel_flags(client_p, ONLY_CHANOPS, source_p, chptr, - "%s %s :%s", command, chptr->chname, text); + sendto_channel_opmod(client_p, source_p, chptr, + command, text); + } + } + else + { + if(p_or_n != NOTICE) + sendto_one_numeric(source_p, ERR_CANNOTSENDTOCHAN, + form_str(ERR_CANNOTSENDTOCHAN), chptr->chname); + } +} +/* + * msg_channel_opmod + * + * inputs - flag privmsg or notice + * - pointer to command "PRIVMSG" or "NOTICE" + * - pointer to client_p + * - pointer to source_p + * - pointer to channel + * output - NONE + * side effects - message given channel ops + * + * XXX - We need to rework this a bit, it's a tad ugly. --nenolod + */ +static void +msg_channel_opmod(int p_or_n, const char *command, + struct Client *client_p, struct Client *source_p, + struct Channel *chptr, const char *text) +{ + char text2[BUFSIZE]; + + if(chptr->mode.mode & MODE_NOCOLOR) + { + rb_strlcpy(text2, text, BUFSIZE); + strip_colour(text2); + text = text2; + if (EmptyString(text)) + { + /* could be empty after colour stripping and + * that would cause problems later */ + if(p_or_n != NOTICE) + sendto_one(source_p, form_str(ERR_NOTEXTTOSEND), me.name, source_p->name); + return; + } + } + + if(chptr->mode.mode & MODE_OPMODERATE && + (!(chptr->mode.mode & MODE_NOPRIVMSGS) || + IsMember(source_p, chptr))) + { + if(!flood_attack_channel(p_or_n, source_p, chptr, chptr->chname)) + { + sendto_channel_opmod(client_p, source_p, chptr, + command, text); } } else diff --git a/src/s_serv.c b/src/s_serv.c index 3acae738..5aeee209 100644 --- a/src/s_serv.c +++ b/src/s_serv.c @@ -91,6 +91,7 @@ struct Capability captab[] = { { "RSFNC", CAP_RSFNC }, { "SAVE", CAP_SAVE }, { "EUID", CAP_EUID }, + { "EOPMOD", CAP_EOPMOD }, {0, 0} }; diff --git a/src/send.c b/src/send.c index 0da51d36..84b7fb71 100644 --- a/src/send.c +++ b/src/send.c @@ -531,6 +531,95 @@ sendto_channel_flags(struct Client *one, int type, struct Client *source_p, rb_linebuf_donebuf(&rb_linebuf_id); } +/* sendto_channel_flags() + * + * inputs - server not to send to, flags needed, source, channel, va_args + * outputs - message is sent to channel members + * side effects - + */ +void +sendto_channel_opmod(struct Client *one, struct Client *source_p, + struct Channel *chptr, const char *command, + const char *text) +{ + static char buf[BUFSIZE]; + va_list args; + buf_head_t rb_linebuf_local; + buf_head_t rb_linebuf_old; + buf_head_t rb_linebuf_new; + struct Client *target_p; + struct membership *msptr; + rb_dlink_node *ptr; + rb_dlink_node *next_ptr; + + rb_linebuf_newbuf(&rb_linebuf_local); + rb_linebuf_newbuf(&rb_linebuf_old); + rb_linebuf_newbuf(&rb_linebuf_new); + + current_serial++; + + if(IsServer(source_p)) + rb_linebuf_putmsg(&rb_linebuf_local, NULL, NULL, + ":%s %s %s :%s", + source_p->name, command, chptr->chname, text); + else + rb_linebuf_putmsg(&rb_linebuf_local, NULL, NULL, + ":%s!%s@%s %s %s :%s", + source_p->name, source_p->username, + source_p->host, command, chptr->chname, text); + + if (chptr->mode.mode & MODE_MODERATED) + rb_linebuf_putmsg(&rb_linebuf_old, NULL, NULL, + ":%s %s %s :%s", + use_id(source_p), command, chptr->chname, text); + else + rb_linebuf_putmsg(&rb_linebuf_old, NULL, NULL, + ":%s NOTICE @%s :<%s:%s> %s", + use_id(source_p->servptr), chptr->chname, + source_p->name, chptr->chname, text); + rb_linebuf_putmsg(&rb_linebuf_new, NULL, NULL, + ":%s %s =%s :%s", + use_id(source_p), command, chptr->chname, text); + + RB_DLINK_FOREACH_SAFE(ptr, next_ptr, chptr->members.head) + { + msptr = ptr->data; + target_p = msptr->client_p; + + if(IsIOError(target_p->from) || target_p->from == one) + continue; + + if((msptr->flags & CHFL_CHANOP) == 0) + continue; + + if(IsDeaf(target_p)) + continue; + + if(!MyClient(target_p)) + { + /* if we've got a specific type, target must support + * CHW.. --fl + */ + if(NotCapable(target_p->from, CAP_CHW)) + continue; + + if(target_p->from->serial != current_serial) + { + if (IsCapable(target_p->from, CAP_EOPMOD)) + send_linebuf_remote(target_p, source_p, &rb_linebuf_new); + else + send_linebuf_remote(target_p, source_p, &rb_linebuf_old); + target_p->from->serial = current_serial; + } + } + else + _send_linebuf(target_p, &rb_linebuf_local); + } + + rb_linebuf_donebuf(&rb_linebuf_local); + rb_linebuf_donebuf(&rb_linebuf_old); + rb_linebuf_donebuf(&rb_linebuf_new); +} /* sendto_channel_local() *