]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
Merge tag 'v2.0.25' into master.
authorPeter Powell <petpow@saberuk.com>
Sun, 12 Nov 2017 17:33:21 +0000 (17:33 +0000)
committerPeter Powell <petpow@saberuk.com>
Sun, 12 Nov 2017 17:33:21 +0000 (17:33 +0000)
14 files changed:
1  2 
docs/conf/inspircd.conf.example
docs/conf/links.conf.example
docs/conf/modules.conf.example
docs/conf/modules/charybdis.conf.example
docs/conf/modules/unrealircd.conf.example
include/command_parse.h
modulemanager
src/coremods/core_oper/cmd_restart.cpp
src/modules/extra/m_mysql.cpp
src/modules/m_cap.cpp
src/modules/m_shun.cpp
src/modules/m_spanningtree/main.h
src/modules/m_timedbans.cpp
src/users.cpp

index b9fa6d3a91e3215c461b60b7d49bdf699acbf5cc,8ec6616bd5dd82d68afb8819286f5a5c4d2cd2a9..61d1a8c122194e5d5380eafddf550ace06cb06f2
  #                                                                      #
  ########################################################################
  
 +#-#-#-#-#-#-#-#-#-#  CONFIGURATION FORMAT  #-#-#-#-#-#-#-#-#-#-#-#-#-#-
 +#                                                                     #
 +# In order to maintain compatibility with older configuration files,  #
 +# you can change the configuration parser to parse as it did in       #
 +# previous releases. When using the "compat" format, you need to use  #
 +# C++ escape sequences (e.g. \n) instead of XML ones (e.g. &nl;) and  #
 +# can not use <define> to create macros.                              #
 +#<config format="compat">
 +
  #-#-#-#-#-#-#-#-#-#  INCLUDE CONFIGURATION  #-#-#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # This optional tag allows you to include another config file         #
  #                                                                     #
  # Variables may be redefined and may reference other variables.       #
  # Value expansion happens at the time the tag is read.                #
 -#                                                                     #
 -# Using variable definitions REQUIRES that the config format be       #
 -# changed to "xml" from the default "compat" that uses escape         #
 -# sequences such as "\"" and "\n", and does not support <define>      #
 -<config format="xml">
  <define name="bindip" value="1.2.2.3">
  <define name="localips" value="&bindip;/24">
  
@@@ -97,7 -93,8 +97,7 @@@
          #id="97K"
  
          # network: Network name given on connect to clients.
 -        # Should be the same on all servers on the network and
 -        # not contain spaces.
 +        # Should be the same on all servers on the network.
          network="Omega">
  
  
  #   |_| \_\___|\__,_|\__,_|   |_| |_| |_|_|___/ |____/|_|\__(_)       #
  #                                                                     #
  #  If you want to link servers to InspIRCd you must load the          #
 -#  m_spanningtree.so module! Please see the modules list for          #
 +#  spanningtree module! Please see the modules list for               #
  #  information on how to load this module! If you do not load this    #
  #  module, server ports will NOT work!                                #
  
        # to this bind section.
        type="clients"
  
 -      # ssl: If you want the port(s) in this bind tag to use SSL, set this
 -      # to either "gnutls" or "openssl". The appropriate SSL module must be
 -      # loaded for SSL to work. If you do not want the port(s) in this bind
 -      # tag to support SSL, just remove or comment out this option.
 +      # ssl: If you want the port(s) in this bind tag to use SSL, set this to
 +      # the name of a custom <sslprofile> tag that you have defined or one
 +      # of "openssl", "gnutls", "mbedtls" if you have not defined any. See the
 +      # wiki page for the SSL module you are using for more details.
 +      #
 +      # You will need to load the ssl_openssl module for OpenSSL, ssl_gnutls
 +      # for GnuTLS and ssl_mbedtls for mbedTLS.
        ssl="gnutls"
 +
 +      # defer: When this is non-zero, connections will not be handed over to
 +      # the daemon from the operating system before data is ready.
 +      # In Linux, the value indicates the time period we'll wait for a
 +      # connection to come up with data. Don't set it too low!
 +      # In BSD the value is ignored; only zero and non-zero is possible.
 +      # Windows ignores this parameter completely.
 +      # Note: This does not take effect on rehash.
 +      # To change it on a running bind, you'll have to comment it out,
 +      # rehash, comment it in and rehash again.
 +      defer="0"
 +
 +      # free: When this is enabled the listener will be created regardless of
 +      # whether the interface that provides the bind address is available. This
 +      # is useful for if you are starting InspIRCd on boot when the server may
 +      # not have brought the network interfaces up yet.
 +      free="no"
  >
  
  <bind address="" port="6660-6669" type="clients">
  
 -# When linking servers, the OpenSSL and GnuTLS implementations are completely
 -# link-compatible and can be used alongside each other
 -# on each end of the link without any significant issues.
 -# Supported SSL types are: "openssl" and "gnutls".
 -# You must load m_ssl_openssl for OpenSSL or m_ssl_gnutls for GnuTLS.
 +# Listener accepting HTML5 WebSocket connections.
 +# Requires the websocket module and SHA-1 hashing support (provided by the sha1
 +# module).
 +#<bind address="" port="7002" type="clients" hook="websocket">
 +
 +# You can define a custom <sslprofile> tag which defines the SSL configuration
 +# for this listener. See the wiki page for the SSL module you are using for
 +# more details.
 +#
 +# Alternatively, you can use one of the default SSL profiles which are created
 +# when you have not defined any:
 +#   "openssl" (requires the ssl_openssl module)
 +#   "gnutls" (requires the ssl_gnutls module)
 +#   "mbedtls" (requires the ssl_mbedtls module)
 +#
 +# When linking servers, the OpenSSL, GnuTLS, and mbedTLS implementations are
 +# completely link-compatible and can be used alongside each other on each end
 +# of the link without any significant issues.
  
  <bind address="" port="7000,7001" type="servers">
  <bind address="1.2.3.4" port="7005" type="servers" ssl="openssl">
  
  
 -#-#-#-#-#-#-#-#-#-#-  DIE/RESTART CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-
 -#                                                                     #
 -#   You can configure the passwords here which you wish to use for    #
 -#   the /DIE and /RESTART commands. Only trusted ircops who will      #
 -#   need this ability should know the die and restart password.       #
 -#                                                                     #
 -
 -<power
 -       # hash: what hash these passwords are hashed with.
 -       # Requires the module for selected hash (m_md5.so, m_sha256.so
 -       # or m_ripemd160.so) be loaded and the password hashing module
 -       # (m_password_hash.so) loaded.
 -       # Options here are: "md5", "sha256" and "ripemd160", or one of
 -       # these prefixed with "hmac-", e.g.: "hmac-sha256".
 -       # Optional, but recommended. Create hashed passwords with:
 -       # /mkpasswd <hash> <password>
 -       #hash="sha256"
 -
 -       # diepass: Password for opers to use if they need to shutdown (die)
 -       # a server.
 -       #
 -       # IMPORTANT: leaving this field empty does not disable the use of
 -       # the DIE command. In order to prevent the use of this command you
 -       # should remove it from the command privileges of your opers.
 -       diepass=""
 -
 -       # restartpass: Password for opers to use if they need to restart
 -       # a server.
 -       #
 -       # IMPORTANT: leaving this field empty does not disable the use of
 -       # the RESTART command. In order to prevent the use of this command
 -       # you should remove it from the command privileges of your opers.
 -       restartpass="">
 -
 -
  #-#-#-#-#-#-#-#-#-#-  CONNECTIONS CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  #   This is where you can configure which connections are allowed     #
           # allow: What IP addresses/hosts to allow for this block.
           allow="203.0.113.*"
  
 -         # hash: what hash this password is hashed with. requires the module
 -         # for selected hash (m_md5.so, m_sha256.so or m_ripemd160.so) be
 -         # loaded and the password hashing module (m_password_hash.so)
 -         # loaded. Options here are: "md5", "sha256" and "ripemd160".
 -         # Optional, but recommended. Create hashed passwords with:
 -         # /mkpasswd <hash> <password>
 -         #hash="sha256"
 +         # hash: the hash function this password is hashed with. Requires the
 +         # module for the selected function (bcrypt, md5, sha1, sha256, or
 +         # ripemd160) and the password hashing module (password_hash) to be
 +         # loaded.
 +         # You may also use any of the above other than bcrypt prefixed with
 +         # either "hmac-" or "pbkdf2-hmac-" (requires the pbkdf2 module).
 +         # Create hashed passwords with: /mkpasswd <hash> <password>
 +         #hash="bcrypt"
  
           # password: Password to use for this block/user(s)
           password="secret"
  
           # maxchans: Maximum number of channels a user in this class
 -         # be in at one time. This overrides every other maxchans setting.
 -         #maxchans="30"
 +         # be in at one time.
 +         maxchans="20"
  
 -         # timeout: How long (in seconds) the server will wait before
 -         # disconnecting a user if they do not do anything on connect.
 +         # timeout: How long the server will wait before disconnecting
 +         # a user if they do not do anything on connect.
           # (Note, this is a client-side thing, if the client does not
           # send /nick, /user or /pass)
           timeout="10"
           # maxconnwarn: Enable warnings when localmax or globalmax are reached (defaults to on)
           maxconnwarn="off"
  
 +         # resolvehostnames: If disabled, no DNS lookups will be performed on connecting users
 +         # in this class. This can save a lot of resources on very busy servers.
 +         resolvehostnames="yes"
 +
           # usednsbl: Defines whether or not users in this class are subject to DNSBL. Default is yes.
 -         # This setting only has effect when m_dnsbl is loaded.
 +         # This setting only has effect when the dnsbl module is loaded.
           #usednsbl="yes"
  
           # useident: Defines if users in this class MUST respond to a ident query or not.
           useident="no"
  
 +         # webirc: Restricts usage of this class to the specified WebIRC gateway.
 +         # This setting only has effect when the cgiirc module is loaded.
 +         #webirc="name"
 +
           # limit: How many users are allowed in this class
           limit="5000"
  
           # modes: Usermodes that are set on users in this block on connect.
 -         # Enabling this option requires that the m_conn_umodes module be loaded.
 +         # Enabling this option requires that the conn_umodes module be loaded.
           # This entry is highly recommended to use for/with IP Cloaking/masking.
 -         # For the example to work, this also requires that the m_cloaking
 +         # For the example to work, this also requires that the "cloaking"
           # module be loaded as well.
           modes="+x"
  
           # requireident, requiressl, requireaccount: require that users of this
           # block have a valid ident response, use SSL, or have authenticated.
 -         # Requires m_ident, m_sslinfo, or m_services_account respectively.
 +         # Requires ident, sslinfo, or the services_account module, respectively.
           requiressl="on"
           # NOTE: For requireaccount, you must complete the signon prior to full
           # connection. Currently, this is only possible by using SASL
           #   \017 or \x = Stop all color sequences
           allowmotdcolors="false"
  
 -         # port: What port this user is allowed to connect on. (optional)
 -         # The port MUST be set to listen in the bind blocks above.
 -         port="6697">
 +         # port: What port range this user is allowed to connect on. (optional)
 +         # The ports MUST be set to listen in the bind blocks above.
 +         port="6697,9999">
  
  <connect
           # name: Name to use for this connect block. Mainly used for
           allow="*"
  
           # maxchans: Maximum number of channels a user in this class
 -         # be in at one time. This overrides every other maxchans setting.
 -         #maxchans="30"
 +         # be in at one time.
 +         maxchans="20"
  
 -         # timeout: How long (in seconds) the server will wait before
 -         # disconnecting a user if they do not do anything on connect.
 +         # timeout: How long the server will wait before disconnecting
 +         # a user if they do not do anything on connect.
           # (Note, this is a client-side thing, if the client does not
           # send /nick, /user or /pass)
           timeout="10"
  
 -         # pingfreq: How often (in seconds) the server tries to ping connecting clients.
 -         pingfreq="120"
 +         # pingfreq: How often the server tries to ping connecting clients.
 +         pingfreq="2m"
  
           # hardsendq: maximum amount of data allowed in a client's send queue
           # before they are dropped. Keep this value higher than the length of
           # globalmax: Maximum global (network-wide) connections per IP.
           globalmax="3"
  
 +         # resolvehostnames: If disabled, no DNS lookups will be performed on connecting users
 +         # in this class. This can save a lot of resources on very busy servers.
 +         resolvehostnames="yes"
 +
           # useident: Defines if users in this class must respond to a ident query or not.
           useident="no"
  
           limit="5000"
  
           # modes: Usermodes that are set on users in this block on connect.
 -         # Enabling this option requires that the m_conn_umodes module be loaded.
 +         # Enabling this option requires that the conn_umodes module be loaded.
           # This entry is highly recommended to use for/with IP Cloaking/masking.
 -         # For the example to work, this also requires that the m_cloaking
 +         # For the example to work, this also requires that the cloaking
           # module be loaded as well.
           modes="+x">
  
  
  # This file has all the information about oper classes, types and o:lines.
  # You *MUST* edit it.
 -<include file="conf/examples/opers.conf.example">
 +<include file="examples/opers.conf.example">
  
  # This file has all the information about server links and ulined servers.
  # You *MUST* edit it if you intend to link servers.
 -<include file="conf/examples/links.conf.example">
 +<include file="examples/links.conf.example">
  
  #-#-#-#-#-#-#-#-#-#-  MISCELLANEOUS CONFIGURATION  -#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # Files block - contains files whose contents are used by the ircd
  #
  #   motd - displayed on connect and when a user executes /MOTD
 -#   rules - displayed when the user executes /RULES
  # Modules can also define their own files
 -<files motd="conf/examples/motd.txt.example" rules="conf/examples/rules.txt.example">
 +<files motd="examples/motd.txt.example">
  
  # Example of an executable file include. Note this will be read on rehash,
  # not when the command is run.
 -#<execfiles rules="wget -O - http://www.example.com/rules.txt">
 -
 -#-#-#-#-#-#-#-#-#-#-#-# MAXIMUM CHANNELS -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 -#                                                                     #
 -
 -<channels
 -          # users: Maximum number of channels a user can be in at once.
 -          users="20"
 -
 -          # opers: Maximum number of channels an oper can be in at once.
 -          opers="60">
 +#<execfiles motd="wget -O - http://www.example.com/motd.txt">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-# DNS SERVER -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # If these values are not defined, InspIRCd uses the default DNS resolver
       #
       # server="127.0.0.1"
  
 -     # timeout: seconds to wait to try to resolve DNS/hostname.
 +     # timeout: time to wait to try to resolve DNS/hostname.
       timeout="5">
  
  # An example of using an IPv6 nameserver
  # matches the channels name applies the banlimit to that channel.     #
  # It is advisable to put an entry with the channel as '*' at the      #
  # bottom of the list. If none are specified or no maxbans tag is      #
 -# matched, the banlist size defaults to 64 entries.                   #
 +# matched, the banlist size defaults to 100 entries.                  #
  #                                                                     #
  
 -<banlist chan="#largechan" limit="128">
 -<banlist chan="*" limit="69">
 +<banlist chan="#largechan" limit="200">
 +<banlist chan="*" limit="100">
  
  #-#-#-#-#-#-#-#-#-#-#-  DISABLED FEATURES  -#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
           # the correct parameters are.
           syntaxhints="no"
  
 -         # cyclehosts: If enabled, when a user gets a host set, it will cycle
 -         # them in all their channels. If not, it will simply change their host
 -         # without cycling them.
 -         cyclehosts="yes"
 +         # casemapping: This sets the case mapping method to be used by the
 +         # server. This MUST be the same on all servers. Possible values are:
 +         # "ascii" (recommended)
 +         # "rfc1459" (default, required for linking to 2.0 servers)
 +         # NOTE: if you are using the nationalchars module this setting will be
 +         # ignored. You should use <nationalchars:casemapping> instead.
 +         casemapping="ascii"
  
           # cyclehostsfromuser: If enabled, the source of the mode change for
           # cyclehosts will be the user who cycled. This can look nicer, but
           # triggers anti-takeover mechanisms of some obsolete bots.
           cyclehostsfromuser="no"
  
 -         # ircumsgprefix: Use undernet-style message prefixing for NOTICE and
 -         # PRIVMSG. If enabled, it will add users' prefix to the line, if not,
 -         # it will just message the user normally.
 -         ircumsgprefix="no"
 -
           # announcets: If set to yes, when the timestamp on a channel changes, all users
           # in the channel will be sent a NOTICE about it.
           announcets="yes"
           # in the topic. If set to no, it will only show the nick of the topic setter.
           hostintopic="yes"
  
 -         # pingwarning: If a server does not respond to a ping within x seconds,
 +         # pingwarning: If a server does not respond to a ping within this period,
           # it will send a notice to opers with snomask +l informing that the server
           # is about to ping timeout.
           pingwarning="15"
  
 -         # serverpingfreq: How often pings are sent between servers (in seconds).
 -         serverpingfreq="60"
 +         # serverpingfreq: How often pings are sent between servers.
 +         serverpingfreq="1m"
  
           # defaultmodes: What modes are set on a empty channel when a user
           # joins it and it is unregistered.
 -         defaultmodes="nt"
 +         defaultmodes="not"
  
 -         # moronbanner: This is the text that is sent to a user when they are
 +         # xlinemessage: This is the text that is sent to a user when they are
           # banned from the server.
 -         moronbanner="You're banned! Email abuse@example.com with the ERROR line below for help."
 +         xlinemessage="You're banned! Email irc@example.com with the ERROR line below for help."
  
           # exemptchanops: exemptions for channel access restrictions based on prefix.
           exemptchanops="nonick:v flood:o"
  
           # nosnoticestack: This prevents snotices from 'stacking' and giving you
           # the message saying '(last message repeated X times)'. Defaults to no.
 -         nosnoticestack="no"
 -
 -         # welcomenotice: When turned on, this sends a NOTICE to connecting users
 -         # with the text Welcome to <networkname>! after successful registration.
 -         # Defaults to yes.
 -         welcomenotice="yes">
 +         nosnoticestack="no">
  
  
  #-#-#-#-#-#-#-#-#-#-#-# PERFORMANCE CONFIGURATION #-#-#-#-#-#-#-#-#-#-#
               # in the accept queue. This is *NOT* the total maximum number of
               # connections per server. Some systems may only allow this to be up
               # to 5, while others (such as Linux and *BSD) default to 128.
 +             # Setting this above the limit imposed by your OS can have undesired
 +             # effects.
               somaxconn="128"
  
 -             # limitsomaxconn: By default, somaxconn (see above) is limited to a
 -             # safe maximum value in the 2.0 branch for compatibility reasons.
 -             # This setting can be used to disable this limit, forcing InspIRCd
 -             # to use the value specified above.
 -             limitsomaxconn="true"
 -
               # softlimit: This optional feature allows a defined softlimit for
               # connections. If defined, it sets a soft max connections value.
               softlimit="12800"
  
 +             # clonesonconnect: If this is set to false, we won't check for clones
 +             # on initial connection, but only after the DNS check is done.
 +             # This can be useful where your main class is more restrictive
 +             # than some other class a user can be assigned after DNS lookup is complete.
 +             # Turning this option off will make the server spend more time on users we may
 +             # potentially not want. Normally this should be neglible, though.
 +             # Default value is true
 +             clonesonconnect="true"
 +
               # quietbursts: When syncing or splitting from a network, a server
               # can generate a lot of connect and quit messages to opers with
               # +C and +Q snomasks. Setting this to yes squelches those messages,
               # which makes it easier for opers, but degrades the functionality of
               # bots like BOPM during netsplits.
 -             quietbursts="yes"
 -
 -             # nouserdns: If enabled, no DNS lookups will be performed on
 -             # connecting users. This can save a lot of resources on very busy servers.
 -             nouserdns="no">
 +             quietbursts="yes">
  
  #-#-#-#-#-#-#-#-#-#-#-# SECURITY CONFIGURATION  #-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  
  <security
 +          # allowcoreunload: If this value is set to yes, Opers will be able to
 +          # unload core modules (e.g. core_privmsg).
 +          allowcoreunload="no"
  
            # announceinvites: This option controls which members of the channel
            # receive an announcement when someone is INVITEd. Available values:
            #             higher ranked users. This is the recommended setting.
            announceinvites="dynamic"
  
 -          # hidemodes: If enabled, then the listmodes given will be hidden
 -          # from users below halfop. This is not recommended to be set on +b
 -          # as it may break some functionality in popular clients such as mIRC.
 -          hidemodes="eI"
 -
            # hideulines: If this value is set to yes, U-lined servers will
            # be hidden from non-opers in /links and /map.
            hideulines="no"
            # (Commands like /notice, /privmsg, /kick, etc)
            maxtargets="20"
  
 -          # customversion: Displays a custom string when a user /version's
 -          # the ircd. This may be set for security reasons or vanity reasons.
 +          # customversion: A custom message to be displayed in the comments field
 +          # of the VERSION command response. This does not hide the InspIRCd version.
            customversion=""
  
            # operspywhois: show opers (users/auspex) the +s channels a user is in. Values:
  
  <limits
          # maxnick: Maximum length of a nickname.
 -        maxnick="31"
 +        maxnick="30"
  
          # maxchan: Maximum length of a channel name.
          maxchan="64"
          maxmodes="20"
  
          # maxident: Maximum length of a ident/username.
 -        maxident="11"
 +        maxident="10"
 +
 +        # maxhost: Maximum length of a hostname.
 +        maxhost="64"
  
          # maxquit: Maximum length of a quit message.
          maxquit="255"
          # maxaway: Maximum length of an away message.
          maxaway="200">
  
 +#-#-#-#-#-#-#-#-#-#-#-#-# PATHS CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
 +#                                                                     #
 +# This configuration tag defines the location that InspIRCd stores    #
 +# various types of files such as configuration files, log files and   #
 +# modules. You will probably not need to change these from the values #
 +# set when InspIRCd was built unless you are using a binary package   #
 +# where you do not have the ability to set build time configuration.  #
 +#<path configdir="conf" datadir="data" logdir="logs" moduledir="modules">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Logging
  # Logging is covered with the <log> tag, which you may use to change
  # the behaviour of the logging of the IRCd.
  #
- # In InspIRCd as of 1.2, logging is pluggable and very extensible.
- # Different files can log the same thing, different 'types' of log can
- # go to different places, and modules can even extend the log tag
- # to do what they want.
- #
  # An example log tag would be:
 -#  <log method="file" type="OPER" level="default" target="logs/opers.log">
 +#  <log method="file" type="OPER" level="default" target="opers.log">
  # which would log all information on /oper (failed and successful) to
  # a file called opers.log.
  #
  #  - OPER - succesful and failed oper attempts
  #  - KILL - kill related messages
  #  - snomask - server notices (*all* snomasks will be logged)
 -#  - FILTER - messages related to filter matches (m_filter)
 +#  - FILTER - messages related to filter matches (filter module)
  #  - CONFIG - configuration related messages
  #  - COMMAND - die and restart messages, and messages related to unknown user types
  #  - SOCKET - socket engine informational/error messages
  #  - USERINPUT
  #  - USEROUTPUT
  #
 +# If your server is producing a high levels of log messages you can also set the
 +# flush="[positive number]" attribute to specify how many log messages should be
 +# buffered before flushing to disk. You should probably not specify this unless
 +# you are having problems.
 +#
  # The following log tag is highly default and uncustomised. It is recommended you
  # sort out your own log tags. This is just here so you get some output.
  
 -<log method="file" type="* -USERINPUT -USEROUTPUT" level="default" target="logs/ircd.log">
 +<log method="file" type="* -USERINPUT -USEROUTPUT" level="default" target="ircd.log">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-  WHOWAS OPTIONS   -#-#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
           nick="ChanServ"
  
           # reason: Reason to display on /nick.
 -         reason="Reserved For Services">
 -
 -<badnick nick="NickServ" reason="Reserved For Services">
 -<badnick nick="OperServ" reason="Reserved For Services">
 -<badnick nick="MemoServ" reason="Reserved For Services">
 +         reason="Reserved for a network service">
  
  <badhost
           # host: ident@hostname to ban.
  # provide almost all the features of InspIRCd. :)                     #
  #                                                                     #
  # The default does nothing -- we include it for simplicity for you.   #
 -<include file="conf/examples/modules.conf.example">
 +<include file="examples/modules.conf.example">
  
  # Here are some pre-built modules.conf files that closely match the
  # default configurations of some popular IRCd's. You still may want to
  # recommended that you make your own modules file based on modules.conf.example.
  
  # Settings similar to UnrealIRCd defaults.
 -#<include file="conf/examples/modules/unrealircd.conf.example">
 +#<include file="examples/modules/unrealircd.conf.example">
  
  # Settings similar to Charybdis IRCd defaults.
 -#<include file="conf/examples/modules/charybdis.conf.example">
 +#<include file="examples/modules/charybdis.conf.example">
  
 +#-#-#-#-#-#-#-#-#-#-#-# SERVICES CONFIGURATION  #-#-#-#-#-#-#-#-#-#-#-#
 +#                                                                     #
 +# If you use services you will probably want to include one of the    #
 +# following files which set up aliases, nick reservations and filter  #
 +# exemptions for services pseudoclients:                              #
 +#
 +# Anope users should uncomment this:
 +#<include file="examples/services/anope.conf.example">
 +#
 +# Atheme users should uncomment this:
 +#<include file="examples/services/atheme.conf.example">
 +#
 +# Users of other services should uncomment this:
 +#<include file="examples/services/generic.conf.example">
  
  #########################################################################
  #                                                                       #
  #                     - InspIRCd Development Team -                     #
- #                        http://www.inspircd.org                        #
+ #                        https://www.inspircd.org                       #
  #                                                                       #
  #########################################################################
index f3c0807f81a29412da7f7f64768e74b1fde929e5,ba18c5325205ff46150a2f80709f74e1c46f060d..e11c4fe32a9ed2875d888d786235be4162aa0d71
@@@ -10,7 -10,7 +10,7 @@@
  #   |_| \_\___|\__,_|\__,_|   |_| |_| |_|_|___/ |____/|_|\__(_)       #
  #                                                                     #
  #  If you want to link servers to InspIRCd you must load the          #
 -#  m_spanningtree.so module!                                          #
 +#  spanningtree module!                                               #
  #                                                                     #
  #                                                                     #
  
  
        # allowmask: Range of IP addresses to allow for this link.
        # Can be a CIDR (see example).
 -      allowmask="203.0.113.0/24"
 +      allowmask="203.0.113.0/24 127.0.0.0/8 2001:db8::/32"
  
        # timeout: If defined, this option defines how long the server
        # will wait to consider the connect attempt failed and try the
        # failover (see above).
 -      timeout="300"
 +      timeout="5m"
  
 -      # ssl: If defined, this states the SSL module that will be used when
 -      # making an outbound connection to the server. Options are: "openssl"
 -      # and "gnutls" (they are compatible with each other).
 +      # ssl: If defined, this states the SSL profile that will be used when
 +      # making an outbound connection to the server. Options are the name of an
 +      # <sslprofile> tag that you have defined or one of "openssl", "gnutls",
 +      # "mbedtls" if you have not defined any. See the wiki page for the SSL
 +      # module you are using for more details.
        #
 -      # You will need to load the m_ssl_openssl.so module for OpenSSL,
 -      # m_ssl_gnutls.so for GnuTLS. The server port that you connect to
 -      # must be capable of accepting this type of connection.
 +      # You will need to load the ssl_openssl module for OpenSSL, ssl_gnutls
 +      # for GnuTLS and ssl_mbedtls for mbedTLS. The server port that you
 +      # connect to must be capable of accepting this type of connection.
        ssl="gnutls"
  
        # fingerprint: If defined, this option will force servers to be
 -      # authenticated using SSL Fingerprints. See https://wiki.inspircd.org/SSL
 -      # for more information. This will require an SSL link for both inbound
 -      # and outbound connections.
 +      # authenticated using SSL certificate fingerprints. See
-       # http://wiki.inspircd.org/SSL for more information. This will
++      # https://wiki.inspircd.org/SSL for more information. This will
 +      # require an SSL link for both inbound and outbound connections.
        #fingerprint=""
  
        # bind: Local IP address to bind to.
@@@ -77,7 -75,7 +77,7 @@@
        ipaddr="penguin.example.org"
        port="7000"
        allowmask="203.0.113.0/24"
 -      timeout="300"
 +      timeout="5m"
        ssl="gnutls"
        bind="1.2.3.4"
        statshidden="no"
  # Simple autoconnect block. This enables automatic connection of a server
  # Recommended setup is to have leaves connect to the hub, and have no
  # automatic connections started by the hub.
 -<autoconnect period="300" server="hub.example.org">
 +<autoconnect period="10m" server="hub.example.org">
  
  # Failover autoconnect block. If you have multiple hubs, or want your network
  # to automatically link even if the hub is down, you can specify multiple
  # space separated servers to autoconnect; they will be tried in a round
  # robin fashion until one succeeds. Period defines the time for restarting
  # a single loop.
 -<autoconnect period="120"
 +<autoconnect period="2m"
        server="hub.us.example.org hub.eu.example.org leaf.eu.example.org">
  
  
index 5382eb354ca9bc4377389eb2250c3cd1a189ecba,b672367f967e93bc85906f093e55361ad548a251..cf79dafce3b538f5fd8d9a667e5bfaa7b6cb7739
@@@ -10,7 -10,7 +10,7 @@@
  #                                                                     #
  #  By default, ALL modules are commented out. You must uncomment them #
  #  or add lines to your config to load modules. Please refer to       #
- #  http://wiki.inspircd.org/Modules for a list of modules and         #
 -#  https://wiki.inspircd.org/2.0/Modules for a list of modules and    #
++#  https://wiki.inspircd.org/3.0/Modules for a list of modules and    #
  #  each modules link for any additional conf tags they require.       #
  #                                                                     #
  #    ____                _   _____ _     _       ____  _ _   _        #
@@@ -19,8 -19,8 +19,8 @@@
  #   |  _ <  __/ (_| | (_| |   | | | | | | \__ \ | |_) | | |_|_|       #
  #   |_| \_\___|\__,_|\__,_|   |_| |_| |_|_|___/ |____/|_|\__(_)       #
  #                                                                     #
 -# To link servers to InspIRCd, you MUST load the m_spanningtree       #
 -# module. If you don't do this, server links will NOT work at all.    #
 +# To link servers to InspIRCd, you MUST load the spanningtree module. #
 +# If you don't do this, server links will NOT work at all.            #
  # This is by design, to allow for the implementation of other linking #
  # protocols in modules in the future. This module is at the bottom of #
  # this file.                                                          #
  # cryptographic uses and security.
  #
  # IMPORTANT:
 -# Other modules such as m_cloaking.so and m_password_hash.so may rely on
 +# Other modules such as cloaking and password_hash may rely on
  # this module being loaded to function.
  #
 -#<module name="m_md5.so">
 +#<module name="md5">
  #
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SHA256 module: Allows other modules to generate SHA256 hashes,
  # usually for cryptographic uses and security.
  #
  # IMPORTANT:
 -# Other modules such as m_password_hash.so may rely on this module being
 -# loaded to function. Certain modules such as m_spanningtree.so will
 +# Other modules such as password_hash may rely on this module being
 +# loaded to function. Certain modules such as spanningtree will
  # function without this module but when it is loaded their features will
  # be enhanced (for example the addition of HMAC authentication).
  #
 -#<module name="m_sha256.so">
 +#<module name="sha256">
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # RIPEMD160 module: Allows other modules to generate RIPEMD160 hashes,
  # usually for cryptographic uses and security.
  #
  # IMPORTANT:
  # Other modules may rely on this module being loaded to function.
 -#<module name="m_ripemd160.so">
 +#<module name="ripemd160">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Abbreviation module: Provides the ability to abbreviate commands a-la
  # BBC BASIC keywords.
 -#<module name="m_abbreviation.so">
 +#<module name="abbreviation">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Alias module: Allows you to define server-side command aliases.
 -#<module name="m_alias.so">
 +#<module name="alias">
  #
  # Set the 'prefix' for in-channel aliases (fantasy commands) to the
  # specified character. If not set, the default is "!".
