]> arthur.barton.de Git - ngircd-alex.git/commitdiff
Merge branch 'xop' of https://github.com/kart0ffelsack/ngircd into bug92-xop
authorAlexander Barton <alex@barton.de>
Tue, 11 Sep 2012 10:30:19 +0000 (12:30 +0200)
committerAlexander Barton <alex@barton.de>
Tue, 11 Sep 2012 10:30:19 +0000 (12:30 +0200)
* 'xop' of https://github.com/kart0ffelsack/ngircd:
  Tests and documentation for xop
  Implemented xop support

Conflicts (because of merge of the 'cmode-M' branch):
src/ngircd/channel.c
src/ngircd/defines.h
src/ngircd/messages.h

1  2 
doc/Modes.txt
src/ngircd/channel.c
src/ngircd/defines.h
src/ngircd/irc-info.c
src/ngircd/irc-mode.c
src/ngircd/irc-server.c
src/ngircd/messages.h

diff --combined doc/Modes.txt
index c2c533f2a67e1784561869cfee1ac49c5bf0fd90,d61bd2d02e4612f325140e369700dabe8924f959..dec80ce819756e3576ee6d70dfe39e21fe676379
@@@ -2,7 -2,7 +2,7 @@@
                       ngIRCd - Next Generation IRC Server
                             http://ngircd.barton.de/
  
 -               (c)2001-2011 Alexander Barton and Contributors.
 +               (c)2001-2012 Alexander Barton and Contributors.
                 ngIRCd is free software and published under the
                     terms of the GNU General Public License.
  
@@@ -22,7 -22,6 +22,7 @@@ channels he is using at the moment
    mode        since   description
  
    a   0.3.0   User is away.
 +  B   20      User is flagged as a "bot".
    c   17      IRC operator wants to receive connect/disconnect NOTICEs.
    C   19      Only users that share a channel are allowed to send messages.
    i   0.0.1   User is "invisible".
