Merge branch 'bug92-xop'
authorAlexander Barton <alex@barton.de>
Sun, 23 Sep 2012 17:58:50 +0000 (19:58 +0200)
committerAlexander Barton <alex@barton.de>
Sun, 23 Sep 2012 18:06:14 +0000 (20:06 +0200)
By Alexander Barton (5) and Sebastian K√∂hler (2)

* bug92-xop:
  Fix NAMES/WHO response when client has multi-prefix
  Fix prefix of "halfop" when "multi-prefix" is active
  Clean up doc/.gitignore
  doc/Modes.txt: add version number to new channel modes
  Fix some "whitespace glitches"
  Tests and documentation for xop
  Implemented xop support

Conflicts (because of "multi-prefix fix"):
src/ngircd/irc-info.c

This fixes bug #92 "ngircd does not support XOP usermodes".

doc/Modes.txt
src/ngircd/channel.c
src/ngircd/defines.h
src/ngircd/irc-channel.c
src/ngircd/irc-info.c
src/ngircd/irc-mode.c
src/ngircd/irc-op.c
src/ngircd/irc-server.c
src/ngircd/messages.h
src/ngircd/numeric.c
src/testsuite/mode-test.e

index c2c533f2a67e1784561869cfee1ac49c5bf0fd90..e3b754d69815c1aaa0729177b25a80b35986d447 100644 (file)
@@ -68,7 +68,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.
 
 
index f0a9525d348a5d992596f453fd9ba709cbd47c9a..59fe00229576d018900a600c548925fd4d9668cb 100644 (file)
@@ -299,6 +299,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);
@@ -319,14 +321,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;
                }
+       }
+
+       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++;
+               }
 
-               /* Check if user has operator status */
-               if (!strchr(Channel_UserModes(chan, Origin), 'o')) {
-                       IRC_WriteStrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG,
-                                          Client_ID(Origin), Name);
+               if(!can_kick) {
+                       IRC_WriteStrClient(Origin, ERR_CHANOPPRIVTOLOW_MSG,
+                               Client_ID(Origin), Name);
                        return;
                }
        }
@@ -812,9 +851,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)
@@ -824,8 +863,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;
        }
 
        /*
@@ -841,7 +886,7 @@ Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From)
            && !Client_HasMode(From, 'o'))
                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'))
@@ -1196,64 +1241,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 )
 {
index 82837599aadbb6485f0f044b447739d7b5f922fa..ba7adf17adb204840418b7482b75fef975844138 100644 (file)
 #define USERMODES "aBcCiorRswx"
 
 /** Supported channel modes. */
-#define CHANMODES "beiIklmMnoOPrRstvz"
+#define CHANMODES "abehiIklmMnoOPqrRstvz"
 
 /** Away message for users connected to linked servers. */
 #define DEFAULT_AWAY_MSG "Away"
index d714b48fcb8e3d28aed8cb2101bc6e452295290b..9e88e1bd0031de480a33d3ddfb5b447fed81d013 100644 (file)
@@ -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] : "<none>");
 
-       if (use_servermode)
+       if (Conf_OperServerMode)
                from = Client_ThisServer();
 
        /* Update channel and forward new topic to other servers */
index fc04773ae14ef307d1ac5adb0b53f2a18a64dd03..4909a96a063a3a9940be0e52d9b9e070925a28c9 100644 (file)
@@ -117,7 +117,7 @@ IRC_INFO(CLIENT * Client, REQUEST * Req)
                target = Client_Search(Req->argv[0]);
        else
                target = Client_ThisServer();
-       
+
        /* Make sure that the target is a server */
        if (target && Client_Type(target) != CLIENT_SERVER)
                target = Client_Introducer(target);
@@ -807,22 +807,48 @@ who_flags_status(const char *client_modes)
 }
 
 
-static const char *
-who_flags_qualifier(CLIENT *Client, const char *chan_user_modes)
+/**
+ * Return channel user mode prefix(es).
+ *
+ * @param Client The client requesting the mode prefixes.
+ * @param chan_user_modes String with channel user modes.
+ * @param str String buffer to which the prefix(es) will be appended.
+ * @param len Size of "str" buffer.
+ * @return Pointer to "str".
+ */
+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 (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;
 }
 
 
@@ -840,8 +866,7 @@ IRC_WHO_Channel(CLIENT *Client, CHANNEL *Chan, bool OnlyOps)
        bool is_visible, is_member, is_ircop;
        CL2CHAN *cl2chan;
        const char *client_modes;
-       const char *chan_user_modes;
-       char flags[8];
+       char flags[10];
        CLIENT *c;
        int count = 0;
 