@@@ -72,9 -72,9 +72,9 @@@
  #
  #-#-#-#-#-#-#-#-#-#-#-  ALIAS DEFINITIONS  -#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# If you have the m_alias.so module loaded, you may also define       #
 -# aliases as shown below. They are commonly used to provide shortcut  #
 -# commands to services, however they are not limited to just this use.#
 +# If you have the alias module loaded, you may also define aliases as #
 +# shown below. They are commonly used to provide shortcut commands to #
 +# services, however they are not limited to just this use.            #
  # An alias tag requires the following values to be defined in it:     #
  #                                                                     #
  # text        -      The text to detect as the actual command line.   #
  #                    If a non-oper attempts to use the alias, it will #
  #                    appear to not exist.                             #
  #                                                                     #
 -#<alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
 -#<alias text="CHANSERV" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
 -#<alias text="OPERSERV" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
 -#<alias text="BOTSERV" replace="PRIVMSG BotServ :$2-" requires="BotServ" uline="yes">
 -#<alias text="HOSTSERV" replace="PRIVMSG HostServ :$2-" requires="HostServ" uline="yes">
 -#<alias text="MEMOSERV" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes">
 -#<alias text="NS" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
 -#<alias text="CS" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
 -#<alias text="OS" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
 -#<alias text="BS" replace="PRIVMSG BotServ :$2-" requires="BotServ" uline="yes">
 -#<alias text="HS" replace="PRIVMSG HostServ :$2-" requires="HostServ" uline="yes">
 -#<alias text="MS" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes">
  #
  # An example of using the format value to create an alias with two
  # different behaviours depending on the format of the parameters.
  #
  # This alias fixes a glitch in xchat 2.6.x and above and the way it
  # assumes IDENTIFY must be prefixed by a colon (:) character. It should
 -# be placed ABOVE the default NICKSERV alias (the first example) listed
 -# above.
 +# be placed ABOVE the default NICKSERV alias.
  #
  #<alias text="NICKSERV" format=":IDENTIFY *" replace="PRIVMSG NickServ :IDENTIFY $3-"
  #  requires="NickServ" uline="yes">
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Allowinvite module: Gives channel mode +A to allow all users to use
  # /INVITE, and extban A to deny invite from specific masks.
 -#<module name="m_allowinvite.so">
 +#<module name="allowinvite">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Alltime module: Shows time on all connected servers at once.
  # This module is oper-only and provides /ALLTIME.
  # To use, ALLTIME must be in one of your oper class blocks.
 -#<module name="m_alltime.so">
 +#<module name="alltime">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Auditorium module: Adds channel mode +u which makes everyone else
  # except you in the channel invisible, used for large meetings etc.
 -#<module name="m_auditorium.so">
 +#<module name="auditorium">
  #
  # Auditorium settings:
  #
  # Another useful combination is with SSL client certificate
  # fingerprints: +w h:z:72db600734bb9546c1bdd02377bc21d2a9690d48 will
  # give halfop to the user(s) having the given certificate.
 -#<module name="m_autoop.so">
 +#<module name="autoop">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Ban except module: Adds support for channel ban exceptions (+e).
 -#<module name="m_banexception.so">
 +#<module name="banexception">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Ban redirection module: Allows bans which redirect to a specified
  # channel. e.g. +b nick!ident@host#channelbanneduserissentto
 -#<module name="m_banredirect.so">
 +#<module name="banredirect">
 +
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# bcrypt module: Allows other modules to generate bcrypt hashes,
 +# usually for cryptographic uses and security.
 +#<module name="bcrypt">
 +#
 +# rounds: Defines how many rounds the bcrypt function will run when
 +# generating new hashes.
 +#<bcrypt rounds="10">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Block amsg module: Attempt to block all usage of /amsg and /ame.
 -#<module name="m_blockamsg.so">
 +#<module name="blockamsg">
  #
  #-#-#-#-#-#-#-#-#-#-#-  BLOCKAMSG CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# If you have the m_blockamsg.so module loaded, you can configure it  #
 -# with the <blockamsg> tag:                                           #
 +# If you have the blockamsg module loaded, you can configure it with  #
 +# the <blockamsg> tag:                                                #
  #                                                                     #
 -# delay          -   How many seconds between two messages to force   #
 -#                    them to be recognised as unrelated.              #
 +# delay          -   How much time between two messages to force them #
 +#                    to be recognised as unrelated.                   #
  # action         -   Any of 'notice', 'noticeopers', 'silent', 'kill' #
  #                    or 'killopers'. Define how to take action when   #
  #                    a user uses /amsg or /ame.                       #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Block CAPS module: Adds channel mode +B, blocks all-CAPS messages.
 -#<module name="m_blockcaps.so">
 +#<module name="blockcaps">
  #
  #-#-#-#-#-#-#-#-#-#-#-  BLOCKCAPS CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# percent        - How many percent of text must be caps before text  #
 -#                  will be blocked.                                   #
 +# percent        - What percentage of the text must be caps before    #
 +#                  text will be blocked.                              #
  #                                                                     #
  # minlen         - The minimum length a line must be for the block    #
  #                  percent to have any effect.                        #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Block color module: Blocking color-coded messages with chan mode +c.
 -#<module name="m_blockcolor.so">
 +#<module name="blockcolor">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Botmode module: Adds the user mode +B. If set on a user, it will
  # show that the user is a bot in /WHOIS.
 -#<module name="m_botmode.so">
 +#<module name="botmode">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # CallerID module: Adds usermode +g which activates hybrid-style
  # callerid: block all private messages unless you /ACCEPT first.
 -#<module name="m_callerid.so">
 +#<module name="callerid">
  #
  #-#-#-#-#-#-#-#-#-#-#- CALLERID  CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
  # maxaccepts     - Maximum number of entries a user can add to his    #
  # tracknick      - Preserve /accept entries when a user changes nick? #
  #                  If no (the default), the user is removed from      #
  #                  everyone's accept list if he changes nickname.     #
 -# cooldown       - Amount of time (in seconds) that must pass since   #
 -#                  the last notification sent to a user before he can #
 -#                  be sent another. Default is 60 (1 minute).         #
 +# cooldown       - Amount of time that must pass since the last       #
 +#                  notification sent to a user before he can be sent  #
 +#                  another. Default is 1 minute.                      #
  #<callerid maxaccepts="16"
  #          operoverride="no"
  #          tracknick="no"
 -#          cooldown="60">
 +#          cooldown="1m">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # CAP module: Provides the CAP negotiation mechanism required by the
 -# m_sasl, m_namesx, m_uhnames, and m_ircv3 modules.
 -# It is also recommended for the STARTTLS support in m_ssl_gnutls.
 -#<module name="m_cap.so">
 +# sasl, namesx, uhnames, and ircv3 modules.
 +# It is also recommended for STARTTLS support in the starttls module.
 +#<module name="cap">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # CBAN module: Lets you disallow channels from being used at runtime.
  # This module is oper-only and provides /CBAN.
  # To use, CBAN must be in one of your oper class blocks.
 -#<module name="m_cban.so">
 +#<module name="cban">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Censor module: Adds channel and user mode +G.
 -#<module name="m_censor.so">
 +#<module name="censor">
  #
  #-#-#-#-#-#-#-#-#-#-#-  CENSOR  CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# Optional - If you specify to use the m_censor module, then you must #
 +# Optional - If you specify to use the censor module, then you must   #
  # specify some censor tags. See also:                                 #
- # http://wiki.inspircd.org/Modules/censor                             #
 -# https://wiki.inspircd.org/Modules/2.0/censor                        #
++# https://wiki.inspircd.org/Modules/3.0/censor                        #
  #
 -#<include file="conf/examples/censor.conf.example">
 +#<include file="examples/censor.conf.example">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 -# CGI:IRC module: Adds support for automatic host changing in CGI:IRC
 -# (http://cgiirc.sourceforge.net).
 -#<module name="m_cgiirc.so">
 +# CGI:IRC module: Enables forwarding the real IP address of a user from
 +# a gateway to the IRC server.
 +#<module name="cgiirc">
  #
  #-#-#-#-#-#-#-#-#-#-#-# CGIIRC  CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
  #
 -# Optional - If you specify to use m_cgiirc, then you must specify one
 -# or more cgihost tags which indicate authorised CGI:IRC servers which
 -# will be connecting to your network, and an optional cgiirc tag.
 -# For more information see: https://wiki.inspircd.org/Modules/2.0/cgiirc
 -#
 -# Set to yes if you want to notice opers when CGI:IRC clients connect.
 +# If you use the cgiirc module then you must specify the gateways which
 +# are authorised to forward IP/host information to your server. There
 +# are currently two ways to do this:
 +#
 +# The webirc method is the recommended way to allow gateways to forward
 +# IP/host information. When using this method the gateway sends a WEBIRC
 +# message to the server on connection. For more details please read the
 +# IRCv3 WebIRC specification at http://ircv3.net/specs/extensions/webirc.html.
 +#
 +# When using this method you must specify a wildcard mask or CIDR range
 +# to allow gateway connections from and at least one of either a SSL
 +# client certificate fingerprint for the gateway or a password to be
 +# sent in the WEBIRC command.
 +#
 +# <cgihost type="webirc"
 +#          fingerprint="bd90547b59c1942b85f382bc059318f4c6ca54c5"
 +#          mask="192.0.2.0/24">
 +# <cgihost type="webirc"
 +#          password="$2a$10$WEUpX9GweJiEF1WxBDSkeODBstIBMlVPweQTG9cKM8/Vd58BeM5cW"
 +#          hash="bcrypt"
 +#          mask="*.webirc.gateway.example.com">
 +#
 +# Alternatively if your gateway does not support sending the WEBIRC
 +# message then you can configure InspIRCd to look for the client IP
 +# address in the ident sent by the user. This is not recommended as it
 +# only works with IPv4 connections.
 +#
 +# When using this method you must specify a wildcard mask or CIDR range to
 +# allow gateway connections from.
 +#
 +# <cgihost type="ident" mask="198.51.100.0/24">
 +# <cgihost type="ident" mask="*.ident.gateway.example.com">
 +#
 +# By default gateway connections are logged to the +w snomask. If you
 +# do not want this to happen then you can uncomment this to disable it.
  # <cgiirc opernotice="no">
 -#
 -# The type field indicates where the module should get the real
 -# client's IP address from, for further information, please see the
 -# CGI:IRC documentation.
 -#
 -# Old style:
 -# <cgihost type="pass" mask="www.example.com">       # Get IP from PASS
 -# <cgihost type="ident" mask="otherbox.example.com"> # Get IP from ident
 -# <cgihost type="passfirst" mask="www.example.com">  # See the docs
 -# New style:
 -# <cgihost type="webirc" password="foobar"
 -#   mask="somebox.example.com">                      # Get IP from WEBIRC
 -#
 +
  # IMPORTANT NOTE:
  # ---------------
  #
 -# When you connect CGI:IRC clients, there are two connect classes which
 +# When you connect gateway clients, there are two connect classes which
  # apply to these clients. When the client initially connects, the connect
 -# class which matches the CGI:IRC site's host is checked. Therefore you
 -# must raise the maximum local/global clients for this ip as high as you
 -# want to allow cgi clients. After the client has connected and is
 -# determined to be a cgi:irc client, the class which matches the client's
 +# class which matches the gateway site's host is checked. Therefore you
 +# must raise the maximum local/global clients for this IP as high as you
 +# want to allow gateway clients. After the client has connected and is
 +# determined to be a gateway client, the class which matches the client's
  # real IP is then checked. You may set this class to a lower value, so that
  # the real IP of the client can still be restricted to, for example, 3
  # sessions maximum.
  # Channel create module: Adds snomask +j, which will notify opers of
  # any new channels that are created.
  # This module is oper-only.
 -#<module name="m_chancreate.so">
 +#<module name="chancreate">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Channel filter module: Allows channel-op defined message filtering
  # using simple string matches (channel mode +g).
 -#<module name="m_chanfilter.so">
 +#<module name="chanfilter">
  #
  # If hidemask is set to yes, the user will not be shown the mask when
  # his/her message is blocked.
  # joining a channel with +H 'X:T' set; 'T' is the maximum time to keep
  # lines in the history buffer. Designed so that the new user knows what
  # the current topic of conversation is when joining the channel.
 -#<module name="m_chanhistory.so">
 +#<module name="chanhistory">
  #
  # Set the maximum number of lines allowed to be stored per channel below.
  # This is the hard limit for 'X'.
  # If notice is set to yes, joining users will get a NOTICE before playback
  # telling them about the following lines being the pre-join history.
 -#<chanhistory maxlines="20" notice="yes">
 +# If bots is set to yes, it will also send to users marked with +B
 +#<chanhistory maxlines="20" notice="yes" bots="yes">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Channel logging module: Used to send snotice output to channels, to
  # The "channel" field is where you want the messages to go, "snomasks"
  # is what snomasks you want to be sent to that channel. Multiple tags
  # are allowed.
 -#<module name="m_chanlog.so">
 +#<module name="chanlog">
  #<chanlog snomasks="AOcC" channel="#opers">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # characters in the channel name such as bold, colorcodes, etc. which
  # can be quite annoying and allow users to on occasion have a channel
  # that looks like the name of another channel on the network.
 -#<module name="m_channames.so">
 +#<module name="channames">
  
  <channames
        # denyrange: characters or range of characters to deny in channel
  # Note that by default wildcard characters * and ? are allowed in
  # channel names. To disallow them, load m_channames and add characters
  # 42 and 63 to denyrange (see above).
 -#<module name="m_channelban.so">
 -
 -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 -# Chanprotect module: Gives +q and +a channel modes.
 -#
 -# IMPORTANT: This module has been removed in the next major version of
 -# InspIRCd. You should use m_customprefix instead.
 -#<module name="m_chanprotect.so">
 -
 -<chanprotect
 -      # noservices: With this set to yes, when a user joins an empty channel,
 -      # the server will set +q on them. If set to no, it will only set +o
 -      # on them until they register the channel.
 -      noservices="no"
 -
 -      # qprefix: Prefix (symbol) to use for +q users.
 -      qprefix="~"
 -
 -      # aprefix: Prefix (symbol) to use for +a users.
 -      aprefix="&amp;"
 -
 -      # deprotectself: If this value is set (true, yes or 1), it will allow
 -      # +a and +q users to remove the +a and +q from themselves, otherwise,
 -      # the status will have to be removed by services.
 -      deprotectself="yes"
 -
 -      # deprotectothers: If this value is set to yes, true, or 1, then any
 -      # user with +q or +a may remove the +q or +a from other users.
 -      deprotectothers="yes">
 -
 +#<module name="channelban">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Check module: Adds the /CHECK command.
  # IP addresses and hosts.
  # This module is oper-only.
  # To use, CHECK must be in one of your oper class blocks.
 -#<module name="m_check.so">
 +#<module name="check">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # CHGHOST module: Adds the /CHGHOST command.
  # NOTE: Services will not be able to set vhosts on users if this module
  # isn't loaded. If you're planning on running services, you probably
  # want to load this.
 -#<module name="m_chghost.so">
 +#<module name="chghost">
  #
  #-#-#-#-#-#-#-#-# /CHGHOST - /SETHOST  CONFIGURATION #-#-#-#-#-#-#-#-#
  # Optional - If you want to use special chars for hostnames you can  #
  # CHGIDENT module: Adds the /CHGIDENT command.
  # This module is oper-only.
  # To use, CHGIDENT must be in one of your oper class blocks.
 -#<module name="m_chgident.so">
 +#<module name="chgident">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # CHGNAME module: Adds the /CHGNAME command.
  # This module is oper-only.
  # To use, CHGNAME must be in one of your oper class blocks.
 -#<module name="m_chgname.so">
 +#<module name="chgname">
 +#
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# Connection class ban module: Adds support for extban 'n' which 
 +# matches against the class name of the user's connection.
 +# This module assumes that connection classes are named in a uniform
 +# way on all servers of the network.
 +#<module name="classban">
 +
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# Clear chan module: Allows opers to masskick, masskill or mass-G/ZLine
 +# all users on a channel using /CLEARCHAN.
 +#<module name="clearchan">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Cloaking module: Adds usermode +x and cloaking support.
 -# Relies on the module m_md5.so being loaded.
 -# To cloak users when they connect, load m_conn_umodes and set
 +# Relies on the md5 module being loaded.
 +# To cloak users when they connect, load the conn_umodes module and set
  # <connect:modes> to include the +x mode. The example <connect> tag
 -# shows this. See the m_conn_umodes module for more information.
 -#<module name="m_cloaking.so">
 +# shows this. See the conn_umodes module for more information.
 +#<module name="cloaking">
  #
  #-#-#-#-#-#-#-#-#-#-#- CLOAKING  CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# To use m_cloaking, you must define a cloak key, and optionally a    #
 +# To use cloaking, you must define a cloak key, and optionally a      #
  # cloak prefix as shown below. The cloak key must be shared across    #
  # the network for correct cloaking.                                   #
  #                                                                     #
 -# There are four methods of cloaking:                                 #
 +# There are two methods of cloaking:                                  #
  #                                                                     #
 -#   half           Cloak only the "unique" portion of a host; show    #
 -#                  the last 2 parts of the domain, /16 subnet of IPv4 #
 -#                  or /48 subnet of the IPv6 address.                 #
 +#   half           Cloak only the "unique" portion of a host; by      #
 +#                  default show the last 2 parts of the domain,       #
 +#                  /16 subnet of IPv4 or /48 subnet of the IPv6       #
 +#                  address.                                           #
 +#                  To change the number of shown parts, modify the    #
 +#                  domainparts option.                                #
  #                                                                     #
  #   full           Cloak the users completely, using three slices for #
  #                  common CIDR bans (IPv4: /16, /24; IPv6: /48, /64). #
  #                                                                     #
 -# These methods use a single key that can be any length of text.      #
 +# The methods use a single key that can be any length of text.        #
  # An optional prefix may be specified to mark cloaked hosts.          #
 -#                                                                     #
 -# The following methods are maintained for backwards compatibility;   #
 -# they are slightly less secure, and always hide unresolved IPs.      #
 -#                                                                     #
 -#   compat-host    InspIRCd 1.2-compatible host-based cloaking.       #
 -#   compat-ip      InspIRCd 1.2-compatible ip-always cloaking.        #
 -#                                                                     #
 -# If you use a compat cloaking mode then you must specify key1, key2, #
 -# key3, key4; the values must be less than 0x80000000 and should be   #
 -# picked at random. Prefix is mandatory, will default to network name #
 -# if not specified, and will always have a "-" appended.              #
 -#                                                                     #
 -# IMPORTANT: The compat-host and compat-ip modes have been removed in #
 -# the next major version of InspIRCd. You should ONLY use them if you #
 -# need backwards compatibility with InspIRCd 1.2.                     #
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #
  #<cloak mode="half"
  #       key="secret"
 +#       domainparts="3"
  #       prefix="net-">
  
  #-#-#-#-#-#-#-#-#-#-#-#- CLOSE MODULE #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Close module: Allows an oper to close all unregistered connections.
  # This module is oper-only and provides the /CLOSE command.
  # To use, CLOSE must be in one of your oper class blocks.
 -#<module name="m_close.so">
 +#<module name="close">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Clones module: Adds an oper command /CLONES for detecting cloned
  # issued, use with care.
  # This module is oper-only.
  # To use, CLONES must be in one of your oper class blocks.
 -#<module name="m_clones.so">
 +#<module name="clones">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Common channels module: Adds user mode +c, which, when set, requires
  # that users must share a common channel with you to PRIVMSG or NOTICE
  # you.
 -#<module name="m_commonchans.so">
 +#<module name="commonchans">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Auto join on connect module: Allows you to force users to join one
 -# or more channels automatically upon connecting to the server.
 -#<module name="m_conn_join.so">
 +# or more channels automatically upon connecting to the server, or
 +# join them in case they aren't on any channels after being online
 +# for X seconds.
 +#<module name="conn_join">
  #
  #-#-#-#-#-#-#-#-#-#-#-#- CONNJOIN CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
  #
 -# If you have m_conn_join.so loaded, you can configure it using the
 -# following values, or set autojoin="#chat,#help" in <connect> blocks.
 +# If you have the conn_join module loaded, you can configure it below
 +# or set autojoin="#chat,#help" in <connect> blocks.
  #
 +# Join users immediately after connection to #one #two and #three.
  #<autojoin channel="#one,#two,#three">
 +# Join users to #chat after 15 seconds if they aren't on any channels.
 +#<autojoin channel="#chat" delay="15">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Set modes on connect module: When this module is loaded <connect>
  # blocks may have an optional modes="" value, which contains modes to
  # add or remove from users when they connect to the server.
 -#<module name="m_conn_umodes.so">
 +#<module name="conn_umodes">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Wait for PONG on connect module: Send a PING to all connecting users
  # and don't let them connect until they reply with a PONG.
  # This is useful to stop certain kinds of bots and proxies.
 -#<module name="m_conn_waitpong.so">
 +#<module name="conn_waitpong">
  #
  #-#-#-#-#-#-#-#-#-#-#-   WAITPONG CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# If you have the m_conn_waitpong.so module loaded, configure it with #
 -# the <waitpong> tag:                                                 #
 +# If you have the conn_waitpong module loaded, configure it with the  #
 +# <waitpong> tag:                                                     #
  #                                                                     #
  # sendsnotice    -   Whether to send a helpful notice to users on     #
  #                    connect telling them how to connect, should      #
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Channel cycle module: Adds the /CYCLE command which is a server-side
  # /HOP that bypasses restrictive modes.
 -#<module name="m_cycle.so">
 +#<module name="cycle">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Connectban: Provides IP connection throttling. Any IP range that
  # connects too many times (configurable) in an hour is Z-Lined for a
  # (configurable) duration, and their count resets to 0.
 -#<module name="m_connectban.so">
 +#<module name="connectban">
  #
  # ipv4cidr and ipv6cidr allow you to turn the comparison from
  # individual IP addresses (32 and 128 bits) into CIDR masks, to allow
  #
  # This allows for 10 connections in an hour with a 10 minute ban if
  # that is exceeded.
 -#<connectban threshold="10" duration="10m" ipv4cidr="32" ipv6cidr="128">
 +#<connectban threshold="10" duration="10m" ipv4cidr="32" ipv6cidr="128"
 +# A custom ban message may optionally be specified.
 +# banmessage="Your IP range has been attempting to connect too many times in too short a duration. Wait a while, and you will be able to connect.">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Connection throttle module.
 -#<module name="m_connflood.so">
 +#<module name="connflood">
  #
  #-#-#-#-#-#-#-#-#-#-#- CONNTHROTTLE CONFIGURATION  -#-#-#-#-#-#-#-#-#-#
 -#  seconds, maxconns -  Amount of connections per <seconds>.
 +#  period, maxconns -  Amount of connections per <period>.
  #
  #  timeout           -  Time to wait after the throttle was activated
  #                       before deactivating it. Be aware that the time
  #   quitmsg="Throttled" bootwait="10">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 -# Custom prefixes: Allows for channel prefixes to be added.
 -# This replaces m_chanprotect and m_halfop.
 -#<module name="m_customprefix.so">
 +# Custom prefixes: Allows for channel prefixes to be configured.
 +#<module name="customprefix">
  #
  # name       The name of the mode, must be unique from other modes.
  # letter     The letter used for this mode. Required.
  # rank       A numeric rank for this prefix, defining what permissions it gives.
  #            The rank of voice, halfop and op is 10000, 20000, and 30000,
  #            respectively.
 -# ranktoset  The numeric rank required to set/unset this mode. Defaults to rank.
 +# ranktoset  The numeric rank required to set this mode. Defaults to rank.
 +# ranktounset The numeric rank required to unset this mode. Defaults to ranktoset.
  # depriv     Can you remove the mode from yourself? Defaults to yes.
  #<customprefix name="founder" letter="q" prefix="~" rank="50000" ranktoset="50000">
  #<customprefix name="admin" letter="a" prefix="&amp;" rank="40000" ranktoset="50000">
  #<customprefix name="halfop" letter="h" prefix="%" rank="20000" ranktoset="30000">
 -#<customprefix name="halfvoice" letter="V" prefix="-" rank="1" ranktoset="20000">
  #
 -# Do /RELOADMODULE m_customprefix.so after changing the settings of this module.
 +# You can also override the configuration of prefix modes added by both the core
 +# and other modules by adding a customprefix tag with change="yes" specified.
 +# <customprefix name="op" change="yes" rank="30000" ranktoset="30000"">
 +# <customprefix name="voice" change="yes" rank="10000" ranktoset="10000" depriv="no">
 +#
 +# Do /RELOADMODULE customprefix after changing the settings of this module.
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Custom title module: Adds the /TITLE command which allows for trusted
  # users to gain a custom whois line and an optional vhost can be
  # specified.
 -#<module name="m_customtitle.so">
 +#<module name="customtitle">
  #
  #-#-#-#-#-#-#-#-#-#-  CUSTOM TITLE CONFIGURATION   -#-#-#-#-#-#-#-#-#-#
  #  name     - The username used to identify.
  #  password - The password used to identify.
  #  hash     - The hash for the specific user's password (optional).
 -#             m_password_hash.so and a hashing module must be loaded
 +#             password_hash and a hashing module must be loaded
  #             for this to work.
  #  host     - Allowed hostmask (optional).
  #  title    - Title shown in whois.
  #
  #<title name="foo" password="bar" title="Official Chat Helper">
  #<title name="bar" password="foo" host="ident@test.org" title="Official Chat Helper" vhost="helper.test.org">
 -#<title name="foo" password="fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9" hash="sha256" title="Official Chat Helper">
 +#<title name="foo" password="$2a$10$UYZ4OcO8NNTCCGyCdY9SK.2GHiqGgxZfHFPOPmWuxEVWVQTtoDC7C" hash="bcrypt" title="Official Chat Helper">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # DCCALLOW module: Adds the /DCCALLOW command.
 -#<module name="m_dccallow.so">
 +#<module name="dccallow">
  #
  #-#-#-#-#-#-#-#-#-#-#-  DCCALLOW CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
  #  blockchat         - Whether to block DCC CHAT as well as DCC SEND.
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Deaf module: Adds support for the usermode +d - deaf to channel
  # messages and channel notices.
 -#<module name="m_deaf.so">
 +#<module name="deaf">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Delay join module: Adds the channel mode +D which delays all JOIN
  # speaking, their quit or part message will not be shown to the channel
  # which helps cut down noise on large channels in a more friendly way
  # than the auditorium mode. Only channel ops may set the +D mode.
 -#<module name="m_delayjoin.so">
 +#<module name="delayjoin">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Delay message module: Adds the channel mode +d which disallows a user
  # from talking in the channel unless they've been joined for X seconds.
  # Settable using /MODE #chan +d 30
 -#<module name="m_delaymsg.so">
 +#<module name="delaymsg">
  # Set allownotice to no to disallow NOTICEs too. Defaults to yes.
  #<delaymsg allownotice="no">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Deny channels module: Deny channels from being used by users.
 -#<module name="m_denychans.so">
 +#<module name="denychans">
  #
  #-#-#-#-#-#-#-#-#-#-#-   DENYCHAN DEFINITIONS  -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# If you have the m_denychans.so module loaded, you need to specify   #
 -# the channels to deny:                                               #
 +# If you have the denychans module loaded, you need to specify the    #
 +# channels to deny:                                                   #
  #                                                                     #
  # name        -      The channel name to deny (glob masks are ok).    #
  # allowopers  -      If operators are allowed to override the deny.   #
  #<goodchan name="#funtimes">                                          #
  # Glob masks are accepted here also.                                  #
  
 -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 -# Devoice module: Let users devoice themselves using /DEVOICE #chan.
 -#<module name="m_devoice.so">
 -
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # DNS blacklist module: Provides support for looking up IPs on one or #
  # more blacklists.                                                    #
 -#<module name="m_dnsbl.so">                                           #
 +#<module name="dnsbl">                                                #
  #                                                                     #
 -# For configuration options please see the wiki page for m_dnsbl at   #
 -# https://wiki.inspircd.org/Modules/2.0/dnsbl                         #
 +# For configuration options please see the wiki page for dnsbl at     #
- # http://wiki.inspircd.org/Modules/dnsbl                              #
++# https://wiki.inspircd.org/Modules/3.0/dnsbl                         #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Exempt channel operators module: Provides support for allowing      #
  # channel operators to be exempt from some channel modes.  Supported  #
  # modes are blockcaps, noctcp, blockcolor, nickflood, flood, censor,  #
  # filter, regmoderated, nonick, nonotice, and stripcolor.             #
 -#<module name="m_exemptchanops.so">                                   #
 +#<module name="exemptchanops">                                        #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Filter module: Provides message filtering, similar to SPAMFILTER.   #
 -#<module name="m_filter.so">
 +#<module name="filter">
  #                                                                     #
 -# This module depends upon a regex provider such as m_regex_pcre or   #
 -# m_regex_glob to function. You must specify which of these you want  #
 -# m_filter to use via the tag below.                                  #
 +# This module depends upon a regex provider such as regex_pcre or     #
 +# regex_glob to function. You must specify which of these you want    #
 +# the filter module to use via the tag below.                         #
  #                                                                     #
  # Valid engines are:                                                  #
  #                                                                     #
 -# glob   - Glob patterns, provided via m_regex_glob.                  #
 -# pcre   - PCRE regexps, provided via m_regex_pcre, needs libpcre.    #
 -# tre    - TRE regexps, provided via m_regex_tre, requires libtre.    #
 -# posix  - POSIX regexps, provided via m_regex_posix, not available   #
 +# glob   - Glob patterns, provided via regex_glob.                    #
 +# pcre   - PCRE regexps, provided via regex_pcre, needs libpcre.      #
 +# tre    - TRE regexps, provided via regex_tre, requires libtre.      #
 +# posix  - POSIX regexps, provided via regex_posix, not available     #
  #          on Windows, no dependencies on other operating systems.    #
 -# stdlib - stdlib regexps, provided via m_regex_stdlib, see comment   #
 +# stdlib - stdlib regexps, provided via regex_stdlib, see comment     #
  #          at the <module> tag for info on availability.              #
  #                                                                     #
  #<filteropts engine="glob">                                           #
  #                                                                     #
  # Your choice of regex engine must match on all servers network-wide.
  #
 -# You may specify specific channels that are exempt from being filtered:
 -#<exemptfromfilter channel="#blah">
 +# To learn more about the configuration of this module, read          #
 +# examples/filter.conf.example, which covers the various types of     #
 +# filters and shows how to add exemptions.                            #
  #
  #-#-#-#-#-#-#-#-#-#-#-  FILTER  CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# Optional - If you specify to use the m_filter module, then          #
 +# Optional - If you specify to use the filter module, then            #
  # specify below the path to the filter.conf file, or define some      #
  # <filter> tags.                                                      #
  #                                                                     #
 -#<include file="conf/examples/filter.conf.example">
 +#<include file="examples/filter.conf.example">
 +
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# Flash Policy Daemon module: Allows Flash IRC clients (e.g. LightIRC)#
 +# to connect. If no file is specified, it'll serve a default policy   #
 +# allowing all IPs to connect to all plaintext IRC ports              #
 +#<bind address="" port="8430" type="flashpolicyd">                    #
 +#<flashpolicyd timeout="5" file="">                                   #
 +#<module name="flashpolicyd">                                         #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Gecos ban: Implements extended ban 'r', which stops anyone matching
  # a mask like +b r:*realname?here* from joining a channel.
 -#<module name="m_gecosban.so">
 +#<module name="gecosban">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # GeoIP module: Allows the server admin to match users by country code.
  # This module requires GeoIP to be installed on your system,
  # use your package manager to find the appropriate packages
  # or check the InspIRCd wiki page for this module.
 -#<module name="m_geoip.so">
 +#<module name="geoip">
  #
  # The actual allow/ban actions are done by connect classes, not by the
  # GeoIP module. An example connect class to ban people from russia or
  # Globops module: Provides the /GLOBOPS command and snomask +g.
  # This module is oper-only.
  # To use, GLOBOPS must be in one of your oper class blocks.
 -#<module name="m_globops.so">
 +#<module name="globops">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Global load module: Allows loading and unloading of modules network-
  # and /GRELOADMODULE.
  # To use, GLOADMODULE, GUNLOADMODULE and GRELOADMODULE
  # must be in one of your oper class blocks.
 -#<module name="m_globalload.so">
 -
 -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 -# Halfop module: Provides the +h (halfops) channel status mode.
 -#
 -# IMPORTANT: This module has been removed in the next major version of
 -# InspIRCd. You should use m_customprefix instead.
 -#<module name="m_halfop.so">
 +#<module name="globalload">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 -# HELPOP module: Provides the /HELPOP command.
 -#<module name="m_helpop.so">
 +# HELPOP module: Provides the /HELPOP command
 +#<module name="helpop">
  #
  #-#-#-#-#-#-#-#-#-#-#-#-  HELPOP  CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# If you specify to use the m_helpop.so module, then specify below    #
 -# the path to the helpop.conf file.                                   #
 -#<include file="conf/examples/inspircd.helpop-full.example">
 +# If you specify to use the helpop module, then specify below the     #
 +# path to the helpop.conf file.                                       #
 +#                                                                     #
 +#<include file="examples/inspircd.helpop-full.example">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Hide chans module: Allows users to hide their channels list from non-
  # opers by setting user mode +I on themselves.
 -#<module name="m_hidechans.so">
 +#<module name="hidechans">
  #
  # This mode can optionally prevent opers from seeing channels on a +I
  # user, for more privacy if set to true.
  # This setting is not recommended for most mainstream networks.
  #<hidechans affectsopers="false">
  
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# Hide list module: Allows for hiding the list of listmodes from users
 +# who do not have sufficient channel rank.
 +#<module name="hidelist">
 +#
 +# Each <hidelist> tag configures one listmode to hide.
 +# mode: Name of the listmode to hide.
 +# rank: Minimum rank required to view the list. If set to 0, all
 +# members of the channel may view the list, but non-members may not.
 +# The rank of the built-in op and voice mode is 30000 and 10000,
 +# respectively; the rank of other prefix modes is configurable.
 +# Defaults to 20000.
 +#
 +# Hiding the ban list is not recommended because it may break some
 +# clients.
 +#
 +# Hide filter (+g) list:
 +#<hidelist mode="filter" rank="30000">
 +# Only show invite exceptions (+I) to channel members:
 +#<hidelist mode="invex" rank="0">
 +
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Hide oper module: Allows opers to hide their oper status from non-
  # opers by setting user mode +H on themselves.
  # This module is oper-only.
 -#<module name="m_hideoper.so">
 +#<module name="hideoper">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Hostchange module: Allows a different style of cloaking.
 -#<module name="m_hostchange.so">
 +#<module name="hostchange">
  #
  #-#-#-#-#-#-#-#-#-#-#-  HOSTCHANGE  CONFIGURATION  -#-#-#-#-#-#-#-#-#-#
  #                                                                     #
- # See http://wiki.inspircd.org/Modules/hostchange for help.           #
 -# See https://wiki.inspircd.org/Modules/2.0/hostchange for help.      #
++# See https://wiki.inspircd.org/Modules/3.0/hostchange for help.      #
  #                                                                     #
  #<host suffix="invalid.org" separator="." prefix="">
  #<hostchange mask="*@42.theanswer.example.org" action="addnick">
  #<hostchange mask="a@example.com" action="set" value="foo.bar.baz">
  #<hostchange mask="localhost" ports="7000,7001,7005-7007" action="set" value="blahblah.foo">
  
 +# hostcycle: If loaded, when a user gets a host or ident set, it will
 +# cycle them in all their channels. If not loaded it will simply change
 +# their host/ident without cycling them.
 +# This module is compatible with the ircv3_chghost module. Clients
 +# supporting the chghost extension will get the chghost message instead
 +# of seeing a host cycle.
 +#<module name="hostcycle">
 +
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # httpd module: Provides HTTP server support for InspIRCd.
 -#<module name="m_httpd.so">
 +#<module name="httpd">
  #
  #-#-#-#-#-#-#-#-#-#-#-#-  HTTPD   CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
  #
 -# If you choose to use the m_httpd.so module, then you will need to add
 +# If you choose to use the httpd module, then you will need to add
  # a <bind> tag with type "httpd", and load at least one of the other
 -# m_httpd_* modules to provide pages to display.
 +# httpd_* modules to provide pages to display.
  #
  # You can adjust the timeout for HTTP connections below. All HTTP
 -# connections will be closed after (roughly) this many seconds.
 +# connections will be closed after (roughly) this time period.
  #<httpd timeout="20">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 -# HTTP ACL module: Provides access control lists for m_httpd dependent
 +# HTTP ACL module: Provides access control lists for httpd dependent
  # modules. Use this module to restrict pages by IP address and by
  # password.
 -#<module name="m_httpd_acl.so">
 +#<module name="httpd_acl">
  #
  #-#-#-#-#-#-#-#-#-#-#-#- HTTPD ACL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
  #
 -# Restrict access to the m_httpd_stats module to all but the local
 +# Restrict access to the httpd_stats module to all but the local
  # network and when the correct password is specified:
  # <httpdacl path="/stats*" types="password,whitelist"
  #    username="secrets" password="mypasshere" whitelist="127.0.0.*,10.*">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # HTTP config module: Allows the configuration of the server to be
 -# viewed over HTTP. Requires m_httpd.so to be loaded for it to function.
 -#<module name="m_httpd_config.so">
 +# viewed over HTTP. Requires httpd to be loaded for it to function.
 +#<module name="httpd_config">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # HTTP stats module: Provides basic stats pages over HTTP.
 -# Requires m_httpd.so to be loaded for it to function.
 -#<module name="m_httpd_stats.so">
 +# Requires httpd to be loaded for it to function.
 +#<module name="httpd_stats">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Ident: Provides RFC 1413 ident lookup support.
  # When this module is loaded <connect:allow> tags may have an optional
  # useident="yes|no" boolean value, determining whether or not to lookup
  # ident on users matching that connect tag.
 -#<module name="m_ident.so">
 +#<module name="ident">
  #
  #-#-#-#-#-#-#-#-#-#-#-#-   IDENT CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# Optional - If you are using the m_ident.so module, then you can     #
 -# specify the timeout for ident lookups here. If not defined, it will #
 -# default to 5 seconds. This is a non-blocking timeout which holds    #
 -# the user in a 'connecting' state until the lookup is complete.      #
 +# Optional - If you are using the ident module, then you can specify  #
 +# the timeout for ident lookups here. If not defined, it will default #
 +# to 5 seconds. This is a non-blocking timeout which holds the user   #
 +# in a 'connecting' state until the lookup is complete.               #
  # The bind value indicates which IP to bind outbound requests to.     #
 +# nolookupprefix: If on, the idents of users being in a connect class #
 +# with ident lookups disabled (i.e. <connect useident="off">) will be #
 +# prefixed with a "~". If off, the ident of those users will not be   #
 +# prefixed. Default is off.                                           #
  #
 -#<ident timeout="5">
 +#<ident timeout="5" nolookupprefix="no">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Invite exception module: Adds support for channel invite exceptions
  # (+I).
 -#<module name="m_inviteexception.so">
 +#<module name="inviteexception">
  # bypasskey: If this is enabled, exceptions will bypass +k as well as +i
  #<inviteexception bypasskey="yes">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # IRCv3 module: Provides the following IRCv3.1 extensions:
+ # IRCv3 module: Provides the following IRCv3 extensions:
  # extended-join, away-notify and account-notify. These are optional
  # enhancements to the client-to-server protocol. An extension is only
  # active for a client when the client specifically requests it, so this
 -# module needs m_cap to work.
 +# module needs the cap module to work.
  #
  # Further information on these extensions can be found at the IRCv3
  # working group website:
- # http://ircv3.org/extensions/
+ # http://ircv3.net/irc/
  #
 -#<module name="m_ircv3.so">
 +#<module name="ircv3">
  # The following block can be used to control which extensions are
 -# enabled. Note that extended-join can be incompatible with m_delayjoin
 +# enabled. Note that extended-join can be incompatible with delayjoin
  # and host cycling.
  #<ircv3 accountnotify="on" awaynotify="on" extendedjoin="on">
  
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# IRCv3 cap-notify module: Provides the cap-notify IRCv3.2 extension.
 +# Required for IRCv3.2 conformance.
 +#<module name="ircv3_capnotify">
 +
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# IRCv3 chghost module: Provides the chghost IRCv3.2 extension which
 +# allows capable clients to learn when the host/ident of another user
 +# changes without cycling the user. This module is compatible with the
 +# hostcycle module. If both are loaded, clients supporting the chghost
 +# extension will get the chghost message and won't see host cycling.
 +#<module name="ircv3_chghost">
 +
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# IRCv3 echo-message module: Provides the echo-message IRCv3.2
 +# extension which allows capable clients to get an acknowledgement when
 +# their messages are delivered and learn what modifications, if any,
 +# were applied to them.
 +#<module name="ircv3_echomessage">
 +
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# IRCv3 invite-notify module: Provides the invite-notify IRCv3.2
 +# extension which notifies supporting clients when a user invites
 +# another user into a channel. This respects <options:announceinvites>.
 +#<module name="ircv3_invitenotify">
 +
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Join flood module: Adds support for join flood protection +j X:Y.
 -# Closes the channel for 60 seconds if X users join in Y seconds.
 -#<module name="m_joinflood.so">
 +# Closes the channel for N seconds if X users join in Y seconds.
 +#<module name="joinflood">
 +#
 +# The number of seconds to close the channel for:
 +#<joinflood duration="1m">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Jump server module: Adds support for the RPL_REDIR numeric.
  # To use, JUMPSERVER must be in one of your oper class blocks.
  # If your server is redirecting new clients and you get disconnected,
  # do a REHASH from shell to open up again.
 -#<module name="m_jumpserver.so">
 +#<module name="jumpserver">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Anti auto rejoin: Adds support for prevention of auto-rejoin (+J).
 -#<module name="m_kicknorejoin.so">
 -# Set the maximum time that is accepted as a parameter for +J here.
 -#<kicknorejoin maxtime="1m">
 +#<module name="kicknorejoin">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Knock module: Adds the /KNOCK command and channel mode +K.
 -#<module name="m_knock.so">
 +#<module name="knock">
  #
  # This setting specifies what to do when someone successfully /KNOCKs.
  # If set to "notice", then a NOTICE will be sent to the channel.
  # If set to "both" then (surprise!) both will be sent.
  #<knock notify="notice">
  
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# LDAP module: Allows other SQL modules to access a LDAP database
 +# through a unified API.
 +# This modules is in extras. Re-run configure with:
 +# ./configure --enable-extras=m_ldap.cpp
 +# and run make install, then uncomment this module to enable it.
 +#
 +#<module name="ldap">
 +#<database module="ldap" id="ldapdb" server="ldap://localhost" binddn="cn=Manager,dc=inspircd,dc=org" bindauth="mysecretpass" searchscope="subtree">
 +# The server parameter indicates the LDAP server to connect to. The   #
 +# ldap:// style scheme before the hostname proper is MANDATORY.       #
 +#                                                                     #
 +# The binddn and bindauth indicate the DN to bind to for searching,   #
 +# and the password for the distinguished name. Some LDAP servers will #
 +# allow anonymous searching in which case these two values do not     #
 +# need defining, otherwise they should be set similar to the examples #
 +# above.                                                              #
 +#                                                                     #
 +# The searchscope value indicates the subtree to search under. On our #
 +# test system this is 'subtree'. Your mileage may vary.               #
 +
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # LDAP authentication module: Adds the ability to authenticate users  #
 -# via LDAP. This is an extra module which must be enabled explicitly  #
 -# by symlinking it from modules/extra, and requires the OpenLDAP libs #
 -# This module is in extras. To enable it, Re-run configure with:      #
 -# ./configure --enable-extras=m_ldapauth.cpp                          #
 -# and run make install, then uncomment this module.                   #
 -#<module name="m_ldapauth.so">
 +# via LDAP.                                                           #
 +#<module name="ldapauth">
  #                                                                     #
  # Configuration:                                                      #
  #                                                                     #
 -# <ldapauth baserdn="ou=People,dc=brainbox,dc=cc"                     #
 +# <ldapauth dbid="ldapdb"                                             #
 +#           baserdn="ou=People,dc=brainbox,dc=cc"                     #
  #           attribute="uid"                                           #
 -#           server="ldap://brainwave.brainbox.cc"                     #
 -#           allowpattern="Guest*"                                     #
 +#           allowpattern="Guest* Bot*"                                #
  #           killreason="Access denied"                                #
 -#           searchscope="subtree"                                     #
 -#           binddn="cn=Manager,dc=brainbox,dc=cc"                     #
 -#           bindauth="mysecretpass"                                   #
  #           verbose="yes"                                             #
  #           host="$uid.$ou.inspircd.org"                              #
  #           useusername="no">                                         #
  # The attribute value indicates the attribute which is used to locate #
  # a user account by name. On POSIX systems this is usually 'uid'.     #
  #                                                                     #
 +# The allowpattern value allows you to specify a space separated list #
 +# of wildcard masks which will always be allowed to connect           #
 +# regardless of if they have an account, for example guest and bot    #
 +# users.                                                              #
 +#                                                                     #
  # The useusername setting chooses whether the user's username or      #
  # nickname is used when locating a user account, if a username isn't  #
  # provided in PASS.                                                   #
  #                                                                     #
 -# The server parameter indicates the LDAP server to connect to. The   #
 -# ldap:// style scheme before the hostname proper is MANDATORY.       #
 -#                                                                     #
 -# The allowpattern value allows you to specify a wildcard mask which  #
 -# will always be allowed to connect regardless of if they have an     #
 -# account, for example guest users.                                   #
 -#                                                                     #
  # Killreason indicates the QUIT reason to give to users if they fail  #
  # to authenticate.                                                    #
  #                                                                     #
 -# The searchscope value indicates the subtree to search under. On our #
 -# test system this is 'subtree'. Your mileage may vary.               #
 -#                                                                     #
  # Setting the verbose value causes an oper notice to be sent out for  #
  # every failed authentication to the server, with an error string.    #
  #                                                                     #
 -# The binddn and bindauth indicate the DN to bind to for searching,   #
 -# and the password for the distinguished name. Some LDAP servers will #
 -# allow anonymous searching in which case these two values do not     #
 -# need defining, otherwise they should be set similar to the examples #
 -# above.                                                              #
 -#                                                                     #
  # ldapwhitelist indicates that clients connecting from an IP in the   #
  # provided CIDR do not need to authenticate against LDAP. It can be   #
  # repeated to whitelist multiple CIDRs.                               #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # LDAP oper configuration module: Adds the ability to authenticate    #
 -# opers via LDAP. This is an extra module which must be enabled       #
 -# explicitly by symlinking it from modules/extra, and requires the    #
 -# OpenLDAP libs. Re-run configure with:                               #
 -# ./configure --enable-extras=m_ldapoper.cpp
 -# and run make install, then uncomment this module to enable it.      #
 -#<module name="m_ldapoper.so">
 +# opers via LDAP.                                                     #
 +#<module name="ldapoper">
  #                                                                     #
  # Configuration:                                                      #
  #                                                                     #
 -# <ldapoper baserdn="ou=People,dc=brainbox,dc=cc"
 -#           server="ldap://brainwave.brainbox.cc"
 -#           searchscope="subtree"
 -#           binddn="cn=Manager,dc=brainbox,dc=cc"
 -#           bindauth="mysecretpass"
 +# <ldapoper dbid="ldapdb"
 +#           baserdn="ou=People,dc=brainbox,dc=cc"
  #           attribute="uid">
  #                                                                     #
  # Available configuration items are identical to the same items in    #
 -# m_ldapauth above (except for the verbose setting, that is only      #
 -# supported in m_ldapauth).                                           #
 +# ldapauth above (except for the verbose setting, that is only        #
 +# supported in ldapauth).                                             #
  # Please always specify a password in your <oper> tags even if the    #
  # opers are to be authenticated via LDAP, so in case this module is   #
  # not loaded the oper accounts are still protected by a password.     #
  # If your server is locked and you get disconnected, do a REHASH from #
  # shell to open up again.                                             #
  # This module is oper-only.
 -#<module name="m_lockserv.so">
 +#<module name="lockserv">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Map hiding module: replaces /MAP and /LINKS output to users with a  #
  # message to see a website, set by maphide="http://test.org/map" in   #
  # the <security> tag, instead.                                        #
 -#<module name="m_maphide.so">
 +#<module name="maphide">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Message flood module: Adds message/notice flood protection via
  # channel mode +f.
 -#<module name="m_messageflood.so">
 +#<module name="messageflood">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # MLOCK module: Adds support for server-side enforcement of services
  # side MLOCKs. Basically, this module suppresses any mode change that
  # would likely be immediately bounced by services.
 -#<module name="m_mlock.so">
 +#<module name="mlock">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 -# MsSQL module: Allows other SQL modules to access MS SQL Server
 -# through a unified API.
 -# This module is in extras. Re-run configure with:
 -# ./configure --enable-extras=m_mssql.cpp
 -# and run make install, then uncomment this module to enable it.
 -#<module name="m_mssql.so">
 -#
 -#-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#-#
 -#                                                                     #
 -# m_mssql.so is more complex than described here, see wiki for more   #
 -# info https://wiki.inspircd.org/Modules/2.0/mssql                    #
 +# Modenotice module: Adds the /MODENOTICE command that allows opers to
 +# send notices to all users having the given user mode(s) set.
 +#<module name="modenotice">
 +
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# Monitor module: Adds support for MONITOR which is used by clients to
 +# maintain notify lists.
 +#<module name="monitor">
  #
 -#<database module="mssql" name="db" user="user" pass="pass" host="localhost" id="db1">
 +# Set the maximum number of entries on a user's monitor list below.
 +#<monitor maxentries="30">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # MySQL module: Allows other SQL modules to access MySQL databases
  # This module is in extras. Re-run configure with:
  # ./configure --enable-extras=m_mysql.cpp
  # and run make install, then uncomment this module to enable it.
 -#<module name="m_mysql.so">
 +#<module name="mysql">
  #
  #-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# m_mysql.so is more complex than described here, see the wiki for    #
 -# more: https://wiki.inspircd.org/Modules/2.0/mysql                   #
 +# mysql is more complex than described here, see the wiki for more    #
- # info: http://wiki.inspircd.org/Modules/mysql                        #
++# info: https://wiki.inspircd.org/Modules/3.0/mysql                   #
  #
  #<database module="mysql" name="mydb" user="myuser" pass="mypass" host="localhost" id="my_database2">
  
  # modes via long-form mode names via +Z and the /PROP command.
  # For example, to set a ban, do /mode #channel +Z ban=foo!bar@baz or
  # /PROP #channel ban=foo!bar@baz
 -#<module name="m_namedmodes.so">
 +#<module name="namedmodes">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # NAMESX module: Provides support for the NAMESX extension which allows
  # clients to see all the prefixes set on a user without getting confused.
  # This is supported by mIRC, x-chat, klient, and maybe more.
 -#<module name="m_namesx.so">
 +#<module name="namesx">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # National characters module:
  # 1) Allows using national characters in nicknames.
  # 2) Allows using custom (national) casemapping over the network.
 -#<module name="m_nationalchars.so">
 +#<module name="nationalchars">
  #
  # file - Location of the file which contains casemapping rules. If this
  #        is a relative path then it is relative to "<PWD>/../locales"
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Nickchange flood protection module: Provides channel mode +F X:Y
  # which allows up to X nick changes in Y seconds.
 -#<module name="m_nickflood.so">
 +#<module name="nickflood">
 +#
 +# The number of seconds to prevent nick changes for:
 +#<nickflood duration="1m">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Nicklock module: Let opers change a user's nick and then stop that
  # user from changing their nick again until unlocked.
  # This module is oper-only.
  # To use, NICKLOCK and NICKUNLOCK must be in one of your oper class blocks.
 -#<module name="m_nicklock.so">
 +#<module name="nicklock">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # No CTCP module: Adds the channel mode +C to block CTCPs and extban
  # 'C' to block CTCPs sent by specific users.
 -#<module name="m_noctcp.so">
 +#<module name="noctcp">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # No kicks module: Adds the +Q channel mode and the Q: extban to deny
  # certain users from kicking.
 -#<module name="m_nokicks.so">
 +#<module name="nokicks">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # No nicks module: Adds the +N channel mode, as well as the 'N' extban.
  # +N stops all users from changing their nick, the N extban stops
  # anyone from matching a +b N:nick!user@host mask from changing their
  # nick.
 -#<module name="m_nonicks.so">
 +#<module name="nonicks">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # No part message module: Adds extban 'p' to block part messages from #
  # matching users.                                                     #
 -#<module name="m_nopartmsg.so">
 +#<module name="nopartmsg">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # No notice module: Adds the channel mode +T and the extban 'T' to
  # block specific users from noticing the channel.
 -#<module name="m_nonotice.so">
 +#<module name="nonotice">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Network business join module:
  # Allows an oper to join a channel using /OJOIN, giving them +Y on the
  # channel which makes them immune to kick/deop/etc.
 -#<module name="m_ojoin.so">
 +#<module name="ojoin">
  #
  # Specify the prefix that +Y will grant here.
  # Leave 'prefix' empty if you do not wish +Y to grant a prefix.
  # /mode #channel +iI O:* is equivalent to channel mode +O, but you
  # may also set +iI O:AdminTypeOnly to only allow admins.
  # Modes +I and +e work in a similar fashion.
 -#<module name="m_operchans.so">
 +#<module name="operchans">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Oper join module: Auto-joins opers to a channel upon oper-up.
 -# This module is oper-only. For the user equivalent, see m_conn_join.
 -#<module name="m_operjoin.so">
 +# This module is oper-only. For the user equivalent, see the conn_join
 +# module.
 +#<module name="operjoin">
  #
  #-#-#-#-#-#-#-#-#-#-#   OPERJOIN CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# If you are using the m_operjoin.so module, specify options here:    #
 +# If you are using the operjoin module, specify options here:         #
  #                                                                     #
  # channel     -      The channel name to join, can also be a comma    #
  #                    separated list e.g. "#channel1,#channel2".       #
  # type "m_operlog" at default loglevel), and optionally to the 'r'
  # snomask.
  # This module is oper-only.
 -#<module name="m_operlog.so">
 +#<module name="operlog">
  #
  # If the following option is on then all oper commands will be sent to
  # the snomask 'r'. The default is off.
  #
  # Load this module if you want all your IRC operators to have channel
  # operator powers.
 -#<module name="m_operprefix.so">
 +#<module name="operprefix">
  #
  # You may additionally customise the prefix character.
  #<operprefix prefix="!">
  # Oper MOTD module: Provides support for separate message of the day
  # on oper-up.
  # This module is oper-only.
 -#<module name="m_opermotd.so">
 +#<module name="opermotd">
  #
  #-#-#-#-#-#-#-#-#-#-#   OPERMOTD CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# If you are using the m_opermotd.so module, specify the motd here.   #
 +# If you are using the opermotd module, specify the motd here.   #
  #                                                                     #
  # onoper        - If on, the message is sent on /OPER, otherwise it's #
  #                 only sent when /OPERMOTD is used.                   #
  #                 Read the comment above <connect:allowmotdcolors> in #
  #                 inspircd.conf.example for details.                  #
  #                                                                     #
 -#<opermotd file="conf/examples/opermotd.txt.example" onoper="yes" processcolors="false">
 +#<opermotd file="examples/opermotd.txt.example" onoper="yes" processcolors="false">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Override module: Adds support for oper override.
  # This module is oper-only.
 -#<module name="m_override.so">
 +#<module name="override">
  #
  #-#-#-#-#-#-#-#-#-#-#   OVERRIDE CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# m_override.so is too complex it describe here, see the wiki:        #
 -# https://wiki.inspircd.org/Modules/2.0/override                      #
 +# override is too complex it describe here, see the wiki:             #
 +# http://wiki.inspircd.org/Modules/override                           #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Oper levels module: Gives each oper a level and prevents actions
  # being taken by lower level opers against higher level opers.
  # Specify the level as the 'level' parameter of the <type> tag.
  # This module is oper-only.
 -#<module name="m_operlevels.so">
 +#<module name="operlevels">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Oper modes module: Allows you to specify modes to add/remove on oper.
  # Specify the modes as the 'modes' parameter of the <type> tag
  # and/or as the 'modes' parameter of the <oper> tag.
 -# This module is oper-only. For the user equivalent, see m_conn_umodes.
 -#<module name="m_opermodes.so">
 +# This module is oper-only. For the user equivalent, see the 
 +# conn_umodes module.
 +#<module name="opermodes">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Password forwarding module: Forwards a password users can send on
  # connect to the specified client below. The client is usually NickServ
  # and this module is usually used to authenticate users with NickServ
  # using their connect password.
 -#<module name="m_passforward.so">
 +#<module name="passforward">
  
  <passforward
                # nick: nick to forward connect passwords to.
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Password hash module: Allows hashed passwords to be used.
 -# To be useful, a hashing module like m_sha256.so also needs to be loaded.
 -#<module name="m_password_hash.so">
 +# To be useful, a hashing module like bcrypt also needs to be loaded.
 +#<module name="password_hash">
  #
  #-#-#-#-#-#-#-#-#-# PASSWORD HASH CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#
  #
  #
  #     <oper name="Brain"
  #           host="ident@dialup15.isp.test.com"
 -#           hash="sha256"
 -#           password="01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"
 +#           hash="bcrypt"
 +#           password="$2a$10$Mss9AtHHslZTLBrXqM0FB.JBwD.UTSu8A48SfrY9exrpxbsRiRTbO"
  #           type="NetAdmin">
  #
 -# Starting from 2.0, you can use a more secure salted hash that prevents simply
 -# looking up the hash's value in a rainbow table built for the hash.
 +# If you are using a hash algorithm which does not perform salting you can use
 +# HMAC to salt your passwords in order to prevent them from being looked up in
 +# a rainbow table.
 +#
  #    hash="hmac-sha256" password="lkS1Nbtp$CyLd/WPQXizsbxFUTqFRoMvaC+zhOULEeZaQkUJj+Gg"
  #
  # Generate hashes using the /MKPASSWD command on the server.
  # Don't run it on a server you don't trust with your password.
  
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# PBKDF2 module: Allows other modules to generate PBKDF2 hashes,
 +# usually for cryptographic uses and security.
 +# This module relies on other hash providers (e.g. SHA256).
 +#<module name="pbkdf2">
 +#
 +# iterations: Iterations the hashing function runs when generating new
 +# hashes.
 +# length: Length in bytes of the derived key.
 +#<pbkdf2 iterations="12288" length="32">
 +# You can override these values with specific values
 +# for specific providers if you want to. Example given for SHA256.
 +#<pbkdf2prov hash="sha256" iterations="24576">
 +
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Permanent channels module: Channels with the permanent channel mode
  # will remain open even after everyone else has left the channel, and
  # channels -may- need support from your Services package to function
  # properly with them. This adds channel mode +P.
  # This module is oper-only.
 -#<module name="m_permchannels.so">
 +#<module name="permchannels">
  #
 -# If you like, m_permchannels can write a config file of permanent channels
 +# If you like, this module can write a config file of permanent channels
  # whenever +P is set, unset, or the topic/modes on a +P channel is changed.
  # If you want to do this, set the filename below, and uncomment the include.
  #
  # If 'listmodes' is true then all list modes (+b, +I, +e, +g...) will be
  # saved. Defaults to false.
 -#<permchanneldb filename="data/permchannels.conf" listmodes="true">
 -#<include file="data/permchannels.conf">
 +#<permchanneldb filename="permchannels.conf" listmodes="true">
 +#<include file="permchannels.conf">
  #
  # You may also create channels on startup by using the <permchannels> block.
 -# Don't forget to set them +P in the modes, or they won't stay permanent.
  #<permchannels channel="#opers" modes="isP" topic="Opers only.">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # This module is in extras. Re-run configure with:
  # ./configure --enable-extras=m_pgsql.cpp
  # and run make install, then uncomment this module to enable it.
 -#<module name="m_pgsql.so">
 +#<module name="pgsql">
  #
  #-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
- # pgsql is more complex than described here, see the wiki for    #
- # more: http://wiki.inspircd.org/Modules/pgsql                        #
 -# m_pgsql.so is more complex than described here, see the wiki for    #
 -# more: https://wiki.inspircd.org/Modules/2.0/pgsql                   #
++# pgsql is more complex than described here, see the wiki for         #
++# more: https://wiki.inspircd.org/Modules/3.0/pgsql                   #
  #
  #<database module="pgsql" name="mydb" user="myuser" pass="mypass" host="localhost" id="my_database" ssl="no">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Muteban: Implements extended ban 'm', which stops anyone matching
  # a mask like +b m:nick!user@host from speaking on channel.
 -#<module name="m_muteban.so">
 +#<module name="muteban">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Random quote module: Provides a random quote on connect.
  # NOTE: Some of these may mimic fatal errors and confuse users and
  # opers alike - BEWARE!
 -#<module name="m_randquote.so">
 +#<module name="randquote">
  #
  #-#-#-#-#-#-#-#-#-#-  RANDOMQUOTES CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# Optional - If you specify to use the m_randquote.so module, then    #
 -# specify below the path to the quotes file.                          #
 +# Optional - If you specify to use the randquote module, then specify #
 +# below the path to the quotes file.                                  #
  #                                                                     #
  #<randquote file="quotes.txt">
  
  # This also breaks linking to servers that do not have the option.    #
  # This defaults to false for the 2.0 version, it will be enabled in   #
  # all the future versions.                                            #
 -#<module name="m_redirect.so">
 +#<module name="redirect">
  #<redirect antiredirect="true">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Regular expression provider for glob or wildcard (?/*) matching.
 -# You must have at least 1 provider loaded to use m_filter or m_rline
 +# You must have at least 1 provider loaded to use the filter or rline
  # modules. This module has no additional requirements, as it uses the
  # matching already present in InspIRCd core.
 -#<module name="m_regex_glob.so">
 +#<module name="regex_glob">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Regular expression provider for PCRE (Perl-Compatible Regular
  # Expressions). You need libpcre installed to compile and load this
 -# module. You must have at least 1 provider loaded to use m_filter or
 -# m_rline.
 -#<module name="m_regex_pcre.so">
 +# module. You must have at least 1 provider loaded to use the filter or
 +# rline modules.
 +#<module name="regex_pcre">
 +
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# Regular Expression Provider for RE2 Regular Expressions.
 +# You need libre2 installed and in your include/library paths in order
 +# to compile and load this module.
 +#<module name="regex_re2">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Regular expression provider for POSIX regular expressions.
  # You shouldn't need any additional libraries on a POSIX-compatible
  # system (i.e.: any Linux, BSD, but not Windows). You must have at
 -# least 1 provider loaded to use m_filter or m_rline.
 +# least 1 provider loaded to use filter or rline.
  # On POSIX-compliant systems, regex syntax can be found by using the
  # command: 'man 7 regex'.
 -#<module name="m_regex_posix.so">
 +#<module name="regex_posix">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Regular expression provider for C++11 std::regex regular expressions.
  # You should verify that std::regex is supported by your setup before
  # using this module, as it may compile normally but won't do anything
  # on some implementations.
 -#<module name="m_regex_stdlib.so">
 +#<module name="regex_stdlib">
  #
  # Specify the regular expression engine to use here. Valid settings are
  # bre, ere, awk, grep, egrep, ecmascript (default if not specified).
  # if you are most familiar with the syntax of /SPAMFILTER from there,
  # this is the provider you want. You need libtre installed in order
  # to compile and load this module.
 -#<module name="m_regex_tre.so">
 +#<module name="regex_tre">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Registered users only channel creation module. If enabled, only
  #
  # You probably *DO NOT* want to load this module on a public network.
  #
 -#<module name="m_regonlycreate.so">
 +#<module name="regonlycreate">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Remove module: Adds the /REMOVE command which is a peaceful
  # alternative to /KICK.
 -#<module name="m_remove.so">
 +#<module name="remove">
 +#
 +# supportnokicks: If true, /REMOVE is not allowed on channels where the
 +# nokicks (+Q) mode is set. Defaults to false.
 +# protectedrank: Members having this rank or above may not be /REMOVE'd
 +# by anyone. Set to 0 to disable this feature. Defaults to 50000.
 +#<remove supportnokicks="true" protectedrank="50000">
 +
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# A module to block, kick or ban upon similar messages being uttered several times.
 +# Syntax [~*][lines]:[sec]{[:difference]}{[:matchlines]}
 +# ~ is to block, * is to ban, default is kick.
 +# lines - In mode 1 the amount of lines that has to match consecutively - In mode 2 the size of the backlog to keep for matching
 +# seconds - How old the message has to be before it's invalidated.
 +# distance - Edit distance, in percent, between two strings to trigger on.
 +# matchlines - When set, the function goes into mode 2. In this mode the function will trigger if this many of the last <lines> matches.
 +#
 +# As this module can be rather CPU-intensive, it comes with some options.
 +# maxbacklog - Maximum size that can be specified for backlog. 0 disables multiline matching.
 +# maxdistance - Max percentage of difference between two lines we'll allow to match. Set to 0 to disable edit-distance matching.
 +# maxlines - Max lines of backlog to match against.
 +# maxtime - Maximum period of time a user can set. 0 to allow any.
 +# size - Maximum number of characters to check for, can be used to truncate messages
 +# before they are checked, resulting in less CPU usage. Increasing this beyond 512
 +# doesn't have any effect, as the maximum length of a message on IRC cannot exceed that.
 +#<repeat maxbacklog="20" maxlines="20" maxdistance="50" maxtime="0" size="512">
 +#<module name="repeat">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Restricted channels module: Allows only opers to create channels.
  #
  # You probably *DO NOT* want to load this module on a public network.
  #
 -#<module name="m_restrictchans.so">
 +#<module name="restrictchans">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Restrict message module: Allows users to only message opers.
  #
  # You probably *DO NOT* want to load this module on a public network.
  #
 -#<module name="m_restrictmsg.so">
 -#
 -# Uncomment this to allow users to message ulines (e.g. services):
 -#<restrictmsg uline="yes">
 +#<module name="restrictmsg">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # R-Line module: Ban users through regular expression patterns.
 -#<module name="m_rline.so">
 +#<module name="rline">
  #
  #-#-#-#-#-#-#-#-#-#-#-#- RLINE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
  #
  # Also, this is where you set what Regular Expression engine is to be
  # used. If you ever change it while running, all of your R-Lines will
  # be wiped. This is the regex engine used by all R-Lines set, and
 -# m_regex_<engine>.so must be loaded, or rline will be non-functional
 +# regex_<engine> must be loaded, or rline will be non-functional
  # until you load it or change the engine to one that is loaded.
  #
  #<rline matchonnickchange="yes" engine="pcre">
  # use glob. For this reason, is recommended to use a real regex engine
  # so that at least \s or [[:space:]] is available.
  
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# RMODE module: Adds the /RMODE command
 +# Allows channel mods to remove list modes en masse.
 +# Syntax: /rmode <channel> <mode> [pattern]
 +# E.g. '/rmode #Channel b m:*' will remove all mute-extbans on the channel.
 +#<module name="rmode">
 +
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SAJOIN module: Adds the /SAJOIN command which forcibly joins a user
  # to the given channel.
  # This module is oper-only.
  # To use, SAJOIN must be in one of your oper class blocks.
 -#<module name="m_sajoin.so">
 +# Opers need the users/sajoin-others priv to be able to /SAJOIN users
 +# other than themselves.
 +#<module name="sajoin">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SAKICK module: Adds the /SAKICK command which kicks a user from the
  # given channel.
  # This module is oper-only.
  # To use, SAKICK must be in one of your oper class blocks.
 -#<module name="m_sakick.so">
 +#<module name="sakick">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SAMODE module: Adds the /SAMODE command which allows server operators
  # channel priviliges. Also allows changing user modes for any user.
  # This module is oper-only.
  # To use, SAMODE must be in one of your oper class blocks.
 -#<module name="m_samode.so">
 +#<module name="samode">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SANICK module: Adds the /SANICK command which allows opers to change
  # users' nicks.
  # This module is oper-only.
  # To use, SANICK must be in one of your oper class blocks.
 -#<module name="m_sanick.so">
 +#<module name="sanick">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SAPART module: Adds the /SAPART command which forcibly parts a user
  # from a channel.
  # This module is oper-only.
  # To use, SAPART must be in one of your oper class blocks.
 -#<module name="m_sapart.so">
 +#<module name="sapart">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SAQUIT module: Adds the /SAQUIT command which forcibly quits a user.
  # This module is oper-only.
  # To use, SAQUIT must be in one of your oper class blocks.
 -#<module name="m_saquit.so">
 +#<module name="saquit">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SATOPIC module: Adds the /SATOPIC command which allows changing the
  # topic on a channel without requiring any channel priviliges.
  # This module is oper-only.
  # To use, SATOPIC must be in one of your oper class blocks.
 -#<module name="m_satopic.so">
 +#<module name="satopic">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SASL authentication module: Provides support for IRC Authentication
 -# Layer via AUTHENTICATE. Note: You also need to have m_cap.so loaded
 +# Layer via AUTHENTICATE. Note: You also need to have cap loaded
  # for SASL to work.
 -#<module name="m_sasl.so">
 +#<module name="sasl">
+ # Define the following to your services server name to improve security
+ # by ensuring the SASL messages are only sent to the services server
+ # and not to all connected servers. This prevents a rogue server from
 -# capturing SASL messages.
++# capturing SASL messages and disables the SASL cap when services is
++# down.
+ #<sasl target="services.mynetwork.com">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Secure list module: Prevent /LIST in the first minute of connection,
  # crippling most spambots and trojan spreader bots.
 -#<module name="m_securelist.so">
 +#<module name="securelist">
  #
  #-#-#-#-#-#-#-#-#-# SECURELIST CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # Define the following variable to change how long a user must wait   #
  # before issuing a LIST. If not defined, defaults to 60 seconds.      #
  #                                                                     #
 -#<securelist waittime="60">                                           #
 +#<securelist waittime="1m">                                           #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Servprotect module: Provides support for Austhex style +k /
  # UnrealIRCD +S services mode.
 -#<module name="m_servprotect.so">
 +#<module name="servprotect">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # See nicks module: Adds snomask +n and +N which show local and remote
  # nick changes.
  # This module is oper-only.
 -#<module name="m_seenicks.so">
 +#<module name="seenicks">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Set idle module: Adds a command for opers to change their idle time.
  # This module is oper-only.
  # To use, SETIDLE must be in one of your oper class blocks.
 -#<module name="m_setidle.so">
 +#<module name="setidle">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Services support module: Adds several usermodes such as +R and +M.
  # +b R: (stop matching account names from joining)
  # +b U:n!u@h (blocks matching unregistered users)
  #
 -#<module name="m_services_account.so">
 +#<module name="services_account">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Sethost module: Adds the /SETHOST command.
  # This module is oper-only.
  # To use, SETHOST must be in one of your oper class blocks.
 -# See m_chghost for how to customise valid chars for hostnames.
 -#<module name="m_sethost.so">
 +# See the chghost module for how to customise valid chars for hostnames.
 +#<module name="sethost">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Setident module: Adds the /SETIDENT command.
  # This module is oper-only.
  # To use, SETIDENT must be in one of your oper class blocks.
 -#<module name="m_setident.so">
 +#<module name="setident">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SETNAME module: Adds the /SETNAME command.
 -#<module name="m_setname.so">
 +#<module name="setname">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Serverban: Implements extended ban 's', which stops anyone connected
  # to a server matching a mask like +b s:server.mask.here from joining.
 -#<module name="m_serverban.so">
 +#<module name="serverban">
 +
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# SHA1 module: Allows other modules to generate SHA1 hashes.
 +# Required by the WebSocket module.
 +#<module name="sha1">
 +
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# Showfile: Provides support for showing a text file to users when    #
 +# they enter a command.                                               #
 +# This module adds one command for each <showfile> tag that shows the #
 +# given file to the user as a series of messages or numerics.         #
 +#<module name="showfile">                                             #
 +#                                                                     #
 +#-#-#-#-#-#-#-#-#-#-# SHOWFILE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
 +#                                                                     #
 +# name    - The name of the command which displays this file. This is #
 +#           the only mandatory setting, all others are optional.      #
 +# file    - The text file to be shown to the user.                    #
 +#           By default same as the command name.                      #
 +# method  - How should the file be shown?                             #
 +#           * numeric: Send contents using a numeric                  #
 +#             (similar to /MOTD; the default).                        #
 +#           * notice:  Send contents as a series of notices.          #
 +#           * msg:     Send contents as a series of private messages. #
 +# colors  - If true, color codes (\c, \b, \u, etc.) will be processed #
 +#           and sent as ANSI colors. If false (default) the file will #
 +#           be displayed as-is.                                       #
 +#                                                                     #
 +# When using the method "numeric", the following extra settings are   #
 +# available:                                                          #
 +#                                                                     #
 +# introtext    - Introductory line, "Showing <name>" by default.      #
 +# intronumeric - Numeric used for the introductory line.              #
 +# numeric      - Numeric used for sending the text itself.            #
 +# endtext      - Ending line, "End of <name>" by default.             #
 +# endnumeric   - Numeric used for the ending line.                    #
 +#                                                                     #
 +#<showfile name="RULES"
 +#          file="rules.txt"
 +#          colors="true"
 +#          introtext="Server rules:"
 +#          endtext="End of server rules.">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Show whois module: Adds the +W usermode which allows opers to see
  # when they are /WHOIS'd.
  # This module is oper-only by default.
 -#<module name="m_showwhois.so">
 +#<module name="showwhois">
  #
  # If you wish, you may also let users set this mode. Only opers with the
 -# users/auspex priv will see real hosts of people, though. This setting
 -# is not reloadable via /REHASH, changing it requires /RELOADMODULE.
 +# users/auspex priv will see real hosts of people, though.
  #<showwhois opersonly="yes"
  #
  # You may also set whether or not users should receive whois notices,
  # executing all except configured commands.
  # This module is oper-only.
  # To use, SHUN must be in one of your oper class blocks.
 -#<module name="m_shun.so">
 +#<module name="shun">
  #
  # You may also configure which commands you wish a user to be able to
  # perform when shunned. It should be noted that if a shunned user
  # channel mode +z and the 'z' extban which matches SSL client
  # certificate fingerprints.
  # Does not do anything useful without a working SSL module and the
 -# m_sslinfo module (see below).
 -#<module name="m_sslmodes.so">
 +# sslinfo module (see below).
 +#<module name="sslmodes">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # GnuTLS SSL module: Adds support for SSL connections using GnuTLS,
  # if enabled. You must answer 'yes' in ./configure when asked or
  # manually symlink the source for this module from the directory
  # src/modules/extra, if you want to enable this, or it will not load.
 -#<module name="m_ssl_gnutls.so">
 +#<module name="ssl_gnutls">
  #
  #-#-#-#-#-#-#-#-#-#-#-  GNUTLS CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# m_ssl_gnutls.so is too complex to describe here, see the wiki:      #
 -# https://wiki.inspircd.org/Modules/2.0/ssl_gnutls                    #
 +# ssl_gnutls is too complex to describe here, see the wiki:           #
