From 7b01bb833f5bc3386611dc0c015a99c236e941f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20K=C3=B6hler?= Date: Sat, 4 Aug 2012 14:19:58 +0200 Subject: [PATCH 1/2] Implemented xop support 3 new channel user modes have been added. Half Op: +h(Prefix: %) can set the channel modes +imntvIbek and kick all +v and normal users. Admin: +a(Prefix: &) can set channel modes +imntvIbekoRsz and kick all +o, +h, +v and normal users. Owner: +q(Prefix: ~) can set channel modes +imntvIbekoRsz and kick all +a, +o, +h, +v and normal users --- doc/.gitignore | 1 + src/ngircd/channel.c | 119 +++++++++++++++------------------- src/ngircd/defines.h | 2 +- src/ngircd/irc-channel.c | 19 +++--- src/ngircd/irc-info.c | 62 ++++++++++-------- src/ngircd/irc-mode.c | 136 ++++++++++++++++++++++++++++----------- src/ngircd/irc-op.c | 7 +- src/ngircd/irc-server.c | 16 ++++- src/ngircd/messages.h | 3 +- src/ngircd/numeric.c | 11 +++- 10 files changed, 228 insertions(+), 148 deletions(-) diff --git a/doc/.gitignore b/doc/.gitignore index 0035855c..a7b6a22c 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -1 +1,2 @@ sample-ngircd.conf +Add_Modes.txt diff --git a/src/ngircd/channel.c b/src/ngircd/channel.c index ff470246..90d2efab 100644 --- a/src/ngircd/channel.c +++ b/src/ngircd/channel.c @@ -294,6 +294,8 @@ Channel_Kick(CLIENT *Peer, CLIENT *Target, CLIENT *Origin, const char *Name, const char *Reason ) { CHANNEL *chan; + char *ptr, *target_modes; + bool can_kick = false; assert(Peer != NULL); assert(Target != NULL); @@ -314,14 +316,51 @@ Channel_Kick(CLIENT *Peer, CLIENT *Target, CLIENT *Origin, const char *Name, /* Check that user is on the specified channel */ if (!Channel_IsMemberOf(chan, Origin)) { IRC_WriteStrClient( Origin, ERR_NOTONCHANNEL_MSG, - Client_ID(Origin), Name); + Client_ID(Origin), Name); return; } + } - /* Check if user has operator status */ - if (!strchr(Channel_UserModes(chan, Origin), 'o')) { - IRC_WriteStrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, - Client_ID(Origin), Name); + if(Client_Type(Peer) == CLIENT_USER) { + /* Check if client has the rights to kick target */ + ptr = Channel_UserModes(chan, Peer); + target_modes = Channel_UserModes(chan, Target); + while(*ptr) { + /* Owner can kick everyone */ + if ( *ptr == 'q') { + can_kick = true; + break; + } + /* Admin can't kick owner */ + if ( *ptr == 'a' ) { + if (!strchr(target_modes, 'q')) { + can_kick = true; + break; + } + } + /* Op can't kick owner | admin */ + if ( *ptr == 'o' ) { + if (!strchr(target_modes, 'q') && + !strchr(target_modes, 'a')) { + can_kick = true; + break; + } + } + /* Half Op can't kick owner | admin | op */ + if ( *ptr == 'h' ) { + if (!strchr(target_modes, 'q') && + !strchr(target_modes, 'a') && + !strchr(target_modes, 'o')) { + can_kick = true; + break; + } + } + ptr++; + } + + if(!can_kick) { + IRC_WriteStrClient(Origin, ERR_CHANOPPRIVTOLOW_MSG, + Client_ID(Origin), Name); return; } } @@ -807,9 +846,9 @@ Channel_SetMaxUsers(CHANNEL *Chan, unsigned long Count) static bool Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From) { - bool is_member, has_voice, is_op; + bool is_member, has_voice, is_halfop, is_op, is_chanadmin, is_owner; - is_member = has_voice = is_op = false; + is_member = has_voice = is_halfop = is_op = is_chanadmin = is_owner = false; /* The server itself always can send messages :-) */ if (Client_ThisServer() == From) @@ -819,8 +858,14 @@ Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From) is_member = true; if (strchr(Channel_UserModes(Chan, From), 'v')) has_voice = true; + if (strchr(Channel_UserModes(Chan, From), 'h')) + is_halfop = true; if (strchr(Channel_UserModes(Chan, From), 'o')) is_op = true; + if (strchr(Channel_UserModes(Chan, From), 'a')) + is_chanadmin = true; + if (strchr(Channel_UserModes(Chan, From), 'q')) + is_owner = true; } /* @@ -832,7 +877,7 @@ Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From) if (strchr(Channel_Modes(Chan), 'n') && !is_member) return false; - if (is_op || has_voice) + if (has_voice || is_halfop || is_op || is_chanadmin || is_owner) return true; if (strchr(Channel_Modes(Chan), 'm')) @@ -1187,64 +1232,6 @@ Channel_CheckKey(CHANNEL *Chan, CLIENT *Client, const char *Key) } /* Channel_CheckKey */ -/** - * Check wether a client is allowed to administer a channel or not. - * - * @param Chan The channel to test. - * @param Client The client from which the command has been received. - * @param Origin The originator of the command (or NULL). - * @param OnChannel Set to true if the originator is member of the channel. - * @param AdminOk Set to true if the client is allowed to do - * administrative tasks on this channel. - * @param UseServerMode Set to true if ngIRCd should emulate "server mode", - * that is send commands as if originating from a server - * and not the originator of the command. - */ -GLOBAL void -Channel_CheckAdminRights(CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, - bool *OnChannel, bool *AdminOk, bool *UseServerMode) -{ - assert (Chan != NULL); - assert (Client != NULL); - assert (OnChannel != NULL); - assert (AdminOk != NULL); - assert (UseServerMode != NULL); - - /* Use the client as origin, if no origin has been given (no prefix?) */ - if (!Origin) - Origin = Client; - - *OnChannel = false; - *AdminOk = false; - *UseServerMode = false; - - if (Client_Type(Client) != CLIENT_USER - && Client_Type(Client) != CLIENT_SERVER - && Client_Type(Client) != CLIENT_SERVICE) - return; - - /* Allow channel administration if the client is a server or service */ - if (Client_Type(Client) != CLIENT_USER) { - *AdminOk = true; - return; - } - - *OnChannel = Channel_IsMemberOf(Chan, Origin); - - if (*OnChannel && strchr(Channel_UserModes(Chan, Origin), 'o')) { - /* User is a channel operator */ - *AdminOk = true; - } else if (Conf_OperCanMode) { - /* IRC operators are allowed to administer channels as well */ - if (Client_OperByMe(Origin)) { - *AdminOk = true; - if (Conf_OperServerMode) - *UseServerMode = true; - } - } -} /* Channel_CheckAdminRights */ - - static CL2CHAN * Get_First_Cl2Chan( CLIENT *Client, CHANNEL *Chan ) { diff --git a/src/ngircd/defines.h b/src/ngircd/defines.h index cd0a1666..d0dc9ce1 100644 --- a/src/ngircd/defines.h +++ b/src/ngircd/defines.h @@ -164,7 +164,7 @@ #define USERMODES "acCiorRswx" /** Supported channel modes. */ -#define CHANMODES "beiIklmnoOPrRstvz" +#define CHANMODES "abehiIklmnoOPqrRstvz" /** Away message for users connected to linked servers. */ #define DEFAULT_AWAY_MSG "Away" diff --git a/src/ngircd/irc-channel.c b/src/ngircd/irc-channel.c index d714b48f..9e88e1bd 100644 --- a/src/ngircd/irc-channel.c +++ b/src/ngircd/irc-channel.c @@ -510,7 +510,7 @@ IRC_TOPIC( CLIENT *Client, REQUEST *Req ) CHANNEL *chan; CLIENT *from; char *topic; - bool onchannel, topicok, use_servermode, r; + bool r, is_oper; assert( Client != NULL ); assert( Req != NULL ); @@ -533,10 +533,9 @@ IRC_TOPIC( CLIENT *Client, REQUEST *Req ) return IRC_WriteStrClient(from, ERR_NOSUCHCHANNEL_MSG, Client_ID(from), Req->argv[0]); - Channel_CheckAdminRights(chan, Client, from, - &onchannel, &topicok, &use_servermode); - - if (!onchannel && !topicok) + /* Only IRC opers and channel members allowed */ + is_oper = Client_OperByMe(from); + if (!Channel_IsMemberOf(chan, from) && !is_oper) return IRC_WriteStrClient(from, ERR_NOTONCHANNEL_MSG, Client_ID(from), Req->argv[0]); @@ -565,8 +564,12 @@ IRC_TOPIC( CLIENT *Client, REQUEST *Req ) } if (strchr(Channel_Modes(chan), 't')) { - /* Topic Lock. Is the user a channel or IRC operator? */ - if (!topicok) + /* Topic Lock. Is the user a channel op or IRC operator? */ + if(!strchr(Channel_UserModes(chan, from), 'h') && + !strchr(Channel_UserModes(chan, from), 'o') && + !strchr(Channel_UserModes(chan, from), 'a') && + !strchr(Channel_UserModes(chan, from), 'q') && + !is_oper) return IRC_WriteStrClient(from, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(from), Channel_Name(chan)); @@ -578,7 +581,7 @@ IRC_TOPIC( CLIENT *Client, REQUEST *Req ) Client_TypeText(from), Client_Mask(from), Channel_Name(chan), Req->argv[1][0] ? Req->argv[1] : ""); - if (use_servermode) + if (Conf_OperServerMode) from = Client_ThisServer(); /* Update channel and forward new topic to other servers */ diff --git a/src/ngircd/irc-info.c b/src/ngircd/irc-info.c index 0ea85874..879da3da 100644 --- a/src/ngircd/irc-info.c +++ b/src/ngircd/irc-info.c @@ -807,22 +807,38 @@ who_flags_status(const char *client_modes) } -static const char * -who_flags_qualifier(CLIENT *Client, const char *chan_user_modes) +static char * +who_flags_qualifier(CLIENT *Client, const char *chan_user_modes, char *str, size_t len) { assert(Client != NULL); - - if (Client_Cap(Client) & CLIENT_CAP_MULTI_PREFIX) { - if (strchr(chan_user_modes, 'o') && - strchr(chan_user_modes, 'v')) - return "@+"; + + if (Client_Cap(Client) & CLIENT_CAP_MULTI_PREFIX) { + if (strchr(chan_user_modes, 'q')) + strlcat(str, "~", len); + if (strchr(chan_user_modes, 'a')) + strlcat(str, "&", len); + if (strchr(chan_user_modes, 'o')) + strlcat(str, "@", len); + if (strchr(chan_user_modes, 'h')) + strlcat(str, "&", len); + if (strchr(chan_user_modes, 'v')) + strlcat(str, "+", len); + + return str; } - - if (strchr(chan_user_modes, 'o')) - return "@"; + + if (strchr(chan_user_modes, 'q')) + strlcat(str, "~", len); + else if (strchr(chan_user_modes, 'a')) + strlcat(str, "&", len); + else if (strchr(chan_user_modes, 'o')) + strlcat(str, "@", len); + else if (strchr(chan_user_modes, 'h')) + strlcat(str, "%", len); else if (strchr(chan_user_modes, 'v')) - return "+"; - return ""; + strlcat(str, "+", len); + + return str; } @@ -841,7 +857,7 @@ IRC_WHO_Channel(CLIENT *Client, CHANNEL *Chan, bool OnlyOps) CL2CHAN *cl2chan; const char *client_modes; const char *chan_user_modes; - char flags[8]; + char flags[10]; CLIENT *c; int count = 0; @@ -874,9 +890,8 @@ IRC_WHO_Channel(CLIENT *Client, CHANNEL *Chan, bool OnlyOps) strlcat(flags, "*", sizeof(flags)); chan_user_modes = Channel_UserModes(Chan, c); - strlcat(flags, who_flags_qualifier(c, chan_user_modes), - sizeof(flags)); - + who_flags_qualifier(c, chan_user_modes, flags, sizeof(flags)); + if (!write_whoreply(Client, c, Channel_Name(Chan), flags)) return DISCONNECTED; @@ -1087,8 +1102,7 @@ IRC_WHOIS_SendReply(CLIENT *Client, CLIENT *from, CLIENT *c) if (str[strlen(str) - 1] != ':') strlcat(str, " ", sizeof(str)); - strlcat(str, who_flags_qualifier(c, Channel_UserModes(chan, c)), - sizeof(str)); + who_flags_qualifier(c, Channel_UserModes(chan, c), str, sizeof(str)); strlcat(str, Channel_Name(chan), sizeof(str)); if (strlen(str) > (LINE_LEN - CHANNEL_NAME_LEN - 4)) { @@ -1578,16 +1592,8 @@ IRC_Send_NAMES(CLIENT * Client, CHANNEL * Chan) if (is_member || is_visible) { if (str[strlen(str) - 1] != ':') strlcat(str, " ", sizeof(str)); - if (Client_Cap(cl) & CLIENT_CAP_MULTI_PREFIX) { - if (strchr(Channel_UserModes(Chan, cl), 'o') && - strchr(Channel_UserModes(Chan, cl), 'v')) - strlcat(str, "@+", sizeof(str)); - } else { - if (strchr(Channel_UserModes(Chan, cl), 'o')) - strlcat(str, "@", sizeof(str)); - else if (strchr(Channel_UserModes(Chan, cl), 'v')) - strlcat(str, "+", sizeof(str)); - } + + who_flags_qualifier(cl, Channel_UserModes(Chan, cl), str, sizeof(str)); strlcat(str, Client_ID(cl), sizeof(str)); if (strlen(str) > (LINE_LEN - CLIENT_NICK_LEN - 4)) { diff --git a/src/ngircd/irc-mode.c b/src/ngircd/irc-mode.c index fa35cdd0..3679531d 100644 --- a/src/ngircd/irc-mode.c +++ b/src/ngircd/irc-mode.c @@ -396,13 +396,16 @@ static bool Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel) { char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], x[2], - argadd[CLIENT_PASS_LEN], *mode_ptr; - bool connected, set, skiponce, retval, onchannel, modeok, use_servermode; + argadd[CLIENT_PASS_LEN], *mode_ptr, *o_mode_ptr; + bool connected, set, skiponce, retval, use_servermode, + is_halfop, is_op, is_admin, is_owner, is_machine, is_oper; int mode_arg, arg_arg, mode_arg_count = 0; CLIENT *client; long l; size_t len; + is_halfop = is_op = is_admin = is_owner = is_machine = is_oper = false; + if (Channel_IsModeless(Channel)) return IRC_WriteStrClient(Client, ERR_NOCHANMODES_MSG, Client_ID(Client), Channel_Name(Channel)); @@ -411,10 +414,20 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel) if (Req->argc <= 1) return Channel_Mode_Answer_Request(Origin, Channel); - Channel_CheckAdminRights(Channel, Client, Origin, - &onchannel, &modeok, &use_servermode); + /* Check if origin is oper and opers can use mode */ + use_servermode = Conf_OperServerMode; + if(Client_OperByMe(Client) && Conf_OperCanMode) { + is_oper = true; + } + + /* Check if client is a server/service */ + if(Client_Type(Client) == CLIENT_SERVER || + Client_Type(Client) == CLIENT_SERVICE) { + is_machine = true; + } - if (!onchannel && !modeok) + /* Check if client is member of channel or an oper or an server/service */ + if(!Channel_IsMemberOf(Channel, Client) && !is_oper && !is_machine) return IRC_WriteStrClient(Origin, ERR_NOTONCHANNEL_MSG, Client_ID(Origin), Channel_Name(Channel)); @@ -492,6 +505,21 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel) /* Are there arguments left? */ if (arg_arg >= Req->argc) arg_arg = -1; + + if(!is_machine) { + o_mode_ptr = Channel_UserModes(Channel, Client); + while( *o_mode_ptr ) { + if ( *o_mode_ptr == 'q') + is_owner = true; + if ( *o_mode_ptr == 'a') + is_admin = true; + if ( *o_mode_ptr == 'o') + is_op = true; + if ( *o_mode_ptr == 'h') + is_halfop = true; + o_mode_ptr++; + } + } /* Validate modes */ x[0] = '\0'; @@ -499,14 +527,22 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel) client = NULL; switch (*mode_ptr) { /* --- Channel modes --- */ + case 'R': /* Registered users only */ + case 's': /* Secret channel */ + case 'z': /* Secure connections only */ + if(!is_oper && !is_machine && !is_owner && + !is_admin && !is_op) { + connected = IRC_WriteStrClient(Origin, + ERR_CHANOPRIVSNEEDED_MSG, + Client_ID(Origin), Channel_Name(Channel)); + goto chan_exit; + } case 'i': /* Invite only */ case 'm': /* Moderated */ case 'n': /* Only members can write */ - case 'R': /* Registered users only */ - case 's': /* Secret channel */ case 't': /* Topic locked */ - case 'z': /* Secure connections only */ - if (modeok) + if(is_oper || is_machine || is_owner || + is_admin || is_op || is_halfop) x[0] = *mode_ptr; else connected = IRC_WriteStrClient(Origin, @@ -517,7 +553,8 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel) if (Mode_Limit_Reached(Client, mode_arg_count++)) goto chan_exit; if (!set) { - if (modeok) + if (is_oper || is_machine || is_owner || + is_admin || is_op || is_halfop) x[0] = *mode_ptr; else connected = IRC_WriteStrClient(Origin, @@ -527,7 +564,8 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel) break; } if (arg_arg > mode_arg) { - if (modeok) { + if (is_oper || is_machine || is_owner || + is_admin || is_op || is_halfop) { Channel_ModeDel(Channel, 'k'); Channel_SetKey(Channel, Req->argv[arg_arg]); @@ -553,7 +591,8 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel) if (Mode_Limit_Reached(Client, mode_arg_count++)) goto chan_exit; if (!set) { - if (modeok) + if (is_oper || is_machine || is_owner || + is_admin || is_op || is_halfop) x[0] = *mode_ptr; else connected = IRC_WriteStrClient(Origin, @@ -563,7 +602,8 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel) break; } if (arg_arg > mode_arg) { - if (modeok) { + if (is_oper || is_machine || is_owner || + is_admin || is_op || is_halfop) { l = atol(Req->argv[arg_arg]); if (l > 0 && l < 0xFFFF) { Channel_ModeDel(Channel, 'l'); @@ -588,44 +628,47 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel) } break; case 'O': /* IRC operators only */ - if (modeok) { + if (set) { /* Only IRC operators are allowed to * set the 'O' channel mode! */ - if (set && !(Client_OperByMe(Client) - || Client_Type(Client) == CLIENT_SERVER)) + if(is_oper || is_machine) + x[0] = 'O'; + else connected = IRC_WriteStrClient(Origin, ERR_NOPRIVILEGES_MSG, Client_ID(Origin)); - else - x[0] = 'O'; - } else + } else if(is_oper || is_machine || is_owner || + is_admin || is_op) + x[0] = 'O'; + else connected = IRC_WriteStrClient(Origin, - ERR_CHANOPRIVSNEEDED_MSG, - Client_ID(Origin), - Channel_Name(Channel)); - break; + ERR_CHANOPRIVSNEEDED_MSG, + Client_ID(Origin), + Channel_Name(Channel)); + break; case 'P': /* Persistent channel */ - if (modeok) { + if (set) { /* Only IRC operators are allowed to * set the 'P' channel mode! */ - if (set && !(Client_OperByMe(Client) - || Client_Type(Client) == CLIENT_SERVER)) + if(is_oper || is_machine) + x[0] = 'P'; + else connected = IRC_WriteStrClient(Origin, ERR_NOPRIVILEGES_MSG, Client_ID(Origin)); - else - x[0] = 'P'; - } else + } else if(is_oper || is_machine || is_owner || + is_admin || is_op) + x[0] = 'P'; + else connected = IRC_WriteStrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), Channel_Name(Channel)); break; /* --- Channel user modes --- */ - case 'a': - case 'h': - case 'q': - if (Client_Type(Client) != CLIENT_SERVER) { + case 'q': /* Owner */ + case 'a': /* Channel admin */ + if(!is_oper && !is_machine && !is_owner) { connected = IRC_WriteStrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(Origin), @@ -633,16 +676,34 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel) goto chan_exit; } case 'o': /* Channel operator */ + if(!is_oper && !is_machine && !is_owner && + !is_admin && !is_op) { + connected = IRC_WriteStrClient(Origin, + ERR_CHANOPRIVSNEEDED_MSG, + Client_ID(Origin), + Channel_Name(Channel)); + goto chan_exit; + } + case 'h': /* Half Op */ + if(!is_oper && !is_machine && !is_owner && + !is_admin && !is_op) { + connected = IRC_WriteStrClient(Origin, + ERR_CHANOPRIVSNEEDED_MSG, + Client_ID(Origin), + Channel_Name(Channel)); + goto chan_exit; + } case 'v': /* Voice */ if (arg_arg > mode_arg) { - if (modeok) { + if (is_oper || is_machine || is_owner || + is_admin || is_op || is_halfop) { client = Client_Search(Req->argv[arg_arg]); if (client) x[0] = *mode_ptr; else - connected = IRC_WriteStrClient(Client, + connected = IRC_WriteStrClient(Origin, ERR_NOSUCHNICK_MSG, - Client_ID(Client), + Client_ID(Origin), Req->argv[arg_arg]); } else { connected = IRC_WriteStrClient(Origin, @@ -667,7 +728,8 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel) goto chan_exit; if (arg_arg > mode_arg) { /* modify list */ - if (modeok) { + if (is_oper || is_machine || is_owner || + is_admin || is_op || is_halfop) { connected = set ? Add_To_List(*mode_ptr, Origin, Client, Channel, diff --git a/src/ngircd/irc-op.c b/src/ngircd/irc-op.c index 5e36b02b..08495475 100644 --- a/src/ngircd/irc-op.c +++ b/src/ngircd/irc-op.c @@ -166,8 +166,11 @@ IRC_INVITE(CLIENT *Client, REQUEST *Req) /* Is the channel "invite-only"? */ if (strchr(Channel_Modes(chan), 'i')) { - /* Yes. The user must be channel operator! */ - if (!strchr(Channel_UserModes(chan, from), 'o')) + /* Yes. The user must be channel owner/admin/operator/halfop! */ + if (!strchr(Channel_UserModes(chan, from), 'q') && + !strchr(Channel_UserModes(chan, from), 'a') && + !strchr(Channel_UserModes(chan, from), 'o') && + !strchr(Channel_UserModes(chan, from), 'h')) return IRC_WriteStrClient(from, ERR_CHANOPRIVSNEEDED_MSG, Client_ID(from), Channel_Name(chan)); remember = true; diff --git a/src/ngircd/irc-server.c b/src/ngircd/irc-server.c index cca295ac..2ce4fadd 100644 --- a/src/ngircd/irc-server.c +++ b/src/ngircd/irc-server.c @@ -202,7 +202,7 @@ GLOBAL bool IRC_NJOIN( CLIENT *Client, REQUEST *Req ) { char nick_in[COMMAND_LEN], nick_out[COMMAND_LEN], *channame, *ptr, modes[8]; - bool is_op, is_voiced; + bool is_owner, is_chanadmin, is_op, is_halfop, is_voiced; CHANNEL *chan; CLIENT *c; @@ -221,9 +221,13 @@ IRC_NJOIN( CLIENT *Client, REQUEST *Req ) is_op = is_voiced = false; /* cut off prefixes */ - while(( *ptr == '@' ) || ( *ptr == '+' )) - { + while(( *ptr == '~') || ( *ptr == '&' ) || ( *ptr == '@' ) || + ( *ptr == '%') || ( *ptr == '+' )) + { + if( *ptr == '~' ) is_owner = true; + if( *ptr == '&' ) is_chanadmin = true; if( *ptr == '@' ) is_op = true; + if( *ptr == 'h' ) is_halfop = true; if( *ptr == '+' ) is_voiced = true; ptr++; } @@ -235,7 +239,10 @@ IRC_NJOIN( CLIENT *Client, REQUEST *Req ) chan = Channel_Search( channame ); assert( chan != NULL ); + if( is_owner ) Channel_UserModeAdd( chan, c, 'q' ); + if( is_chanadmin ) Channel_UserModeAdd( chan, c, 'a' ); if( is_op ) Channel_UserModeAdd( chan, c, 'o' ); + if( is_halfop ) Channel_UserModeAdd( chan, c, 'h' ); if( is_voiced ) Channel_UserModeAdd( chan, c, 'v' ); /* announce to channel... */ @@ -250,7 +257,10 @@ IRC_NJOIN( CLIENT *Client, REQUEST *Req ) } if( nick_out[0] != '\0' ) strlcat( nick_out, ",", sizeof( nick_out )); + if( is_owner ) strlcat( nick_out, "~", sizeof( nick_out )); + if( is_chanadmin ) strlcat( nick_out, "&", sizeof( nick_out )); if( is_op ) strlcat( nick_out, "@", sizeof( nick_out )); + if( is_halfop ) strlcat( nick_out, "%", sizeof( nick_out )); if( is_voiced ) strlcat( nick_out, "+", sizeof( nick_out )); strlcat( nick_out, ptr, sizeof( nick_out )); } diff --git a/src/ngircd/messages.h b/src/ngircd/messages.h index 96ff2dea..d8041bfd 100644 --- a/src/ngircd/messages.h +++ b/src/ngircd/messages.h @@ -21,7 +21,7 @@ #define RPL_YOURHOST_MSG "002 %s :Your host is %s, running version ngircd-%s (%s/%s/%s)" #define RPL_CREATED_MSG "003 %s :This server has been started %s" #define RPL_MYINFO_MSG "004 %s %s ngircd-%s %s %s" -#define RPL_ISUPPORT1_MSG "005 %s RFC2812 IRCD=ngIRCd CASEMAPPING=ascii PREFIX=(ov)@+ CHANTYPES=#&+ CHANMODES=beI,k,l,imnOPRstz CHANLIMIT=#&+:%d :are supported on this server" +#define RPL_ISUPPORT1_MSG "005 %s RFC2812 IRCD=ngIRCd CASEMAPPING=ascii PREFIX=(qaohv)~&@%%+ CHANTYPES=#&+ CHANMODES=beI,k,l,imnOPRstz CHANLIMIT=#&+:%d :are supported on this server" #define RPL_ISUPPORT2_MSG "005 %s CHANNELLEN=%d NICKLEN=%d TOPICLEN=%d AWAYLEN=%d KICKLEN=%d MODES=%d MAXLIST=beI:%d EXCEPTS=e INVEX=I PENALTY :are supported on this server" #define RPL_TRACELINK_MSG "200 %s Link %s-%s %s %s V%s %ld %d %d" @@ -135,6 +135,7 @@ #define ERR_LISTFULL_MSG "478 %s %s %s: Channel list is full (%d)" #define ERR_NOPRIVILEGES_MSG "481 %s :Permission denied" #define ERR_CHANOPRIVSNEEDED_MSG "482 %s %s :You are not channel operator" +#define ERR_CHANOPPRIVTOLOW_MSG "482 %s %s :Your privileges are to low" #define ERR_CANTKILLSERVER_MSG "483 %s :You can't kill a server!" #define ERR_RESTRICTED_MSG "484 %s :Your connection is restricted" #define ERR_NOOPERHOST_MSG "491 %s :Not configured for your host" diff --git a/src/ngircd/numeric.c b/src/ngircd/numeric.c index d59a1dc3..4bce60fb 100644 --- a/src/ngircd/numeric.c +++ b/src/ngircd/numeric.c @@ -67,10 +67,17 @@ Announce_Channel(CLIENT *Client, CHANNEL *Chan) * (if user is channel operator or has voice) */ if (str[strlen(str) - 1] != ':') strlcat(str, ",", sizeof(str)); - if (strchr(Channel_UserModes(Chan, cl), 'v')) - strlcat(str, "+", sizeof(str)); + if (strchr(Channel_UserModes(Chan, cl), 'q')) + strlcat(str, "~", sizeof(str)); + if (strchr(Channel_UserModes(Chan, cl), 'a')) + strlcat(str, "&", sizeof(str)); if (strchr(Channel_UserModes(Chan, cl), 'o')) strlcat(str, "@", sizeof(str)); + if (strchr(Channel_UserModes(Chan, cl), 'h')) + strlcat(str, "%", sizeof(str)); + if (strchr(Channel_UserModes(Chan, cl), 'v')) + strlcat(str, "+", sizeof(str)); + strlcat(str, Client_ID(cl), sizeof(str)); /* Send the data if the buffer is "full" */ From 097c72aa65d9914d688eaece718648ca060e287a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20K=C3=B6hler?= Date: Sun, 5 Aug 2012 23:38:47 +0200 Subject: [PATCH 2/2] Tests and documentation for xop --- doc/Modes.txt | 5 +++ src/testsuite/mode-test.e | 68 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/doc/Modes.txt b/doc/Modes.txt index e47e2707..d61bd2d0 100644 --- a/doc/Modes.txt +++ b/doc/Modes.txt @@ -66,7 +66,12 @@ channel of which he is a member. mode since description + q 20? User is channel owner can only be set by a service, other + owner and irc op. Can promote other users to q, a, o, h, v. + a 20? User is channel admin and can promote other users to v, h, o o 0.2.0 User is channel operator and can op/kick/... other members. + h 20? User is half op and can set channel modes imntvIbek and kick + voiced and normal users. v 0.2.0 User is "voiced" and can speak even if channel is moderated. diff --git a/src/testsuite/mode-test.e b/src/testsuite/mode-test.e index 260cd03c..44b6e5df 100644 --- a/src/testsuite/mode-test.e +++ b/src/testsuite/mode-test.e @@ -31,6 +31,46 @@ expect { "@* MODE nick :-i" } +send "join #usermode\r" +expect { + timeout { exit 1 } + "@* JOIN :#usermode" +} +expect { + timeout { exit 1 } + "366" +} + +send "mode #usermode +v nick\r" +expect { + timeout { exit 1 } + "@* MODE #usermode +v nick\r" +} + +send "mode #usermode +h nick\r" +expect { + timeout { exit 1 } + "@* MODE #usermode +h nick\r" +} + +send "mode #usermode +a nick\r" +expect { + timeout { exit 1 } + "482 nick" +} + +send "mode #usermode +q nick\r" +expect { + timeout { exit 1 } + "482 nick" +} + +send "mode #usermode -vho nick nick nick\r" +expect { + timeout { exit 1 } + "@* MODE #usermode -vho nick nick nick" +} + send "oper TestOp 123\r" expect { timeout { exit 1 } @@ -47,6 +87,34 @@ expect { "221 nick +o" } +send "mode #usermode +a nick\r" +expect { + timeout { exit 1 } + "@* MODE #usermode +a nick" +} + +send "mode #usermode +q nick\r" +expect { + timeout { exit 1 } + "@* MODE #usermode +q nick" +} + +send "names #usermode\r" +expect { + timeout { exit 1 } + "353 nick = #usermode :~nick" +} +expect { + timeout { exit 1 } + "366 nick #usermode" +} + +send "part #usermode\r" +expect { + timeout { exit 1 } + "@* PART #usermode" +} + send "join #channel\r" expect { timeout { exit 1 }