]> jfr.im git - irc/rqf/shadowircd.git/commitdiff
Merge.
authorB.Greenham <redacted>
Mon, 11 Oct 2010 15:58:39 +0000 (11:58 -0400)
committerB.Greenham <redacted>
Mon, 11 Oct 2010 15:58:39 +0000 (11:58 -0400)
13 files changed:
1  2 
doc/example.conf
doc/reference.conf
include/channel.h
include/s_conf.h
modules/core/m_join.c
modules/core/m_message.c
modules/core/m_mode.c
modules/m_info.c
src/channel.c
src/chmode.c
src/newconf.c
src/s_conf.c
src/s_user.c

diff --combined doc/example.conf
index 1e621018c0589aead38add057f802387366ec556,ff28dae14caa27da50d6b87edbe9e76aa74eb696..cba5676b2c8c1d54bd5749a6baf614b299036b2d
@@@ -45,6 -45,10 +45,6 @@@ loadmodule "extensions/sno_globalkline.
  loadmodule "extensions/sno_globaloper.so";
  #loadmodule "extensions/sno_whois.so";
  
 -/* Modesets, load only one */
 -loadmodule "modes/shadowircd.so";
 -#loadmodule "modes/charybdis.so";
 -
  serverinfo {
        name = "hades.arpa";
        sid = "42X";
@@@ -373,13 -377,13 +373,14 @@@ exempt 
  
  channel {
        autochanmodes = "nt";
+       admin_on_channel_create = no;
        exemptchanops = "NT";
        use_halfop = yes;
        use_admin = yes;
        use_invex = yes;
        use_except = yes;
        use_knock = yes;
 +      use_forward = yes;
        use_local_channels = yes;
        knock_delay = 5 minutes;
        knock_delay_channel = 1 minute;
        cycle_host_change = yes;
        host_in_topic = yes;
        resv_forcepart = yes;
+       channel_target_change = yes;
  };
  
  serverhide {
@@@ -470,6 -475,10 +472,10 @@@ alias "InfoServ" 
        target = "InfoServ";
  };
  
+ alias "GroupServ" {
+       target = "GroupServ";
+ };
  alias "NS" {
        target = "NickServ";
  };
@@@ -498,6 -507,10 +504,10 @@@ alias "IS" 
        target = "InfoServ";
  };
  
+ alias "GS" {
+       target = "GroupServ";
+ };
  general {
        hide_error_messages = opers;
        hide_spoof_ips = yes;
        #static_quit = "I like turtles!";
        servicestring = "is a Network Service";
        disable_fake_channels = no;
+       hide_channel_below_users = 3;
          tkline_expire_notices = no;
          default_floodcount = 10;
        failed_oper_notice = yes;
diff --combined doc/reference.conf
index bbb2b26be24539bed2932dac5f6f42665bfc5310,122fa78eceddb341b051f1487f8419d9fbd5e9d9..4a6cea54dca1b191717fad5c6b9e4dc58eec2f97
@@@ -115,6 -115,31 +115,6 @@@ loadmodule "extensions/sno_globalkline.
  loadmodule "extensions/sno_globaloper.so";
  #loadmodule "extensions/sno_whois.so";
  
 -/* modesets: Modesets are modules that enable various user and channel
 - * modes. When enabled, they enable usermodes and channel modes equivalent
 - * to the named ircd. This is useful for linking to other TS6 IRCds. 
 - * If you do not know what this does or do not know if you need it
 - * then you should just load modes/shadowircd.so for full functionality.
 - * Without any modeset loaded, you will only have the modes that are included
 - * in ircd-ratbox. Unloading a modeset while the ircd is running is possible,
 - * but will be confusing for users, as it will leave all existent unloaded
 - * modes in place without users being able to remove them. Said modes will, 
 - * however, not function. You should be able to load a module that provides 
 - * more modes than your current (say, going from charybdis to shadowircd) 
 - * on the fly without any problems. The slight exception to this is unloading
 - * a module that provides quiets (cmode +q). Users affected by quiets will
 - * have to part and rejoin the channel before the quiet will cease affecting
 - * them. In addition, unloaded modes will remain in the IRCd's VERSION reply
 - * until restart. For these reasons, it is highly recommended to restart if you wish
 - * to change modeset.
 - * Modesets currently only affect cmodes. 
 - * You should only load one of these at a time.
 - * Modules are listed in order of highest functionality to least,
 - * while no module provides the least functionality of all. */
 - 
 -loadmodule "modes/shadowircd.so";
 -#loadmodule "modes/charybdis.so";
 -
   
  /* serverinfo {}:  Contains information about the server. (OLD M:) */
  serverinfo {
@@@ -745,6 -770,11 +745,11 @@@ channel 
         */
        autochanmodes = "nt";
  
+       /* admin_on_channel_create: If set to yes, users joining new channels
+        * will be given +ao instead of just +o. Requires use_admin.
+        */
+       admin_on_channel_create = no;
        /* exemptchanops: Channel modes that any form of channel ops (+aoh) 
         * will be exempt from. Even if the mode is set, it will not apply to the
         * channel ops if it is listed in this option. Valid modes are cCDTNGK.
         */
        use_except = yes;
  
 +      /* forward: Enable/disable channel mode +f, which allows you to set 
 +       * a channel to forward users to if they can't join because of +i etc.
 +       * Disabling this option via rehash will leave all previously set 
 +       * forwards hanging around, though they will not do anything. For 
 +       * this reason, you may want to restart to disable this option.
 +       */
 +      use_forward = yes;
 +
        /* knock: Allows users to request an invite to a channel that
         * is locked somehow (+ikl).  If the channel is +p or you are banned
         * the knock will not be sent.
         * when a RESV is issued.
         */
        resv_forcepart = yes;
+       /* channel target change: restrict how many channels users can
+        * message per unit of time. IRC operators, channel operators and
+        * voiced users are exempt.
+        */
+       channel_target_change = yes;
  };
  
  
@@@ -996,6 -1024,10 +1007,10 @@@ alias "InfoServ" 
        target = "InfoServ";
  };
  
+ alias "GroupServ" {
+       target = "GroupServ";
+ };
  alias "NS" {
        target = "NickServ";
  };
@@@ -1024,6 -1056,10 +1039,10 @@@ alias "IS" 
        target = "InfoServ";
  };
  
+ alias "GS" {
+       target = "GroupServ";
+ };
  /* The general block contains many of the options that were once compiled
   * in options in config.h.  The general block is read at start time.
   */
@@@ -1091,6 -1127,12 +1110,12 @@@ general 
         */
        disable_fake_channels = no;
  
+       /* hide_channel_below_users: Amount of users a channel must have in it
+        * before it is shown in a standard LIST. This can be overridden by
+        * invoking LIST like: /LIST <3
+        */
+       hide_channel_below_users = 3;
          /* tkline_expire_notices: give a notice to opers when a tkline
           * expires
           */