- # http://wiki.inspircd.org/Modules/ssl_gnutls                         #
++# https://wiki.inspircd.org/Modules/3.0/ssl_gnutls                    #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SSL info module: Allows users to retrieve information about other
  # users' peer SSL certificates and keys. This can be used by client
 -# scripts to validate users. For this to work, one of m_ssl_gnutls.so
 -# or m_ssl_openssl.so must be loaded. This module also adds the
 +# scripts to validate users. For this to work, one of ssl_gnutls
 +# or ssl_openssl must be loaded. This module also adds the
  # "* <user> is using a secure connection" whois line, the ability for
 -# opers to use SSL fingerprints to verify their identity and the
 +# opers to use SSL cert fingerprints to verify their identity and the
  # ability to force opers to use SSL connections in order to oper up.
  # It is highly recommended to load this module if you use SSL on your
  # network.
  # For how to use the oper features, please see the first example <oper> tag
  # in opers.conf.example.
  #
 -#<module name="m_sslinfo.so">
 +#<module name="sslinfo">
 +
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# mbedTLS SSL module: Adds support for SSL/TLS connections using mbedTLS.
 +#<module name="ssl_mbedtls">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # OpenSSL SSL module: Adds support for SSL connections using OpenSSL,
  # if enabled. You must answer 'yes' in ./configure when asked or symlink
  # the source for this module from the directory src/modules/extra, if
  # you want to enable this, or it will not load.
 -#<module name="m_ssl_openssl.so">
 +#<module name="ssl_openssl">
  #
  #-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# m_ssl_openssl.so is too complex to describe here, see the wiki:     #
 -# https://wiki.inspircd.org/Modules/2.0/ssl_openssl                   #
 +# ssl_openssl is too complex to describe here, see the wiki:          #
- # http://wiki.inspircd.org/Modules/ssl_openssl                        #
++# https://wiki.inspircd.org/Modules/3.0/ssl_openssl                   #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 -# Strip color module: Adds channel mode +S that strips mIRC color
 -# codes from all messages sent to the channel.
 -#<module name="m_stripcolor.so">
 +# Strip color module: Adds channel mode +S that strips color codes and
 +# all control codes except CTCP from all messages sent to the channel.
 +#<module name="stripcolor">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Silence module: Adds support for the /SILENCE command, which allows
  # users to have a server-side ignore list for their client.
 -#<module name="m_silence.so">
 +#<module name="silence">
  #
  # Set the maximum number of entries allowed on a user's silence list.
 -#<silence maxentries="32">
 +#<silence maxentries="32"
 +#
 +# Whether messages from U-lined servers will bypass silence masks.
 +#exemptuline="yes">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SQLite3 module: Allows other SQL modules to access SQLite3          #
  # ./configure --enable-extras=m_sqlite3.cpp
  # and run make install, then uncomment this module to enable it.      #
  #
 -#<module name="m_sqlite3.so">
 +#<module name="sqlite3">
  #
  #-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# m_sqlite.so is more complex than described here, see the wiki for   #
 -# more: https://wiki.inspircd.org/Modules/2.0/sqlite3                 #
 +# sqlite is more complex than described here, see the wiki for more   #
- # info: http://wiki.inspircd.org/Modules/sqlite3                      #
++# info: https://wiki.inspircd.org/Modules/3.0/sqlite3                 #
  #
  #<database module="sqlite" hostname="/full/path/to/database.db" id="anytext">
  
  # ./configure --enable-extras=m_sqlauth.cpp
  # and run make install, then uncomment this module to enable it.
  #
 -#<module name="m_sqlauth.so">
 +#<module name="sqlauth">
  #
  #-#-#-#-#-#-#-#-#-#-#- SQLAUTH CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# m_sqlauth.so is too complex to describe here, see the wiki:         #
 -# https://wiki.inspircd.org/Modules/2.0/sqlauth                       #
 +# sqlauth is too complex to describe here, see the wiki:              #
- # http://wiki.inspircd.org/Modules/sqlauth                            #
++# https://wiki.inspircd.org/Modules/3.0/sqlauth                       #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SQL oper module: Allows you to store oper credentials in an SQL table
  # ./configure --enable-extras=m_sqloper.cpp
  # and run make install, then uncomment this module to enable it.
  #
 -#<module name="m_sqloper.so">
 +#<module name="sqloper">
  #
  #-#-#-#-#-#-#-#-#-#-#- SQLOPER CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # dbid       - Database ID to use (see SQL modules).                  #
  # hash       - Hashing provider to use for password hashing.          #
  #                                                                     #
- # See also: http://wiki.inspircd.org/Modules/sqloper                  #
 -# See also: https://wiki.inspircd.org/Modules/2.0/sqloper             #
++# See also: https://wiki.inspircd.org/Modules/3.0/sqloper             #
  #                                                                     #
 -#<sqloper dbid="1" hash="md5">
 +#<sqloper dbid="1" hash="bcrypt">
 +
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# StartTLS module: Implements STARTTLS, which allows clients          #
 +# connected to non SSL enabled ports to enable SSL, if a proper SSL   #
 +# module is loaded (either ssl_gnutls or ssl_openssl).                #
 +#<module name="starttls">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SVSHold module: Implements SVSHOLD. Like Q:Lines, but can only be   #
  # added/removed by Services.                                          #
 -#<module name="m_svshold.so">
 -# If silent is true no snotices will be generated by SVSHOLD.
 +#<module name="svshold">
 +# SVSHOLD does not generate server notices by default, you can turn
 +# notices on by uncommenting the next line.
  #<svshold silent="false">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SWHOIS module: Allows you to add arbitrary lines to user WHOIS.
  # This module is oper-only.
  # To use, SWHOIS must be in one of your oper class blocks.
 -#<module name="m_swhois.so">
 -
 -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 -# Test module: Enable this to create a command useful in testing
 -# flood control. To avoid accidental use on live networks, the server
 -# name must contain ".test" to load the module
 -#<module name="m_testnet.so">
 +#<module name="swhois">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Timed bans module: Adds timed channel bans with the /TBAN command.
 -#<module name="m_timedbans.so">
 +#<module name="timedbans">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Test line module: Adds the /TLINE command, used to test how many
  # users a /GLINE or /ZLINE etc. would match.
  # This module is oper-only.
  # To use, TLINE must be in one of your oper class blocks.
 -#<module name="m_tline.so">
 +#<module name="tline">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Topiclock module: implements server-side topic locking to achieve deeper
  # integration with services packages.
 -#<module name="m_topiclock.so">
 +#<module name="topiclock">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # UHNAMES support module: Adds support for the IRCX style UHNAMES
  # each user, saving clients from doing a WHO on the channel.
  # If a client does not support UHNAMES it will not enable it, this will
  # not break incompatible clients.
 -#<module name="m_uhnames.so">
 +#<module name="uhnames">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Uninvite module: Adds the /UNINVITE command which lets users remove
  # pending invites from channels without waiting for the user to join.
 -#<module name="m_uninvite.so">
 +#<module name="uninvite">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Userip module: Adds the /USERIP command.
  # Allows users to query their own IP, also allows opers to query the IP
  # of anyone else.
 -#<module name="m_userip.so">
 +#<module name="userip">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Vhost module: Adds the VHOST command which allows for adding virtual
  # hosts which are accessible using a username and password in the config.
 -#<module name="m_vhost.so">
 +#<module name="vhost">
  #
  #-#-#-#-#-#-#-#-#-#-#- VHOST CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # pass       - Password for the vhost.                                #
  #                                                                     #
  # hash       - The hash for the specific user (optional)              #
 -#              m_password_hash.so and a hashing module must be loaded #
 -#              for this to work.                                      #
 +#              password_hash and a hashing module must be loaded for  #
 +#              this to work.                                          #
  #                                                                     #
  # host       - Vhost to set.                                          #
  #
  #<vhost user="some_username" pass="some_password" host="some.host.test.cc">
 -#<vhost user="foo" password="fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9" hash="sha256" host="some.other.host.example.com">
 +#<vhost user="foo" password="$2a$10$iTuYLT6BRhRlOgzfsW9oPe62etW.oXwSpyKw5rJit64SGZanLXghO" hash="bcrypt" host="some.other.host.example.com">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Watch module: Adds the WATCH command, which is used by clients to
  # maintain notify lists.
 -#<module name="m_watch.so">
 +#<module name="watch">
  #
  # Set the maximum number of entries on a user's watch list below.
  #<watch maxentries="32">
  
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# WebSocket module: Adds HTML5 WebSocket support.
 +# Specify hook="websocket" in a <bind> tag to make that port accept
 +# WebSocket connections. Compatible with SSL/TLS.
 +# Requires SHA-1 hash support available in the sha1 module.
 +#<module name="websocket">
 +
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # XLine database: Stores all *Lines (G/Z/K/R/any added by other modules)
  # in a file which is re-loaded on restart. This is useful
  # for two reasons: it keeps bans so users may not evade them, and on
  # bigger networks, server connections will take less time as there will
  # be a lot less bans to apply - as most of them will already be there.
 -#<module name="m_xline_db.so">
 +#<module name="xline_db">
  
  # Specify the filename for the xline database here.
 -#<xlinedb filename="data/xline.db">
 +#<xlinedb filename="xline.db">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #    ____                _   _____ _     _       ____  _ _   _        #
  #   |  _ <  __/ (_| | (_| |   | | | | | | \__ \ | |_) | | |_|_|       #
  #   |_| \_\___|\__,_|\__,_|   |_| |_| |_|_|___/ |____/|_|\__(_)       #
  #                                                                     #
 -# To link servers to InspIRCd, you MUST load the m_spanningtree       #
 -# module. If you don't do this, server links will NOT work at all.    #
 +# To link servers to InspIRCd, you MUST load the spanningtree module. #
 +# If you don't do this, server links will NOT work at all.            #
  # This is by design, to allow for the implementation of other linking #
  # protocols in modules in the future.                                 #
  
  # tree protocol (see the READ THIS BIT section above).
  # You will almost always want to load this.
  #
 -#<module name="m_spanningtree.so">
 +#<module name="spanningtree">
index 6f8171b41111ecda3528f565e2de17a81b8a0730,91260c7d3133979923736996ce05c3d7d02d789a..97436c54891a0e2bebe1267bdbc55ca3c0275519
@@@ -1,6 -1,6 +1,6 @@@
 -<module name="m_md5.so">
 -<module name="m_sha256.so">
 -<module name="m_alias.so">
 +<module name="md5">
 +<module name="sha256">
 +<module name="alias">
  <alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
  <alias text="CHANSERV" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
  <alias text="OPERSERV" replace="PRIVMSG OperServ :$2-" requires="OperServ" uline="yes" operonly="yes">
  <alias text="MS" replace="PRIVMSG MemoServ :$2-" requires="MemoServ" uline="yes">
  <alias text="ID" replace="PRIVMSG NickServ :IDENTIFY $2" requires="NickServ" uline="yes">
  
 -<module name="m_banexception.so">
 -<module name="m_banredirect.so">
 -<module name="m_blockcolor.so">
 -<module name="m_callerid.so">
 +<module name="banexception">
 +<module name="banredirect">
 +<module name="blockcolor">
 +<module name="callerid">
  <callerid maxaccepts="16"
            operoverride="no"
            tracknick="no"
            cooldown="60">
  
 -<module name="m_cap.so">
 -<module name="m_cban.so">
 +<module name="cap">
 +<module name="cban">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # CGI:IRC module: Adds support for automatic host changing in CGI:IRC
  # (http://cgiirc.sourceforge.net).
 -#<module name="m_cgiirc.so">
 +#<module name="cgiirc">
  #
  #-#-#-#-#-#-#-#-#-#-#-# CGIIRC  CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
  #
 -# Optional - If you specify to use m_cgiirc, then you must specify one
 +# Optional - If you specify to use cgiirc, then you must specify one
  # or more cgihost tags which indicate authorised CGI:IRC servers which
  # will be connecting to your network, and an optional cgiirc tag.
- # For more information see: http://wiki.inspircd.org/Modules/cgiirc
 -# For more information see: https://wiki.inspircd.org/Modules/2.0/cgiirc