@@@ -50,7 -49,6 +50,7 @@@ users to lists (e.g. "invite list", "ba
    k   0.6.0   Channel has a "key" (a password).
    l   0.6.0   Channel has a user limit.
    m   0.3.0   Channel is moderated, only "voiced" users can send messages.
 +  M   20      Only registered users (and IRC Ops) can send messages.
    n   0.3.0   Channel doesn't allow messages of users not being members.
    O   18      Only IRC operators are allowed to join this channel.
    P   0.5.0   Channel is "persistent".
@@@ -68,7 -66,12 +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.
  
  
diff --combined src/ngircd/channel.c
index f0a9525d348a5d992596f453fd9ba709cbd47c9a,90d2efab3e191f7ef1f3931bb1f2de6e90e5837d..8d001a825c013e8c77e339f5915a54eac792b6fa
@@@ -66,8 -66,16 +66,8 @@@ static void Set_KeyFile PARAMS((CHANNE
  GLOBAL void
  Channel_Init( void )
  {
 -      CHANNEL *sc;
 -
        My_Channels = NULL;
        My_Cl2Chan = NULL;
 -
 -      sc = Channel_Create("&SERVER");
 -      if (sc) {
 -              Channel_SetModes(sc, "mnPt");
 -              Channel_SetTopic(sc, Client_ThisServer(), "Server Messages");
 -      }
  } /* Channel_Init */
  
  
@@@ -95,12 -103,11 +95,12 @@@ Channel_GetListInvites(CHANNEL *c
  }
  
  
 +/**
 + * Generate predefined persistent channels and &SERVER
 + */
  GLOBAL void
  Channel_InitPredefined( void )
  {
 -      /* Generate predefined persistent channels */
 -
        CHANNEL *new_chan;
        const struct Conf_Channel *conf_chan;
        const char *c;
        }
        if (channel_count)
                array_free(&Conf_Channels);
 +
 +      /* Make sure the local &SERVER channel exists */
 +      if (!Channel_Search("&SERVER")) {
 +              new_chan = Channel_Create("&SERVER");
 +              if (new_chan) {
 +                      Channel_SetModes(new_chan, "mnPt");
 +                      Channel_SetTopic(new_chan, Client_ThisServer(),
 +                                       "Server Messages");
 +              } else
 +                      Log(LOG_ERR, "Failed to create \"&SERVER\" channel!");
 +      } else
 +              LogDebug("Required channel \"&SERVER\" already exists, ok.");
  } /* Channel_InitPredefined */
  
  
@@@ -299,6 -294,8 +299,8 @@@ Channel_Kick(CLIENT *Peer, CLIENT *Targ
             const char *Reason )
  {
        CHANNEL *chan;
+       char *ptr, *target_modes;
+       bool can_kick = false;
  
        assert(Peer != NULL);
        assert(Target != NULL);
                /* 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;
                }
        }
@@@ -812,9 -846,9 +851,9 @@@ Channel_SetMaxUsers(CHANNEL *Chan, unsi
  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)
                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;
        }
  
        /*
        if (strchr(Channel_Modes(Chan), 'n') && !is_member)
                return false;
  
-       if (is_op || has_voice)
 +      if (strchr(Channel_Modes(Chan), 'M') && !Client_HasMode(From, 'R')
 +          && !Client_HasMode(From, 'o'))
 +              return false;
 +
+       if (has_voice || is_halfop || is_op || is_chanadmin || is_owner)
                return true;
  
        if (strchr(Channel_Modes(Chan), 'm'))
@@@ -1196,64 -1232,6 +1241,6 @@@ Channel_CheckKey(CHANNEL *Chan, CLIENT 
  } /* 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 --combined src/ngircd/defines.h
index 82837599aadbb6485f0f044b447739d7b5f922fa,d0dc9ce18862f742617caed8c69579007303ec8a..ba7adf17adb204840418b7482b75fef975844138
  #endif
  
  /** Supported user modes. */
 -#define USERMODES "acCiorRswx"
 +#define USERMODES "aBcCiorRswx"
  
  /** Supported channel modes. */
- #define CHANMODES "beiIklmMnoOPrRstvz"
 -#define CHANMODES "abehiIklmnoOPqrRstvz"
++#define CHANMODES "abehiIklmMnoOPqrRstvz"
  
  /** Away message for users connected to linked servers. */
  #define DEFAULT_AWAY_MSG "Away"
diff --combined src/ngircd/irc-info.c
index 6eb8d9421922e8606e0e5cbd83729f1c90c7d4b5,879da3daadeed7782bd8af15ae616a0eaaf345b9..7a122ef790a0c5ec0148c3224b83956a8de00020
@@@ -807,22 -807,38 +807,38 @@@ who_flags_status(const char *client_mod
  }
  
  
- 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,15 -857,13 +857,15 @@@ IRC_WHO_Channel(CLIENT *Client, CHANNE
        CL2CHAN *cl2chan;
        const char *client_modes;
        const char *chan_user_modes;
-       char flags[8];
+       char flags[10];
        CLIENT *c;
        int count = 0;
  
        assert( Client != NULL );
        assert( Chan != NULL );
  
 +      IRC_SetPenalty(Client, 1);
 +
        is_member = Channel_IsMemberOf(Chan, Client);
  
        /* Secret channel? */
  
                is_visible = strchr(client_modes, 'i') == NULL;
                if (is_member || is_visible) {
 -                      if (IRC_CheckListTooBig(Client, count, MAX_RPL_WHO, "WHO"))
 -                              break;
 -
                        strcpy(flags, who_flags_status(client_modes));
                        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(c, chan_user_modes, flags, sizeof(flags));
+  
                        if (!write_whoreply(Client, c, Channel_Name(Chan),
                                            flags))
                                return DISCONNECTED;
                        count++;
                }
        }
 +
 +      /* If there are a lot of clients, augment penalty a bit */
 +      if (count > MAX_RPL_WHO)
 +              IRC_SetPenalty(Client, 1);
 +
        return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client),
                                  Channel_Name(Chan));
  }