diff --combined include/channel.h
index 62f53265a413f5e90e33f138d7242dc062f830a5,268d4db1e7dfbe90bd173361cd4a9d24eaa77126..22f9d533fe2174d5fec9db5bf137da28d9452d23
@@@ -50,6 -50,7 +50,7 @@@ struct Channe
  {
        rb_dlink_node node;
        struct Mode mode;
+       char *mode_lock;
        char *topic;
        char *topic_info;
        time_t topic_time;
@@@ -136,6 -137,32 +137,6 @@@ struct ChannelMod
        long mode_type;
  };
  
 -/* modes added by the module files in modes/ */
 -struct module_modes
 -{
 -      unsigned int MODE_REGONLY; 
 -      unsigned int MODE_NOCTCP; /* Block CTCPs directed to this channel */
 -      unsigned int MODE_NOCOLOR;
 -      unsigned int MODE_EXLIMIT; /* exempt from list limits, +b/+e/+I/+q */
 -      unsigned int MODE_PERMANENT; /* permanant channel, +P */
 -      unsigned int MODE_OPMODERATE; /* send rejected messages to ops */
 -      unsigned int MODE_FREEINVITE; /* allow free use of /invite */
 -      unsigned int MODE_FREETARGET; /* can be forwarded to without authorization */
 -      unsigned int MODE_DISFORWARD; /* disable channel forwarding */
 -      unsigned int MODE_THROTTLE; /* throttle joins */
 -      unsigned int MODE_FORWARD;
 -      unsigned int MODE_NONOTICE; /* Block notices directed to this channel */
 -      unsigned int MODE_NOACTION; /* Block CTCP ACTION directed to this channel */
 -      unsigned int MODE_NOKICK; /* Disable /kick on this channel */
 -      unsigned int MODE_NONICK; /* Disable /nick for anyone on this channel */
 -      unsigned int MODE_NOCAPS; /* Block messages in all capital letters */
 -      unsigned int MODE_NOREJOIN; /* Block rejoin immediately after kick */
 -      unsigned int MODE_NOREPEAT; /* Block repeat messages */
 -      unsigned int MODE_NOOPERKICK; /* disallow kicking opers */
 -
 -      unsigned int CHFL_QUIET;
 -};
 -
  typedef int (*ExtbanFunc)(const char *data, struct Client *client_p,
                struct Channel *chptr, long mode_type);
  
  #define MODE_TOPICLIMIT 0x0008
  #define MODE_INVITEONLY 0x0010
  #define MODE_NOPRIVMSGS 0x0020
 +#define MODE_REGONLY  0x0040
 +#define MODE_NOCOLOR  0x0080
 +#define MODE_EXLIMIT  0x0100  /* exempt from list limits, +b/+e/+I/+q */
 +#define MODE_PERMANENT  0x0200  /* permanant channel, +P */
 +#define MODE_OPMODERATE 0x0400  /* send rejected messages to ops */
 +#define MODE_FREEINVITE 0x0800  /* allow free use of /invite */
 +#define MODE_FREETARGET 0x1000  /* can be forwarded to without authorization */
 +#define MODE_DISFORWARD 0x2000  /* disable channel forwarding */
 +#define MODE_NOCTCP     0x8000  /* Block CTCPs directed to this channel */
 +#define MODE_NONOTICE 0x10000 /* Block notices directed to this channel */
 +#define MODE_NOACTION 0x20000 /* Block CTCP ACTION directed to this channel */
 +#define MODE_NOKICK   0x40000 /* Disable /kick on this channel */
 +#define MODE_NONICK   0x80000 /* Disable /nick for anyone on this channel */
 +#define MODE_NOCAPS   0x100000 /* Block messages in all capital letters */
 +#define MODE_NOREJOIN 0x200000 /* Block rejoin immediately after kick */
 +#define MODE_NOREPEAT 0x400000 /* Block repeat messages */
 +#define MODE_NOOPERKICK       0x800000 /* disallow kicking opers */
  
  #define CHFL_BAN        0x10000000    /* ban channel flag */
  #define CHFL_EXCEPTION  0x20000000    /* exception to ban channel flag */
  #define CHFL_INVEX      0x40000000
 +#define CHFL_QUIET      0x80000000
  
  /* mode flags for direction indication */
  #define MODE_QUERY     0
  
  extern rb_dlink_list global_channel_list;
  void init_channels(void);
 -void init_module_modes(void);
  
  struct Channel *allocate_channel(const char *chname);
  void free_channel(struct Channel *chptr);