++# For more information see: https://wiki.inspircd.org/Modules/3.0/cgiirc
  #
  # Set to yes if you want to notice opers when CGI clients connect
  # <cgiirc opernotice="no">
  # sessions maximum.
  #
  
 -<module name="m_chancreate.so">
 +<module name="chancreate">
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Channel names module: Allows disabling channels which have certain
  # characters in the channel name such as bold, colorcodes, etc. which
  # can be quite annoying and allow users to on occasion have a channel
  # that looks like the name of another channel on the network.
 -<module name="m_channames.so">
 +<module name="channames">
  
  <channames
        # denyrange: characters or range of characters to deny in channel
        # in channel names.
        allowrange="">
  
 -<module name="m_channelban.so">
 -<module name="m_chghost.so">
 +<module name="channelban">
 +<module name="chghost">
  <hostname charmap="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_/0123456789">
 -<module name="m_chgident.so">
 -<module name="m_chgname.so">
 -<module name="m_cloaking.so">
 +<module name="chgident">
 +<module name="chgname">
 +<module name="cloaking">
  #
  #-#-#-#-#-#-#-#-#-#-#- CLOAKING  CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# If you specify the m_cloaking.so module as above, you must define   #
 +# If you specify the cloaking module as above, you must define        #
  # cloak keys, and optionally a cloak prefix as shown below. The cloak #
  # keys must be shared across the network for correct cloaking.        #
  #                                                                     #
         key="secret"
         prefix="net-">
  
 -<module name="m_close.so">
 -<module name="m_conn_umodes.so">
 +<module name="close">
 +<module name="conn_umodes">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Connectban: Provides IP connection throttling. Any IP range that connects
  #<connectban threshold="10" duration="10m" ipv4cidr="32" ipv6cidr="128">
  # This allows for 10 connections in an hour with a 10 minute ban if that is exceeded.
  #
 -#<module name="m_connectban.so">
 +#<module name="connectban">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Connection throttle module.
 -#<module name="m_connflood.so">
 +#<module name="connflood">
  #
  #-#-#-#-#-#-#-#-#-#-#- CONTHROTTLE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
  #  seconds, maxconns -  Amount of connections per <seconds>.
  #<connflood seconds="30" maxconns="3" timeout="30"
  #   quitmsg="Throttled" bootwait="10">
  
 -<module name="m_deaf.so">
 -<module name="m_dnsbl.so">
 -<module name="m_gecosban.so">
 -<module name="m_globalload.so">
 -<module name="m_ident.so">
 +<module name="deaf">
 +<module name="dnsbl">
 +<module name="gecosban">
 +<module name="globalload">
 +<module name="ident">
  <ident timeout="1">
 -<module name="m_inviteexception.so">
 -<module name="m_joinflood.so">
 -<module name="m_knock.so">
 -<module name="m_namesx.so">
 -<module name="m_operchans.so">
 -<module name="m_operlog.so">
 -<module name="m_opermodes.so">
 -<module name="m_password_hash.so">
 -<module name="m_permchannels.so">
 -<module name="m_muteban.so">
 -<module name="m_redirect.so">
 +<module name="inviteexception">
 +<module name="joinflood">
 +<module name="knock">
 +<module name="namesx">
 +<module name="operchans">
 +<module name="operlog">
 +<module name="opermodes">
 +<module name="password_hash">
 +<module name="permchannels">
 +<module name="muteban">
 +<module name="redirect">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Regular expression provider for glob or wildcard (?/*) matching.
 -# You must have at least 1 provider loaded to use m_filter or m_rline
 -# modules. This module has no additional requirements, as it uses the
 -# matching already present in InspIRCd core.
 -#<module name="m_regex_glob.so">
 +# You must have at least 1 provider loaded to use the filter or the
 +# rline modules. This module has no additional requirements, as it uses
 +# the matching already present in InspIRCd core.
 +#<module name="regex_glob">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Regular expression provider for PCRE (Perl-Compatible Regular
  # Expressions). You need libpcre installed to compile and load this
 -# module. You must have at least 1 provider loaded to use m_filter or
 -# m_rline.
 -#<module name="m_regex_pcre.so">
 +# module. You must have at least 1 provider loaded to use the filter or
 +# the rline module.
 +#<module name="regex_pcre">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Regular expression provider for POSIX regular expressions.
  # You shouldn't need any additional libraries on a POSIX-compatible
  # system (ie: any Linux, BSD, but not Windows). You must have at least
 -# 1 provider loaded to use m_filter or m_rline.
 +# 1 provider loaded to use the filter or the rline module.
  # On POSIX-compliant systems, regex syntax can be found by using the
  # command: 'man 7 regex'.
 -#<module name="m_regex_posix.so">
 +#<module name="regex_posix">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Registered users only channel creation
  #
  # You probably *DO NOT* want to load this module on a public network.
  #
 -#<module name="m_regonlycreate.so">
 +#<module name="regonlycreate">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Ban users through regular expression patterns
 -#<module name="m_rline.so">
 +#<module name="rline">
  #
  #-#-#-#-#-#-#-#-#-#-#-#- RLINE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
  #
  # Also, this is where you set what Regular Expression engine is to be
  # used. If you ever change it while running, all of your R-Lines will be
  # wiped. This is the regex engine used by all R-Lines set, and
 -# m_regex_<engine>.so must be loaded, or rline will be nonfunctional
 +# regex_<engine> must be loaded, or rline will be nonfunctional
  # until you load it or change the engine to one that is loaded.
  #
  #<rline matchonnickchange="yes" engine="pcre">
  # use glob. For this reason, is recommended to use a real regex engine
  # so that at least \s or [[:space:]] is available.
  
 -<module name="m_sasl.so">
 -<module name="m_servprotect.so">
 -<module name="m_services_account.so">
 -<module name="m_sethost.so">
 -<module name="m_serverban.so">
 -<module name="m_showwhois.so">
 +<module name="sasl">
 +<module name="servprotect">
 +<module name="services_account">
 +<module name="sethost">
 +<module name="serverban">
 +<module name="showwhois">
  <showwhois opersonly="yes" showfromopers="yes">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # channel mode +z and the 'z' extban which matches SSL client
  # certificate fingerprints.
  # Does not do anything useful without a working SSL module (see below).
 -#<module name="m_sslmodes.so">
 +#<module name="sslmodes">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # GnuTLS SSL module: Adds support for SSL connections using GnuTLS,
  # if enabled. You must answer 'yes' in ./configure when asked or
  # manually symlink the source for this module from the directory
  # src/modules/extra, if you want to enable this, or it will not load.
 -#<module name="m_ssl_gnutls.so">
 +#<module name="ssl_gnutls">
  #
  #-#-#-#-#-#-#-#-#-#-#-  GNUTLS CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# m_ssl_gnutls.so is too complex to describe here, see the wiki:      #
 -# https://wiki.inspircd.org/Modules/2.0/ssl_gnutls                    #
 +# ssl_gnutls is too complex to describe here, see the wiki:           #
- # http://wiki.inspircd.org/Modules/ssl_gnutls                         #
++# https://wiki.inspircd.org/Modules/3.0/ssl_gnutls                    #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SSL Info module: Allows users to retrieve information about other
  # user's peer SSL certificates and keys. This can be used by client
 -# scripts to validate users. For this to work, one of m_ssl_gnutls.so
 -# or m_ssl_openssl.so must be loaded. This module also adds the
 +# scripts to validate users. For this to work, one of ssl_gnutls
 +# or ssl_openssl must be loaded. This module also adds the
  # "* <user> is using a secure connection" whois line, the ability for
 -# opers to use SSL fingerprints to verify their identity and the ability
 -# to force opers to use SSL connections in order to oper up.
 +# opers to use SSL cert fingerprints to verify their identity and the
 +# ability to force opers to use SSL connections in order to oper up.
  # It is highly recommended to load this module especially if
  # you use SSL on your network.
  # For how to use the oper features, please see the first example <oper> tag
  # in opers.conf.example.
  #
 -#<module name="m_sslinfo.so">
 +#<module name="sslinfo">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # OpenSSL SSL module: Adds support for SSL connections using OpenSSL,
  # if enabled. You must answer 'yes' in ./configure when asked or symlink
  # the source for this module from the directory src/modules/extra, if
  # you want to enable this, or it will not load.
 -#<module name="m_ssl_openssl.so">
 +#<module name="ssl_openssl">
  #
  #-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# m_ssl_openssl.so is too complex to describe here, see the wiki:     #
 -# https://wiki.inspircd.org/Modules/2.0/ssl_openssl                   #
 +# ssl_openssl is too complex to describe here, see the wiki:          #
- # http://wiki.inspircd.org/Modules/ssl_openssl                        #
++# https://wiki.inspircd.org/Modules/3.0/ssl_openssl                   #
  
 -<module name="m_stripcolor.so">
 -<module name="m_svshold.so">
 -<module name="m_tline.so">
 -<module name="m_uhnames.so">
 -<module name="m_watch.so">
 +<module name="stripcolor">
 +<module name="svshold">
 +<module name="tline">
 +<module name="uhnames">
 +<module name="watch">
  <watch maxentries="32">
 -<module name="m_xline_db.so">
 +<module name="xline_db">
  
 -<module name="m_spanningtree.so">
 +<module name="spanningtree">
index 45a0ac3d6d4033aa172d170dc91af1d9bbe1b259,65d71339455709cf2ca2b430798282b11ea3312c..8ebdd2c1f6db5f137efef6db23bf7bd5580562df
@@@ -1,8 -1,8 +1,8 @@@
 -<module name="m_md5.so">
 -<module name="m_sha256.so">
 +<module name="md5">
 +<module name="sha256">
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Alias module: Allows you to define server-side command aliases.
 -<module name="m_alias.so">
 +<module name="alias">
  <fantasy prefix="!" allowbots="no">
  # Aliases
  <alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
  #<alias text="NICKSERV" format=":IDENTIFY *" replace="PRIVMSG NickServ :IDENTIFY $3-"
  #  requires="NickServ" uline="yes">
  
 -<module name="m_allowinvite.so">
 -<module name="m_alltime.so">
 -<module name="m_auditorium.so">
 +<module name="allowinvite">
 +<module name="alltime">
 +<module name="auditorium">
  <auditorium showops="yes" operoverride="yes">
 -<module name="m_banexception.so">
 -<module name="m_blockcaps.so">
 +<module name="banexception">
 +<module name="blockcaps">
  <blockcaps percent="50"
             minlen="5"
             capsmap="ABCDEFGHIJKLMNOPQRSTUVWXYZ! ">
 -<module name="m_blockcolor.so">
 -<module name="m_botmode.so">
 -<module name="m_censor.so">
 +<module name="blockcolor">
 +<module name="botmode">
 +<module name="censor">
  <include file="inspircd.censor.example">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # CGI:IRC module: Adds support for automatic host changing in CGI:IRC
  # (http://cgiirc.sourceforge.net).
 -#<module name="m_cgiirc.so">
 +#<module name="cgiirc">
  #
  #-#-#-#-#-#-#-#-#-#-#-# CGIIRC  CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
  #
 -# Optional - If you specify to use m_cgiirc, then you must specify one
 +# Optional - If you specify to use cgiirc, then you must specify one
  # or more cgihost tags which indicate authorised CGI:IRC servers which
  # will be connecting to your network, and an optional cgiirc tag.
- # For more information see: http://wiki.inspircd.org/Modules/cgiirc
 -# For more information see: https://wiki.inspircd.org/Modules/2.0/cgiirc
++# For more information see: https://wiki.inspircd.org/Modules/3.0/cgiirc
  #
  # Set to yes if you want to notice opers when CGI clients connect
  # <cgiirc opernotice="no">
  # sessions maximum.
  #
  
 -<module name="m_chanfilter.so">
 +<module name="chanfilter">
  <chanfilter hidemask="yes">
  
 -<module name="m_chanprotect.so">
 -
 -<chanprotect
 -      noservices="no"
 -      qprefix="~"
 -      aprefix="&amp;"
 -      deprotectself="yes"
 -      deprotectothers="yes">
 -
 -<module name="m_check.so">
 -<module name="m_chghost.so">
 +<module name="check">
 +<module name="chghost">
  <hostname charmap="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_/0123456789">
  
 -<module name="m_chgident.so">
 -<module name="m_chgname.so">
 -<module name="m_cloaking.so">
 +<module name="chgident">
 +<module name="chgname">
 +<module name="cloaking">
  <cloak mode="half"
         key="secret"
         prefix="net-">
  
 -<module name="m_close.so">
 -<module name="m_clones.so">
 -<module name="m_commonchans.so">
 +<module name="close">
 +<module name="clones">
 +<module name="commonchans">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Auto join on connect module: Allows you to force users to join one
  # or more channels automatically upon connecting to the server.
 -#<module name="m_conn_join.so">
 +#<module name="conn_join">
  #
  #-#-#-#-#-#-#-#-#-#-#-#- CONNJOIN CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
  #
 -# If you have m_conn_join.so loaded, you can configure it using the
 -# follow values:
 +# If you have the conn_join module loaded, you can configure it below:
  #
  #<autojoin channel="#one,#two,#three">
  
 -<module name="m_conn_umodes.so">
 -<module name="m_cycle.so">
 +<module name="conn_umodes">
 +<module name="cycle">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Connection throttle module.
 -#<module name="m_connflood.so">
 +#<module name="connflood">
  #
  #-#-#-#-#-#-#-#-#-#-#- CONTHROTTLE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
  #  seconds, maxconns -  Amount of connections per <seconds>.
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # DCCALLOW module: Adds the /DCCALLOW command.
 -<module name="m_dccallow.so">
 +<module name="dccallow">
  #
  #-#-#-#-#-#-#-#-#-#-#-  DCCALLOW CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
  #  blockchat         - Whether to block DCC CHAT as well as DCC SEND
  #
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  
 -<module name="m_deaf.so">
 -<module name="m_denychans.so"> 
 +<module name="deaf">
 +<module name="denychans"> 
  #<badchan name="#gods*" allowopers="yes" reason="Tortoises!">         #
  #<badchan name="#heaven" redirect="#hell" reason="Nice try!">         #
  
 -<module name="m_devoice.so">
 -
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Filter module: Provides message filtering, similar to SPAMFILTER.
 -<module name="m_filter.so">
 +<module name="filter">
  #                                                                     #
 -# This module depends upon a regex provider such as m_regex_pcre or   #
 -# m_regex_glob to function. You must specify which of these you want  #
 -# m_filter to use via the tag below.                                  #
 +# This module depends upon a regex provider such as regex_pcre or     #
 +# regex_glob to function. You must specify which of these you want    #
 +# the filter module to use via the tag below.                         #
  #                                                                     #
  # Valid engines are:                                                  #
  #                                                                     #
 -# glob   - Glob patterns, provided via m_regex_glob.                  #
 -# pcre   - PCRE regexps, provided via m_regex_pcre, needs libpcre.    #
 -# tre    - TRE regexps, provided via m_regex_tre, requires libtre.    #
 -# posix  - POSIX regexps, provided via m_regex_posix, not available   #
 +# glob   - Glob patterns, provided via regex_glob.                    #
 +# pcre   - PCRE regexps, provided via regex_pcre, needs libpcre.      #
 +# tre    - TRE regexps, provided via regex_tre, requires libtre.      #
 +# posix  - POSIX regexps, provided via regex_posix, not available     #
  #          on Windows, no dependencies on other operating systems.    #
 -# stdlib - stdlib regexps, provided via m_regex_stdlib, see comment   #
 +# stdlib - stdlib regexps, provided via regex_stdlib, see comment     #
  #          at the <module> tag for info on availability.              #
  #                                                                     #
  <filteropts engine="glob">
  #                                                                     #
  # Your choice of regex engine must match on all servers network-wide.
  #
 -# You may specify specific channels that are exempt from being filtered:
 -#<exemptfromfilter channel="#blah">
 +# To learn more about the configuration of this module, read          #
 +# examples/filter.conf.example, which covers the various types of     #
 +# filters and shows how to add exemptions.                            #
  #
  #-#-#-#-#-#-#-#-#-#-#-  FILTER  CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# Optional - If you specify to use the m_filter module, then          #
 +# Optional - If you specify to use the filter module, then            #
  # specfiy below the path to the filter.conf file, or define some      #
  # <filter> tags.                                                      #
  #                                                                     #
  #<include file="filter.conf">
  
 -<module name="m_gecosban.so">
 -<module name="m_globops.so">
 -<module name="m_globalload.so">
 -<module name="m_halfop.so">
 -<module name="m_helpop.so">
 +<module name="gecosban">
 +<module name="globops">
 +<module name="globalload">
 +<module name="halfop">
 +<module name="helpop">
  <include file="inspircd.helpop-full.example">
  
 -<module name="m_hidechans.so">
 +<module name="hidechans">
  <hidechans affectsopers="false">
  
 -<module name="m_hideoper.so">
 -<module name="m_ident.so">
 +<module name="hideoper">
 +<module name="ident">
  <ident timeout="1">
 -<module name="m_inviteexception.so">
 -<module name="m_joinflood.so">
 -<module name="m_jumpserver.so">
 -<module name="m_knock.so">
 -<module name="m_messageflood.so">
 -<module name="m_namesx.so">
 -<module name="m_nickflood.so">
 -<module name="m_noctcp.so">
 -<module name="m_nokicks.so">
 -<module name="m_nonicks.so">
 -<module name="m_nopartmsg.so">
 -<module name="m_nonotice.so">
 -<module name="m_operchans.so">
 +<module name="inviteexception">
 +<module name="joinflood">
 +<module name="jumpserver">
 +<module name="knock">
 +<module name="messageflood">
 +<module name="namesx">
 +<module name="nickflood">
 +<module name="noctcp">
 +<module name="nokicks">
 +<module name="nonicks">
 +<module name="nopartmsg">
 +<module name="nonotice">
 +<module name="operchans">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Oper join module: Auto-joins opers to a channel upon oper-up.
 -# This module is oper-only. For the user equivalent, see m_conn_join.
 -<module name="m_operjoin.so">
 +# This module is oper-only. For the user equivalent, see the conn_join
 +# module.
 +<module name="operjoin">
  #
  #-#-#-#-#-#-#-#-#-#-#   OPERJOIN CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# If you are using the m_operjoin.so module, specify options here:    #
 +# If you are using the operjoin module, specify options here:         #
  #                                                                     #
  # channel     -      The channel name to join, can also be a comma    #
  #                    separated list eg. "#channel1,#channel2".        #
  #
  #<type name="Helper" autojoin="#help" classes="...">
  
 -<module name="m_operlog.so">
 +<module name="operlog">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Oper MOTD module: Provides support for separate message of the day
  # on oper-up.
  # This module is oper-only.
 -#<module name="m_opermotd.so">
 +#<module name="opermotd">
  #
  #-#-#-#-#-#-#-#-#-#-#   OPERMOTD CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# If you are using the m_opermotd.so module, specify the motd here    #
 +# If you are using the opermotd module, specify the motd here         #
  #                                                                     #
  # onoper        - If on, the message is sent on /OPER, otherwise it's #
  #                 only sent when /OPERMOTD is used.                   #
  #                                                                     #
  #<opermotd file="oper.motd" onoper="yes">
  
 -<module name="m_override.so">
 +<module name="override">
  #-#-#-#-#-#-#-#-#-#-#   OVERRIDE CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# m_override.so is too complex to describe here, see the wiki:        #
 -# https://wiki.inspircd.org/Modules/2.0/override                      #
 +# The override module is too complex to describe here, see the wiki:  #
- # http://wiki.inspircd.org/Modules/override                           #
++# https://wiki.inspircd.org/Modules/3.0/override                      #
  
 -<module name="m_operlevels.so">
 -<module name="m_opermodes.so">
 -<module name="m_password_hash.so">
 -<module name="m_muteban.so">
 +<module name="operlevels">
 +<module name="opermodes">
 +<module name="password_hash">
 +<module name="muteban">
  
 -<module name="m_redirect.so">
 -<module name="m_regex_glob.so">
 +<module name="redirect">
 +<module name="regex_glob">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Regular expression provider for PCRE (Perl-Compatible Regular
  # Expressions). You need libpcre installed to compile and load this
 -# module. You must have at least 1 provider loaded to use m_filter or
 -# m_rline.
 -#<module name="m_regex_pcre.so">
 +# module. You must have at least 1 provider loaded to use the filter
 +# or the rline module.
 +#<module name="regex_pcre">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Regular expression provider for POSIX Regular Expressions.
  # You shouldn't need any additional libraries on a POSIX-compatible
  # system (ie: any Linux, BSD, but not Windows). You must have at least
 -# 1 provider loaded to use m_filter or m_rline.
 +# 1 provider loaded to use the filter or rline module.
  # On POSIX-compliant systems, regex syntax can be found by using the
  # command: 'man 7 regex'.
 -#<module name="m_regex_posix.so">
 +#<module name="regex_posix">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Regular expression provider for TRE Regular Expressions.
  # if you are most familiar with the syntax of /spamfilter from there,
  # this is the provider you want. You need libtre installed in order
  # to compile and load this module.
 -#<module name="m_regex_tre.so">
 +#<module name="regex_tre">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Registered users only channel creation module. If enabled, only
  #
  # You probably *DO NOT* want to load this module on a public network.
  #
 -#<module name="m_regonlycreate.so">
 +#<module name="regonlycreate">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Restricted channels module: Allows only opers to create channels.
  #
  # You probably *DO NOT* want to load this module on a public network.
  #
 -#<module name="m_restrictchans.so">
 +#<module name="restrictchans">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Restrict message module: Allows users to only message opers.
  #
  # You probably *DO NOT* want to load this module on a public network.
  #
 -#<module name="m_restrictmsg.so">
 -
 -<module name="m_sajoin.so">
 -<module name="m_sakick.so">
 -<module name="m_samode.so">
 -<module name="m_sanick.so">
 -<module name="m_sapart.so">
 -<module name="m_saquit.so">
 -<module name="m_satopic.so">
 -<module name="m_servprotect.so">
 -<module name="m_seenicks.so">
 -<module name="m_setidle.so">
 -<module name="m_services_account.so">
 -<module name="m_sethost.so">
 -<module name="m_setident.so">
 -<module name="m_setname.so">
 -<module name="m_showwhois.so">
 +#<module name="restrictmsg">
 +
 +<module name="sajoin">
 +<module name="sakick">
 +<module name="samode">
 +<module name="sanick">
 +<module name="sapart">
 +<module name="saquit">
 +<module name="satopic">
 +<module name="servprotect">
 +<module name="seenicks">
 +<module name="setidle">
 +<module name="services_account">
 +<module name="sethost">
 +<module name="setident">
 +<module name="setname">
 +<module name="showwhois">
  <showwhois opersonly="yes" showfromopers="yes">
  
 -<module name="m_shun.so">
 +<module name="shun">
  <shun enabledcommands="PING PONG QUIT PART JOIN" notifyuser="no" affectopers="no">
  
 -<module name="m_sslmodes.so">
 +<module name="sslmodes">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # GnuTLS SSL module: Adds support for SSL connections using GnuTLS,
  # if enabled. You must answer 'yes' in ./configure when asked or symlink
  # the source for this module from the directory src/modules/extra, if
  # you want to enable this, or it will not load.
 -#<module name="m_ssl_gnutls.so">
 +#<module name="ssl_gnutls">
  #
  #-#-#-#-#-#-#-#-#-#-#-  GNUTLS CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# m_ssl_gnutls.so is too complex to describe here, see the wiki:      #
 -# https://wiki.inspircd.org/Modules/2.0/ssl_gnutls                    #
 +# ssl_gnutls is too complex to describe here, see the wiki:           #
- # http://wiki.inspircd.org/Modules/ssl_gnutls                         #
++# https://wiki.inspircd.org/Modules/3.0/ssl_gnutls                    #
  
 -<module name="m_sslinfo.so">
 +<module name="sslinfo">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # OpenSSL SSL module: Adds support for SSL connections using OpenSSL,
  # if enabled. You must answer 'yes' in ./configure when asked or symlink
  # the source for this module from the directory src/modules/extra, if
  # you want to enable this, or it will not load.
 -#<module name="m_ssl_openssl.so">
 +#<module name="ssl_openssl">
  #
  #-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
 -# m_ssl_openssl.so is too complex to describe here, see the wiki:     #
 -# https://wiki.inspircd.org/Modules/2.0/ssl_openssl                   #
 -
 -<module name="m_stripcolor.so">
 -<module name="m_svshold.so">
 -<module name="m_swhois.so">
 -<module name="m_tline.so">
 -<module name="m_uhnames.so">
 -<module name="m_userip.so">
 -<module name="m_watch.so">
 +# ssl_openssl is too complex to describe here, see the wiki:          #
- # http://wiki.inspircd.org/Modules/ssl_openssl                        #
++# https://wiki.inspircd.org/Modules/3.0/ssl_openssl                   #
 +
 +<module name="stripcolor">
 +<module name="svshold">
 +<module name="swhois">
 +<module name="tline">
 +<module name="uhnames">
 +<module name="userip">
 +<module name="watch">
  <watch maxentries="32">
  
 -<module name="m_spanningtree.so">
 +<module name="spanningtree">
diff --combined include/command_parse.h
index c3d67af23906c447f45fa704eb862416d02083da,f9e3a740c3012bc3e955f2b6da841600707cdde7..ec5ebba485ad1f02c82f15f559c96ff54d603024
@@@ -20,7 -20,8 +20,7 @@@
   */
  
  
 -#ifndef COMMAND_PARSE_H
 -#define COMMAND_PARSE_H
 +#pragma once
  
  /** This class handles command management and parsing.
   * It allows you to add and remove commands from the map,
   */
  class CoreExport CommandParser
  {
 - private:
 -      /** Process a parameter string into a list of items
 -       * @param command_p The output list of items
 -       * @param parameters The input string
 -       * @return The number of parameters parsed into command_p
 -       */
 -      int ProcessParameters(std::vector<std::string>& command_p, char* parameters);
 + public:
-       typedef TR1NS::unordered_map<std::string, Command*> CommandMap;
++      typedef TR1NS::unordered_map<std::string, Command*, irc::insensitive, irc::StrHashComp> CommandMap;
  
 + private:
        /** Process a command from a user.
         * @param user The user to parse the command for
         * @param cmd The command string to process
         */
 -      bool ProcessCommand(LocalUser *user, std::string &cmd);
 +      void ProcessCommand(LocalUser* user, std::string& cmd);
  
 - public:
        /** Command list, a hash_map of command names to Command*
         */
 -      Commandtable cmdlist;
 +      CommandMap cmdlist;
  
 + public:
        /** Default constructor.
         */
        CommandParser();
  
 +      /** Get a command name -> Command* map containing all client to server commands
 +       * @return A map of command handlers keyed by command names
 +       */
 +      const CommandMap& GetCommands() const { return cmdlist; }
 +
        /** Calls the handler for a given command.
         * @param commandname The command to find. This should be in uppercase.
         * @param parameters Parameter list
         * @param user The user to call the handler on behalf of
 +       * @param cmd If non-NULL and the command was executed it is set to the command handler,
 +       * otherwise it isn't written to.
         * @return This method will return CMD_SUCCESS if the command handler was found and called,
         * and the command completeld successfully. It will return CMD_FAILURE if the command handler was found
         * and called, but the command did not complete successfully, and it will return CMD_INVALID if the
         * command simply did not exist at all or the wrong number of parameters were given, or the user
         * was not privilaged enough to execute the command.
         */
 -      CmdResult CallHandler(const std::string &commandname, const std::vector<std::string>& parameters, User *user);
 +      CmdResult CallHandler(const std::string& commandname, const std::vector<std::string>& parameters, User* user, Command** cmd = NULL);
  
        /** Get the handler function for a command.
         * @param commandname The command required. Always use uppercase for this parameter.
         */
        Command* GetHandler(const std::string &commandname);
  
 -      /** This function returns true if a command is valid with the given number of parameters and user.
 -       * @param commandname The command name to check
 -       * @param pcnt The parameter count
 -       * @param user The user to check against
 -       * @return If the user given has permission to execute the command, and the parameter count is
 -       * equal to or greater than the minimum number of parameters to the given command, then this
 -       * function will return true, otherwise it will return false.
 -       */
 -      bool IsValidCommand(const std::string &commandname, unsigned int pcnt, User * user);
 -
 -      /** LoopCall is used to call a command classes handler repeatedly based on the contents of a comma seperated list.
 -       * There are two overriden versions of this method, one of which takes two potential lists and the other takes one.
 -       * We need a version which takes two potential lists for JOIN, because a JOIN may contain two lists of items at once,
 +      /** LoopCall is used to call a command handler repeatedly based on the contents of a comma seperated list.
 +       * There are two ways to call this method, either with one potential list or with two potential lists.
 +       * We need to handle two potential lists for JOIN, because a JOIN may contain two lists of items at once:
         * the channel names and their keys as follows:
         *
         * JOIN \#chan1,\#chan2,\#chan3 key1,,key3
         *
 -       * Therefore, we need to deal with both lists concurrently. The first instance of this method does that by creating
 -       * two instances of irc::commasepstream and reading them both together until the first runs out of tokens.
 -       * The second version is much simpler and just has the one stream to read, and is used in NAMES, WHOIS, PRIVMSG etc.
 -       * Both will only parse until they reach ServerInstance->Config->MaxTargets number of targets, to stop abuse via spam.
 +       * Therefore, we need to deal with both lists concurrently. If there are two lists then the method reads
 +       * them both together until the first runs out of tokens.
 +       * With one list it is much simpler, and is used in NAMES, WHOIS, PRIVMSG etc.
 +       *
 +       * If there is only one list and there are duplicates in it, then the command handler is only called for
 +       * unique items. Entries are compared using "irc comparison".
 +       * If the usemax parameter is true (the default) the function only parses until it reaches
 +       * ServerInstance->Config->MaxTargets number of targets, to stop abuse via spam.
 +       *
 +       * The OnPostCommand hook is executed for each item after it has been processed by the handler, with the
 +       * original line parameter being empty (to indicate that the command in that form was created by this function).
 +       * This only applies if the user executing the command is local.
 +       *
 +       * If there are two lists and the second list runs out of tokens before the first list then parameters[extra]
 +       * will be an EMPTY string when Handle() is called for the remaining tokens in the first list, even if it is
 +       * in the middle of parameters[]! Moreover, empty tokens in the second list are allowed, and those will also
 +       * result in the appropiate entry being empty in parameters[].
 +       * This is different than what command handlers usually expect; the command parser only allows an empty param
 +       * as the last item in the vector.
         *
         * @param user The user who sent the command
 -       * @param CommandObj the command object to call for each parameter in the list
 -       * @param parameters Parameter list as an array of array of char (that's not a typo).
 +       * @param handler The command handler to call for each parameter in the list
 +       * @param parameters Parameter list as a vector of strings
         * @param splithere The first parameter index to split as a comma seperated list
 -       * @param extra The second parameter index to split as a comma seperated list
 -       * @param usemax Limit the command to MaxTargets targets
 -       * @return This function will return 1 when there are no more parameters to process. When this occurs, its
 -       * caller should return without doing anything, otherwise it should continue into its main section of code.
 +       * @param extra The second parameter index to split as a comma seperated list, or -1 (the default) if there is only one list
 +       * @param usemax True to limit the command to MaxTargets targets (default), or false to process all tokens
 +       * @return This function returns true when it identified a list in the given parameter and finished calling the
 +       * command handler for each entry on the list. When this occurs, the caller should return without doing anything,
 +       * otherwise it should continue into its main section of code.
         */
 -      int LoopCall(User* user, Command* CommandObj, const std::vector<std::string>& parameters, unsigned int splithere, int extra = -1, bool usemax = true);
 +      static bool LoopCall(User* user, Command* handler, const std::vector<std::string>& parameters, unsigned int splithere, int extra = -1, bool usemax = true);
  
        /** Take a raw input buffer from a recvq, and process it on behalf of a user.
         * @param buffer The buffer line to process
         * @param user The user to whom this line belongs
         */
 -      bool ProcessBuffer(std::string &buffer,LocalUser *user);
 +      void ProcessBuffer(std::string &buffer,LocalUser *user);
  
        /** Add a new command to the commands hash
         * @param f The new Command to add to the list
         */
        void RemoveCommand(Command* x);
  
 -      /** Translate nicknames in a string into UIDs, based on the TranslationType given.
 -       * @param to The translation type to use for the process.
 -       * @param source The input string
 -       * @param dest The output string, it is safe to pass source and dest as the same variable only for translation type TR_TEXT.
 -       * @return returns the number of substitutions made. Will always be 0 or 1
 +      /** Translate a single item based on the TranslationType given.
 +       * @param to The translation type to use for the process
 +       * @param item The input string
 +       * @param dest The output string. The translation result will be appended to this string
 +       * @param custom_translator Used to translate the parameter if the translation type is TR_CUSTOM, if NULL, TR_CUSTOM will act like TR_TEXT
 +       * @param paramnumber The index of the parameter we are translating.
         */
 -      int TranslateUIDs(TranslateType to, const std::string &source, std::string &dest);
 +      static void TranslateSingleParam(TranslateType to, const std::string& item, std::string& dest, CommandBase* custom_translator = NULL, unsigned int paramnumber = 0);
  
        /** Translate nicknames in a list of strings into UIDs, based on the TranslateTypes given.
         * @param to The translation types to use for the process. If this list is too short, TR_TEXT is assumed for the rest.
         * @param source The strings to translate
 -       * @param dest The output string
         * @param prefix_final True if the final source argument should have a colon prepended (if it could contain a space)
 -       * @param custom_translator Used to translate the parameter if the TR_CUSTOM type is found in to
 -       * @return returns the number of substitutions made.
 +       * @param custom_translator Used to translate the parameter if the translation type is TR_CUSTOM, if NULL, TR_CUSTOM will act like TR_TEXT
 +       * @return dest The output string
         */
 -      int TranslateUIDs(const std::vector<TranslateType> to, const std::vector<std::string> &source, std::string &dest, bool prefix_final = false, Command* custom_translator = NULL);
 +      static std::string TranslateUIDs(const std::vector<TranslateType>& to, const std::vector<std::string>& source, bool prefix_final = false, CommandBase* custom_translator = NULL);
  };
  
  /** A lookup table of values for multiplier characters used by
@@@ -173,3 -165,5 +173,3 @@@ const int duration_multi[] 
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
  };
 -
 -#endif
diff --combined modulemanager
index 7471dcc77cab66c188b9e5dcad7ae9c2dafc9fd4,bc46194082c4ec724d6e4108d03789ea40941f40..3e4eee9c95fcd86d24a11dcf7f60a4bbff49e458
@@@ -3,7 -3,6 +3,7 @@@
  #
  # InspIRCd -- Internet Relay Chat Daemon
  #
 +#   Copyright (C) 2012-2017 Peter Powell <petpow@saberuk.com>
  #   Copyright (C) 2008-2009 Robin Burchell <robin+git@viroteck.net>
  #
  # This file is part of InspIRCd.  InspIRCd is free software: you can
  #
  
  
 -use strict;
 -use warnings FATAL => qw(all);
 -
 -BEGIN {
 -      require 5.8.0;
 -      push @INC, '.';
 -}
 -
  BEGIN {
 -      # HACK: for some reason this needs to be in a second BEGIN block
 -      # or it doesn't receive the updated @INC from above.
 -      use make::configure;
 -      unless (module_installed("LWP::Simple")) {
 +      require 5.10.0;
 +      unless (eval "use LWP::Simple; 1") {
                die "Your system is missing the LWP::Simple Perl module!";
        }
 -      unless (module_installed("Crypt::SSLeay") || module_installed("IO::Socket::SSL")) {
 +      unless (eval "use Crypt::SSLeay; 1" || eval "use IO::Socket::SSL; 1") {
                die "Your system is missing the Crypt::SSLeay or IO::Socket::SSL Perl modules!";
        }
  }
  
 -use LWP::Simple;
 +use feature ':5.10';
 +use strict;
 +use warnings FATAL => qw(all);
 +
 +use File::Basename qw(basename);
 +use FindBin        qw($RealDir);
  
 -our @modlist;
 +use lib $RealDir;
 +use make::common;
 +use make::console;
  
  my %installed;
  # $installed{name} = $version
@@@ -73,7 -75,7 +73,7 @@@ sub parse_url 
        }
  
        my $mod;
-       for (split /\n+/, $response->decoded_content) {
+       for (split /\n+/, $response->content) {
                s/^\s+//; # ignore whitespace at start
                next if /^#/;
                if (/^module (\S+) (\S+) (\S+)/) {
  }
  
  # hash of installed module versions from our mini-database, key (m_foobar) to version (00abacca..).
 -my %mod_versions;
 +my %mod_versions = read_config_file '.modulemanager';
  
  # useless helper stub
  sub getmodversion {
        return $mod_versions{$file};
  }
  
 -# read in installed versions
 -if (-e '.modulemanager')
 -{
 -      open SRC, '.modulemanager' or die ".modulemanager exists but i can't read it: $!";
 -      while (<SRC>)
 -      {
 -              s/\n//;
 -              (my $mod, my $ver) = split(/ /, $_);
 -              $mod_versions{$mod} = $ver;
 -      }
 -      close SRC;
 -}
 -
  # read in external URL sources
  open SRC, 'sources.list' or die "Could not open sources.list: $!";
  while (<SRC>) {
  }
  close SRC;
  
 -getmodules(1);
 -
  # determine core version
  `./src/version.sh` =~ /InspIRCd-([0-9.]+)/ or die "Cannot determine inspircd version";
  $installed{core} = $1;
@@@ -145,8 -162,9 +145,8 @@@ $modules{core}{$1} = 
  };
  
  # set up core module list
 -for my $modname (@modlist) {
 -      my $mod = "m_$modname";
 -      my $modfile = "src/modules/$mod.cpp";
 +for my $modname (<src/modules/m_*.cpp>) {
 +      my $mod = basename($modname, '.cpp');
        my $ver = getmodversion($mod) || '0.0';
        $ver =~ s/\$Rev: (.*) \$/$1/; # for storing revision in SVN
        $installed{$mod} = $ver;
@@@ -253,8 -271,10 +253,8 @@@ sub resolve_deps 
        }
  }
  
 -my $action = $#ARGV >= 0 ? lc shift @ARGV : 'help';
 -
 -if ($action eq 'install') {
 -      for my $mod (@ARGV) {
 +command 'install', 'Install a third-party module', sub {
 +      for my $mod (@_) {
                my $vers = $mod =~ s/=([-0-9.]+)// ? $1 : undef;
                $mod = lc $mod;
                unless ($modules{$mod}) {
                }
                $todo{$mod} = $ver;
        }
 -} elsif ($action eq 'upgrade') {
 +};
 +
 +command 'upgrade', 'Upgrade a third-party module', sub {
        my @installed = sort keys %installed;
        for my $mod (@installed) {
                next unless $mod =~ /^m_/;
                        %todo = %saved;
                }
        }
 -} elsif ($action eq 'list') {
 +};
 +
 +command 'list', 'List available third-party modules', sub {
        my @all = sort keys %modules;
        for my $mod (@all) {
                my @vers = sort { ver_cmp() } keys %{$modules{$mod}};
                my $vers = join ' ', map { $_ eq $instver ? "\e[1m$_\e[m" : $_ } @vers;
                print "$mod ($vers) - $desc\n";
        }
 -} else {
 -      print <<ENDUSAGE
 -Use: $0 <action> <args>
 -Action is one of the following
 - install   install new modules
 - upgrade   upgrade installed modules
 - list      lists available modules
 -
 -For installing a package, specify its name or name=version to force the
 -installation of a specific version.
 -ENDUSAGE
 -;exit 1;
 -}
 +      exit 0;
 +};
 +
 +execute_command @ARGV;
  
  resolve_deps(0);
  
  $| = 1; # immediate print of lines without \n
  
 -print "Processing changes for $action...\n";
 +print "Processing changes...\n";
  for my $mod (keys %installed) {
        next if $todo{$mod};
        print "Uninstalling $mod $installed{$mod}\n";
@@@ -334,7 -359,7 +334,7 @@@ for my $mod (sort keys %todo) 
  
        if ($response->is_success) {
                open(MF, ">src/modules/$mod.cpp") or die "\nFilesystem not writable: $!";
-               print MF $response->decoded_content;
+               print MF $response->content;
                close(MF);
                print " - done\n";
        } else {
  }
  
  # write database of installed versions
 -open SRC, '>.modulemanager' or die "can't write installed versions to .modulemanager, won't be able to track upgrades properly: $!";
 -foreach my $key (keys %mod_versions)
 -{
 -      print SRC "$key $mod_versions{$key}\n";
 -}
 -close SRC;
 +write_config_file '.modulemanager', %mod_versions;
  
  print "Finished!\n";
index f76fd098dcef860a3634e748f496627f22a1c77c,0000000000000000000000000000000000000000..6c19329c30aac78f1e4e8c78f0f79ded3fa379d0
mode 100644,000000..100644
--- /dev/null
@@@ -1,64 -1,0 +1,64 @@@
-               /* XXX: This hack sets FD_CLOEXEC on all possible file descriptors, so they're closed if the execv() below succeeds.
 +/*
 + * InspIRCd -- Internet Relay Chat Daemon
 + *
 + *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
 + *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
 + *
 + * This file is part of InspIRCd.  InspIRCd is free software: you can
 + * redistribute it and/or modify it under the terms of the GNU General Public
 + * License as published by the Free Software Foundation, version 2.
 + *
 + * This program is distributed in the hope that it will be useful, but WITHOUT
 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 + * details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 + */
 +
 +
 +#include "inspircd.h"
 +#include "core_oper.h"
 +
 +CommandRestart::CommandRestart(Module* parent)
 +      : Command(parent, "RESTART", 1, 1)
 +{
 +      flags_needed = 'o';
 +      syntax = "<server>";
 +}
 +
 +CmdResult CommandRestart::Handle (const std::vector<std::string>& parameters, User *user)
 +{
 +      ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Restart: %s", user->nick.c_str());
 +      if (DieRestart::CheckPass(user, parameters[0], "restartpass"))
 +      {
 +              ServerInstance->SNO->WriteGlobalSno('a', "RESTART command from %s, restarting server.", user->GetFullRealHost().c_str());
 +
 +              DieRestart::SendError("Server restarting.");
 +
 +#ifndef _WIN32
-               execv(ServerInstance->Config->cmdline.argv[0], ServerInstance->Config->cmdline.argv);
++              /* XXX: This hack sets FD_CLOEXEC on all possible file descriptors, so they're closed if the execvp() below succeeds.
 +               * Certainly, this is not a nice way to do things and it's slow when the fd limit is high.
 +               *
 +               * A better solution would be to set the close-on-exec flag for each fd we create (or create them with O_CLOEXEC),
 +               * however there is no guarantee that third party libs will do the same.
 +               */
 +              for (int i = getdtablesize(); --i > 2;)
 +              {
 +                      int flags = fcntl(i, F_GETFD);
 +                      if (flags != -1)
 +                              fcntl(i, F_SETFD, flags | FD_CLOEXEC);
 +              }
 +#endif
 +
++              execvp(ServerInstance->Config->cmdline.argv[0], ServerInstance->Config->cmdline.argv);
 +              ServerInstance->SNO->WriteGlobalSno('a', "Failed RESTART - could not execute '%s' (%s)",
 +                      ServerInstance->Config->cmdline.argv[0], strerror(errno));
 +      }
 +      else
 +      {
 +              ServerInstance->SNO->WriteGlobalSno('a', "Failed RESTART Command from %s.", user->GetFullRealHost().c_str());
 +      }
 +      return CMD_FAILURE;
 +}
index f9986f3f1913014f5b2a2414734cc11853cdb89d,159a0b8b2318b1b336300a243da7e00c2c848cc8..3490f7fa60f66291d0a8d19ba4faf5ee2bfbe234
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
 +/// $CompilerFlags: execute("mysql_config --include" "MYSQL_CXXFLAGS")
 +/// $LinkerFlags: execute("mysql_config --libs_r" "MYSQL_LDFLAGS" "-lmysqlclient")
  
 -/* Stop mysql wanting to use long long */
 -#define NO_CLIENT_LONG_LONG
 +/// $PackageInfo: require_system("centos" "6.0" "6.99") mysql-devel
 +/// $PackageInfo: require_system("centos" "7.0") mariadb-devel
 +/// $PackageInfo: require_system("darwin") mysql-connector-c
 +/// $PackageInfo: require_system("debian") libmysqlclient-dev
 +/// $PackageInfo: require_system("ubuntu") libmysqlclient-dev
 +
 +
 +// Fix warnings about the use of `long long` on C++03.
 +#if defined __clang__
 +# pragma clang diagnostic ignored "-Wc++11-long-long"
 +#elif defined __GNUC__
 +# pragma GCC diagnostic ignored "-Wlong-long"
 +#endif
  
  #include "inspircd.h"
  #include <mysql.h>
 -#include "sql.h"
 +#include "modules/sql.h"
  
  #ifdef _WIN32
  # pragma comment(lib, "libmysql.lib")
  
  /* VERSION 3 API: With nonblocking (threaded) requests */
  
 -/* $ModDesc: SQL Service Provider module for all other m_sql* modules */
 -/* $CompileFlags: exec("mysql_config --include") */
 -/* $LinkerFlags: exec("mysql_config --libs_r") rpath("mysql_config --libs_r") */
 -
  /* THE NONBLOCKING MYSQL API!
   *
   * MySQL provides no nonblocking (asyncronous) API of its own, and its developers recommend
@@@ -76,8 -67,6 +76,6 @@@
   * if a module is ever put in a re-enterant state (stack corruption could occur, crashes, data
   * corruption, and worse, so DONT think about it until the day comes when InspIRCd is 100%
   * gauranteed threadsafe!)
-  *
-  * For a diagram of this system please see http://wiki.inspircd.org/Mysql2
   */
  
  class SQLConnection;
@@@ -99,7 -88,7 +97,7 @@@ struct RQueueIte
        RQueueItem(SQLQuery* Q, MySQLresult* R) : q(Q), r(R) {}
  };
  
 -typedef std::map<std::string, SQLConnection*> ConnMap;
 +typedef insp::flat_map<std::string, SQLConnection*> ConnMap;
  typedef std::deque<QQueueItem> QueryQueue;
  typedef std::deque<RQueueItem> ResultQueue;
  
@@@ -114,11 -103,11 +112,11 @@@ class ModuleSQL : public Modul
        ConnMap connections; // main thread only
  
        ModuleSQL();
 -      void init();
 +      void init() CXX11_OVERRIDE;
        ~ModuleSQL();
 -      void OnRehash(User* user);
 -      void OnUnloadModule(Module* mod);
 -      Version GetVersion();
 +      void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE;
 +      void OnUnloadModule(Module* mod) CXX11_OVERRIDE;
 +      Version GetVersion() CXX11_OVERRIDE;
  };
  
  class DispatcherThread : public SocketThread
   public:
        DispatcherThread(ModuleSQL* CreatorModule) : Parent(CreatorModule) { }
        ~DispatcherThread() { }
 -      virtual void Run();
 -      virtual void OnNotify();
 +      void Run();
 +      void OnNotify();
  };
  
  #if !defined(MYSQL_VERSION_ID) || MYSQL_VERSION_ID<32224