@@@ -915,7 -926,6 +930,7 @@@ IRC_WHO_Mask(CLIENT *Client, char *Mask
        if (Mask)
                ngt_LowerStr(Mask);
  
 +      IRC_SetPenalty(Client, 3);
        for (c = Client_First(); c != NULL; c = Client_Next(c)) {
                if (Client_Type(c) != CLIENT_USER)
                        continue;
@@@ -1019,11 -1029,13 +1034,11 @@@ IRC_WHO(CLIENT *Client, REQUEST *Req
                chan = Channel_Search(Req->argv[0]);
                if (chan) {
                        /* Members of a channel have been requested */
 -                      IRC_SetPenalty(Client, 1);
                        return IRC_WHO_Channel(Client, chan, only_ops);
                }
                if (strcmp(Req->argv[0], "0") != 0) {
                        /* A mask has been given. But please note this RFC
                         * stupidity: "0" is same as no arguments ... */
 -                      IRC_SetPenalty(Client, 3);
                        return IRC_WHO_Mask(Client, Req->argv[0], only_ops);
                }
        }
@@@ -1090,8 -1102,7 +1105,7 @@@ IRC_WHOIS_SendReply(CLIENT *Client, CLI
                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)) {
                                Client_ID(from), Client_ID(c)))
                return DISCONNECTED;
  
 +      /* IRC-Bot? */
 +      if (Client_HasMode(c, 'B') &&
 +          !IRC_WriteStrClient(from, RPL_WHOISBOT_MSG,
 +                              Client_ID(from), Client_ID(c)))
 +              return DISCONNECTED;
 +
        /* Connected using SSL? */
        if (Conn_UsesSSL(Client_Conn(c)) &&
            !IRC_WriteStrClient(from, RPL_WHOISSSL_MSG, Client_ID(from),
@@@ -1486,15 -1491,7 +1500,15 @@@ Show_MOTD_Sendline(CLIENT *Client, cons
  static bool
  Show_MOTD_End(CLIENT *Client)
  {
 -      return IRC_WriteStrClient( Client, RPL_ENDOFMOTD_MSG, Client_ID( Client ));
 +      if (!IRC_WriteStrClient(Client, RPL_ENDOFMOTD_MSG, Client_ID(Client)))
 +              return DISCONNECTED;
 +
 +      if (*Conf_CloakHost)
 +              return IRC_WriteStrClient(Client, RPL_HOSTHIDDEN_MSG,
 +                                        Client_ID(Client),
 +                                        Client_Hostname(Client));
 +
 +      return CONNECTED;
  }
  
  #ifdef SSL_SUPPORT
@@@ -1595,16 -1592,8 +1609,8 @@@ IRC_Send_NAMES(CLIENT * Client, CHANNE
                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 --combined src/ngircd/irc-mode.c
index 71557201d8d938f4a5ff65416f50eb487f78333d,3679531d6963909502bf1895c2ea3dc6a0801052..f39463433ce5261b0d3736174845f92aadb21e76
@@@ -138,7 -138,6 +138,7 @@@ Client_Mode( CLIENT *Client, REQUEST *R
  {
        char the_modes[COMMAND_LEN], x[2], *mode_ptr;
        bool ok, set;
 +      bool send_RPL_HOSTHIDDEN_MSG = false;
        int mode_arg;
        size_t len;
  
                                                        ERR_NOPRIVILEGES_MSG,
                                                        Client_ID(Origin));
                        break;
 +              case 'B': /* Bot */
 +                      if (Client_HasMode(Client, 'r'))
 +                              ok = IRC_WriteStrClient(Origin,
 +                                                      ERR_RESTRICTED_MSG,
 +                                                      Client_ID(Origin));
 +                      else
 +                              x[0] = 'B';
 +                      break;
                case 'c': /* Receive connect notices
                           * (only settable by IRC operators!) */
                        if (!set || Client_Type(Client) == CLIENT_SERVER
                                                        ERR_RESTRICTED_MSG,
                                                        Client_ID(Origin));
                        break;
 +              case 'R': /* Registered (not [un]settable by clients) */
 +                      if (Client_Type(Client) == CLIENT_SERVER)
 +                              x[0] = 'R';
 +                      else
 +                              ok = IRC_WriteStrClient(Origin,
 +                                                      ERR_NICKREGISTER_MSG,
 +                                                      Client_ID(Origin));
 +                      break;
                case 'x': /* Cloak hostname */
                        if (Client_HasMode(Client, 'r'))
                                ok = IRC_WriteStrClient(Origin,
                                                        Client_ID(Origin));
                        else
                                x[0] = 'x';
 +                              send_RPL_HOSTHIDDEN_MSG = true;
                        break;
                default:
                        if (Client_Type(Client) != CLIENT_SERVER) {
                                                  "MODE %s :%s",
                                                  Client_ID(Target),
                                                  the_modes);
 +                      if (send_RPL_HOSTHIDDEN_MSG)
 +                              IRC_WriteStrClient(Client, RPL_HOSTHIDDEN_MSG,
 +                                                 Client_ID(Client),
 +                                                 Client_HostnameCloaked(Client));
                }
                LogDebug("%s \"%s\": Mode change, now \"%s\".",
                         Client_TypeText(Target), Client_Mask(Target),
@@@ -418,13 -396,16 +418,16 @@@ static boo
  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));
        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));
                /* 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';
                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,
                        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,
                                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]);
                        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,
                                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');
                        }
                        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),
                                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,
                                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 --combined src/ngircd/irc-server.c
index 8526a573ec361b0851222314343e079b447114f6,2ce4fadd8ef8430a45155e824ebbeec501a6717a..f9182d984ca9c2c6f1f743b419a198d89b31ada7
@@@ -80,8 -80,7 +80,8 @@@ IRC_SERVER( CLIENT *Client, REQUEST *Re
                        Conn_Close( Client_Conn( Client ), NULL, "Server not configured here", true);
                        return DISCONNECTED;
                }
 -              if( strcmp( Client_Password( Client ), Conf_Server[i].pwd_in ) != 0 )
 +              if( strcmp( Conn_Password( Client_Conn( Client ) ),
 +                          Conf_Server[i].pwd_in ) != 0 )
                {
                        /* wrong password */
                        Log( LOG_ERR, "Connection %d: Got bad password from server \"%s\"!", Client_Conn( Client ), Req->argv[0] );
@@@ -203,7 -202,7 +203,7 @@@ GLOBAL boo
  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;
        
                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++;
                }
                        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... */
                        }
  
                        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 --combined src/ngircd/messages.h