@@@ -282,6 -292,8 +283,8 @@@ void resv_chan_forcepart(const char *na
  
  extern void set_channel_mode(struct Client *client_p, struct Client *source_p,
                struct Channel *chptr, struct membership *msptr, int parc, const char *parv[]);
+ extern void set_channel_mlock(struct Client *client_p, struct Client *source_p,
+               struct Channel *chptr, const char *newmlock, int propagate);
  
  extern struct ChannelMode chmode_table[256];
  
diff --combined include/s_conf.h
index 65a971d4412105beedf7015a9c58c43f70ca8b51,a54da0c7ffac92e1f61e92dee981c732b7503ee0..e754070e34e00e571b461913d5cd49d8f8fd0247
@@@ -166,6 -166,7 +166,7 @@@ struct config_file_entr
  
        unsigned char compression_level;
        int disable_fake_channels;
+       int hide_channel_below_users;
        int dots_in_ident;
        int failed_oper_notice;
        int anti_nick_flood;
@@@ -238,12 -239,12 +239,13 @@@ struct config_channel_entr
  {
        char * autochanmodes;
        char * exemptchanops;
+       int admin_on_channel_create;
        int use_halfop;
        int use_admin;
        int use_except;
        int use_invex;
        int use_knock;
 +      int use_forward;
        int use_local_channels;
        int knock_delay;
        int knock_delay_channel;
        int cycle_host_change;
        int host_in_topic;
        int resv_forcepart;
+       int channel_target_change;
  
        int exempt_cmode_c;
        int exempt_cmode_C;
diff --combined modules/core/m_join.c
index 73c497e8a00849f213210b87e1f618b6d926c50f,94b13293f28130556b8a196e571c145f526d69fe..82d5d708f2a27120f778dda9c0d7712c03e656e5
@@@ -46,6 -46,8 +46,6 @@@ static int me_svsjoin(struct Client *, 
  static int ms_join(struct Client *, struct Client *, int, const char **);
  static int ms_sjoin(struct Client *, struct Client *, int, const char **);
  
 -struct module_modes ModuleModes;
 -
  struct Message join_msgtab = {
        "JOIN", 0, 0, 0, MFLG_SLOW,
        {mg_unreg, {m_join, 2}, {ms_join, 2}, mg_ignore, mg_ignore, {m_join, 2}}
@@@ -111,6 -113,9 +111,9 @@@ me_svsjoin(struct Client *client_p, str
        if((target_p = find_person(parv[1])) == NULL)
                return 0;
  
+       if(!MyClient(target_p))
+               return 0;
        user_join(&me, target_p, parv[2], NULL);
        return 0;
  }
@@@ -227,6 -232,9 +230,9 @@@ ms_join(struct Client *client_p, struc
                                        source_p->servptr->name,
                                        chptr->chname, modebuf, parabuf);
                *omodebuf = *modebuf = *parabuf = '\0';
+               /* since we're dropping our modes, we want to clear the mlock as well. --nenolod */
+               set_channel_mlock(client_p, source_p, chptr, NULL, FALSE);
        }
  
        if(!IsMember(source_p, chptr))
@@@ -488,6 -496,9 +494,9 @@@ ms_sjoin(struct Client *client_p, struc
                /* Update capitalization in channel name, this makes the
                 * capitalization timestamped like modes are -- jilles */
                strcpy(chptr->chname, parv[2]);
+               /* since we're dropping our modes, we want to clear the mlock as well. --nenolod */
+               set_channel_mlock(client_p, source_p, chptr, NULL, FALSE);
        }
  
        if(*modebuf != '\0')
        {
                fl = 0;
  
-               for (i = 0; i < 2; i++)
+               for (i = 0; i < 4; i++)
                {
                        if(*s == '!')
                        {
                                para[pargs++] = target_p->name;
                        }
                }
-               if(fl & CHFL_CHANOP)
+               else if(fl & CHFL_CHANOP)
                {
                        *mbuf++ = 'o';
                        para[pargs++] = target_p->name;
                                para[pargs++] = target_p->name;
                        }
                }
-               if(fl & CHFL_HALFOP)
+               else if(fl & CHFL_HALFOP)
                {
                        *mbuf++ = 'h';
                        para[pargs++] = target_p->name;
                                     CheckEmpty(para[2]), CheckEmpty(para[3]));
        }
  
 -      if(!joins && !(chptr->mode.mode & ModuleModes.MODE_PERMANENT) && isnew)
 +      if(!joins && !(chptr->mode.mode & MODE_PERMANENT) && isnew)
        {
                destroy_channel(chptr);
  
@@@ -970,7 -981,7 +979,7 @@@ set_final_mode(struct Mode *mode, struc
                len = rb_sprintf(pbuf, "%d:%d ", mode->join_num, mode->join_time);
                pbuf += len;
        }
 -      if(mode->forward[0] && strcmp(oldmode->forward, mode->forward) && ModuleModes.MODE_FORWARD)
 +      if(mode->forward[0] && strcmp(oldmode->forward, mode->forward) && ConfigChannel.use_forward)
        {
                if(dir != MODE_ADD)
                {
diff --combined modules/core/m_message.c
index 6f931173293e370359bcac731f63487ffbf107e7,46ba4e9ffb547a67d24eb2e8b8324b027832e9d0..ac3cc3b6733895ccc7584a88d7494030a0e631de
@@@ -45,6 -45,7 +45,6 @@@
  #include "tgchange.h"
  #include "inline/stringops.h"
  #include "irc_dictionary.h"
 -#include "channel.h"
  
  static int m_message(int, const char *, struct Client *, struct Client *, int, const char **);
  static int m_privmsg(struct Client *, struct Client *, int, const char **);
@@@ -53,6 -54,8 +53,6 @@@ static int m_notice(struct Client *, st
  static void expire_tgchange(void *unused);
  static struct ev_entry *expire_tgchange_event;
  
 -struct module_modes ModuleModes;
 -
  static int
  modinit(void)
  {
@@@ -497,7 -500,7 +497,7 @@@ msg_channel(int p_or_n, const char *com
                        source_p->localClient->last = rb_current_time();
        }
  
 -      if(chptr->mode.mode & ModuleModes.MODE_NOREPEAT)
 +      if(chptr->mode.mode & MODE_NOREPEAT)
        {
                rb_strlcpy(text2, text, BUFSIZE);
                strip_unprintable(text2);
                channel_metadata_add(chptr, "NOREPEAT", text2, 0);
        }
  
 -      if(chptr->mode.mode & ModuleModes.MODE_NOCOLOR && (!ConfigChannel.exempt_cmode_c || !is_any_op(msptr)))
 +      if(chptr->mode.mode & MODE_NOCOLOR && (!ConfigChannel.exempt_cmode_c || !is_any_op(msptr)))
        {
                rb_strlcpy(text2, text, BUFSIZE);
                strip_colour(text2);
        /* chanops and voiced can flood their own channel with impunity */
        if((result = can_send(chptr, source_p, NULL)))
        {
+               if(result != CAN_SEND_OPV && MyClient(source_p) &&
+                  !IsOper(source_p) &&
+                  !add_channel_target(source_p, chptr))
+               {
+                       sendto_one(source_p, form_str(ERR_TARGCHANGE),
+                                  me.name, source_p->name, chptr->chname);
+                       return;
+               }
                if(result == CAN_SEND_OPV ||
                   !flood_attack_channel(p_or_n, source_p, chptr, chptr->chname))
                {
 -                      if (strlen(text) > 10 && chptr->mode.mode & ModuleModes.MODE_NOCAPS && (!ConfigChannel.exempt_cmode_G || !is_any_op(msptr)))
 +                      if (strlen(text) > 10 && chptr->mode.mode & MODE_NOCAPS && (!ConfigChannel.exempt_cmode_G || !is_any_op(msptr)))
                        {
                                rb_strlcpy(text2, text, BUFSIZE);
                                strip_unprintable(text2);
                                        return;
                                }
                        }
 -                      if (p_or_n != PRIVMSG && chptr->mode.mode & ModuleModes.MODE_NONOTICE && (!ConfigChannel.exempt_cmode_T || !is_any_op(msptr)))
 +                      if (p_or_n != PRIVMSG && chptr->mode.mode & MODE_NONOTICE && (!ConfigChannel.exempt_cmode_T || !is_any_op(msptr)))
                        {
                                sendto_one_numeric(source_p, 404, "%s :Cannot send to channel - Notices are disallowed (+T set)", chptr->chname);
                                return;
                        }
 -                      if (p_or_n != NOTICE && chptr->mode.mode & ModuleModes.MODE_NOACTION &&
 +                      if (p_or_n != NOTICE && chptr->mode.mode & MODE_NOACTION &&
                                        !strncasecmp(text + 1, "ACTION", 6) &&
                                        (!ConfigChannel.exempt_cmode_D || !is_any_op(msptr)))
                        {
                        if (p_or_n != NOTICE && *text == '\001' &&
                                        strncasecmp(text + 1, "ACTION", 6))
                        {
 -                              if (chptr->mode.mode & ModuleModes.MODE_NOCTCP && (!ConfigChannel.exempt_cmode_C || !is_any_op(msptr)))
 +                              if (chptr->mode.mode & MODE_NOCTCP && (!ConfigChannel.exempt_cmode_C || !is_any_op(msptr)))
                                {
                                        sendto_one_numeric(source_p, 404, "%s :Cannot send to channel - CTCPs to this channel are disallowed (+C set)", chptr->chname);
                                        return;
                                             "%s %s :%s", command, chptr->chname, text);
                }
        }
 -      else if(chptr->mode.mode & ModuleModes.MODE_OPMODERATE &&
 +      else if(chptr->mode.mode & MODE_OPMODERATE &&
                        (!(chptr->mode.mode & MODE_NOPRIVMSGS) ||
                         IsMember(source_p, chptr)))
        {
+               if(MyClient(source_p) && !IsOper(source_p) &&
+                  !add_channel_target(source_p, chptr))
+               {
+                       sendto_one(source_p, form_str(ERR_TARGCHANGE),
+                                  me.name, source_p->name, chptr->chname);
+                       return;
+               }
                if(!flood_attack_channel(p_or_n, source_p, chptr, chptr->chname))
                {
                        sendto_channel_opmod(client_p, source_p, chptr,
@@@ -616,7 -634,7 +631,7 @@@ msg_channel_opmod(int p_or_n, const cha
  {
        char text2[BUFSIZE];
  
 -      if(chptr->mode.mode & ModuleModes.MODE_NOCOLOR)
 +      if(chptr->mode.mode & MODE_NOCOLOR)
        {
                rb_strlcpy(text2, text, BUFSIZE);
                strip_colour(text2);
                }
        }
  
 -      if(chptr->mode.mode & ModuleModes.MODE_OPMODERATE &&
 +      if(chptr->mode.mode & MODE_OPMODERATE &&
                        (!(chptr->mode.mode & MODE_NOPRIVMSGS) ||
                         IsMember(source_p, chptr)))
        {
@@@ -736,6 -754,32 +751,32 @@@ msg_client(int p_or_n, const char *comm
  
        if(MyClient(source_p))
        {
+               /*
+                * XXX: Controversial? Allow target users to send replies
+                * through a +g.  Rationale is that people can presently use +g
+                * as a way to taunt users, e.g. harass them and hide behind +g
+                * as a way of griefing.  --nenolod
+                */
+               if(p_or_n != NOTICE && MyClient(source_p) &&
+                               IsSetCallerId(source_p) &&
+                               IsSetSCallerId(source_p) &&
+                               !accept_message(target_p, source_p))
+               {
+                       if(rb_dlink_list_length(&source_p->localClient->allow_list) <
+                                       ConfigFileEntry.max_accept)
+                       {
+                               rb_dlinkAddAlloc(target_p, &source_p->localClient->allow_list);
+                               rb_dlinkAddAlloc(source_p, &target_p->on_allow_list);
+                       }
+                       else
+                       {
+                               sendto_one_numeric(source_p, ERR_OWNMODE,
+                                               form_str(ERR_OWNMODE),
+                                               target_p->name, "+g");
+                               return;
+                       }
+               }
                /* reset idle time for message only if its not to self 
                 * and its not a notice */
                if(p_or_n != NOTICE)
diff --combined modules/core/m_mode.c
index 737c1bed5abf4cc0fe8a7edd739ea70e8898a42e,53455199b050fe762cebb74e68247e617b977a44..fd1b18358b2fc8b837dc8f479828a7aff65d5222
  #include "packet.h"
  #include "s_newconf.h"
  
 -struct module_modes ModuleModes;
 -
  static int m_mode(struct Client *, struct Client *, int, const char **);
  static int ms_mode(struct Client *, struct Client *, int, const char **);
  static int ms_tmode(struct Client *, struct Client *, int, const char **);
+ static int ms_mlock(struct Client *, struct Client *, int, const char **);
  static int ms_bmask(struct Client *, struct Client *, int, const char **);
  
  struct Message mode_msgtab = {
@@@ -54,12 -57,16 +55,16 @@@ struct Message tmode_msgtab = 
        "TMODE", 0, 0, 0, MFLG_SLOW,
        {mg_ignore, mg_ignore, {ms_tmode, 4}, {ms_tmode, 4}, mg_ignore, mg_ignore}
  };
+ struct Message mlock_msgtab = {
+       "MLOCK", 0, 0, 0, MFLG_SLOW,
+       {mg_ignore, mg_ignore, {ms_mlock, 3}, {ms_mlock, 3}, mg_ignore, mg_ignore}
+ };
  struct Message bmask_msgtab = {
        "BMASK", 0, 0, 0, MFLG_SLOW,
        {mg_ignore, mg_ignore, mg_ignore, {ms_bmask, 5}, mg_ignore, mg_ignore}
  };
  
- mapi_clist_av1 mode_clist[] = { &mode_msgtab, &tmode_msgtab, &bmask_msgtab, NULL };
+ mapi_clist_av1 mode_clist[] = { &mode_msgtab, &tmode_msgtab, &mlock_msgtab, &bmask_msgtab, NULL };
  
  DECLARE_MODULE_AV1(mode, NULL, NULL, mode_clist, NULL, NULL, "$Revision: 1006 $");
  
@@@ -203,6 -210,37 +208,37 @@@ ms_tmode(struct Client *client_p, struc
        return 0;
  }
  
+ static int
+ ms_mlock(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
+ {
+       struct Channel *chptr = NULL;
+       /* Now, try to find the channel in question */
+       if(!IsChanPrefix(parv[2][0]) || !check_channel_name(parv[2]))
+       {
+               sendto_one_numeric(source_p, ERR_BADCHANNAME, form_str(ERR_BADCHANNAME), parv[2]);
+               return 0;
+       }
+       chptr = find_channel(parv[2]);
+       if(chptr == NULL)
+       {
+               sendto_one_numeric(source_p, ERR_NOSUCHCHANNEL,
+                                  form_str(ERR_NOSUCHCHANNEL), parv[2]);
+               return 0;
+       }
+       /* TS is higher, drop it. */
+       if(atol(parv[1]) > chptr->channelts)
+               return 0;
+       if(IsServer(source_p))
+               set_channel_mlock(client_p, source_p, chptr, parv[3], TRUE);
+       return 0;
+ }
  static int
  ms_bmask(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
  {
  
        case 'q':
                banlist = &chptr->quietlist;
 -              mode_type = ModuleModes.CHFL_QUIET;
 +              mode_type = CHFL_QUIET;
                mems = ALL_MEMBERS;
                break;
  
diff --combined modules/m_info.c
index ca1d6268cae12a9d11fcc8dffd72e5173d7c1f9f,c8ab7de296b6dd19922332a9ef2dd804d790ca5a..dd14294328589a824023db00c33a6551b4be9f51
@@@ -150,6 -150,12 +150,12 @@@ static struct InfoStruct info_table[] 
                &ConfigFileEntry.default_floodcount,
                "Startup value of FLOODCOUNT",
        },
+       {
+               "hide_channel_below_users",
+               OUTPUT_DECIMAL,
+               &ConfigFileEntry.hide_channel_below_users,
+               "Hide channels below this many users in standard /LIST output",
+       },
        {
                "default_adminstring",
                OUTPUT_STRING,
                &ConfigChannel.host_in_topic,
                "Defines whether a topicsetters host or just nick is shown on TOPIC",
        },
+       {
+               "admin_on_channel_create",
+               OUTPUT_BOOLEAN_YN,
+               &ConfigChannel.admin_on_channel_create,
+               "Give users +ao on channel create",
+       },
        {
                "use_halfop",
                OUTPUT_BOOLEAN_YN,
                &ConfigChannel.use_invex,
                "Enable chanmode +I (invite exceptions)",
        },
 +      {
 +              "use_forward",
 +              OUTPUT_BOOLEAN_YN,
 +              &ConfigChannel.use_forward,
 +              "Enable chanmode +f (channel forwarding)",
 +      },
        {
                "use_knock",
                OUTPUT_BOOLEAN_YN,
diff --combined src/channel.c
index 39d2f85d828dd80ddd5e9f9b7cd82f31ae560795,9cb71e8275f7b70ac2b6eefa5c4d1bde42fa6b55..88f187011d4a789a88161acc38b1741c879ae21d
@@@ -66,6 -66,8 +66,6 @@@ static int h_can_join
  static int h_can_create_channel;
  static int h_channel_join;
  
 -struct module_modes ModuleModes;
 -
  /* init_channels()
   *
   * input      -
@@@ -85,6 -87,40 +85,6 @@@ init_channels(void
        h_can_create_channel = register_hook("can_create_channel");
  }
  
 -/* is this the best place to put this? */
 -/* init_module_modes()
 - *
 - * input      -
 - * output     -
 - * side effects - various MODE_ values are set to 0
 - */
 -void
 -init_module_modes()
 -{
 -      ModuleModes.MODE_REGONLY = 0;
 -      ModuleModes.MODE_NOCTCP = 0;
 -      ModuleModes.MODE_NOCOLOR = 0;
 -      ModuleModes.MODE_EXLIMIT = 0;
 -      ModuleModes.MODE_PERMANENT = 0;
 -      ModuleModes.MODE_OPMODERATE = 0;
 -      ModuleModes.MODE_FREEINVITE = 0;
 -      ModuleModes.MODE_FREETARGET = 0;
 -      ModuleModes.MODE_DISFORWARD = 0;
 -      ModuleModes.MODE_THROTTLE = 0;
 -      ModuleModes.MODE_FORWARD = 0;
 -      ModuleModes.MODE_NONOTICE = 0;
 -      ModuleModes.MODE_NOACTION = 0;
 -      ModuleModes.MODE_NOKICK = 0;
 -      ModuleModes.MODE_NONICK = 0;
 -      ModuleModes.MODE_NOCAPS = 0;
 -      ModuleModes.MODE_NOREJOIN = 0;
 -      ModuleModes.MODE_NOREPEAT = 0;
 -      ModuleModes.MODE_NOOPERKICK = 0;
 -
 -      ModuleModes.CHFL_QUIET = 0;
 -}
 -
 -
  /*
   * allocate_channel - Allocates a channel
   */
@@@ -106,6 -142,7 +106,7 @@@ free_channel(struct Channel *chptr
  {
        channel_metadata_clear(chptr);
        rb_free(chptr->chname);
+       rb_free(chptr->mode_lock);
        rb_bh_free(channel_heap, chptr);
  }
  
@@@ -352,7 -389,7 +353,7 @@@ remove_user_from_channel(struct members
        if(client_p->servptr == &me)
                rb_dlinkDelete(&msptr->locchannode, &chptr->locmembers);
  
 -      if(!(chptr->mode.mode & ModuleModes.MODE_PERMANENT) && rb_dlink_list_length(&chptr->members) <= 0)
 +      if(!(chptr->mode.mode & MODE_PERMANENT) && rb_dlink_list_length(&chptr->members) <= 0)
                destroy_channel(chptr);
  
        rb_bh_free(member_heap, msptr);
@@@ -387,7 -424,7 +388,7 @@@ remove_user_from_channels(struct Clien
                if(client_p->servptr == &me)
                        rb_dlinkDelete(&msptr->locchannode, &chptr->locmembers);
  
 -              if(!(chptr->mode.mode & ModuleModes.MODE_PERMANENT) && rb_dlink_list_length(&chptr->members) <= 0)
 +              if(!(chptr->mode.mode & MODE_PERMANENT) && rb_dlink_list_length(&chptr->members) <= 0)
                        destroy_channel(chptr);
  
                rb_bh_free(member_heap, msptr);
@@@ -723,6 -760,10 +724,6 @@@ is_quieted(struct Channel *chptr, struc
        struct Ban *actualBan = NULL;
        struct Ban *actualExcept = NULL;
  
 -      /* check to make sure quiets even exist on this server first */
 -      if(ModuleModes.CHFL_QUIET == 0)
 -              return 0;
 -
        if(!MyClient(who))
                return 0;
  
                if(match(actualBan->banstr, s) ||
                   match(actualBan->banstr, s2) ||
                   match_cidr(actualBan->banstr, s2) ||
 -                 match_extban(actualBan->banstr, who, chptr, ModuleModes.CHFL_QUIET) ||
 +                 match_extban(actualBan->banstr, who, chptr, CHFL_QUIET) ||
                   (s3 != NULL && match(actualBan->banstr, s3)))
                        break;
                else
@@@ -899,15 -940,19 +900,15 @@@ can_join(struct Client *source_p, struc
        if(chptr->mode.limit &&
           rb_dlink_list_length(&chptr->members) >= (unsigned long) chptr->mode.limit)
                i = ERR_CHANNELISFULL;
 -      if(chptr->mode.mode & ModuleModes.MODE_REGONLY && EmptyString(source_p->user->suser))
 +      if(chptr->mode.mode & MODE_REGONLY && EmptyString(source_p->user->suser))
                i = ERR_NEEDREGGEDNICK;
        /* join throttling stuff --nenolod */
 -      /* only check for throttles if they exist on this server --Taros */
 -      else if(ModuleModes.MODE_THROTTLE)
 +      else if(chptr->mode.join_num > 0 && chptr->mode.join_time > 0)
        {
 -              if(chptr->mode.join_num > 0 && chptr->mode.join_time > 0)
 -              {
 -                      if ((rb_current_time() - chptr->join_delta <= 
 -                              chptr->mode.join_time) && (chptr->join_count >=
 -                              chptr->mode.join_num))
 -                              i = ERR_THROTTLE;
 -              }
 +              if ((rb_current_time() - chptr->join_delta <= 
 +                      chptr->mode.join_time) && (chptr->join_count >=
 +                      chptr->mode.join_num))
 +                      i = ERR_THROTTLE;
        }
  
        /* allow /invite to override +l/+r/+j also -- jilles */
@@@ -1042,7 -1087,7 +1043,7 @@@ find_nonickchange_channel(struct Clien
        {
                msptr = ptr->data;
                chptr = msptr->chptr;
 -              if (chptr->mode.mode & ModuleModes.MODE_NONICK && (!ConfigChannel.exempt_cmode_N || !is_any_op(msptr)))
 +              if (chptr->mode.mode & MODE_NONICK && (!ConfigChannel.exempt_cmode_N || !is_any_op(msptr)))
                        return chptr;
        }
        return NULL;
@@@ -1297,7 -1342,7 +1298,7 @@@ channel_modes(struct Channel *chptr, st
                                           chptr->mode.join_time);
        }
  
 -      if(*chptr->mode.forward && (ModuleModes.MODE_FORWARD || !IsClient(client_p)))
 +      if(*chptr->mode.forward && (ConfigChannel.use_forward || !IsClient(client_p)))
        {
                *mbuf++ = 'f';
  
@@@ -1608,7 -1653,7 +1609,7 @@@ check_forward(struct Client *source_p, 
                if (hash_find_resv(chptr->chname))
                        return NULL;
                /* Don't forward to +Q channel */
 -              if (chptr->mode.mode & ModuleModes.MODE_DISFORWARD)
 +              if (chptr->mode.mode & MODE_DISFORWARD)
                        return NULL;
                i = can_join(source_p, chptr, key);
                if (i == 0)
@@@ -1829,7 -1874,10 +1830,10 @@@ void user_join(struct Client * client_p
                                continue;
                        }
  
-                       flags = CHFL_CHANOP;
+                       if(ConfigChannel.admin_on_channel_create && ConfigChannel.use_admin)
+                               flags = CHFL_ADMIN | CHFL_CHANOP;
+                       else
+                               flags = CHFL_CHANOP;
                }
  
                if((rb_dlink_list_length(&source_p->user->channel) >=
                                                me.name, get_oper_name(source_p), chptr->chname);
                        }
                        else if ((i != ERR_NEEDREGGEDNICK && i != ERR_THROTTLE && i != ERR_INVITEONLYCHAN && i != ERR_CHANNELISFULL) ||
 -                          (!ModuleModes.MODE_FORWARD || (chptr = check_forward(source_p, chptr, key)) == NULL))
 +                          (!ConfigChannel.use_forward || (chptr = check_forward(source_p, chptr, key)) == NULL))
                        {
                                /* might be wrong, but is there any other better location for such?
                                 * see extensions/chm_operonly.c for other comments on this
diff --combined src/chmode.c
index cab5b31400989108c9027b59a9ab8bbb72f9660a,8bc5117ac424805d45dbbb4eb9c45a7486fc1cbc..132d393648be4b944957eb1104e700d0bf28d89c
@@@ -56,6 -56,7 +56,7 @@@
  #define SM_ERR_NOPRIVS          0x00000400
  #define SM_ERR_RPL_Q            0x00000800
  #define SM_ERR_RPL_F            0x00001000
+ #define SM_ERR_MLOCK            0x00002000
  
  #define MAXMODES_SIMPLE 46 /* a-zA-Z except bqeIov */
  
@@@ -71,6 -72,8 +72,6 @@@ char cflagsmyinfo[256]
  
  int chmode_flags[256];
  
 -struct module_modes ModuleModes;
 -
  /* OPTIMIZE ME! -- dwr */
  void
  construct_cflags_strings(void)
                        chmode_flags[i] = 0;
                }
                  
 -              if(chmode_flags[i] == ModuleModes.MODE_DISFORWARD)
 +              switch (chmode_flags[i])
                {
 -                      if (ModuleModes.MODE_FORWARD)
 +                  case MODE_EXLIMIT:
 +                  case MODE_DISFORWARD:
 +                      if(ConfigChannel.use_forward)
                        {
 -                              *ptr++ = (char) i;
 +                          *ptr++ = (char) i;
                        }
 -              }
 -              else if(chmode_flags[i] == ModuleModes.MODE_REGONLY)
 -              {
 -                      if (rb_dlink_list_length(&service_list))
 +                      
 +                      break;
 +                  case MODE_REGONLY:
 +                      if(rb_dlink_list_length(&service_list))
                        {
 -                              *ptr++ = (char) i;
 +                          *ptr++ = (char) i;
 +                      }
 +
 +                      break;
 +                  default:
 +                      if(chmode_flags[i] != 0)
 +                      {
 +                          *ptr++ = (char) i;
                        }
 -              }
 -              else if(chmode_flags[i] != ModuleModes.MODE_EXLIMIT && chmode_flags[i] != 0)
 -              {
 -                      *ptr++ = (char) i;
                }
                
                /* Should we leave orphaned check here? -- dwr */
@@@ -219,7 -217,7 +220,7 @@@ add_id(struct Client *source_p, struct 
         */
        if(MyClient(source_p))
        {
 -              if((rb_dlink_list_length(&chptr->banlist) + rb_dlink_list_length(&chptr->exceptlist) + rb_dlink_list_length(&chptr->invexlist) + rb_dlink_list_length(&chptr->quietlist)) >= (chptr->mode.mode & ModuleModes.MODE_EXLIMIT ? ConfigChannel.max_bans_large : ConfigChannel.max_bans))
 +              if((rb_dlink_list_length(&chptr->banlist) + rb_dlink_list_length(&chptr->exceptlist) + rb_dlink_list_length(&chptr->invexlist) + rb_dlink_list_length(&chptr->quietlist)) >= (chptr->mode.mode & MODE_EXLIMIT ? ConfigChannel.max_bans_large : ConfigChannel.max_bans))
                {
                        sendto_one(source_p, form_str(ERR_BANLISTFULL),
                                   me.name, source_p->name, chptr->chname, realban);
        rb_dlinkAdd(actualBan, &actualBan->node, list);
  
        /* invalidate the can_send() cache */
 -      if(mode_type == CHFL_BAN || mode_type == ModuleModes.CHFL_QUIET || mode_type == CHFL_EXCEPTION)
 +      if(mode_type == CHFL_BAN || mode_type == CHFL_QUIET || mode_type == CHFL_EXCEPTION)
                chptr->bants++;
  
        return 1;
@@@ -287,7 -285,7 +288,7 @@@ del_id(struct Channel *chptr, const cha
                        free_ban(banptr);
  
                        /* invalidate the can_send() cache */
 -                      if(mode_type == CHFL_BAN || mode_type == ModuleModes.CHFL_QUIET || mode_type == CHFL_EXCEPTION)
 +                      if(mode_type == CHFL_BAN || mode_type == CHFL_QUIET || mode_type == CHFL_EXCEPTION)
                                chptr->bants++;
  
                        return 1;
@@@ -536,7 -534,7 +537,7 @@@ chm_simple(struct Client *source_p, str
        if((dir == MODE_ADD) && !(chptr->mode.mode & mode_type))
        {
                /* if +f is disabled, ignore an attempt to set +QF locally */
 -              if(!ModuleModes.MODE_FORWARD && MyClient(source_p) &&
 +              if(!ConfigChannel.use_forward && MyClient(source_p) &&
                   (c == 'Q' || c == 'F'))
                        return;
  
@@@ -751,18 -749,17 +752,18 @@@ chm_ban(struct Client *source_p, struc
        int mems;
        int override = 0;
  
 -      if(mode_type == CHFL_BAN)
 +      switch (mode_type)
        {
 +      case CHFL_BAN:
                list = &chptr->banlist;
                errorval = SM_ERR_RPL_B;
                rpl_list = RPL_BANLIST;
                rpl_endlist = RPL_ENDOFBANLIST;
                mems = ALL_MEMBERS;
                caps = 0;
 -      }
 -      else if(mode_type == CHFL_EXCEPTION)
 -      {
 +              break;
 +
 +      case CHFL_EXCEPTION:
                /* if +e is disabled, allow all but +e locally */
                if(!ConfigChannel.use_except && MyClient(source_p) &&
                   ((dir == MODE_ADD) && (parc > *parn)))
                        mems = ONLY_CHANOPS;
                else
                        mems = ONLY_SERVERS;
 -      }
 -      else if(mode_type == CHFL_INVEX)
 -      {
 +              break;
 +
 +      case CHFL_INVEX:
                /* if +I is disabled, allow all but +I locally */
                if(!ConfigChannel.use_invex && MyClient(source_p) &&
                   (dir == MODE_ADD) && (parc > *parn))
                        mems = ONLY_CHANOPS;
                else
                        mems = ONLY_SERVERS;
 -      }
 -      else if(mode_type == ModuleModes.CHFL_QUIET)
 -      {
 +              break;
 +
 +      case CHFL_QUIET:
                list = &chptr->quietlist;
                errorval = SM_ERR_RPL_Q;
                rpl_list = RPL_QUIETLIST;
                rpl_endlist = RPL_ENDOFQUIETLIST;
                mems = ALL_MEMBERS;
                caps = 0;
 -      }
 -      else
 -      {
 +              break;
 +
 +      default:
                sendto_realops_snomask(SNO_GENERAL, L_ALL, "chm_ban() called with unknown type!");
                return;
 +              break;
        }
  
        if(dir == 0 || parc <= *parn)
  
                /* non-ops cant see +eI lists.. */
                if(alevel != CHFL_CHANOP && alevel != CHFL_ADMIN && alevel != CHFL_HALFOP && mode_type != CHFL_BAN &&
 -                              mode_type != ModuleModes.CHFL_QUIET)
 +                              mode_type != CHFL_QUIET)
                {
                        if(IsOverride(source_p))
                        {
@@@ -1508,7 -1504,7 +1509,7 @@@ chm_forward(struct Client *source_p, st
        int override = 0;
  
        /* if +f is disabled, ignore local attempts to set it */
 -      if(!ModuleModes.MODE_FORWARD && MyClient(source_p) &&
 +      if(!ConfigChannel.use_forward && MyClient(source_p) &&
           (dir == MODE_ADD) && (parc > *parn))
                return;
  
                                           form_str(ERR_NOSUCHCHANNEL), forward);
                        return;
                }
 -              if(MyClient(source_p) && !(targptr->mode.mode & ModuleModes.MODE_FREETARGET))
 +              if(MyClient(source_p) && !(targptr->mode.mode & MODE_FREETARGET))
                {
                        if((msptr = find_channel_membership(targptr, source_p)) == NULL ||
 -                              !is_any_op(msptr))
 +                              is_any_op(msptr))
                        {
                                if(IsOverride(source_p))
                                        override = 1;
                mode_changes[mode_count].dir = MODE_ADD;
                mode_changes[mode_count].caps = 0;
                mode_changes[mode_count].nocaps = 0;
 -              mode_changes[mode_count].mems = ModuleModes.MODE_FORWARD ? ALL_MEMBERS : ONLY_SERVERS;
 +              mode_changes[mode_count].mems = ConfigChannel.use_forward ? ALL_MEMBERS : ONLY_SERVERS;
                mode_changes[mode_count].id = NULL;
                mode_changes[mode_count].override = override;
                mode_changes[mode_count++].arg = forward;
@@@ -1757,6 -1753,9 +1758,6 @@@ chm_regonly(struct Client *source_p, st
  }
  
  /* *INDENT-OFF* */
 -/* Only RFC and ircd-ratbox modes are held in this table
 - * All other modes are added via loading modules in shadowircd/modes
 - * Such is documented in the comments for each mode, ex: C - NOCTCP. */
  struct ChannelMode chmode_table[256] =
  {
    {chm_nosuch,  0 },                  /* 0x00 */
    {chm_nosuch,        0 },                    /* @ */
    {chm_nosuch,        0 },                    /* A */
    {chm_nosuch,        0 },                    /* B */
 -  {chm_nosuch,        0 },                    /* C - MODE_NOCTCP */
 -  {chm_nosuch,        0 },                    /* D - MODE_NOACTION */
 -  {chm_nosuch,        0 },                    /* E - MODE_NOKICK */
 -  {chm_nosuch,        0 },                    /* F - MODE_FREETARGET */
 -  {chm_nosuch,        0 },                    /* G - MODE_NOCAPS */
 +  {chm_simple,        MODE_NOCTCP },          /* C */
 +  {chm_simple,        MODE_NOACTION },        /* D */
 +  {chm_simple,        MODE_NOKICK },          /* E */
 +  {chm_simple,        MODE_FREETARGET },      /* F */
 +  {chm_simple,        MODE_NOCAPS },          /* G */
    {chm_nosuch,        0 },                    /* H */
    {chm_ban,   CHFL_INVEX },           /* I */
 -  {chm_nosuch,        0 },                    /* J - MODE_NOREJOIN */
 -  {chm_nosuch,        0 },                    /* K - MODE_NOREPEAT */
 -  {chm_nosuch,        0 },                    /* L - MODE_EXLIMIT */
 -  {chm_nosuch,        0 },                    /* M - MODE_NOOPERKICK */
 -  {chm_nosuch,        0 },                    /* N - MODE_NONICK */
 +  {chm_simple,        MODE_NOREJOIN },        /* J */
 +  {chm_simple,        MODE_NOREPEAT },        /* K */
 +  {chm_staff, MODE_EXLIMIT },         /* L */
 +  {chm_hidden,        MODE_NOOPERKICK },      /* M */
 +  {chm_simple,        MODE_NONICK },          /* N */
    {chm_nosuch,        0 },                    /* O */
 -  {chm_nosuch,        0 },                    /* P - MODE_PERMANENT */
 -  {chm_nosuch,        0 },                    /* Q - MODE_DISFORWARD */
 +  {chm_staff, MODE_PERMANENT },       /* P */
 +  {chm_simple,        MODE_DISFORWARD },      /* Q */
    {chm_nosuch,        0 },                    /* R */
    {chm_nosuch,        0 },                    /* S */
 -  {chm_nosuch,        0 },                    /* T - MODE_NONOTICE */
 +  {chm_simple,        MODE_NONOTICE },        /* T */
    {chm_nosuch,        0 },                    /* U */
    {chm_nosuch,        0 },                    /* V */
    {chm_nosuch,        0 },                    /* W */
    {chm_nosuch,        0 },
    {chm_admin, 0 },                    /* a */
    {chm_ban,   CHFL_BAN },             /* b */
 -  {chm_nosuch,        0 },                    /* c - MODE_NOCOLOR */
 +  {chm_simple,        MODE_NOCOLOR },         /* c */
    {chm_nosuch,        0 },                    /* d */
    {chm_ban,   CHFL_EXCEPTION },       /* e */
 -  {chm_nosuch,        0 },                    /* f - MODE_FORWARD */
 -  {chm_nosuch,        0 },                    /* g - MODE_FREEINVITE */
 +  {chm_forward,       0 },                    /* f */
 +  {chm_simple,        MODE_FREEINVITE },      /* g */
    {chm_halfop,        0 },                    /* h */
    {chm_simple,        MODE_INVITEONLY },      /* i */
 -  {chm_nosuch, 0 },                   /* j - MODE_THROTTLE */
 +  {chm_throttle, 0 },                 /* j */
    {chm_key,   0 },                    /* k */
    {chm_limit, 0 },                    /* l */
    {chm_simple,        MODE_MODERATED },       /* m */
    {chm_simple,        MODE_NOPRIVMSGS },      /* n */
    {chm_op,    0 },                    /* o */
    {chm_simple,        MODE_PRIVATE },         /* p */
 -  {chm_nosuch,        0 },                    /* q - CHFL_QUIET */
 -  {chm_nosuch,        0 },                    /* r - MODE_REGONLY */
 +  {chm_ban,   CHFL_QUIET },           /* q */
 +  {chm_regonly, MODE_REGONLY },               /* r */
    {chm_simple,        MODE_SECRET },          /* s */
    {chm_simple,        MODE_TOPICLIMIT },      /* t */
    {chm_nosuch,        0 },                    /* u */
    {chm_nosuch,        0 },                    /* w */
    {chm_nosuch,        0 },                    /* x */
    {chm_nosuch,        0 },                    /* y */
 -  {chm_nosuch,        0 },                    /* z - MODE_OPMODERATE */
 +  {chm_simple,        MODE_OPMODERATE },      /* z */
  
    {chm_nosuch,  0 },                  /* 0x7b */
    {chm_nosuch,  0 },                  /* 0x7c */
@@@ -2086,6 -2085,19 +2087,19 @@@ set_channel_mode(struct Client *client_
                        dir = MODE_QUERY;
                        break;
                default:
+                       /* If this mode char is locked, don't allow local users to change it. */
+                       if (MyClient(source_p) && chptr->mode_lock && strchr(chptr->mode_lock, c))
+                       {
+                               if (!(errors & SM_ERR_MLOCK))
+                                       sendto_one_numeric(source_p,
+                                                       ERR_MLOCKRESTRICTED,
+                                                       form_str(ERR_MLOCKRESTRICTED),
+                                                       chptr->chname,
+                                                       c,
+                                                       chptr->mode_lock);
+                               errors |= SM_ERR_MLOCK;
+                               continue;
+                       }
                        chmode_table[(unsigned char) c].set_func(fakesource_p, chptr, alevel,
                                       parc, &parn, parv,
                                       &errors, dir, c,
        if(MyClient(source_p) || rb_dlink_list_length(&serv_list) > 1)
                send_cap_mode_changes(client_p, source_p, chptr, mode_changes, mode_count);
  }
+ /* set_channel_mlock()
+  *
+  * inputs     - client, source, channel, params
+  * output     - 
+  * side effects - channel mlock is changed / MLOCK is propagated
+  */
+ void
+ set_channel_mlock(struct Client *client_p, struct Client *source_p,
+                 struct Channel *chptr, const char *newmlock, int propagate)
+ {
+       rb_free(chptr->mode_lock);
+       chptr->mode_lock = newmlock ? rb_strdup(newmlock) : NULL;
+       if (propagate)
+       {
+               sendto_server(client_p, NULL, CAP_TS6 | CAP_MLOCK, NOCAPS, ":%s MLOCK %ld %s :%s",
+                             source_p->id, (long) chptr->channelts, chptr->chname,
+                             chptr->mode_lock ? chptr->mode_lock : "");
+       }
+ }
diff --combined src/newconf.c
index a48f6397231dd999c592d30cc110b28b4bcc3fae,66671091acd7b405e412907a4a8ea64c835a029c..40ef4ec4320601a42e849fc1ccf0c2368566f8ff
@@@ -2201,6 -2201,7 +2201,7 @@@ static struct ConfEntry conf_general_ta
        { "anti_spam_exit_message_time", CF_TIME,  NULL, 0, &ConfigFileEntry.anti_spam_exit_message_time },
        { "use_part_messages",          CF_YESNO, NULL, 0, &ConfigFileEntry.use_part_messages   },
        { "disable_fake_channels",       CF_YESNO, NULL, 0, &ConfigFileEntry.disable_fake_channels },
+       { "hide_channel_below_users", CF_INT, NULL, 0, &ConfigFileEntry.hide_channel_below_users },
        { "min_nonwildcard_simple",      CF_INT,   NULL, 0, &ConfigFileEntry.min_nonwildcard_simple },
        { "non_redundant_klines",        CF_YESNO, NULL, 0, &ConfigFileEntry.non_redundant_klines },
        { "tkline_expire_notices",       CF_YESNO, NULL, 0, &ConfigFileEntry.tkline_expire_notices },
@@@ -2278,14 -2279,15 +2279,16 @@@ static struct ConfEntry conf_channel_ta
        { "only_ascii_channels", CF_YESNO, NULL, 0, &ConfigChannel.only_ascii_channels },
        { "cycle_host_change",  CF_YESNO, NULL, 0, &ConfigChannel.cycle_host_change },
        { "host_in_topic",      CF_YESNO, NULL, 0, &ConfigChannel.host_in_topic },
+       { "admin_on_channel_create", CF_YESNO, NULL, 0, &ConfigChannel.admin_on_channel_create },
        { "use_halfop",         CF_YESNO, NULL, 0, &ConfigChannel.use_halfop            },
        { "use_admin",          CF_YESNO, NULL, 0, &ConfigChannel.use_admin             },
        { "use_except",         CF_YESNO, NULL, 0, &ConfigChannel.use_except            },
        { "use_invex",          CF_YESNO, NULL, 0, &ConfigChannel.use_invex             },
        { "use_knock",          CF_YESNO, NULL, 0, &ConfigChannel.use_knock             },
 +      { "use_forward",        CF_YESNO, NULL, 0, &ConfigChannel.use_forward           },
        { "use_local_channels", CF_YESNO, NULL, 0, &ConfigChannel.use_local_channels    },
        { "resv_forcepart",     CF_YESNO, NULL, 0, &ConfigChannel.resv_forcepart        },
+       { "channel_target_change", CF_YESNO, NULL, 0, &ConfigChannel.channel_target_change  },
        { "exempt_cmode_c",     CF_YESNO, NULL, 0, &ConfigChannel.exempt_cmode_c        },
        { "exempt_cmode_C",     CF_YESNO, NULL, 0, &ConfigChannel.exempt_cmode_C        },
        { "exempt_cmode_D",     CF_YESNO, NULL, 0, &ConfigChannel.exempt_cmode_D        },
diff --combined src/s_conf.c
index 52e59b87bf0a7af22eabdf9c9ede6355e0ee76f4,514716711516ede159fbfaf48a301a4a549a765a..9d525abb0b91fdc3aae1d3b3f507de2f8c57ec1b
@@@ -698,6 -698,7 +698,7 @@@ set_default_conf(void
        ConfigFileEntry.failed_oper_notice = YES;
        ConfigFileEntry.anti_nick_flood = NO;
        ConfigFileEntry.disable_fake_channels = NO;
+       ConfigFileEntry.hide_channel_below_users = 3;
        ConfigFileEntry.max_nick_time = 20;
        ConfigFileEntry.max_nick_changes = 5;
        ConfigFileEntry.max_accept = 20;
  
        ConfigChannel.autochanmodes = rb_strdup("nt");
        ConfigChannel.exemptchanops = rb_strdup("");
+       ConfigChannel.admin_on_channel_create = NO;
        ConfigChannel.use_halfop = YES;
        ConfigChannel.use_admin = YES;
        ConfigChannel.use_except = YES;
        ConfigChannel.use_invex = YES;
        ConfigChannel.use_knock = YES;
 +      ConfigChannel.use_forward = YES;
        ConfigChannel.use_local_channels = YES;
        ConfigChannel.knock_delay = 300;
        ConfigChannel.knock_delay_channel = 60;
        ConfigChannel.no_join_on_split = NO;
        ConfigChannel.no_create_on_split = YES;
        ConfigChannel.resv_forcepart = YES;
+       ConfigChannel.channel_target_change = YES;
  
        ConfigChannel.exempt_cmode_c = NO;
        ConfigChannel.exempt_cmode_C = NO;
diff --combined src/s_user.c
index e6dc1ca60172d51773357d6fe0f9ff693f5a796c,051697cbf99c8434aef0e02fc1b77b81b96e20fc..aa8b17e5629c39df7e3449d7b71f62857d059622
@@@ -54,6 -54,8 +54,6 @@@
  #include "substitution.h"
  #include "chmode.h"
  
 -struct module_modes ModuleModes;
 -
  static void report_and_set_user_flags(struct Client *, struct ConfItem *);
  void user_welcome(struct Client *source_p);
  
@@@ -453,8 -455,8 +453,8 @@@ register_local_user(struct Client *clie
                        source_p->preClient->dnsbl_listed->hits++;
  
                        sendto_realops_snomask(SNO_REJ, L_ALL,
-                                       "%s [%s] is being disconnected due to being listed in DNS Blacklist %s",
-                                       source_p->name, source_p->sockhost, source_p->preClient->dnsbl_listed->host);
+                                       "%s (%s@%s) is being disconnected due to being listed in DNS Blacklist %s",
+                                       source_p->name, source_p->username, source_p->sockhost, source_p->preClient->dnsbl_listed->host);
  
                        add_reject(source_p, NULL, NULL);
                        exit_client(client_p, source_p, &me, "*** Banned (DNS blacklist)");
@@@ -913,7 -915,7 +913,7 @@@ static voi
  expire_umode_p(void *data)
  {
        struct Client *source_p = data;
-       char *parv[4] = {source_p->name, source_p->name, "-p", NULL};
+       const char *parv[4] = {source_p->name, source_p->name, "-p", NULL};
        source_p->localClient->override_timeout_event = NULL;
        user_mode(source_p, source_p, 3, parv);
  }
@@@ -1101,7 -1103,7 +1101,7 @@@ user_mode(struct Client *client_p, stru
                        }
                        /* FALLTHROUGH */
                default:
 -                      if (MyConnect(source_p) && *pm == 'Q' && !ModuleModes.MODE_FORWARD) {
 +                      if (MyConnect(source_p) && *pm == 'Q' && !ConfigChannel.use_forward) {
                                badflag = YES;
                                break;
                        }