@@@ -195,17 -184,21 +193,17 @@@ class MySQLresult : public SQLResul
  
        }
  
 -      ~MySQLresult()
 -      {
 -      }
 -
 -      virtual int Rows()
 +      int Rows()
        {
                return rows;
        }
  
 -      virtual void GetCols(std::vector<std::string>& result)
 +      void GetCols(std::vector<std::string>& result)
        {
                result.assign(colnames.begin(), colnames.end());
        }
  
 -      virtual SQLEntry GetValue(int row, int column)
 +      SQLEntry GetValue(int row, int column)
        {
                if ((row >= 0) && (row < rows) && (column >= 0) && (column < (int)fieldlists[row].size()))
                {
                return SQLEntry();
        }
  
 -      virtual bool GetRow(SQLEntries& result)
 +      bool GetRow(SQLEntries& result)
        {
                if (currentrow < rows)
                {
@@@ -265,12 -258,6 +263,12 @@@ class SQLConnection : public SQLProvide
                bool rv = mysql_real_connect(connection, host.c_str(), user.c_str(), pass.c_str(), dbname.c_str(), port, NULL, 0);
                if (!rv)
                        return rv;
 +
 +              // Enable character set settings
 +              std::string charset = config->getString("charset");
 +              if ((!charset.empty()) && (mysql_set_character_set(connection, charset.c_str())))
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not set character set to \"%s\"", charset.c_str());
 +
                std::string initquery;
                if (config->readString("initialquery", initquery))
                {
@@@ -394,7 -381,12 +392,7 @@@ ModuleSQL::ModuleSQL(
  void ModuleSQL::init()
  {
        Dispatcher = new DispatcherThread(this);
 -      ServerInstance->Threads->Start(Dispatcher);
 -
 -      Implementation eventlist[] = { I_OnRehash, I_OnUnloadModule };
 -      ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
 -
 -      OnRehash(NULL);
 +      ServerInstance->Threads.Start(Dispatcher);
  }
  
  ModuleSQL::~ModuleSQL()
        }
  }
  
 -void ModuleSQL::OnRehash(User* user)
 +void ModuleSQL::ReadConfig(ConfigStatus& status)
  {
        ConnMap conns;
        ConfigTagList tags = ServerInstance->Config->ConfTags("database");
diff --combined src/modules/m_cap.cpp
index 868294fe4d6fb436a2a3189231e7bfc70bae026f,ae9e824f448c61ab2b94dfe20f29c47572c2a5e0..86de15d9559f0dbebddfac1a78d4a73ddc776632
@@@ -1,7 -1,8 +1,7 @@@
  /*
   * InspIRCd -- Internet Relay Chat Daemon
   *
 - *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
 - *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
 + *   Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
   *
   * This file is part of InspIRCd.  InspIRCd is free software: you can
   * redistribute it and/or modify it under the terms of the GNU General Public
  
  
  #include "inspircd.h"
 -#include "m_cap.h"
 +#include "modules/reload.h"
 +#include "modules/cap.h"
  
 -/* $ModDesc: Provides the CAP negotiation mechanism seen in ratbox-derived ircds */
 +namespace Cap
 +{
 +      class ManagerImpl;
 +}
  
 -/*
 -CAP LS
 -:alfred.staticbox.net CAP * LS :multi-prefix sasl
 -CAP REQ :multi-prefix
 -:alfred.staticbox.net CAP * ACK :multi-prefix
 -CAP CLEAR
 -:alfred.staticbox.net CAP * ACK :-multi-prefix
 -CAP REQ :multi-prefix
 -:alfred.staticbox.net CAP * ACK :multi-prefix
 -CAP LIST
 -:alfred.staticbox.net CAP * LIST :multi-prefix
 -CAP END
 -*/
 -
 -/** Handle /CAP
 - */
 -class CommandCAP : public Command
 +static Cap::ManagerImpl* managerimpl;
 +
 +class Cap::ManagerImpl : public Cap::Manager, public ReloadModule::EventListener
  {
 - public:
 -      LocalIntExt reghold;
 -      CommandCAP (Module* mod) : Command(mod, "CAP", 1),
 -              reghold("CAP_REGHOLD", mod)
 +      /** Stores the cap state of a module being reloaded
 +       */
 +      struct CapModData
        {
 -              works_before_reg = true;
 +              struct Data
 +              {
 +                      std::string name;
 +                      std::vector<std::string> users;
 +
 +                      Data(Capability* cap)
 +                              : name(cap->GetName())
 +                      {
 +                      }
 +              };
 +              std::vector<Data> caps;
 +      };
 +
 +      typedef insp::flat_map<std::string, Capability*, irc::insensitive_swo> CapMap;
 +
 +      ExtItem capext;
 +      CapMap caps;
 +      Events::ModuleEventProvider& evprov;
 +
 +      static bool CanRequest(LocalUser* user, Ext usercaps, Capability* cap, bool adding)
 +      {
 +              const bool hascap = ((usercaps & cap->GetMask()) != 0);
 +              if (hascap == adding)
 +                      return true;
 +
 +              return cap->OnRequest(user, adding);
        }
  
 -      CmdResult Handle (const std::vector<std::string> &parameters, User *user)
 +      Capability::Bit AllocateBit() const
        {
 -              irc::string subcommand = parameters[0].c_str();
 +              Capability::Bit used = 0;
 +              for (CapMap::const_iterator i = caps.begin(); i != caps.end(); ++i)
 +              {
 +                      Capability* cap = i->second;
 +                      used |= cap->GetMask();
 +              }
  
 -              if (subcommand == "REQ")
 +              for (unsigned int i = 0; i < MAX_CAPS; i++)
                {
 -                      if (parameters.size() < 2)
 -                              return CMD_FAILURE;
 +                      Capability::Bit bit = (1 << i);
 +                      if (!(used & bit))
 +                              return bit;
 +              }
 +              throw ModuleException("Too many caps");
 +      }
 +
 +      void OnReloadModuleSave(Module* mod, ReloadModule::CustomData& cd) CXX11_OVERRIDE
 +      {
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "OnReloadModuleSave()");
 +              if (mod == creator)
 +                      return;
 +
 +              CapModData* capmoddata = new CapModData;
 +              cd.add(this, capmoddata);
  
 -                      CapEvent Data(creator, user, CapEvent::CAPEVENT_REQ);
 +              for (CapMap::iterator i = caps.begin(); i != caps.end(); ++i)
 +              {
 +                      Capability* cap = i->second;
 +                      // Only save users of caps that belong to the module being reloaded
 +                      if (cap->creator != mod)
 +                              continue;
  
 -                      // tokenize the input into a nice list of requested caps
 -                      std::string cap_;
 -                      irc::spacesepstream cap_stream(parameters[1]);
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Module being reloaded implements cap %s, saving cap users", cap->GetName().c_str());
 +                      capmoddata->caps.push_back(CapModData::Data(cap));
 +                      CapModData::Data& capdata = capmoddata->caps.back();
  
 -                      while (cap_stream.GetToken(cap_))
 +                      // Populate list with uuids of users who are using the cap
 +                      const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
 +                      for (UserManager::LocalList::const_iterator j = list.begin(); j != list.end(); ++j)
                        {
 -                              // Whilst the handling of extraneous spaces is not currently defined in the CAP specification
 -                              // every single other implementation ignores extraneous spaces. Lets copy them for
 -                              // compatibility purposes.
 -                              trim(cap_);
 -                              if (!cap_.empty())
 -                                      Data.wanted.push_back(cap_);
 +                              LocalUser* user = *j;
 +                              if (cap->get(user))
 +                                      capdata.users.push_back(user->uuid);
                        }
 +              }
 +      }
  
 -                      reghold.set(user, 1);
 -                      Data.Send();
 -
 -                      if (Data.wanted.empty())
 +      void OnReloadModuleRestore(Module* mod, void* data) CXX11_OVERRIDE
 +      {
 +              CapModData* capmoddata = static_cast<CapModData*>(data);
 +              for (std::vector<CapModData::Data>::const_iterator i = capmoddata->caps.begin(); i != capmoddata->caps.end(); ++i)
 +              {
 +                      const CapModData::Data& capdata = *i;
 +                      Capability* cap = ManagerImpl::Find(capdata.name);
 +                      if (!cap)
                        {
 -                              user->WriteServ("CAP %s ACK :%s", user->nick.c_str(), parameters[1].c_str());
 -                              return CMD_SUCCESS;
 +                              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cap %s is no longer available after reload", capdata.name.c_str());
 +                              continue;
                        }
  
 -                      // HACK: reset all of the caps which were enabled on this user because a cap request is atomic.
 -                      for (std::vector<std::pair<GenericCap*, int> >::iterator iter = Data.changed.begin(); iter != Data.changed.end(); ++iter)
 -                              iter->first->ext.set(user, iter->second);
 +                      // Set back the cap for all users who were using it before the reload
 +                      for (std::vector<std::string>::const_iterator j = capdata.users.begin(); j != capdata.users.end(); ++j)
 +                      {
 +                              const std::string& uuid = *j;
 +                              User* user = ServerInstance->FindUUID(uuid);
 +                              if (!user)
 +                              {
 +                                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User %s is gone when trying to restore cap %s", uuid.c_str(), capdata.name.c_str());
 +                                      continue;
 +                              }
  
 -                      user->WriteServ("CAP %s NAK :%s", user->nick.c_str(), parameters[1].c_str());
 +                              cap->set(user, true);
 +                      }
                }
 -              else if (subcommand == "END")
 +              delete capmoddata;
 +      }
 +
 + public:
 +      ManagerImpl(Module* mod, Events::ModuleEventProvider& evprovref)
 +              : Cap::Manager(mod)
 +              , ReloadModule::EventListener(mod)
 +              , capext(mod)
 +              , evprov(evprovref)
 +      {
 +              managerimpl = this;
 +      }
 +
 +      ~ManagerImpl()
 +      {
 +              for (CapMap::iterator i = caps.begin(); i != caps.end(); ++i)
                {
 -                      reghold.set(user, 0);
 +                      Capability* cap = i->second;
 +                      cap->Unregister();
                }
 -              else if ((subcommand == "LS") || (subcommand == "LIST"))
 -              {
 -                      CapEvent Data(creator, user, subcommand == "LS" ? CapEvent::CAPEVENT_LS : CapEvent::CAPEVENT_LIST);
 +      }
 +
 +      void AddCap(Cap::Capability* cap) CXX11_OVERRIDE
 +      {
 +              // No-op if the cap is already registered.
 +              // This allows modules to call SetActive() on a cap without checking if it's active first.
 +              if (cap->IsRegistered())
 +                      return;
  
 -                      reghold.set(user, 1);
 -                      Data.Send();
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Registering cap %s", cap->GetName().c_str());
 +              cap->bit = AllocateBit();
 +              cap->extitem = &capext;
 +              caps.insert(std::make_pair(cap->GetName(), cap));
 +              ServerInstance->Modules.AddReferent("cap/" + cap->GetName(), cap);
 +
 +              FOREACH_MOD_CUSTOM(evprov, Cap::EventListener, OnCapAddDel, (cap, true));
 +      }
 +
 +      void DelCap(Cap::Capability* cap) CXX11_OVERRIDE
 +      {
 +              // No-op if the cap is not registered, see AddCap() above
 +              if (!cap->IsRegistered())
 +                      return;
  
 -                      std::string Result;
 -                      if (Data.wanted.size() > 0)
 -                              Result = irc::stringjoiner(" ", Data.wanted, 0, Data.wanted.size() - 1).GetJoined();
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Unregistering cap %s", cap->GetName().c_str());
  
 -                      user->WriteServ("CAP %s %s :%s", user->nick.c_str(), subcommand.c_str(), Result.c_str());
 +              // Fire the event first so modules can still see who is using the cap which is being unregistered
 +              FOREACH_MOD_CUSTOM(evprov, Cap::EventListener, OnCapAddDel, (cap, false));
 +
 +              // Turn off the cap for all users
 +              const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
 +              for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
 +              {
 +                      LocalUser* user = *i;
 +                      cap->set(user, false);
                }
 -              else if (subcommand == "CLEAR")
 +
 +              ServerInstance->Modules.DelReferent(cap);
 +              cap->Unregister();
 +              caps.erase(cap->GetName());
 +      }
 +
 +      Capability* Find(const std::string& capname) const CXX11_OVERRIDE
 +      {
 +              CapMap::const_iterator it = caps.find(capname);
 +              if (it != caps.end())
 +                      return it->second;
 +              return NULL;
 +      }
 +
 +      void NotifyValueChange(Capability* cap) CXX11_OVERRIDE
 +      {
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cap %s changed value", cap->GetName().c_str());
 +              FOREACH_MOD_CUSTOM(evprov, Cap::EventListener, OnCapValueChange, (cap));
 +      }
 +
 +      Protocol GetProtocol(LocalUser* user) const
 +      {
 +              return ((capext.get(user) & CAP_302_BIT) ? CAP_302 : CAP_LEGACY);
 +      }
 +
 +      void Set302Protocol(LocalUser* user)
 +      {
 +              capext.set(user, capext.get(user) | CAP_302_BIT);
 +      }
 +
 +      bool HandleReq(LocalUser* user, const std::string& reqlist)
 +      {
 +              Ext usercaps = capext.get(user);
 +              irc::spacesepstream ss(reqlist);
 +              for (std::string capname; ss.GetToken(capname); )
                {
 -                      CapEvent Data(creator, user, CapEvent::CAPEVENT_CLEAR);
 +                      bool remove = (capname[0] == '-');
 +                      if (remove)
 +                              capname.erase(capname.begin());
  
 -                      reghold.set(user, 1);
 -                      Data.Send();
 +                      Capability* cap = ManagerImpl::Find(capname);
 +                      if ((!cap) || (!CanRequest(user, usercaps, cap, !remove)))
 +                              return false;
  
 -                      std::string Result;
 -                      if (!Data.ack.empty())
 -                              Result = irc::stringjoiner(" ", Data.ack, 0, Data.ack.size() - 1).GetJoined();
 -                      user->WriteServ("CAP %s ACK :%s", user->nick.c_str(), Result.c_str());
 +                      if (remove)
 +                              usercaps = cap->DelFromMask(usercaps);
 +                      else
 +                              usercaps = cap->AddToMask(usercaps);
                }
 -              else
 +
 +              capext.set(user, usercaps);
 +              return true;
 +      }
 +
 +      void HandleList(std::string& out, LocalUser* user, bool show_all, bool show_values, bool minus_prefix = false) const
 +      {
 +              Ext show_caps = (show_all ? ~0 : capext.get(user));
 +
 +              for (CapMap::const_iterator i = caps.begin(); i != caps.end(); ++i)
                {
 -                      user->WriteNumeric(ERR_INVALIDCAPSUBCOMMAND, "%s %s :Invalid CAP subcommand", user->nick.c_str(), subcommand.empty() ? "*" : subcommand.c_str());
 -                      return CMD_FAILURE;
 +                      Capability* cap = i->second;
 +                      if (!(show_caps & cap->GetMask()))
 +                              continue;
 +
 +                      if ((show_all) && (!cap->OnList(user)))
 +                              continue;
 +
 +                      if (minus_prefix)
 +                              out.push_back('-');
 +                      out.append(cap->GetName());
 +
 +                      if (show_values)
 +                      {
 +                              const std::string* capvalue = cap->GetValue(user);
 +                              if ((capvalue) && (!capvalue->empty()) && (capvalue->find(' ') == std::string::npos))
 +                              {
 +                                      out.push_back('=');
 +                                      out.append(*capvalue, 0, MAX_VALUE_LENGTH);
 +                              }
 +                      }
 +                      out.push_back(' ');
                }
 +      }
  
 -              return CMD_SUCCESS;
 +      void HandleClear(LocalUser* user, std::string& result)
 +      {
 +              HandleList(result, user, false, false, true);
 +              capext.unset(user);
        }
  };
  
 -class ModuleCAP : public Module
 +Cap::ExtItem::ExtItem(Module* mod)
 +      : LocalIntExt("caps", ExtensionItem::EXT_USER, mod)
  {
 -      CommandCAP cmd;
 +}
 +
 +std::string Cap::ExtItem::serialize(SerializeFormat format, const Extensible* container, void* item) const
 +{
 +      std::string ret;
 +      // XXX: Cast away the const because IS_LOCAL() doesn't handle it
 +      LocalUser* user = IS_LOCAL(const_cast<User*>(static_cast<const User*>(container)));
 +      if ((format == FORMAT_NETWORK) || (!user))
 +              return ret;
 +
 +      // List requested caps
 +      managerimpl->HandleList(ret, user, false, false);
 +
 +      // Serialize cap protocol version. If building a human-readable string append a new token, otherwise append only a single character indicating the version.
 +      Protocol protocol = managerimpl->GetProtocol(user);
 +      if (format == FORMAT_USER)
 +              ret.append("capversion=3.");
 +      else if (!ret.empty())
 +              ret.erase(ret.length()-1);
 +
 +      if (protocol == CAP_302)
 +              ret.push_back('2');
 +      else
 +              ret.push_back('1');
 +
 +      return ret;
 +}
 +
 +void Cap::ExtItem::unserialize(SerializeFormat format, Extensible* container, const std::string& value)
 +{
 +      if (format == FORMAT_NETWORK)
 +              return;
 +
 +      LocalUser* user = IS_LOCAL(static_cast<User*>(container));
 +      if (!user)
 +              return; // Can't happen
 +
 +      // Process the cap protocol version which is a single character at the end of the serialized string
 +      const char verchar = *value.rbegin();
 +      if (verchar == '2')
 +              managerimpl->Set302Protocol(user);
 +
 +      // Remove the version indicator from the string passed to HandleReq
 +      std::string caplist(value, 0, value.size()-1);
 +      managerimpl->HandleReq(user, caplist);
 +}
 +
 +class CommandCap : public SplitCommand
 +{
 +      Events::ModuleEventProvider evprov;
 +      Cap::ManagerImpl manager;
 +
 +      static void DisplayResult(LocalUser* user, std::string& result)
 +      {
 +              if (*result.rbegin() == ' ')
 +                      result.erase(result.end()-1);
 +              user->WriteCommand("CAP", result);
 +      }
 +
   public:
 -      ModuleCAP()
 -              : cmd(this)
 +      LocalIntExt holdext;
 +
 +      CommandCap(Module* mod)
 +              : SplitCommand(mod, "CAP", 1)
 +              , evprov(mod, "event/cap")
 +              , manager(mod, evprov)
 +              , holdext("cap_hold", ExtensionItem::EXT_USER, mod)
        {
 +              works_before_reg = true;
        }
  
 -      void init()
 +      CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user) CXX11_OVERRIDE
        {
 -              ServerInstance->Modules->AddService(cmd);
 -              ServerInstance->Modules->AddService(cmd.reghold);
 +              if (user->registered != REG_ALL)
 +                      holdext.set(user, 1);
 +
 +              std::string subcommand(parameters[0].length(), ' ');
 +              std::transform(parameters[0].begin(), parameters[0].end(), subcommand.begin(), ::toupper);
 +
 +              if (subcommand == "REQ")
 +              {
 +                      if (parameters.size() < 2)
 +                              return CMD_FAILURE;
 +
 +                      std::string result = (manager.HandleReq(user, parameters[1]) ? "ACK :" : "NAK :");
 +                      result.append(parameters[1]);
 +                      user->WriteCommand("CAP", result);
 +              }
 +              else if (subcommand == "END")
 +              {
 +                      holdext.unset(user);
 +              }
 +              else if ((subcommand == "LS") || (subcommand == "LIST"))
 +              {
 +                      const bool is_ls = (subcommand.length() == 2);
 +                      if ((is_ls) && (parameters.size() > 1) && (parameters[1] == "302"))
 +                              manager.Set302Protocol(user);
  
 -              Implementation eventlist[] = { I_OnCheckReady };
 -              ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
 +                      std::string result = subcommand + " :";
 +                      // Show values only if supports v3.2 and doing LS
 +                      manager.HandleList(result, user, is_ls, ((is_ls) && (manager.GetProtocol(user) != Cap::CAP_LEGACY)));
 +                      DisplayResult(user, result);
 +              }
 +              else if ((subcommand == "CLEAR") && (manager.GetProtocol(user) == Cap::CAP_LEGACY))
 +              {
 +                      std::string result = "ACK :";
 +                      manager.HandleClear(user, result);
 +                      DisplayResult(user, result);
 +              }
 +              else
 +              {
-                       user->WriteNumeric(ERR_INVALIDCAPSUBCOMMAND, subcommand, "Invalid CAP subcommand");
++                      user->WriteNumeric(ERR_INVALIDCAPSUBCOMMAND, subcommand.empty() ? "*" : subcommand, "Invalid CAP subcommand");
 +                      return CMD_FAILURE;
 +              }
 +
 +              return CMD_SUCCESS;
        }
 +};
  
 -      ModResult OnCheckReady(LocalUser* user)
 -      {
 -              /* Users in CAP state get held until CAP END */
 -              if (cmd.reghold.get(user))
 -                      return MOD_RES_DENY;
 +class ModuleCap : public Module
 +{
 +      CommandCap cmd;
  
 -              return MOD_RES_PASSTHRU;
 + public:
 +      ModuleCap()
 +              : cmd(this)
 +      {
        }
  
 -      ~ModuleCAP()
 +      ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
        {
 +              return (cmd.holdext.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU);
        }
  
 -      Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
 -              return Version("Client CAP extension support", VF_VENDOR);
 +              return Version("Provides support for CAP capability negotiation", VF_VENDOR);
        }
  };
  
 -MODULE_INIT(ModuleCAP)
 -
 +MODULE_INIT(ModuleCap)
diff --combined src/modules/m_shun.cpp
index 417d67b69fd1df260358c8e50417d814b3091719,3147d5476f40eda1f19775a88654b27b777a1831..66cc7fd58dc488e2a4585d57d76ffa9675bc1ac0
@@@ -23,6 -23,8 +23,6 @@@
  #include "inspircd.h"
  #include "xline.h"
  
 -/* $ModDesc: Provides the /SHUN command, which stops a user from executing all except configured commands. */
 -
  class Shun : public XLine
  {
  public:
        {
        }
  
 -      ~Shun()
 -      {
 -      }
 -
        bool Matches(User *u)
        {
                // E: overrides shun
 -              if (u->exempt)
 +              LocalUser* lu = IS_LOCAL(u);
 +              if (lu && lu->exempt)
                        return false;
  
                if (InspIRCd::Match(u->GetFullHost(), matchtext) || InspIRCd::Match(u->GetFullRealHost(), matchtext) || InspIRCd::Match(u->nick+"!"+u->ident+"@"+u->GetIPString(), matchtext))
                        return true;
  
 +              if (InspIRCd::MatchCIDR(u->GetIPString(), matchtext, ascii_case_insensitive_map))
 +                      return true;
 +
                return false;
        }
  
                return false;
        }
  
 -      void DisplayExpiry()
 +      const std::string& Displayable()
        {
 -              ServerInstance->SNO->WriteToSnoMask('x',"Removing expired shun %s (set by %s %ld seconds ago)",
 -                      this->matchtext.c_str(), this->source.c_str(), (long int)(ServerInstance->Time() - this->set_time));
 -      }
 -
 -      const char* Displayable()
 -      {
 -              return matchtext.c_str();
 +              return matchtext;
        }
  };
  
@@@ -99,31 -107,35 +99,35 @@@ class CommandShun : public Comman
                /* 'time' is a human-readable timestring, like 2d3h2s. */
  
                std::string target = parameters[0];
 -              
 +
                User *find = ServerInstance->FindNick(target);
                if ((find) && (find->registered == REG_ALL))
                        target = std::string("*!*@") + find->GetIPString();
  
                if (parameters.size() == 1)
                {
-                       if (ServerInstance->XLines->DelLine(target.c_str(), "SHUN", user))
+                       if (ServerInstance->XLines->DelLine(parameters[0].c_str(), "SHUN", user))
                        {
-                               ServerInstance->SNO->WriteToSnoMask('x',"%s removed SHUN on %s",user->nick.c_str(),target.c_str());
+                               ServerInstance->SNO->WriteToSnoMask('x', "%s removed SHUN on %s", user->nick.c_str(), parameters[0].c_str());
+                       }
+                       else if (ServerInstance->XLines->DelLine(target.c_str(), "SHUN", user))
+                       {
 -                              ServerInstance->SNO->WriteToSnoMask('x',"%s removed SHUN on %s",user->nick.c_str(),target.c_str());
++                              ServerInstance->SNO->WriteToSnoMask('x',"%s removed SHUN on %s", user->nick.c_str(), target.c_str());
                        }
                        else
                        {
-                               user->WriteNotice("*** Shun " + target + " not found in list, try /stats H.");
 -                              user->WriteServ("NOTICE %s :*** Shun %s not found in list, try /stats H.", user->nick.c_str(), parameters[0].c_str());
++                              user->WriteNotice("*** Shun " + parameters[0] + " not found in list, try /stats H.");
                                return CMD_FAILURE;
                        }
                }
                else
                {
                        // Adding - XXX todo make this respect <insane> tag perhaps..
 -                      long duration;
 +                      unsigned long duration;
                        std::string expr;
                        if (parameters.size() > 2)
                        {
 -                              duration = ServerInstance->Duration(parameters[1]);
 +                              duration = InspIRCd::Duration(parameters[1]);
                                expr = parameters[2];
                        }
                        else
                                else
                                {
                                        time_t c_requires_crap = duration + ServerInstance->Time();
 -                                      std::string timestr = ServerInstance->TimeString(c_requires_crap);
 +                                      std::string timestr = InspIRCd::TimeString(c_requires_crap);
                                        ServerInstance->SNO->WriteToSnoMask('x', "%s added timed SHUN for %s to expire on %s: %s",
                                                user->nick.c_str(), target.c_str(), timestr.c_str(), expr.c_str());
                                }
                        else
                        {
                                delete r;
 -                              user->WriteServ("NOTICE %s :*** Shun for %s already exists", user->nick.c_str(), target.c_str());
 +                              user->WriteNotice("*** Shun for " + target + " already exists");
                                return CMD_FAILURE;
                        }
                }
@@@ -171,7 -183,7 +175,7 @@@ class ModuleShun : public Modul
  {
        CommandShun cmd;
        ShunFactory f;
 -      std::set<std::string> ShunEnabledCommands;
 +      insp::flat_set<std::string> ShunEnabledCommands;
        bool NotifyOfShun;
        bool affectopers;
  
        {
        }
  
 -      void init()
 +      void init() CXX11_OVERRIDE
        {
                ServerInstance->XLines->RegisterFactory(&f);
 -              ServerInstance->Modules->AddService(cmd);
 -
 -              Implementation eventlist[] = { I_OnStats, I_OnPreCommand, I_OnRehash };
 -              ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
 -              OnRehash(NULL);
        }
  
 -      virtual ~ModuleShun()
 +      ~ModuleShun()
        {
                ServerInstance->XLines->DelAll("SHUN");
                ServerInstance->XLines->UnregisterFactory(&f);
        }
  
 -      void Prioritize()
 +      void Prioritize() CXX11_OVERRIDE
        {
                Module* alias = ServerInstance->Modules->Find("m_alias.so");
 -              ServerInstance->Modules->SetPriority(this, I_OnPreCommand, PRIORITY_BEFORE, &alias);
 +              ServerInstance->Modules->SetPriority(this, I_OnPreCommand, PRIORITY_BEFORE, alias);
        }
  
 -      virtual ModResult OnStats(char symbol, User* user, string_list& out)
 +      ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
        {
 -              if (symbol != 'H')
 +              if (stats.GetSymbol() != 'H')
                        return MOD_RES_PASSTHRU;
  
 -              ServerInstance->XLines->InvokeStats("SHUN", 223, user, out);
 +              ServerInstance->XLines->InvokeStats("SHUN", 223, stats);
                return MOD_RES_DENY;
        }
  
 -      virtual void OnRehash(User* user)
 +      void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("shun");
                std::string cmds = tag->getString("enabledcommands");
  
                ShunEnabledCommands.clear();
  
 -              std::stringstream dcmds(cmds);
 +              irc::spacesepstream dcmds(cmds);
                std::string thiscmd;
  
 -              while (dcmds >> thiscmd)
 +              while (dcmds.GetToken(thiscmd))
                {
                        ShunEnabledCommands.insert(thiscmd);
                }
                affectopers = tag->getBool("affectopers", false);
        }
  
 -      virtual ModResult OnPreCommand(std::string &command, std::vector<std::string>& parameters, LocalUser* user, bool validated, const std::string &original_line)
 +      ModResult OnPreCommand(std::string &command, std::vector<std::string>& parameters, LocalUser* user, bool validated, const std::string &original_line) CXX11_OVERRIDE
        {
                if (validated)
                        return MOD_RES_PASSTHRU;
                        return MOD_RES_PASSTHRU;
                }
  
 -              if (!affectopers && IS_OPER(user))
 +              if (!affectopers && user->IsOper())
                {
                        /* Don't do anything if the user is an operator and affectopers isn't set */
                        return MOD_RES_PASSTHRU;
                }
  
 -              std::set<std::string>::iterator i = ShunEnabledCommands.find(command);
 -
 -              if (i == ShunEnabledCommands.end())
 +              if (!ShunEnabledCommands.count(command))
                {
                        if (NotifyOfShun)
 -                              user->WriteServ("NOTICE %s :*** Command %s not processed, as you have been blocked from issuing commands (SHUN)", user->nick.c_str(), command.c_str());
 +                              user->WriteNotice("*** Command " + command + " not processed, as you have been blocked from issuing commands (SHUN)");
                        return MOD_RES_DENY;
                }
  
                return MOD_RES_PASSTHRU;
        }
  
 -      virtual Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides the /SHUN command, which stops a user from executing all except configured commands.",VF_VENDOR|VF_COMMON);
        }
  };
  
  MODULE_INIT(ModuleShun)
 -
index 00cbd3dcda8e66ef5f9ad6bdce7cd4e30cbca4c3,3e0a831111ed205efc98c9242b0b9c0708a34907..13bab4cff365e44857e6982d8bc04489f83428d0
   */
  
  
 -#ifndef M_SPANNINGTREE_MAIN_H
 -#define M_SPANNINGTREE_MAIN_H
 +#pragma once
  
  #include "inspircd.h"
 -#include <stdarg.h>
 +#include "event.h"
 +#include "modules/dns.h"
 +#include "servercommand.h"
 +#include "commands.h"
 +#include "protocolinterface.h"
  
  /** If you make a change which breaks the protocol, increment this.
   * If you  completely change the protocol, completely change the number.
   *
   * IMPORTANT: If you make changes, document your changes here, without fail:
-  * http://wiki.inspircd.org/List_of_protocol_changes_between_versions
+  * https://wiki.inspircd.org/List_of_protocol_changes_between_versions
   *
   * Failure to document your protocol changes will result in a painfully
   * painful death by pain. You have been warned.
   */
 -const long ProtocolVersion = 1202;
 -const long MinCompatProtocol = 1201;
 +const long ProtocolVersion = 1205;
 +const long MinCompatProtocol = 1202;
  
  /** Forward declarations
   */
 -class SpanningTreeCommands;
  class SpanningTreeUtilities;
  class CacheRefreshTimer;
  class TreeServer;
@@@ -54,51 -52,47 +54,51 @@@ class Autoconnect
   */
  class ModuleSpanningTree : public Module
  {
 -      SpanningTreeCommands* commands;
 +      /** Client to server commands, registered in the core
 +       */
 +      CommandRConnect rconnect;
 +      CommandRSQuit rsquit;
 +      CommandMap map;
 +
 +      /** Server to server only commands, not registered in the core
 +       */
 +      SpanningTreeCommands commands;
 +
 +      /** Next membership id assigned when a local user joins a channel
 +       */
 +      Membership::Id currmembid;
 +
 +      /** The specialized ProtocolInterface that is assigned to ServerInstance->PI on load
 +       */
 +      SpanningTreeProtocolInterface protocolinterface;
 +
 +      /** Event provider for our events
 +       */
 +      Events::ModuleEventProvider eventprov;
  
   public:
 -      SpanningTreeUtilities* Utils;
 +      dynamic_reference<DNS::Manager> DNS;
 +
 +      ServerCommandManager CmdManager;
  
 -      CacheRefreshTimer *RefreshTimer;
        /** Set to true if inside a spanningtree call, to prevent sending
         * xlines and other things back to their source
         */
        bool loopCall;
  
 -      /** If true OnUserPostNick() won't update the nick TS before sending the NICK,
 -       * used when handling SVSNICK.
 -       */
 -      bool KeepNickTS;
 -
        /** Constructor
         */
        ModuleSpanningTree();
 -      void init();
 +      void init() CXX11_OVERRIDE;
  
        /** Shows /LINKS
         */
        void ShowLinks(TreeServer* Current, User* user, int hops);
  
 -      /** Counts local and remote servers
 -       */
 -      int CountServs();
 -
        /** Handle LINKS command
         */
        void HandleLinks(const std::vector<std::string>& parameters, User* user);
  
 -      /** Show MAP output to a user (recursive)
 -       */
 -      void ShowMap(TreeServer* Current, User* user, int depth, int &line, char* names, int &maxnamew, char* stats);
 -
 -      /** Handle MAP command
 -       */
 -      bool HandleMap(const std::vector<std::string>& parameters, User* user);
 -
        /** Handle SQUIT
         */
        ModResult HandleSquit(const std::vector<std::string>& parameters, User* user);
         */
        ModResult HandleRemoteWhois(const std::vector<std::string>& parameters, User* user);
  
 -      /** Ping all local servers
 -       */
 -      void DoPingChecks(time_t curtime);
 -
        /** Connect a server locally
         */
        void ConnectServer(Link* x, Autoconnect* y = NULL);
         */
        ModResult HandleConnect(const std::vector<std::string>& parameters, User* user);
  
 -      /** Attempt to send a message to a user
 -       */
 -      void RemoteMessage(User* user, const char* format, ...) CUSTOM_PRINTF(3, 4);
 -
 -      /** Returns oper-specific MAP information
 -       */
 -      const std::string MapOperInfo(TreeServer* Current);
 -
        /** Display a time as a human readable string
         */
 -      std::string TimeToStr(time_t secs);
 +      static std::string TimeToStr(time_t secs);
 +
 +      const Events::ModuleEventProvider& GetEventProvider() const { return eventprov; }
  
        /**
         ** *** MODULE EVENTS ***
         **/
  
 -      ModResult OnPreCommand(std::string &command, std::vector<std::string>& parameters, LocalUser *user, bool validated, const std::string &original_line);
 -      void OnPostCommand(const std::string &command, const std::vector<std::string>& parameters, LocalUser *user, CmdResult result, const std::string &original_line);
 -      void OnGetServerDescription(const std::string &servername,std::string &description);
 -      void OnUserConnect(LocalUser* source);
 -      void OnUserInvite(User* source,User* dest,Channel* channel, time_t);
 -      void OnPostTopicChange(User* user, Channel* chan, const std::string &topic);
 -      void OnWallops(User* user, const std::string &text);
 -      void OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
 -      void OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
 -      void OnBackgroundTimer(time_t curtime);
 -      void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts);
 -      void OnChangeHost(User* user, const std::string &newhost);
 -      void OnChangeName(User* user, const std::string &gecos);
 -      void OnChangeIdent(User* user, const std::string &ident);
 -      void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts);
 -      void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message);
 -      void OnUserPostNick(User* user, const std::string &oldnick);
 -      void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts);
 -      void OnRemoteKill(User* source, User* dest, const std::string &reason, const std::string &operreason);
 -      void OnPreRehash(User* user, const std::string &parameter);
 -      void OnRehash(User* user);
 -      void OnOper(User* user, const std::string &opertype);
 -      void OnLine(User* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason);
 -      void OnAddLine(User *u, XLine *x);
 -      void OnDelLine(User *u, XLine *x);
 -      void OnMode(User* user, void* dest, int target_type, const std::vector<std::string> &text, const std::vector<TranslateType> &translate);
 -      ModResult OnStats(char statschar, User* user, string_list &results);
 -      ModResult OnSetAway(User* user, const std::string &awaymsg);
 -      void ProtoSendMode(void* opaque, TargetTypeFlags target_type, void* target, const std::vector<std::string> &modeline, const std::vector<TranslateType> &translate);
 -      void ProtoSendMetaData(void* opaque, Extensible* target, const std::string &extname, const std::string &extdata);
 -      void OnLoadModule(Module* mod);
 -      void OnUnloadModule(Module* mod);
 -      ModResult OnAcceptConnection(int newsock, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
 -      void OnRequest(Request& request);
 -      CullResult cull();
 +      ModResult OnPreCommand(std::string &command, std::vector<std::string>& parameters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE;
 +      void OnPostCommand(Command*, const std::vector<std::string>& parameters, LocalUser* user, CmdResult result, const std::string& original_line) CXX11_OVERRIDE;
 +      void OnUserConnect(LocalUser* source) CXX11_OVERRIDE;
 +      void OnUserInvite(User* source, User* dest, Channel* channel, time_t timeout, unsigned int notifyrank, CUList& notifyexcepts) CXX11_OVERRIDE;
 +      ModResult OnPreTopicChange(User* user, Channel* chan, const std::string& topic) CXX11_OVERRIDE;
 +      void OnPostTopicChange(User* user, Channel* chan, const std::string &topic) CXX11_OVERRIDE;
 +      void OnUserMessage(User* user, void* dest, int target_type, const std::string& text, char status, const CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE;
 +      void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE;
 +      void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE;
 +      void OnChangeHost(User* user, const std::string &newhost) CXX11_OVERRIDE;
 +      void OnChangeName(User* user, const std::string &gecos) CXX11_OVERRIDE;
 +      void OnChangeIdent(User* user, const std::string &ident) CXX11_OVERRIDE;
 +      void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts) CXX11_OVERRIDE;
 +      void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE;
 +      void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE;
 +      void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts) CXX11_OVERRIDE;
 +      void OnPreRehash(User* user, const std::string &parameter) CXX11_OVERRIDE;
 +      void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE;
 +      void OnOper(User* user, const std::string &opertype) CXX11_OVERRIDE;
 +      void OnAddLine(User *u, XLine *x) CXX11_OVERRIDE;
 +      void OnDelLine(User *u, XLine *x) CXX11_OVERRIDE;
 +      ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE;
 +      ModResult OnSetAway(User* user, const std::string &awaymsg) CXX11_OVERRIDE;
 +      void OnLoadModule(Module* mod) CXX11_OVERRIDE;
 +      void OnUnloadModule(Module* mod) CXX11_OVERRIDE;
 +      ModResult OnAcceptConnection(int newsock, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE;
 +      void OnMode(User* source, User* u, Channel* c, const Modes::ChangeList& modes, ModeParser::ModeProcessFlag processflags, const std::string& output_mode) CXX11_OVERRIDE;
 +      CullResult cull() CXX11_OVERRIDE;
        ~ModuleSpanningTree();
 -      Version GetVersion();
 -      void Prioritize();
 +      Version GetVersion() CXX11_OVERRIDE;
 +      void Prioritize() CXX11_OVERRIDE;
  };
 -
 -#endif
index 9890800e4c524f4222080cedc30474e85c0455af,bbbc518bdd1b7a7947464c43b83a8942ea2c7d2f..8c1454d7ed2a0fba80809aefa3b39e8a5357288b
   */
  
  
 -/* $ModDesc: Adds timed bans */
 -
  #include "inspircd.h"
 +#include "listmode.h"
  
  /** Holds a timed ban
   */
  class TimedBan
  {
   public:
 -      std::string channel;
        std::string mask;
        time_t expire;
        Channel* chan;
@@@ -40,30 -42,21 +40,30 @@@ timedbans TimedBanList
   */
  class CommandTban : public Command
  {
 -      static bool IsBanSet(Channel* chan, const std::string& mask)
 +      ChanModeReference banmode;
 +
 +      bool IsBanSet(Channel* chan, const std::string& mask)
        {
 -              for (BanList::const_iterator i = chan->bans.begin(); i != chan->bans.end(); ++i)
 +              ListModeBase* banlm = static_cast<ListModeBase*>(*banmode);
 +              const ListModeBase::ModeList* bans = banlm->GetList(chan);
 +              if (bans)
                {
 -                      if (!strcasecmp(i->data.c_str(), mask.c_str()))
 -                              return true;
 +                      for (ListModeBase::ModeList::const_iterator i = bans->begin(); i != bans->end(); ++i)
 +                      {
 +                              const ListModeBase::ListItem& ban = *i;
 +                              if (!strcasecmp(ban.mask.c_str(), mask.c_str()))
 +                                      return true;
 +                      }
                }
 +
                return false;
        }
  
   public:
        CommandTban(Module* Creator) : Command(Creator,"TBAN", 3)
 +              , banmode(Creator, "ban")
        {
                syntax = "<channel> <duration> <banmask>";
 -              TRANSLATE4(TR_TEXT, TR_TEXT, TR_TEXT, TR_END);
        }
  
        CmdResult Handle (const std::vector<std::string> &parameters, User *user)
                Channel* channel = ServerInstance->FindChan(parameters[0]);
                if (!channel)
                {
 -                      user->WriteNumeric(401, "%s %s :No such channel",user->nick.c_str(), parameters[0].c_str());
 +                      user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
                        return CMD_FAILURE;
                }
                int cm = channel->GetPrefixValue(user);
                if (cm < HALFOP_VALUE)
                {
 -                      user->WriteNumeric(482, "%s %s :You do not have permission to set bans on this channel",
 -                              user->nick.c_str(), channel->name.c_str());
 +                      user->WriteNumeric(ERR_CHANOPRIVSNEEDED, channel->name, "You do not have permission to set bans on this channel");
                        return CMD_FAILURE;
 -              }               
 +              }
  
                TimedBan T;
 -              std::string channelname = parameters[0];
 -              long duration = ServerInstance->Duration(parameters[1]);
 +              unsigned long duration = InspIRCd::Duration(parameters[1]);
                unsigned long expire = duration + ServerInstance->Time();
                if (duration < 1)
                {
 -                      user->WriteServ("NOTICE "+user->nick+" :Invalid ban time");
 +                      user->WriteNotice("Invalid ban time");
                        return CMD_FAILURE;
                }
                std::string mask = parameters[2];
 -              std::vector<std::string> setban;
 -              setban.push_back(parameters[0]);
 -              setban.push_back("+b");
                bool isextban = ((mask.size() > 2) && (mask[1] == ':'));
 -              if (!isextban && !ServerInstance->IsValidMask(mask))
 +              if (!isextban && !InspIRCd::IsValidMask(mask))
                        mask.append("!*@*");
 -              if ((mask.length() > 250) || (!ServerInstance->IsValidMask(mask) && !isextban))
 -              {
 -                      user->WriteServ("NOTICE "+user->nick+" :Invalid ban mask");
 -                      return CMD_FAILURE;
 -              }
  
                if (IsBanSet(channel, mask))
                {
 -                      user->WriteServ("NOTICE %s :Ban already set", user->nick.c_str());
 +                      user->WriteNotice("Ban already set");
                        return CMD_FAILURE;
                }
  
 -              setban.push_back(mask);
 -              // use CallHandler to make it so that the user sets the mode
 -              // themselves
 -              ServerInstance->Parser->CallHandler("MODE",setban,user);
 -              if (!IsBanSet(channel, mask))
 +              Modes::ChangeList setban;
 +              setban.push_add(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), mask);
 +              // Pass the user (instead of ServerInstance->FakeClient) to ModeHandler::Process() to
 +              // make it so that the user sets the mode themselves
 +              ServerInstance->Modes->Process(user, channel, NULL, setban);
 +              if (ServerInstance->Modes->GetLastParse().empty())
 +              {
 +                      user->WriteNotice("Invalid ban mask");
                        return CMD_FAILURE;
 +              }
  
                CUList tmp;
 -              T.channel = channelname;
                T.mask = mask;
                T.expire = expire + (IS_REMOTE(user) ? 5 : 0);
                T.chan = channel;
                TimedBanList.push_back(T);
  
+               const std::string addban = user->nick + " added a timed ban on " + mask + " lasting for " + ConvToStr(duration) + " seconds.";
                // If halfop is loaded, send notice to halfops and above, otherwise send to ops and above
 -              ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL);
 +              PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h');
                char pfxchar = (mh && mh->name == "halfop") ? mh->GetPrefix() : '@';
  