@@ -872,9 +897,8 @@ IRC_WHO_Channel(CLIENT *Client, CHANNEL *Chan, bool OnlyOps)
                        if (is_ircop)
                                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(Client, Channel_UserModes(Chan, c),
+                                           flags, sizeof(flags));
 
                        if (!write_whoreply(Client, c, Channel_Name(Chan),
                                            flags))
@@ -1090,8 +1114,8 @@ 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(Client, Channel_UserModes(chan, c),
+                                   str, sizeof(str));
                strlcat(str, Channel_Name(chan), sizeof(str));
 
                if (strlen(str) > (LINE_LEN - CHANNEL_NAME_LEN - 4)) {
@@ -1465,7 +1489,7 @@ IRC_Send_LUSERS(CLIENT *Client)
                        Conn_CountMax(), Conn_CountAccepted()))
                return DISCONNECTED;
 #endif
-       
+
        return CONNECTED;
 } /* IRC_Send_LUSERS */
 
@@ -1595,16 +1619,9 @@ IRC_Send_NAMES(CLIENT * Client, CHANNEL * Chan)
                if (is_member || is_visible) {
                        if (str[strlen(str) - 1] != ':')
                                strlcat(str, " ", sizeof(str));
-                       if (Client_Cap(Client) & CLIENT_CAP_MULTI_PREFIX && 
-                                       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(Client, Channel_UserModes(Chan, cl),
+                                           str, sizeof(str));
                        strlcat(str, Client_ID(cl), sizeof(str));
 
                        if (strlen(str) > (LINE_LEN - CLIENT_NICK_LEN - 4)) {
index 71557201d8d938f4a5ff65416f50eb487f78333d..7380c6eb5c70308b030097aadf58a9ab3bc933e4 100644 (file)
@@ -418,13 +418,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));
@@ -433,10 +436,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));
@@ -515,21 +528,44 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
                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';
                argadd[0] = '\0';
                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': /* Only identified nicks can write */
                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,
@@ -540,7 +576,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,
@@ -550,7 +587,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]);
@@ -576,7 +614,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,
@@ -586,7 +625,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');
@@ -611,44 +651,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),
@@ -656,16 +699,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,
@@ -690,7 +751,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,
index 5e36b02bef478ce9ac2a0d96e411deaf2e11c4cc..08495475f60520c7dd899d7cef9e10b4f4852550 100644 (file)
@@ -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;
index 8526a573ec361b0851222314343e079b447114f6..380ab6224f55cf9b0d504f80be5718eceff824ae 100644 (file)
@@ -54,7 +54,7 @@ IRC_SERVER( CLIENT *Client, REQUEST *Req )
        CLIENT *from, *c;
        int i;
        CONN_ID con;
-       
+
        assert( Client != NULL );
        assert( Req != NULL );
 
@@ -88,7 +88,7 @@ IRC_SERVER( CLIENT *Client, REQUEST *Req )
                        Conn_Close( Client_Conn( Client ), NULL, "Bad password", true);
                        return DISCONNECTED;
                }
-               
+
                /* Is there a registered server with this ID? */
                if( ! Client_CheckID( Client, Req->argv[0] )) return DISCONNECTED;
 
@@ -203,10 +203,10 @@ 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;
-       
+
        assert( Client != NULL );
        assert( Req != NULL );
 
@@ -220,11 +220,15 @@ IRC_NJOIN( CLIENT *Client, REQUEST *Req )
        while( ptr )
        {
                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,8 +239,11 @@ IRC_NJOIN( CLIENT *Client, REQUEST *Req )
                        Channel_Join( c, channame );
                        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... */
@@ -251,12 +258,15 @@ 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 ));
                }
                else Log( LOG_ERR, "Got NJOIN for unknown nick \"%s\" for channel \"%s\"!", ptr, channame );
-               
+
                /* search for next Nick */
                ptr = strtok( NULL, "," );
        }
index 4f3a397b2647eabc6fd6ed21f9aa31d776df69e9..d99930faa5d079995932ba3140d207c08e20e5c9 100644 (file)
@@ -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,imMnOPRstz 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,imMnOPRstz 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"
 #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_NICKREGISTER_MSG           "484 %s :Cannot modify user mode (+R) -- Use IRC services"
index d59a1dc3a9be6895de5f2565337fe8e29a3fbb40..4bce60fb12a947908f14118b7aa200439f4284df 100644 (file)
@@ -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" */
index 260cd03c12898ae46c5e9d58b1f5f4fb4bcf74ff..44b6e5dfe5a3bb3cb05dddba1e08498e1cb58bdd 100644 (file)
@@ -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 }