index 4f3a397b2647eabc6fd6ed21f9aa31d776df69e9,d8041bfd761e3d5dc621eca2864939118b778049..d99930faa5d079995932ba3140d207c08e20e5c9
@@@ -21,7 -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,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,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"
@@@ -72,7 -72,6 +72,7 @@@
  #define RPL_NOTOPIC_MSG                       "331 %s %s :No topic is set"
  #define RPL_TOPIC_MSG                 "332 %s %s :%s"
  #define RPL_TOPICSETBY_MSG            "333 %s %s %s %u"
 +#define RPL_WHOISBOT_MSG              "335 %s %s :is a IRC Bot"
  #define RPL_INVITING_MSG              "341 %s %s %s%s"
  #define RPL_INVITELIST_MSG            "346 %s %s %s"
  #define RPL_ENDOFINVITELIST_MSG               "347 %s %s :End of channel invite list"
  #define RPL_YOUREOPER_MSG             "381 %s :You are now an IRC Operator"
  #define RPL_YOURESERVICE_MSG          "383 %s :You are service %s"
  #define RPL_TIME_MSG                  "391 %s %s :%s"
 +#define RPL_HOSTHIDDEN_MSG            "396 %s %s :is your displayed hostname now"
  
  #define ERR_NOSUCHNICK_MSG            "401 %s %s :No such nick or channel name"
  #define ERR_NOSUCHSERVER_MSG          "402 %s %s :No such server"
  #define ERR_NOSUCHCHANNEL_MSG         "403 %s %s :No such channel"
 -#define ERR_CANNOTSENDTOCHAN_MSG      "404 %s %s :Cannot send to channel"
 +#define ERR_CANNOTSENDTOCHAN_MSG      "404 %s %s :Cannot send to channel (+m) -- Moderated"
  #define ERR_TOOMANYCHANNELS_MSG               "405 %s %s :You have joined too many channels"
  #define ERR_WASNOSUCHNICK_MSG         "406 %s %s :There was no such nickname"
  #define ERR_NOORIGIN_MSG              "409 %s :No origin specified"
  #define ERR_NONICKNAMEGIVEN_MSG               "431 %s :No nickname given"
  #define ERR_ERRONEUSNICKNAME_MSG      "432 %s %s :Erroneous nickname"
  #define ERR_NICKNAMETOOLONG_MSG               "432 %s %s :Nickname too long, max. %u characters"
 +#define ERR_FORBIDDENNICKNAME_MSG     "432 %s %s :Nickname is forbidden/blocked"
  #define ERR_NICKNAMEINUSE_MSG         "433 %s %s :Nickname already in use"
  #define ERR_USERNOTINCHANNEL_MSG      "441 %s %s %s :They aren't on that channel"
  #define ERR_NOTONCHANNEL_MSG          "442 %s %s :You are not on that channel"
  #define ERR_NEEDMOREPARAMS_MSG                "461 %s %s :Syntax error"
  #define ERR_ALREADYREGISTRED_MSG      "462 %s :Connection already registered"
  #define ERR_PASSWDMISMATCH_MSG                "464 %s :Invalid password"
 -#define ERR_CHANNELISFULL_MSG         "471 %s %s :Cannot join channel (+l)"
 -#define ERR_SECURECHANNEL_MSG         "471 %s %s :Cannot join channel (+z)"
 -#define ERR_OPONLYCHANNEL_MSG         "471 %s %s :Cannot join channel (+O)"
 -#define ERR_REGONLYCHANNEL_MSG                "471 %s %s :Cannot join channel (+R)"
 +#define ERR_CHANNELISFULL_MSG         "471 %s %s :Cannot join channel (+l) -- Channel is too full, try later"
 +#define ERR_SECURECHANNEL_MSG         "471 %s %s :Cannot join channel (+z) -- SSL connections only"
 +#define ERR_OPONLYCHANNEL_MSG         "471 %s %s :Cannot join channel (+O) -- IRC opers only"
 +#define ERR_REGONLYCHANNEL_MSG                "471 %s %s :Cannot join channel (+R) -- Registered users only"
  #define ERR_UNKNOWNMODE_MSG           "472 %s %c :is unknown mode char for %s"
 -#define ERR_INVITEONLYCHAN_MSG                "473 %s %s :Cannot join channel (+i)"
 -#define ERR_BANNEDFROMCHAN_MSG                "474 %s %s :Cannot join channel (+b)"
 -#define ERR_BADCHANNELKEY_MSG         "475 %s %s :Cannot join channel (+k)"
 +#define ERR_INVITEONLYCHAN_MSG                "473 %s %s :Cannot join channel (+i) -- Invited users only"
 +#define ERR_BANNEDFROMCHAN_MSG                "474 %s %s :Cannot join channel (+b) -- You are banned"
 +#define ERR_BADCHANNELKEY_MSG         "475 %s %s :Cannot join channel (+k) -- Wrong channel key"
  #define ERR_NOCHANMODES_MSG           "477 %s %s :Channel doesn't support modes"
  #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"
  #define ERR_NOOPERHOST_MSG            "491 %s :Not configured for your host"
  #define ERR_NOTONSAMECHANNEL_MSG      "493 %s :You must share a common channel with %s"