-               channel->WriteAllExcept(ServerInstance->FakeClient, true, pfxchar, tmp, "NOTICE %s :%s added a timed ban on %s lasting for %ld seconds.", channel->name.c_str(), user->nick.c_str(), mask.c_str(), duration);
+               channel->WriteAllExcept(ServerInstance->FakeClient, true, pfxchar, tmp, "NOTICE %s :%s", channel->name.c_str(), addban.c_str());
+               ServerInstance->PI->SendChannelNotice(channel, pfxchar, addban);
                return CMD_SUCCESS;
        }
  
        }
  };
  
 +class BanWatcher : public ModeWatcher
 +{
 + public:
 +      BanWatcher(Module* parent)
 +              : ModeWatcher(parent, "ban", MODETYPE_CHANNEL)
 +      {
 +      }
 +
 +      void AfterMode(User* source, User* dest, Channel* chan, const std::string& banmask, bool adding)
 +      {
 +              if (adding)
 +                      return;
 +
 +              for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end(); ++i)
 +              {
 +                      if (i->chan != chan)
 +                              continue;
 +
 +                      const std::string& target = i->mask;
 +                      if (irc::equals(banmask, target))
 +                      {
 +                              TimedBanList.erase(i);
 +                              break;
 +                      }
 +              }
 +      }
 +};
 +
  class ChannelMatcher
  {
        Channel* const chan;
  class ModuleTimedBans : public Module
  {
        CommandTban cmd;
 +      BanWatcher banwatcher;
 +
   public:
        ModuleTimedBans()
                : cmd(this)
 +              , banwatcher(this)
        {
        }
  
 -      void init()
 -      {
 -              ServerInstance->Modules->AddService(cmd);
 -              Implementation eventlist[] = { I_OnDelBan, I_OnBackgroundTimer, I_OnChannelDelete };
 -              ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
 -      }
 -
 -      virtual ModResult OnDelBan(User* source, Channel* chan, const std::string &banmask)
 -      {
 -              irc::string listitem = banmask.c_str();
 -              irc::string thischan = chan->name.c_str();
 -              for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end(); i++)
 -              {
 -                      irc::string target = i->mask.c_str();
 -                      irc::string tchan = i->channel.c_str();
 -                      if ((listitem == target) && (tchan == thischan))
 -                      {
 -                              TimedBanList.erase(i);
 -                              break;
 -                      }
 -              }
 -              return MOD_RES_PASSTHRU;
 -      }
 -
 -      virtual void OnBackgroundTimer(time_t curtime)
 +      void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE
        {
                timedbans expired;
                for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end();)
  
                for (timedbans::iterator i = expired.begin(); i != expired.end(); i++)
                {
 -                      std::string chan = i->channel;
                        std::string mask = i->mask;
 -                      Channel* cr = ServerInstance->FindChan(chan);
 -                      if (cr)
 +                      Channel* cr = i->chan;
                        {
 -                              std::vector<std::string> setban;
 -                              setban.push_back(chan);
 -                              setban.push_back("-b");
 -                              setban.push_back(mask);
 -
                                CUList empty;
-                               std::string expiry = "*** Timed ban on " + cr->name + " expired.";
-                               cr->WriteAllExcept(ServerInstance->FakeClient, true, '@', empty, "NOTICE %s :%s", cr->name.c_str(), expiry.c_str());
-                               ServerInstance->PI->SendChannelNotice(cr, '@', expiry);
 -                              const std::string expiry = "*** Timed ban on " + chan + " expired.";
++                              const std::string expiry = "*** Timed ban on " + cr->name + " expired.";
+                               // If halfop is loaded, send notice to halfops and above, otherwise send to ops and above
 -                              ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL);
++                              PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h');
+                               char pfxchar = (mh && mh->name == "halfop") ? mh->GetPrefix() : '@';
+                               cr->WriteAllExcept(ServerInstance->FakeClient, true, pfxchar, empty, "NOTICE %s :%s", cr->name.c_str(), expiry.c_str());
+                               ServerInstance->PI->SendChannelNotice(cr, pfxchar, expiry);
  
 -                              ServerInstance->SendGlobalMode(setban, ServerInstance->FakeClient);
 +                              Modes::ChangeList setban;
 +                              setban.push_remove(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), mask);
 +                              ServerInstance->Modes->Process(ServerInstance->FakeClient, cr, NULL, setban);
                        }
                }
        }
  
 -      void OnChannelDelete(Channel* chan)
 +      void OnChannelDelete(Channel* chan) CXX11_OVERRIDE
        {
                // Remove all timed bans affecting the channel from internal bookkeeping
                TimedBanList.erase(std::remove_if(TimedBanList.begin(), TimedBanList.end(), ChannelMatcher(chan)), TimedBanList.end());
        }
  
 -      virtual Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Adds timed bans", VF_COMMON | VF_VENDOR);
        }
  };
  
  MODULE_INIT(ModuleTimedBans)
 -
diff --combined src/users.cpp
index aa5031b2b1632bdda7c885f0ad3b3d1935d6da83,9e06485e5182c6a1ed511a152e7510438261790a..397a132678433e82a41f8f82826b618d53de9341
  
  
  #include "inspircd.h"
 -#include <stdarg.h>
 -#include "socketengine.h"
  #include "xline.h"
 -#include "bancache.h"
 -#include "commands/cmd_whowas.h"
 -
 -already_sent_t LocalUser::already_sent_id = 0;
 -
 -std::string User::ProcessNoticeMasks(const char *sm)
 -{
 -      bool adding = true, oldadding = false;
 -      const char *c = sm;
 -      std::string output;
 -
 -      while (c && *c)
 -      {
 -              switch (*c)
 -              {
 -                      case '+':
 -                              adding = true;
 -                      break;
 -                      case '-':
 -                              adding = false;
 -                      break;
 -                      case '*':
 -                              for (unsigned char d = 'a'; d <= 'z'; d++)
 -                              {
 -                                      if (!ServerInstance->SNO->masks[d - 'a'].Description.empty())
 -                                      {
 -                                              if ((!IsNoticeMaskSet(d) && adding) || (IsNoticeMaskSet(d) && !adding))
 -                                              {
 -                                                      if ((oldadding != adding) || (!output.length()))
 -                                                              output += (adding ? '+' : '-');
 -
 -                                                      this->SetNoticeMask(d, adding);
 -
 -                                                      output += d;
 -                                              }
 -                                              oldadding = adding;
 -                                              char u = toupper(d);
 -                                              if ((!IsNoticeMaskSet(u) && adding) || (IsNoticeMaskSet(u) && !adding))
 -                                              {
 -                                                      if ((oldadding != adding) || (!output.length()))
 -                                                              output += (adding ? '+' : '-');
 -
 -                                                      this->SetNoticeMask(u, adding);
 -
 -                                                      output += u;
 -                                              }
 -                                              oldadding = adding;
 -                                      }
 -                              }
 -                      break;
 -                      default:
 -                              if (isalpha(*c))
 -                              {
 -                                      if ((!IsNoticeMaskSet(*c) && adding) || (IsNoticeMaskSet(*c) && !adding))
 -                                      {
 -                                              if ((oldadding != adding) || (!output.length()))
 -                                                      output += (adding ? '+' : '-');
 -
 -                                              this->SetNoticeMask(*c, adding);
 -
 -                                              output += *c;
 -                                              oldadding = adding;
 -                                      }
 -                              }
 -                              else
 -                                      this->WriteNumeric(ERR_UNKNOWNSNOMASK, "%s %c :is unknown snomask char to me", this->nick.c_str(), *c);
 -
 -                      break;
 -              }
 -
 -              c++;
 -      }
 -
 -      std::string s = this->FormatNoticeMasks();
 -      if (s.length() == 0)
 -      {
 -              this->modes[UM_SNOMASK] = false;
 -      }
 -
 -      return output;
 -}
 -
 -void LocalUser::StartDNSLookup()
 -{
 -      try
 -      {
 -              bool cached = false;
 -              const char* sip = this->GetIPString();
 -              UserResolver *res_reverse;
 -
 -              QueryType resolvtype = this->client_sa.sa.sa_family == AF_INET6 ? DNS_QUERY_PTR6 : DNS_QUERY_PTR4;
 -              res_reverse = new UserResolver(this, sip, resolvtype, cached);
 -
 -              ServerInstance->AddResolver(res_reverse, cached);
 -      }
 -      catch (CoreException& e)
 -      {
 -              ServerInstance->Logs->Log("USERS", DEBUG,"Error in resolver: %s",e.GetReason());
 -              dns_done = true;
 -              ServerInstance->stats->statsDnsBad++;
 -      }
 -}
  
  bool User::IsNoticeMaskSet(unsigned char sm)
  {
        return (snomasks[sm-65]);
  }
  
 -void User::SetNoticeMask(unsigned char sm, bool value)
 -{
 -      if (!isalpha(sm))
 -              return;
 -      snomasks[sm-65] = value;
 -}
 -
 -const char* User::FormatNoticeMasks()
 -{
 -      static char data[MAXBUF];
 -      int offset = 0;
 -
 -      for (int n = 0; n < 64; n++)
 -      {
 -              if (snomasks[n])
 -                      data[offset++] = n+65;
 -      }
 -
 -      data[offset] = 0;
 -      return data;
 -}
 -
 -bool User::IsModeSet(unsigned char m)
 -{
 -      if (!isalpha(m))
 -              return false;
 -      return (modes[m-65]);
 -}
 -
 -void User::SetMode(unsigned char m, bool value)
 +bool User::IsModeSet(unsigned char m) const
  {
 -      if (!isalpha(m))
 -              return;
 -      modes[m-65] = value;
 +      ModeHandler* mh = ServerInstance->Modes->FindMode(m, MODETYPE_USER);
 +      return (mh && modes[mh->GetId()]);
  }
  
 -const char* User::FormatModes(bool showparameters)
 +std::string User::GetModeLetters(bool includeparams) const
  {
 -      static char data[MAXBUF];
 +      std::string ret(1, '+');
        std::string params;
 -      int offset = 0;
  
 -      for (unsigned char n = 0; n < 64; n++)
 +      for (unsigned char i = 'A'; i < 'z'; i++)
        {
 -              if (modes[n])
 +              const ModeHandler* const mh = ServerInstance->Modes.FindMode(i, MODETYPE_USER);
 +              if ((!mh) || (!IsModeSet(mh)))
 +                      continue;
 +
 +              ret.push_back(mh->GetModeChar());
 +              if ((includeparams) && (mh->NeedsParam(true)))
                {
 -                      data[offset++] = n + 65;
 -                      ModeHandler* mh = ServerInstance->Modes->FindMode(n + 65, MODETYPE_USER);
 -                      if (showparameters && mh && mh->GetNumParams(true))
 -                      {
 -                              std::string p = mh->GetUserParameter(this);
 -                              if (p.length())
 -                                      params.append(" ").append(p);
 -                      }
 +                      const std::string val = mh->GetUserParameter(this);
 +                      if (!val.empty())
 +                              params.append(1, ' ').append(val);
                }
        }
 -      data[offset] = 0;
 -      strlcat(data, params.c_str(), MAXBUF);
 -      return data;
 +
 +      ret += params;
 +      return ret;
  }
  
 -User::User(const std::string &uid, const std::string& sid, int type)
 -      : uuid(uid), server(sid), usertype(type)
 +User::User(const std::string& uid, Server* srv, int type)
 +      : age(ServerInstance->Time())
 +      , signon(0)
 +      , uuid(uid)
 +      , server(srv)
 +      , registered(REG_NONE)
 +      , quitting(false)
 +      , usertype(type)
  {
 -      age = ServerInstance->Time();
 -      signon = idle_lastmsg = 0;
 -      registered = 0;
 -      quietquit = quitting = exempt = dns_done = false;
 -      quitting_sendq = false;
        client_sa.sa.sa_family = AF_UNSPEC;
  
 -      ServerInstance->Logs->Log("USERS", DEBUG, "New UUID for user: %s", uuid.c_str());
 +      ServerInstance->Logs->Log("USERS", LOG_DEBUG, "New UUID for user: %s", uuid.c_str());
  
 -      user_hash::iterator finduuid = ServerInstance->Users->uuidlist->find(uuid);
 -      if (finduuid == ServerInstance->Users->uuidlist->end())
 -              (*ServerInstance->Users->uuidlist)[uuid] = this;
 -      else
 -              throw CoreException("Duplicate UUID "+std::string(uuid)+" in User constructor");
 +      // Do not insert FakeUsers into the uuidlist so FindUUID() won't return them which is the desired behavior
 +      if (type != USERTYPE_SERVER)
 +      {
 +              if (!ServerInstance->Users.uuidlist.insert(std::make_pair(uuid, this)).second)
 +                      throw CoreException("Duplicate UUID in User constructor: " + uuid);
 +      }
  }
  
  LocalUser::LocalUser(int myfd, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* servaddr)
 -      : User(ServerInstance->GetUID(), ServerInstance->Config->ServerName, USERTYPE_LOCAL), eh(this),
 -      localuseriter(ServerInstance->Users->local_users.end()),
 -      bytes_in(0), bytes_out(0), cmds_in(0), cmds_out(0), nping(0), CommandFloodPenalty(0),
 -      already_sent(0)
 -{
 +      : User(ServerInstance->UIDGen.GetUID(), ServerInstance->FakeClient->server, USERTYPE_LOCAL)
 +      , eh(this)
 +      , bytes_in(0)
 +      , bytes_out(0)
 +      , cmds_in(0)
 +      , cmds_out(0)
 +      , quitting_sendq(false)
 +      , lastping(true)
 +      , exempt(false)
 +      , nping(0)
 +      , idle_lastmsg(0)
 +      , CommandFloodPenalty(0)
 +      , already_sent(0)
 +{
 +      signon = ServerInstance->Time();
 +      // The user's default nick is their UUID
 +      nick = uuid;
        ident = "unknown";
 -      lastping = 0;
        eh.SetFd(myfd);
        memcpy(&client_sa, client, sizeof(irc::sockets::sockaddrs));
        memcpy(&server_sa, servaddr, sizeof(irc::sockets::sockaddrs));
 -      dhost = host = GetIPString();
 +      ChangeRealHost(GetIPString(), true);
  }
  
  User::~User()
  {
 -      if (ServerInstance->Users->uuidlist->find(uuid) != ServerInstance->Users->uuidlist->end())
 -              ServerInstance->Logs->Log("USERS", DEFAULT, "User destructor for %s called without cull", uuid.c_str());
  }
  
  const std::string& User::MakeHost()
        if (!this->cached_makehost.empty())
                return this->cached_makehost;
  
 -      char nhost[MAXBUF];
 -      /* This is much faster than snprintf */
 -      char* t = nhost;
 -      for(const char* n = ident.c_str(); *n; n++)
 -              *t++ = *n;
 -      *t++ = '@';
 -      for(const char* n = host.c_str(); *n; n++)
 -              *t++ = *n;
 -      *t = 0;
 -
 -      this->cached_makehost.assign(nhost);
 -
 +      // XXX: Is there really a need to cache this?
 +      this->cached_makehost = ident + "@" + GetRealHost();
        return this->cached_makehost;
  }
  
@@@ -128,8 -262,18 +128,8 @@@ const std::string& User::MakeHostIP(
        if (!this->cached_hostip.empty())
                return this->cached_hostip;
  
 -      char ihost[MAXBUF];
 -      /* This is much faster than snprintf */
 -      char* t = ihost;
 -      for(const char* n = ident.c_str(); *n; n++)
 -              *t++ = *n;
 -      *t++ = '@';
 -      for(const char* n = this->GetIPString(); *n; n++)
 -              *t++ = *n;
 -      *t = 0;
 -
 -      this->cached_hostip = ihost;
 -
 +      // XXX: Is there really a need to cache this?
 +      this->cached_hostip = ident + "@" + this->GetIPString();
        return this->cached_hostip;
  }
  
@@@ -138,35 -282,111 +138,35 @@@ const std::string& User::GetFullHost(
        if (!this->cached_fullhost.empty())
                return this->cached_fullhost;
  
 -      char result[MAXBUF];
 -      char* t = result;
 -      for(const char* n = nick.c_str(); *n; n++)
 -              *t++ = *n;
 -      *t++ = '!';
 -      for(const char* n = ident.c_str(); *n; n++)
 -              *t++ = *n;
 -      *t++ = '@';
 -      for(const char* n = dhost.c_str(); *n; n++)
 -              *t++ = *n;
 -      *t = 0;
 -
 -      this->cached_fullhost = result;
 -
 +      // XXX: Is there really a need to cache this?
 +      this->cached_fullhost = nick + "!" + ident + "@" + GetDisplayedHost();
        return this->cached_fullhost;
  }
  
 -char* User::MakeWildHost()
 -{
 -      static char nresult[MAXBUF];
 -      char* t = nresult;
 -      *t++ = '*';     *t++ = '!';
 -      *t++ = '*';     *t++ = '@';
 -      for(const char* n = dhost.c_str(); *n; n++)
 -              *t++ = *n;
 -      *t = 0;
 -      return nresult;
 -}
 -
  const std::string& User::GetFullRealHost()
  {
        if (!this->cached_fullrealhost.empty())
                return this->cached_fullrealhost;
  
 -      char fresult[MAXBUF];
 -      char* t = fresult;
 -      for(const char* n = nick.c_str(); *n; n++)
 -              *t++ = *n;
 -      *t++ = '!';
 -      for(const char* n = ident.c_str(); *n; n++)
 -              *t++ = *n;
 -      *t++ = '@';
 -      for(const char* n = host.c_str(); *n; n++)
 -              *t++ = *n;
 -      *t = 0;
 -
 -      this->cached_fullrealhost = fresult;
 -
 +      // XXX: Is there really a need to cache this?
 +      this->cached_fullrealhost = nick + "!" + ident + "@" + GetRealHost();
        return this->cached_fullrealhost;
  }
  
 -bool LocalUser::IsInvited(const irc::string &channel)
 -{
 -      Channel* chan = ServerInstance->FindChan(channel.c_str());
 -      if (!chan)
 -              return false;
 -
 -      return (Invitation::Find(chan, this) != NULL);
 -}
 -
 -InviteList& LocalUser::GetInviteList()
 -{
 -      RemoveExpiredInvites();
 -      return invites;
 -}
 -
 -void LocalUser::InviteTo(const irc::string &channel, time_t invtimeout)
 -{
 -      Channel* chan = ServerInstance->FindChan(channel.c_str());
 -      if (chan)
 -              Invitation::Create(chan, this, invtimeout);
 -}
 -
 -void LocalUser::RemoveInvite(const irc::string &channel)
 -{
 -      Channel* chan = ServerInstance->FindChan(channel.c_str());
 -      if (chan)
 -      {
 -              Invitation* inv = Invitation::Find(chan, this);
 -              if (inv)
 -              {
 -                      inv->cull();
 -                      delete inv;
 -              }
 -      }
 -}
 -
 -void LocalUser::RemoveExpiredInvites()
 -{
 -      Invitation::Find(NULL, this);
 -}
 -
 -bool User::HasModePermission(unsigned char, ModeType)
 +bool User::HasModePermission(const ModeHandler* mh) const
  {
        return true;
  }
  
 -bool LocalUser::HasModePermission(unsigned char mode, ModeType type)
 +bool LocalUser::HasModePermission(const ModeHandler* mh) const
  {
 -      if (!IS_OPER(this))
 +      if (!this->IsOper())
                return false;
  
 +      const unsigned char mode = mh->GetModeChar();
        if (mode < 'A' || mode > ('A' + 64)) return false;
  
 -      return ((type == MODETYPE_USER ? oper->AllowedUserModes : oper->AllowedChanModes))[(mode - 'A')];
 +      return ((mh->GetModeType() == MODETYPE_USER ? oper->AllowedUserModes : oper->AllowedChanModes))[(mode - 'A')];
  
  }
  /*
@@@ -184,7 -404,7 +184,7 @@@ bool User::HasPermission(const std::str
  bool LocalUser::HasPermission(const std::string &command)
  {
        // are they even an oper at all?
 -      if (!IS_OPER(this))
 +      if (!this->IsOper())
        {
                return false;
        }
@@@ -204,10 -424,10 +204,10 @@@ bool User::HasPrivPermission(const std:
  
  bool LocalUser::HasPrivPermission(const std::string &privstr, bool noisy)
  {
 -      if (!IS_OPER(this))
 +      if (!this->IsOper())
        {
                if (noisy)
 -                      this->WriteServ("NOTICE %s :You are not an oper", this->nick.c_str());
 +                      this->WriteNotice("You are not an oper");
                return false;
        }
  
        }
  
        if (noisy)
 -              this->WriteServ("NOTICE %s :Oper type %s does not have access to priv %s", this->nick.c_str(), oper->NameStr(), privstr.c_str());
 +              this->WriteNotice("Oper type " + oper->name + " does not have access to priv " + privstr);
 +
        return false;
  }
  
@@@ -248,7 -467,7 +248,7 @@@ void UserIOHandler::OnDataReady(
        while (user->CommandFloodPenalty < penaltymax && getSendQSize() < sendqmax)
        {
                std::string line;
 -              line.reserve(MAXBUF);
 +              line.reserve(ServerInstance->Config->Limits.MaxLine);
                std::string::size_type qpos = 0;
                while (qpos < recvq.length())
                {
                        case '\n':
                                goto eol_found;
                        }
 -                      if (line.length() < MAXBUF - 2)
 +                      if (line.length() < ServerInstance->Config->Limits.MaxLine - 2)
                                line.push_back(c);
                }
                // if we got here, the recvq ran out before we found a newline
                return;
  eol_found:
                // just found a newline. Terminate the string, and pull it out of recvq
 -              recvq = recvq.substr(qpos);
 +              recvq.erase(0, qpos);
  
                // TODO should this be moved to when it was inserted in recvq?
 -              ServerInstance->stats->statsRecv += qpos;
 +              ServerInstance->stats.Recv += qpos;
                user->bytes_in += qpos;
                user->cmds_in++;
  
 -              ServerInstance->Parser->ProcessBuffer(line, user);
 +              ServerInstance->Parser.ProcessBuffer(line, user);
                if (user->quitting)
                        return;
        }
@@@ -312,6 -531,7 +312,6 @@@ CullResult User::cull(
  {
        if (!quitting)
                ServerInstance->Users->QuitUser(this, "Culled without QuitUser");
 -      PurgeEmptyChannels();
  
        if (client_sa.sa.sa_family != AF_UNSPEC)
                ServerInstance->Users->RemoveCloneCounts(this);
  
  CullResult LocalUser::cull()
  {
 -      // The iterator is initialized to local_users.end() in the constructor. It is
 -      // overwritten in UserManager::AddUser() with the real iterator so this check
 -      // is only a precaution currently.
 -      if (localuseriter != ServerInstance->Users->local_users.end())
 -      {
 -              ServerInstance->Users->local_count--;
 -              ServerInstance->Users->local_users.erase(localuseriter);
 -      }
 -      else
 -              ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: LocalUserIter does not point to a valid entry for " + this->nick);
 -
 -      ClearInvites();
        eh.cull();
        return User::cull();
  }
@@@ -329,20 -561,20 +329,20 @@@ CullResult FakeUser::cull(
  {
        // Fake users don't quit, they just get culled.
        quitting = true;
 -      ServerInstance->Users->clientlist->erase(nick);
 -      ServerInstance->Users->uuidlist->erase(uuid);
 +      // Fake users are not inserted into UserManager::clientlist or uuidlist, so we don't need to modify those here
        return User::cull();
  }
  
  void User::Oper(OperInfo* info)
  {
 -      if (this->IsModeSet('o'))
 +      ModeHandler* opermh = ServerInstance->Modes->FindMode('o', MODETYPE_USER);
 +      if (this->IsModeSet(opermh))
                this->UnOper();
  
 -      this->modes[UM_OPERATOR] = 1;
 +      this->SetMode(opermh, true);
        this->oper = info;
 -      this->WriteServ("MODE %s :+o", this->nick.c_str());
 -      FOREACH_MOD(I_OnOper, OnOper(this, info->name));
 +      this->WriteCommand("MODE", "+o");
 +      FOREACH_MOD(OnOper, (this, info->name));
  
        std::string opername;
        if (info->oper_block)
                LocalUser* l = IS_LOCAL(this);
                std::string vhost = oper->getConfig("vhost");
                if (!vhost.empty())
 -                      l->ChangeDisplayedHost(vhost.c_str());
 +                      l->ChangeDisplayedHost(vhost);
                std::string opClass = oper->getConfig("class");
                if (!opClass.empty())
                        l->SetClass(opClass);
        }
  
        ServerInstance->SNO->WriteToSnoMask('o',"%s (%s@%s) is now an IRC operator of type %s (using oper '%s')",
 -              nick.c_str(), ident.c_str(), host.c_str(), oper->NameStr(), opername.c_str());
 -      this->WriteNumeric(381, "%s :You are now %s %s", nick.c_str(), strchr("aeiouAEIOU", oper->name[0]) ? "an" : "a", oper->NameStr());
 +              nick.c_str(), ident.c_str(), GetRealHost().c_str(), oper->name.c_str(), opername.c_str());
 +      this->WriteNumeric(RPL_YOUAREOPER, InspIRCd::Format("You are now %s %s", strchr("aeiouAEIOU", oper->name[0]) ? "an" : "a", oper->name.c_str()));
  
 -      ServerInstance->Logs->Log("OPER", DEFAULT, "%s opered as type: %s", GetFullRealHost().c_str(), oper->NameStr());
 +      ServerInstance->Logs->Log("OPER", LOG_DEFAULT, "%s opered as type: %s", GetFullRealHost().c_str(), oper->name.c_str());
        ServerInstance->Users->all_opers.push_back(this);
  
        // Expand permissions from config for faster lookup
        if (IS_LOCAL(this))
                oper->init();
  
 -      FOREACH_MOD(I_OnPostOper,OnPostOper(this, oper->name, opername));
 +      FOREACH_MOD(OnPostOper, (this, oper->name, opername));
  }
  
  void OperInfo::init()
  
  void User::UnOper()
  {
 -      if (!IS_OPER(this))
 +      if (!this->IsOper())
                return;
  
        /*
  
  
        /* Remove all oper only modes from the user when the deoper - Bug #466*/
 -      std::string moderemove("-");
 -
 -      for (unsigned char letter = 'A'; letter <= 'z'; letter++)
 +      Modes::ChangeList changelist;
 +      const ModeParser::ModeHandlerMap& usermodes = ServerInstance->Modes->GetModes(MODETYPE_USER);
 +      for (ModeParser::ModeHandlerMap::const_iterator i = usermodes.begin(); i != usermodes.end(); ++i)
        {
 -              ModeHandler* mh = ServerInstance->Modes->FindMode(letter, MODETYPE_USER);
 -              if (mh && mh->NeedsOper())
 -                      moderemove += letter;
 +              ModeHandler* mh = i->second;
 +              if (mh->NeedsOper())
 +                      changelist.push_remove(mh);
        }
  
 +      ServerInstance->Modes->Process(this, NULL, this, changelist);
  
 -      std::vector<std::string> parameters;
 -      parameters.push_back(this->nick);
 -      parameters.push_back(moderemove);
 -
 -      ServerInstance->Parser->CallHandler("MODE", parameters, this);
 -
 -      /* remove the user from the oper list. Will remove multiple entries as a safeguard against bug #404 */
 -      ServerInstance->Users->all_opers.remove(this);
 +      // Remove the user from the oper list
 +      stdalgo::vector::swaperase(ServerInstance->Users->all_opers, this);
  
 -      this->modes[UM_OPERATOR] = 0;
 -}
 -
 -/* adds or updates an entry in the whowas list */
 -void User::AddToWhoWas()
 -{
 -      Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
 -      if (whowas)
 -      {
 -              WhowasRequest req(NULL, whowas, WhowasRequest::WHOWAS_ADD);
 -              req.user = this;
 -              req.Send();
 -      }
 +      ModeHandler* opermh = ServerInstance->Modes->FindMode('o', MODETYPE_USER);
 +      this->SetMode(opermh, false);
  }
  
  /*
   * Check class restrictions
   */
 -void LocalUser::CheckClass()
 +void LocalUser::CheckClass(bool clone_count)
  {
        ConnectClass* a = this->MyClass;
  
                ServerInstance->Users->QuitUser(this, a->config->getString("reason", "Unauthorised connection"));
                return;
        }
 -      else if ((a->GetMaxLocal()) && (ServerInstance->Users->LocalCloneCount(this) > a->GetMaxLocal()))
 +      else if (clone_count)
        {
 -              ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (local)");
 -              if (a->maxconnwarn)
 -                      ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a->GetMaxLocal(), this->GetIPString());
 -              return;
 -      }
 -      else if ((a->GetMaxGlobal()) && (ServerInstance->Users->GlobalCloneCount(this) > a->GetMaxGlobal()))
 -      {
 -              ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (global)");
 -              if (a->maxconnwarn)
 -                      ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s", a->GetMaxGlobal(), this->GetIPString());
 -              return;
 +              const UserManager::CloneCounts& clonecounts = ServerInstance->Users->GetCloneCounts(this);
 +              if ((a->GetMaxLocal()) && (clonecounts.local > a->GetMaxLocal()))
 +              {
 +                      ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (local)");
 +                      if (a->maxconnwarn)
 +                              ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a->GetMaxLocal(), this->GetIPString().c_str());
 +                      return;
 +              }
 +              else if ((a->GetMaxGlobal()) && (clonecounts.global > a->GetMaxGlobal()))
 +              {
 +                      ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (global)");
 +                      if (a->maxconnwarn)
 +                              ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s", a->GetMaxGlobal(), this->GetIPString().c_str());
 +                      return;
 +              }
        }
  
 -      this->nping = ServerInstance->Time() + a->GetPingTime() + ServerInstance->Config->dns_timeout;
 +      this->nping = ServerInstance->Time() + a->GetPingTime();
  }
  
 -bool User::CheckLines(bool doZline)
 +bool LocalUser::CheckLines(bool doZline)
  {
        const char* check[] = { "G" , "K", (doZline) ? "Z" : NULL, NULL };
  
  
  void LocalUser::FullConnect()
  {
 -      ServerInstance->stats->statsConnects++;
 +      ServerInstance->stats.Connects++;
        this->idle_lastmsg = ServerInstance->Time();
  
        /*
        if (quitting)
                return;
  
 -      if (ServerInstance->Config->WelcomeNotice)
 -              this->WriteServ("NOTICE Auth :Welcome to \002%s\002!",ServerInstance->Config->Network.c_str());
 -      this->WriteNumeric(RPL_WELCOME, "%s :Welcome to the %s IRC Network %s",this->nick.c_str(), ServerInstance->Config->Network.c_str(), GetFullRealHost().c_str());
 -      this->WriteNumeric(RPL_YOURHOSTIS, "%s :Your host is %s, running version %s",this->nick.c_str(),ServerInstance->Config->ServerName.c_str(),BRANCH);
 -      this->WriteNumeric(RPL_SERVERCREATED, "%s :This server was created %s %s", this->nick.c_str(), __TIME__, __DATE__);
 +      this->WriteNumeric(RPL_WELCOME, InspIRCd::Format("Welcome to the %s IRC Network %s", ServerInstance->Config->Network.c_str(), GetFullRealHost().c_str()));
 +      this->WriteNumeric(RPL_YOURHOSTIS, InspIRCd::Format("Your host is %s, running version %s", ServerInstance->Config->ServerName.c_str(), INSPIRCD_BRANCH));
 +      this->WriteNumeric(RPL_SERVERCREATED, InspIRCd::TimeString(ServerInstance->startup_time, "This server was created %H:%M:%S %b %d %Y"));
  
 -      std::string umlist = ServerInstance->Modes->UserModeList();
 -      std::string cmlist = ServerInstance->Modes->ChannelModeList();
 -      std::string pmlist = ServerInstance->Modes->ParaModeList();
 -      this->WriteNumeric(RPL_SERVERVERSION, "%s %s %s %s %s %s", this->nick.c_str(), ServerInstance->Config->ServerName.c_str(), BRANCH, umlist.c_str(), cmlist.c_str(), pmlist.c_str());
 +      const TR1NS::array<std::string, 3>& modelist = ServerInstance->Modes->GetModeListFor004Numeric();
 +      this->WriteNumeric(RPL_SERVERVERSION, ServerInstance->Config->ServerName, INSPIRCD_BRANCH, modelist[0], modelist[1], modelist[2]);
  
 -      ServerInstance->Config->Send005(this);
 -      this->WriteNumeric(RPL_YOURUUID, "%s %s :your unique ID", this->nick.c_str(), this->uuid.c_str());
 +      ServerInstance->ISupport.SendTo(this);
  
        /* Now registered */
        if (ServerInstance->Users->unregistered_count)
  
        /* Trigger MOTD and LUSERS output, give modules a chance too */
        ModResult MOD_RESULT;
 -      std::string command("MOTD");
 +      std::string command("LUSERS");
        std::vector<std::string> parameters;
        FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true, command));
        if (!MOD_RESULT)
 -              ServerInstance->Parser->CallHandler(command, parameters, this);
 +              ServerInstance->Parser.CallHandler(command, parameters, this);
  
        MOD_RESULT = MOD_RES_PASSTHRU;
 -      command = "LUSERS";
 +      command = "MOTD";
        FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true, command));
        if (!MOD_RESULT)
 -              ServerInstance->Parser->CallHandler(command, parameters, this);
 +              ServerInstance->Parser.CallHandler(command, parameters, this);
  
        if (ServerInstance->Config->RawLog)
                WriteServ("PRIVMSG %s :*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.", nick.c_str());
         * We don't set REG_ALL until triggering OnUserConnect, so some module events don't spew out stuff
         * for a user that doesn't exist yet.
         */
 -      FOREACH_MOD(I_OnUserConnect,OnUserConnect(this));
 +      FOREACH_MOD(OnUserConnect, (this));
  
        this->registered = REG_ALL;
  
 -      FOREACH_MOD(I_OnPostConnect,OnPostConnect(this));
 +      FOREACH_MOD(OnPostConnect, (this));
  
        ServerInstance->SNO->WriteToSnoMask('c',"Client connecting on port %d (class %s): %s (%s) [%s]",
 -              this->GetServerPort(), this->MyClass->name.c_str(), GetFullRealHost().c_str(), this->GetIPString(), this->fullname.c_str());
 -      ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCache: Adding NEGATIVE hit for %s", this->GetIPString());
 -      ServerInstance->BanCache->AddHit(this->GetIPString(), "", "");
 +              this->GetServerPort(), this->MyClass->name.c_str(), GetFullRealHost().c_str(), this->GetIPString().c_str(), this->fullname.c_str());
 +      ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Adding NEGATIVE hit for " + this->GetIPString());
 +      ServerInstance->BanCache.AddHit(this->GetIPString(), "", "");
        // reset the flood penalty (which could have been raised due to things like auto +x)
        CommandFloodPenalty = 0;
  }
@@@ -595,25 -844,72 +595,25 @@@ void User::InvalidateCache(
        cached_fullrealhost.clear();
  }
  
 -bool User::ChangeNick(const std::string& newnick, bool force)
 +bool User::ChangeNick(const std::string& newnick, time_t newts)
  {
        if (quitting)
        {
 -              ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: Attempted to change nick of a quitting user: " + this->nick);
 +              ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Attempted to change nick of a quitting user: " + this->nick);
                return false;
        }
  
 -      ModResult MOD_RESULT;
 -
 -      if (force)
 -              ServerInstance->NICKForced.set(this, 1);
 -      FIRST_MOD_RESULT(OnUserPreNick, MOD_RESULT, (this, newnick));
 -      ServerInstance->NICKForced.set(this, 0);
 -
 -      if (MOD_RESULT == MOD_RES_DENY)
 +      User* const InUse = ServerInstance->FindNickOnly(newnick);
 +      if (InUse == this)
        {
 -              ServerInstance->stats->statsCollisions++;
 -              return false;
 -      }
 -
 -      if (assign(newnick) == assign(nick))
 -      {
 -              // case change, don't need to check Q:lines and such
 +              // case change, don't need to check campers
                // and, if it's identical including case, we can leave right now
 +              // We also don't update the nick TS if it's a case change, either
                if (newnick == nick)
                        return true;
        }
        else
        {
 -              /*
 -               * Don't check Q:Lines if it's a server-enforced change, just on the off-chance some fucking *moron*
 -               * tries to Q:Line SIDs, also, this means we just get our way period, as it really should be.
 -               * Thanks Kein for finding this. -- w00t
 -               *
 -               * Also don't check Q:Lines for remote nickchanges, they should have our Q:Lines anyway to enforce themselves.
 -               *              -- w00t
 -               */
 -              if (IS_LOCAL(this) && !force)
 -              {
 -                      XLine* mq = ServerInstance->XLines->MatchesLine("Q",newnick);
 -                      if (mq)
 -                      {
 -                              if (this->registered == REG_ALL)
 -                              {
 -                                      ServerInstance->SNO->WriteGlobalSno('a', "Q-Lined nickname %s from %s: %s",
 -                                              newnick.c_str(), GetFullRealHost().c_str(), mq->reason.c_str());
 -                              }
 -                              this->WriteNumeric(432, "%s %s :Invalid nickname: %s",this->nick.c_str(), newnick.c_str(), mq->reason.c_str());
 -                              return false;
 -                      }
 -
 -                      if (ServerInstance->Config->RestrictBannedUsers)
 -                      {
 -                              for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
 -                              {
 -                                      Channel *chan = *i;
 -                                      if (chan->GetPrefixValue(this) < VOICE_VALUE && chan->IsBanned(this))
 -                                      {
 -                                              this->WriteNumeric(404, "%s %s :Cannot send to channel (you're banned)", this->nick.c_str(), chan->name.c_str());
 -                                              return false;
 -                                      }
 -                              }
 -                      }
 -              }
 -
                /*
                 * Uh oh.. if the nickname is in use, and it's not in use by the person using it (doh) --
                 * then we have a potential collide. Check whether someone else is camping on the nick
                 * If the guy using the nick is already using it, tell the incoming nick change to gtfo,
                 * because the nick is already (rightfully) in use. -- w00t
                 */
 -              User* InUse = ServerInstance->FindNickOnly(newnick);
 -              if (InUse && (InUse != this))
 +              if (InUse)
                {
                        if (InUse->registered != REG_ALL)
                        {
                                /* force the camper to their UUID, and ask them to re-send a NICK. */
 -                              InUse->WriteTo(InUse, "NICK %s", InUse->uuid.c_str());
 -                              InUse->WriteNumeric(433, "%s %s :Nickname overruled.", InUse->nick.c_str(), InUse->nick.c_str());
 -
 -                              ServerInstance->Users->clientlist->erase(InUse->nick);
 -                              (*(ServerInstance->Users->clientlist))[InUse->uuid] = InUse;
 -
 -                              InUse->nick = InUse->uuid;
 -                              InUse->InvalidateCache();
 -                              InUse->registered &= ~REG_NICK;
 +                              LocalUser* const localuser = static_cast<LocalUser*>(InUse);
 +                              localuser->OverruleNick();
                        }
                        else
                        {
                                /* No camping, tell the incoming user  to stop trying to change nick ;p */
 -                              this->WriteNumeric(433, "%s %s :Nickname is already in use.", this->registered >= REG_NICK ? this->nick.c_str() : "*", newnick.c_str());
 +                              this->WriteNumeric(ERR_NICKNAMEINUSE, newnick, "Nickname is already in use.");
                                return false;
                        }
                }
 +
 +              age = newts ? newts : ServerInstance->Time();
        }
  
        if (this->registered == REG_ALL)
        nick = newnick;
  
        InvalidateCache();
 -      ServerInstance->Users->clientlist->erase(oldnick);
 -      (*(ServerInstance->Users->clientlist))[newnick] = this;
 +      ServerInstance->Users->clientlist.erase(oldnick);
 +      ServerInstance->Users->clientlist[newnick] = this;
  
        if (registered == REG_ALL)
 -              FOREACH_MOD(I_OnUserPostNick,OnUserPostNick(this,oldnick));
 +              FOREACH_MOD(OnUserPostNick, (this,oldnick));
  
        return true;
  }
  
 +void LocalUser::OverruleNick()
 +{
 +      this->WriteFrom(this, "NICK %s", this->uuid.c_str());
 +      this->WriteNumeric(ERR_NICKNAMEINUSE, this->nick, "Nickname overruled.");
 +
 +      // Clear the bit before calling ChangeNick() to make it NOT run the OnUserPostNick() hook
 +      this->registered &= ~REG_NICK;
 +      this->ChangeNick(this->uuid);
 +}
 +
  int LocalUser::GetServerPort()
  {
        switch (this->server_sa.sa.sa_family)
        return 0;
  }
  
 -const char* User::GetIPString()
 +const std::string& User::GetIPString()
  {
 -      int port;
        if (cachedip.empty())
        {
 -              irc::sockets::satoap(client_sa, cachedip, port);
 +              cachedip = client_sa.addr();
                /* IP addresses starting with a : on irc are a Bad Thing (tm) */
 -              if (cachedip.c_str()[0] == ':')
 +              if (cachedip[0] == ':')
                        cachedip.insert(cachedip.begin(),1,'0');
        }
  
 -      return cachedip.c_str();
 +      return cachedip;
 +}
 +
 +const std::string& User::GetHost(bool uncloak) const
 +{
 +      return uncloak ? GetRealHost() : GetDisplayedHost();
 +}
 +
 +const std::string& User::GetDisplayedHost() const
 +{
 +      return displayhost.empty() ? realhost : displayhost;
 +}
 +
 +const std::string& User::GetRealHost() const
 +{
 +      return realhost;
  }
  
  irc::sockets::cidr_mask User::GetCIDRMask()
        return irc::sockets::cidr_mask(client_sa, range);
  }
  
 -bool User::SetClientIP(const char* sip, bool recheck_eline)
 +bool User::SetClientIP(const std::string& address, bool recheck_eline)
  {
        this->InvalidateCache();
 -      return irc::sockets::aptosa(sip, 0, client_sa);
 +      return irc::sockets::aptosa(address, 0, client_sa);
  }
  
  void User::SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_eline)
  {
-       cachedip.clear();
-       cached_hostip.clear();
+       this->InvalidateCache();
        memcpy(&client_sa, &sa, sizeof(irc::sockets::sockaddrs));
  }
  
 -bool LocalUser::SetClientIP(const char* sip, bool recheck_eline)
 +bool LocalUser::SetClientIP(const std::string& address, bool recheck_eline)
  {
        irc::sockets::sockaddrs sa;
 -      if (!irc::sockets::aptosa(sip, 0, sa))
 +      if (!irc::sockets::aptosa(address, 0, sa))
                // Invalid
                return false;
  
@@@ -754,7 -1031,7 +753,7 @@@ void LocalUser::SetClientIP(const irc::
                if (recheck_eline)
                        this->exempt = (ServerInstance->XLines->MatchesLine("E", this) != NULL);
  
 -              FOREACH_MOD(I_OnSetUserIP,OnSetUserIP(this));
 +              FOREACH_MOD(OnSetUserIP, (this));
        }
  }
  
@@@ -770,23 -1047,23 +769,23 @@@ void User::Write(const char *text, ...
  
  void LocalUser::Write(const std::string& text)
  {
 -      if (!ServerInstance->SE->BoundsCheckFd(&eh))
 +      if (!SocketEngine::BoundsCheckFd(&eh))
                return;
  
 -      if (text.length() > MAXBUF - 2)
 +      if (text.length() > ServerInstance->Config->Limits.MaxLine - 2)
        {
                // this should happen rarely or never. Crop the string at 512 and try again.
 -              std::string try_again = text.substr(0, MAXBUF - 2);
 +              std::string try_again(text, 0, ServerInstance->Config->Limits.MaxLine - 2);
                Write(try_again);
                return;
        }
  
 -      ServerInstance->Logs->Log("USEROUTPUT", RAWIO, "C[%s] O %s", uuid.c_str(), text.c_str());
 +      ServerInstance->Logs->Log("USEROUTPUT", LOG_RAWIO, "C[%s] O %s", uuid.c_str(), text.c_str());
  
        eh.AddWriteBuf(text);
        eh.AddWriteBuf(wide_newline);
  
 -      ServerInstance->stats->statsSent += text.length() + 2;
 +      ServerInstance->stats.Sent += text.length() + 2;
        this->bytes_out += text.length() + 2;
        this->cmds_out++;
  }
   */
  void LocalUser::Write(const char *text, ...)
  {
 -      va_list argsPtr;
 -      char textbuffer[MAXBUF];
 -
 -      va_start(argsPtr, text);
 -      vsnprintf(textbuffer, MAXBUF, text, argsPtr);
 -      va_end(argsPtr);
 -
 -      this->Write(std::string(textbuffer));
 +      std::string textbuffer;
 +      VAFORMAT(textbuffer, text, text);
 +      this->Write(textbuffer);
  }
  
  void User::WriteServ(const std::string& text)
   */
  void User::WriteServ(const char* text, ...)
  {
 -      va_list argsPtr;
 -      char textbuffer[MAXBUF];
 -
 -      va_start(argsPtr, text);
 -      vsnprintf(textbuffer, MAXBUF, text, argsPtr);
 -      va_end(argsPtr);
 -
 -      this->WriteServ(std::string(textbuffer));
 +      std::string textbuffer;
 +      VAFORMAT(textbuffer, text, text);
 +      this->WriteServ(textbuffer);
  }
  
 -
 -void User::WriteNumeric(unsigned int numeric, const char* text, ...)
 +void User::WriteCommand(const char* command, const std::string& text)
  {
 -      va_list argsPtr;
 -      char textbuffer[MAXBUF];
 -
 -      va_start(argsPtr, text);
 -      vsnprintf(textbuffer, MAXBUF, text, argsPtr);
 -      va_end(argsPtr);
 +      this->WriteServ(command + (this->registered & REG_NICK ? " " + this->nick : " *") + " " + text);
 +}
  
 -      this->WriteNumeric(numeric, std::string(textbuffer));
 +namespace
 +{
 +      std::string BuildNumeric(const std::string& source, User* targetuser, unsigned int num, const std::vector<std::string>& params)
 +      {
 +              const char* const target = (targetuser->registered & REG_NICK ? targetuser->nick.c_str() : "*");
 +              std::string raw = InspIRCd::Format(":%s %03u %s", source.c_str(), num, target);
 +              if (!params.empty())
 +              {
 +                      for (std::vector<std::string>::const_iterator i = params.begin(); i != params.end()-1; ++i)
 +                              raw.append(1, ' ').append(*i);
 +                      raw.append(" :").append(params.back());
 +              }
 +              return raw;
 +      }
  }
  
 -void User::WriteNumeric(unsigned int numeric, const std::string &text)
 +void User::WriteNumeric(const Numeric::Numeric& numeric)
  {
 -      char textbuffer[MAXBUF];
        ModResult MOD_RESULT;
  
 -      FIRST_MOD_RESULT(OnNumeric, MOD_RESULT, (this, numeric, text));
 +      FIRST_MOD_RESULT(OnNumeric, MOD_RESULT, (this, numeric));
  
        if (MOD_RESULT == MOD_RES_DENY)
                return;
  
 -      snprintf(textbuffer,MAXBUF,":%s %03u %s",ServerInstance->Config->ServerName.c_str(), numeric, text.c_str());
 -      this->Write(std::string(textbuffer));
 +      const std::string& servername = (numeric.GetServer() ? numeric.GetServer()->GetName() : ServerInstance->Config->ServerName);
 +      this->Write(BuildNumeric(servername, this, numeric.GetNumeric(), numeric.GetParams()));
  }
  
  void User::WriteFrom(User *user, const std::string &text)
  {
 -      char tb[MAXBUF];
 -
 -      snprintf(tb,MAXBUF,":%s %s",user->GetFullHost().c_str(),text.c_str());
 -
 -      this->Write(std::string(tb));
 +      const std::string message = ":" + user->GetFullHost() + " " + text;
 +      this->Write(message);
  }
  
  
  
  void User::WriteFrom(User *user, const char* text, ...)
  {
 -      va_list argsPtr;
 -      char textbuffer[MAXBUF];
 -
 -      va_start(argsPtr, text);
 -      vsnprintf(textbuffer, MAXBUF, text, argsPtr);
 -      va_end(argsPtr);
 -
 -      this->WriteFrom(user, std::string(textbuffer));
 +      std::string textbuffer;
 +      VAFORMAT(textbuffer, text, text);
 +      this->WriteFrom(user, textbuffer);
  }
  
 -
 -/* write text to an destination user from a source user (e.g. user privmsg) */
 -
 -void User::WriteTo(User *dest, const char *data, ...)
 +void User::WriteRemoteNotice(const std::string& text)
  {
 -      char textbuffer[MAXBUF];
 -      va_list argsPtr;
 -
 -      va_start(argsPtr, data);
 -      vsnprintf(textbuffer, MAXBUF, data, argsPtr);
 -      va_end(argsPtr);
 -
 -      this->WriteTo(dest, std::string(textbuffer));
 +      ServerInstance->PI->SendUserNotice(this, text);
  }
  
 -void User::WriteTo(User *dest, const std::string &data)
 +void LocalUser::WriteRemoteNotice(const std::string& text)
  {
 -      dest->WriteFrom(this, data);
 +      WriteNotice(text);
  }
  
 -void User::WriteCommon(const char* text, ...)
 +namespace
  {
 -      char textbuffer[MAXBUF];
 -      va_list argsPtr;
 -
 -      if (this->registered != REG_ALL || quitting)
 -              return;
 -
 -      int len = snprintf(textbuffer,MAXBUF,":%s ",this->GetFullHost().c_str());
 +      class WriteCommonRawHandler : public User::ForEachNeighborHandler
 +      {
 +              const std::string& msg;
  
 -      va_start(argsPtr, text);
 -      vsnprintf(textbuffer + len, MAXBUF - len, text, argsPtr);
 -      va_end(argsPtr);
 +              void Execute(LocalUser* user) CXX11_OVERRIDE
 +              {
 +                      user->Write(msg);
 +              }
  
 -      this->WriteCommonRaw(std::string(textbuffer), true);
 +       public:
 +              WriteCommonRawHandler(const std::string& message)
 +                      : msg(message)
 +              {
 +              }
 +      };
  }
  
 -void User::WriteCommonExcept(const char* text, ...)
 +void User::WriteCommon(const char* text, ...)
  {
 -      char textbuffer[MAXBUF];
 -      va_list argsPtr;
 -
 -      if (this->registered != REG_ALL || quitting)
 -              return;
 -
 -      int len = snprintf(textbuffer,MAXBUF,":%s ",this->GetFullHost().c_str());
 -
 -      va_start(argsPtr, text);
 -      vsnprintf(textbuffer + len, MAXBUF - len, text, argsPtr);
 -      va_end(argsPtr);
 -
 -      this->WriteCommonRaw(std::string(textbuffer), false);
 +      std::string textbuffer;
 +      VAFORMAT(textbuffer, text, text);
 +      textbuffer = ":" + this->GetFullHost() + " " + textbuffer;
 +      this->WriteCommonRaw(textbuffer, true);
  }
  
  void User::WriteCommonRaw(const std::string &line, bool include_self)
  {
 -      if (this->registered != REG_ALL || quitting)
 -              return;
 -
 -      LocalUser::already_sent_id++;
 -
 -      UserChanList include_c(chans);
 -      std::map<User*,bool> exceptions;
 -
 -      exceptions[this] = include_self;
 -
 -      FOREACH_MOD(I_OnBuildNeighborList,OnBuildNeighborList(this, include_c, exceptions));
 -
 -      for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
 -      {
 -              LocalUser* u = IS_LOCAL(i->first);
 -              if (u && !u->quitting)
 -              {
 -                      u->already_sent = LocalUser::already_sent_id;
 -                      if (i->second)
 -                              u->Write(line);
 -              }
 -      }
 -      for (UCListIter v = include_c.begin(); v != include_c.end(); ++v)
 -      {
 -              Channel* c = *v;
 -              const UserMembList* ulist = c->GetUsers();
 -              for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++)
 -              {
 -                      LocalUser* u = IS_LOCAL(i->first);
 -                      if (u && !u->quitting && u->already_sent != LocalUser::already_sent_id)
 -                      {
 -                              u->already_sent = LocalUser::already_sent_id;
 -                              u->Write(line);
 -                      }
 -              }
 -      }
 +      WriteCommonRawHandler handler(line);
 +      ForEachNeighbor(handler, include_self);
  }
  
 -void User::WriteCommonQuit(const std::string &normal_text, const std::string &oper_text)
 +void User::ForEachNeighbor(ForEachNeighborHandler& handler, bool include_self)
  {
 -      char tb1[MAXBUF];
 -      char tb2[MAXBUF];
 -
 -      if (this->registered != REG_ALL)
 -              return;
 +      // The basic logic for visiting the neighbors of a user is to iterate the channel list of the user
 +      // and visit all users on those channels. Because two users may share more than one common channel,
 +      // we must skip users that we have already visited.
 +      // To do this, we make use of a global counter and an integral 'already_sent' field in LocalUser.
 +      // The global counter is incremented every time we do something for each neighbor of a user. Then,
 +      // before visiting a member we examine user->already_sent. If it's equal to the current counter, we
 +      // skip the member. Otherwise, we set it to the current counter and visit the member.
  
 -      already_sent_t uniq_id = ++LocalUser::already_sent_id;
 -
 -      snprintf(tb1,MAXBUF,":%s QUIT :%s",this->GetFullHost().c_str(),normal_text.c_str());
 -      snprintf(tb2,MAXBUF,":%s QUIT :%s",this->GetFullHost().c_str(),oper_text.c_str());
 -      std::string out1 = tb1;
 -      std::string out2 = tb2;
 -
 -      UserChanList include_c(chans);
 -      std::map<User*,bool> exceptions;
 +      // Ask modules to build a list of exceptions.
 +      // Mods may also exclude entire channels by erasing them from include_chans.
 +      IncludeChanList include_chans(chans.begin(), chans.end());
 +      std::map<User*, bool> exceptions;
 +      exceptions[this] = include_self;
 +      FOREACH_MOD(OnBuildNeighborList, (this, include_chans, exceptions));
  
 -      FOREACH_MOD(I_OnBuildNeighborList,OnBuildNeighborList(this, include_c, exceptions));
 +      // Get next id, guaranteed to differ from the already_sent field of all users
 +      const already_sent_t newid = ServerInstance->Users.NextAlreadySentId();
  
 -      for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
 +      // Handle exceptions first
 +      for (std::map<User*, bool>::const_iterator i = exceptions.begin(); i != exceptions.end(); ++i)
        {
 -              LocalUser* u = IS_LOCAL(i->first);
 -              if (u && !u->quitting)
 +              LocalUser* curr = IS_LOCAL(i->first);
 +              if (curr)
                {
 -                      u->already_sent = uniq_id;
 -                      if (i->second)
 -                              u->Write(IS_OPER(u) ? out2 : out1);
 +                      // Mark as visited to ensure we won't visit again if there is a common channel
 +                      curr->already_sent = newid;
 +                      // Always treat quitting users as excluded
 +                      if ((i->second) && (!curr->quitting))
 +                              handler.Execute(curr);
                }
        }
 -      for (UCListIter v = include_c.begin(); v != include_c.end(); ++v)
 +
 +      // Now consider the real neighbors
 +      for (IncludeChanList::const_iterator i = include_chans.begin(); i != include_chans.end(); ++i)
        {
 -              const UserMembList* ulist = (*v)->GetUsers();
 -              for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++)
 +              Channel* chan = (*i)->chan;
 +              const Channel::MemberMap& userlist = chan->GetUsers();
 +              for (Channel::MemberMap::const_iterator j = userlist.begin(); j != userlist.end(); ++j)
                {
 -                      LocalUser* u = IS_LOCAL(i->first);
 -                      if (u && !u->quitting && (u->already_sent != uniq_id))
 +                      LocalUser* curr = IS_LOCAL(j->first);
 +                      // User not yet visited?
 +                      if ((curr) && (curr->already_sent != newid))
                        {
 -                              u->already_sent = uniq_id;
 -                              u->Write(IS_OPER(u) ? out2 : out1);
 +                              // Mark as visited and execute function
 +                              curr->already_sent = newid;
 +                              handler.Execute(curr);
                        }
                }
        }
  }
  
 -void LocalUser::SendText(const std::string& line)
 -{
 -      Write(line);
 -}
 -
 -void RemoteUser::SendText(const std::string& line)
 -{
 -      ServerInstance->PI->PushToClient(this, line);
 -}
 -
 -void FakeUser::SendText(const std::string& line)
 +void User::WriteRemoteNumeric(const Numeric::Numeric& numeric)
  {
 -}
 -
 -void User::SendText(const char *text, ...)
 -{
 -      va_list argsPtr;
 -      char line[MAXBUF];
 -
 -      va_start(argsPtr, text);
 -      vsnprintf(line, MAXBUF, text, argsPtr);
 -      va_end(argsPtr);
 -
 -      SendText(std::string(line));
 -}
 -
 -void User::SendText(const std::string &LinePrefix, std::stringstream &TextStream)
 -{
 -      std::string line;
 -      std::string Word;
 -      while (TextStream >> Word)
 -      {
 -              size_t lineLength = LinePrefix.length() + line.length() + Word.length() + 13;
 -              if (lineLength > MAXBUF)
 -              {
 -                      SendText(LinePrefix + line);
 -                      line.clear();
 -              }
 -              line += " " + Word;
 -      }
 -      SendText(LinePrefix + line);
 +      WriteNumeric(numeric);
  }
  
  /* return 0 or 1 depending if users u and u2 share one or more common channels
   */
  bool User::SharesChannelWith(User *other)
  {
 -      if ((!other) || (this->registered != REG_ALL) || (other->registered != REG_ALL))
 -              return false;
 -
        /* Outer loop */
 -      for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
 +      for (User::ChanList::iterator i = this->chans.begin(); i != this->chans.end(); ++i)
        {
                /* Eliminate the inner loop (which used to be ~equal in size to the outer loop)
                 * by replacing it with a map::find which *should* be more efficient
                 */
 -              if ((*i)->HasUser(other))
 +              if ((*i)->chan->HasUser(other))
                        return true;
        }
        return false;
  }
  
 -bool User::ChangeName(const char* gecos)
 +bool User::ChangeName(const std::string& gecos)
  {
        if (!this->fullname.compare(gecos))
                return true;
                FIRST_MOD_RESULT(OnChangeLocalUserGECOS, MOD_RESULT, (IS_LOCAL(this),gecos));
                if (MOD_RESULT == MOD_RES_DENY)
                        return false;
 -              FOREACH_MOD(I_OnChangeName,OnChangeName(this,gecos));
 +              FOREACH_MOD(OnChangeName, (this,gecos));
        }
        this->fullname.assign(gecos, 0, ServerInstance->Config->Limits.MaxGecos);
  
        return true;
  }
  
 -void User::DoHostCycle(const std::string &quitline)
 -{
 -      char buffer[MAXBUF];
 -
 -      if (!ServerInstance->Config->CycleHosts)
 -              return;
 -
 -      already_sent_t silent_id = ++LocalUser::already_sent_id;
 -      already_sent_t seen_id = ++LocalUser::already_sent_id;
 -
 -      UserChanList include_c(chans);
 -      std::map<User*,bool> exceptions;
 -
 -      FOREACH_MOD(I_OnBuildNeighborList,OnBuildNeighborList(this, include_c, exceptions));
 -
 -      // Users shouldn't see themselves quitting when host cycling
 -      exceptions.erase(this);
 -      for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
 -      {
 -              LocalUser* u = IS_LOCAL(i->first);
 -              if (u && !u->quitting)
 -              {
 -                      if (i->second)
 -                      {
 -                              u->already_sent = seen_id;
 -                              u->Write(quitline);
 -                      }
 -                      else
 -                      {
 -                              u->already_sent = silent_id;
 -                      }
 -              }
 -      }
 -      for (UCListIter v = include_c.begin(); v != include_c.end(); ++v)
 -      {
 -              Channel* c = *v;
 -              snprintf(buffer, MAXBUF, ":%s JOIN %s", GetFullHost().c_str(), c->name.c_str());
 -              std::string joinline(buffer);
 -              Membership* memb = c->GetUser(this);
 -              std::string modeline = memb->modes;
 -              if (modeline.length() > 0)
 -              {
 -                      for(unsigned int i=0; i < memb->modes.length(); i++)
 -                              modeline.append(" ").append(nick);
 -                      snprintf(buffer, MAXBUF, ":%s MODE %s +%s",
 -                              ServerInstance->Config->CycleHostsFromUser ? GetFullHost().c_str() : ServerInstance->Config->ServerName.c_str(),
 -                              c->name.c_str(), modeline.c_str());
 -                      modeline = buffer;
 -              }
 -
 -              const UserMembList *ulist = c->GetUsers();
 -              for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++)
 -              {
 -                      LocalUser* u = IS_LOCAL(i->first);
 -                      if (u == NULL || u == this)
 -                              continue;
 -                      if (u->already_sent == silent_id)
 -                              continue;
 -
 -                      if (u->already_sent != seen_id)
 -                      {
 -                              u->Write(quitline);
 -                              u->already_sent = seen_id;
 -                      }
 -                      u->Write(joinline);
 -                      if (modeline.length() > 0)
 -                              u->Write(modeline);
 -              }
 -      }
 -}
 -
 -bool User::ChangeDisplayedHost(const char* shost)
 +bool User::ChangeDisplayedHost(const std::string& shost)
  {
 -      if (dhost == shost)
 +      if (GetDisplayedHost() == shost)
                return true;
  
        if (IS_LOCAL(this))
                        return false;
        }
  
 -      FOREACH_MOD(I_OnChangeHost, OnChangeHost(this,shost));
 +      FOREACH_MOD(OnChangeHost, (this,shost));
  
 -      std::string quitstr = ":" + GetFullHost() + " QUIT :Changing host";
 -
 -      /* Fix by Om: User::dhost is 65 long, this was truncating some long hosts */
 -      this->dhost.assign(shost, 0, 64);
 +      if (realhost == shost)
 +              this->displayhost.clear();
 +      else
 +              this->displayhost.assign(shost, 0, ServerInstance->Config->Limits.MaxHost);
  
        this->InvalidateCache();
  
 -      this->DoHostCycle(quitstr);
 -
        if (IS_LOCAL(this))
 -              this->WriteNumeric(RPL_YOURDISPLAYEDHOST, "%s %s :is now your displayed host",this->nick.c_str(),this->dhost.c_str());
 +              this->WriteNumeric(RPL_YOURDISPLAYEDHOST, this->GetDisplayedHost(), "is now your displayed host");
  
        return true;
  }
  
 -bool User::ChangeIdent(const char* newident)
 +void User::ChangeRealHost(const std::string& host, bool resetdisplay)
  {
 -      if (this->ident == newident)
 -              return true;
 -
 -      FOREACH_MOD(I_OnChangeIdent, OnChangeIdent(this,newident));
 +      if (displayhost == host)
 +              return;
 +      
 +      if (displayhost.empty() && !resetdisplay)
 +              displayhost = realhost;
  
 -      std::string quitstr = ":" + GetFullHost() + " QUIT :Changing ident";
 -
 -      this->ident.assign(newident, 0, ServerInstance->Config->Limits.IdentMax);
 +      else if (displayhost == host || resetdisplay)
 +              displayhost.clear();
  
 +      realhost = host;
        this->InvalidateCache();
 -
 -      this->DoHostCycle(quitstr);
 -
 -      return true;
  }
  
 -void User::SendAll(const char* command, const char* text, ...)
 +bool User::ChangeIdent(const std::string& newident)
  {
 -      char textbuffer[MAXBUF];
 -      char formatbuffer[MAXBUF];
 -      va_list argsPtr;
 -
 -      va_start(argsPtr, text);
 -      vsnprintf(textbuffer, MAXBUF, text, argsPtr);
 -      va_end(argsPtr);
 -
 -      snprintf(formatbuffer,MAXBUF,":%s %s $* :%s", this->GetFullHost().c_str(), command, textbuffer);
 -      std::string fmt = formatbuffer;
 -
 -      for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); i++)
 -      {
 -              if ((*i)->registered == REG_ALL)
 -                      (*i)->Write(fmt);
 -      }
 -}
 -
 -
 -std::string User::ChannelList(User* source, bool spy)
 -{
 -      std::string list;
 -
 -      for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
 -      {
 -              Channel* c = *i;
 -              /* If the target is the sender, neither +p nor +s is set, or
 -               * the channel contains the user, it is not a spy channel
 -               */
 -              if (spy != (source == this || !(c->IsModeSet('p') || c->IsModeSet('s')) || c->HasUser(source)))
 -                      list.append(c->GetPrefixChar(this)).append(c->name).append(" ");
 -      }
 -
 -      return list;
 -}
 -
 -void User::SplitChanList(User* dest, const std::string &cl)
 -{
 -      std::string line;
 -      std::ostringstream prefix;
 -      std::string::size_type start, pos;
 -
 -      prefix << this->nick << " " << dest->nick << " :";
 -      line = prefix.str();
 -      int namelen = ServerInstance->Config->ServerName.length() + 6;
 +      if (this->ident == newident)
 +              return true;
  
 -      for (start = 0; (pos = cl.find(' ', start)) != std::string::npos; start = pos+1)
 -      {
 -              if (line.length() + namelen + pos - start > 510)
 -              {
 -                      ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());
 -                      line = prefix.str();
 -              }
 +      FOREACH_MOD(OnChangeIdent, (this,newident));
  
 -              line.append(cl.substr(start, pos - start + 1));
 -      }
 +      this->ident.assign(newident, 0, ServerInstance->Config->Limits.IdentMax);
 +      this->InvalidateCache();
  
 -      if (line.length() != prefix.str().length())
 -      {
 -              ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());
 -      }
 +      return true;
  }
  
  /*
@@@ -1077,27 -1577,27 +1076,27 @@@ void LocalUser::SetClass(const std::str
  {
        ConnectClass *found = NULL;
  
 -      ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Setting connect class for UID %s", this->uuid.c_str());
 +      ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Setting connect class for UID %s", this->uuid.c_str());
  
        if (!explicit_name.empty())
        {
 -              for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
 +              for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); ++i)
                {
                        ConnectClass* c = *i;
  
                        if (explicit_name == c->name)
                        {
 -                              ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Explicitly set to %s", explicit_name.c_str());
 +                              ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Explicitly set to %s", explicit_name.c_str());
                                found = c;
                        }
                }
        }
        else
        {
 -              for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
 +              for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); ++i)
                {
                        ConnectClass* c = *i;
 -                      ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Checking %s", c->GetName().c_str());
 +                      ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Checking %s", c->GetName().c_str());
  
                        ModResult MOD_RESULT;
                        FIRST_MOD_RESULT(OnSetConnectClass, MOD_RESULT, (this,c));
                                continue;
                        if (MOD_RESULT == MOD_RES_ALLOW)
                        {
 -                              ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Class forced by module to %s", c->GetName().c_str());
 +                              ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Class forced by module to %s", c->GetName().c_str());
                                found = c;
                                break;
                        }
  
                        /* check if host matches.. */
                        if (!InspIRCd::MatchCIDR(this->GetIPString(), c->GetHost(), NULL) &&
 -                          !InspIRCd::MatchCIDR(this->host, c->GetHost(), NULL))
 +                          !InspIRCd::MatchCIDR(this->GetRealHost(), c->GetHost(), NULL))
                        {
 -                              ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "No host match (for %s)", c->GetHost().c_str());
 +                              ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "No host match (for %s)", c->GetHost().c_str());
                                continue;
                        }
  
                         */
                        if (c->limit && (c->GetReferenceCount() >= c->limit))
                        {
 -                              ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "OOPS: Connect class limit (%lu) hit, denying", c->limit);
 +                              ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "OOPS: Connect class limit (%lu) hit, denying", c->limit);
                                continue;
                        }
  
                        /* if it requires a port ... */
 -                      int port = c->config->getInt("port");
 -                      if (port)
 +                      if (!c->ports.empty())
                        {
 -                              ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Requires port (%d)", port);
 -
                                /* and our port doesn't match, fail. */
 -                              if (this->GetServerPort() != port)
 +                              if (!c->ports.count(this->GetServerPort()))
 +                              {
 +                                      ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Requires a different port, skipping");
                                        continue;
 +                              }
                        }
  
                        if (regdone && !c->config->getString("password").empty())
                        {
 -                              if (ServerInstance->PassCompare(this, c->config->getString("password"), password, c->config->getString("hash")))
 +                              if (!ServerInstance->PassCompare(this, c->config->getString("password"), password, c->config->getString("hash")))
                                {
 -                                      ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Bad password, skipping");
 +                                      ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Bad password, skipping");
                                        continue;
                                }
                        }
        }
  }
  
 -/* looks up a users password for their connection class (<ALLOW>/<DENY> tags)
 - * NOTE: If the <ALLOW> or <DENY> tag specifies an ip, and this user resolves,
 - * then their ip will be taken as 'priority' anyway, so for example,
 - * <connect allow="127.0.0.1"> will match joe!bloggs@localhost
 - */
 -ConnectClass* LocalUser::GetClass()
 -{
 -      return MyClass;
 -}
 -
 -ConnectClass* User::GetClass()
 -{
 -      return NULL;
 -}
 -
  void User::PurgeEmptyChannels()
  {
        // firstly decrement the count on each channel
 -      for (UCListIter f = this->chans.begin(); f != this->chans.end(); f++)
 +      for (User::ChanList::iterator i = this->chans.begin(); i != this->chans.end(); )
        {
 -              Channel* c = *f;
 +              Channel* c = (*i)->chan;
 +              ++i;
                c->DelUser(this);
        }
  
@@@ -1187,30 -1701,31 +1186,30 @@@ const std::string& FakeUser::GetFullHos
  {
        if (!ServerInstance->Config->HideWhoisServer.empty())
                return ServerInstance->Config->HideWhoisServer;
 -      return server;
 +      return server->GetName();
  }
  
  const std::string& FakeUser::GetFullRealHost()
  {
        if (!ServerInstance->Config->HideWhoisServer.empty())
                return ServerInstance->Config->HideWhoisServer;
 -      return server;
 +      return server->GetName();
  }
  
  ConnectClass::ConnectClass(ConfigTag* tag, char t, const std::string& mask)
        : config(tag), type(t), fakelag(true), name("unnamed"), registration_timeout(0), host(mask),
        pingtime(0), softsendqmax(0), hardsendqmax(0), recvqmax(0),
 -      penaltythreshold(0), commandrate(0), maxlocal(0), maxglobal(0), maxconnwarn(true), maxchans(0), limit(0)
 +      penaltythreshold(0), commandrate(0), maxlocal(0), maxglobal(0), maxconnwarn(true), maxchans(ServerInstance->Config->MaxChans),
 +      limit(0), resolvehostnames(true)
  {
  }
  
  ConnectClass::ConnectClass(ConfigTag* tag, char t, const std::string& mask, const ConnectClass& parent)
 -      : config(tag), type(t), fakelag(parent.fakelag), name("unnamed"),
 -      registration_timeout(parent.registration_timeout), host(mask), pingtime(parent.pingtime),
 -      softsendqmax(parent.softsendqmax), hardsendqmax(parent.hardsendqmax), recvqmax(parent.recvqmax),
 -      penaltythreshold(parent.penaltythreshold), commandrate(parent.commandrate),
 -      maxlocal(parent.maxlocal), maxglobal(parent.maxglobal), maxconnwarn(parent.maxconnwarn), maxchans(parent.maxchans),
 -      limit(parent.limit)
  {
 +      Update(&parent);
 +      name = "unnamed";
 +      type = t;
 +      config = tag;
  }
  
  void ConnectClass::Update(const ConnectClass* src)
        maxconnwarn = src->maxconnwarn;
        maxchans = src->maxchans;
        limit = src->limit;
 +      resolvehostnames = src->resolvehostnames;
 +      ports = src->ports;
  }