]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
Merge insp20
authorAttila Molnar <attilamolnar@hush.com>
Mon, 20 Apr 2015 15:40:12 +0000 (17:40 +0200)
committerAttila Molnar <attilamolnar@hush.com>
Mon, 20 Apr 2015 15:40:12 +0000 (17:40 +0200)
40 files changed:
1  2 
.gitignore
README.md
docs/conf/helpop-full.conf.example
docs/conf/helpop.conf.example
docs/conf/inspircd.conf.example
docs/conf/links.conf.example
docs/conf/modules.conf.example
docs/conf/opers.conf.example
include/modules.h
include/usermanager.h
make/template/main.mk
modulemanager
src/command_parse.cpp
src/configreader.cpp
src/coremods/core_dns.cpp
src/coremods/core_info/cmd_motd.cpp
src/coremods/core_list.cpp
src/coremods/core_reloadmodule.cpp
src/coremods/core_stats.cpp
src/coremods/core_user/cmd_user.cpp
src/coremods/core_user/core_user.cpp
src/coremods/core_userhost.cpp
src/coremods/core_who.cpp
src/hashcomp.cpp
src/modmanager_dynamic.cpp
src/modules/m_abbreviation.cpp
src/modules/m_check.cpp
src/modules/m_dccallow.cpp
src/modules/m_globalload.cpp
src/modules/m_hideoper.cpp
src/modules/m_httpd.cpp
src/modules/m_md5.cpp
src/modules/m_operprefix.cpp
src/modules/m_sasl.cpp
src/modules/m_spanningtree/fjoin.cpp
src/modules/m_spanningtree/treeserver.cpp
src/modules/m_timedbans.cpp
src/threadengines/threadengine_pthread.cpp
src/usermanager.cpp
win/inspircd.rc.cmake

diff --combined .gitignore
index ca364ecfc94f0bef876218a63a9819f6b449552c,f39aa4a55c73c92d57ef9e017ec358a30ad9b4d7..9400478be58d7473a6890b72163bcac10da46104
@@@ -2,21 -2,19 +2,22 @@@
  *.pem
  *.swp
  
 -/.config.cache
 -/.modulemanager
 +.*
 +!.git*
 +
  /BSDmakefile
  /GNUmakefile
  /build
+ /docs/doxygen
  /inspircd
 +/inspircd.1
 +/inspircd-genssl.1
 +/inspircd.service
  /org.inspircd.plist
  /run
  /bin
  
 -/include/inspircd_config.h
 -/include/inspircd_version.h
 +/include/config.h
  
  /src/modules/m_geoip.cpp
  /src/modules/m_ldapauth.cpp
@@@ -26,7 -24,6 +27,7 @@@
  /src/modules/m_pgsql.cpp
  /src/modules/m_regex_pcre.cpp
  /src/modules/m_regex_posix.cpp
 +/src/modules/m_regex_re2.cpp
  /src/modules/m_regex_stdlib.cpp
  /src/modules/m_regex_tre.cpp
  /src/modules/m_sqlite3.cpp
diff --combined README.md
index 89524e7e7b52343af09f581b5922ac40bb09fcd2,f3b3c3c32af37b99a08b4ff962bf6590ea5252f1..6e61106963590ab779cc11dbb4ee83226e46b63a
+++ b/README.md
@@@ -1,10 -1,3 +1,10 @@@
 +### Important Notice
 +
 +The `master` branch contains the latest development version. If you are running
 +a server then you probably want the `insp20` branch. You can obtain this from
 +the [releases](https://github.com/inspircd/inspircd/releases) page or by running
 +`git checkout insp20` if you are installing via Git.
 +
  ### About
  
  InspIRCd is a modular Internet Relay Chat (IRC) server written in C++ for Linux,
@@@ -25,4 -18,4 +25,4 @@@ many different users
  
  * [Website](http://inspircd.org)
  * [GitHub](https://github.com/inspircd)
- * [IRC](irc://irc.chatspike.net/inspircd)
+ * IRC: \#inspircd on irc.inspircd.org
index 353270c33ab9cdf4eab9041a298a135474d667db,a3529b9dcfd51d6af3e4ac7d4a7c025ce879dde3..7899586f81aad03ac263374e2eb3f94a01ca9d19
@@@ -34,7 -34,7 +34,7 @@@ UNINVITE  AWAY     DCCALLOW  SILENCE   
  MKPASSWD  VHOST    TITLE     SETNAME
  
  WHOIS     WHOWAS   ISON      USERHOST  WATCH
 -LIST      NAMES    WHO       MOTD      RULES
 +LIST      NAMES    WHO       MOTD
  ADMIN     MAP      LINKS     LUSERS    TIME
  STATS     VERSION  INFO      MODULES   COMMANDS
  SSLINFO
@@@ -102,21 -102,18 +102,21 @@@ This command accepts multiple nicks lik
  
  Authenticate for a vhost using the specified username and password.">
  
 -<helpop key="remove" value="/REMOVE <nick> <channel> [<reason>]
 +<helpop key="remove" value="/REMOVE <channel> <nick> [<reason>]
  
  Removes a user from a channel you specify. You must be at least a
  channel halfoperator to remove a user. A removed user will part with
  a message stating they were removed from the channel and by whom.">
  
 +<helpop key="rmode" value="/RMODE [channel] [modeletter] {[pattern]}
 +
 +Removes listmodes from a channel.
 +E.g. /RMODE #Chan b m:* will remove all mute extbans.">
 +
  <helpop key="fpart" value="/FPART <channel> <nick> [<reason>]
  
- This behaves identically to /REMOVE. /REMOVE is a builtin mIRC command
 -This behaves identically to /REMOVE, the only difference is that the
 -<channel> and <nick> parameters are switched around to match /KICK's
 -syntax. Also, /REMOVE is a built-in mIRC command which caused trouble
 -for some users.">
++This behaves identically to /REMOVE. /REMOVE is a built-in mIRC command
 +which caused trouble for some users.">
  
  <helpop key="devoice" value="/DEVOICE <channel>
  
@@@ -280,6 -277,11 +280,6 @@@ Show the message of the day for <server
  contain important server rules and notices and should be read prior
  to using a server.">
  
 -<helpop key="rules" value="/RULES
 -
 -Show the rules file for the local server. This is similar in effect to
 -except that these are not sent automatically on connect.">
 -
  <helpop key="oper" value="/OPER <login> <password>
  
  Attempts to authenticate a user as an IRC operator.
@@@ -378,7 -380,7 +378,7 @@@ Sets your name to the specified name."
  -------------
  
  OPERMOTD  CHECK     CLONES      USERIP   TLINE
- ALLTIME   WALLOPS   GLOBOPS
+ ALLTIME   WALLOPS   GLOBOPS     MODENOTICE
  
  SETHOST   SETIDENT  CHGHOST     CHGIDENT CHGNAME
  SETIDLE   SWHOIS
@@@ -389,7 -391,7 +389,7 @@@ SAJOIN    SAPART    SAMODE      SATOPI
  
  KILL      SAQUIT    GLINE       ZLINE    QLINE
  KLINE     RLINE     ELINE       CBAN     SHUN
 -FILTER    OJOIN
 +FILTER    OJOIN     CLEARCHAN
  
  CONNECT   SQUIT     RCONNECT    RSQUIT
  
@@@ -535,14 -537,13 +535,14 @@@ The duration may be specified in second
  1y2w3d4h5m6s - meaning one year, two weeks, three days, 4 hours,
  5 minutes and 6 seconds. All fields in this format are optional.">
  
 -<helpop key="sajoin" value="/SAJOIN <nick> <channel>
 +<helpop key="sajoin" value="/SAJOIN [<nick>] <channel>[,<channel>]
  
 -Forces the user to join the channel.">
 +Forces the user to join the channel(s).
 +If no nick is given, it joins the oper doing the /SAJOIN.">
  
 -<helpop key="sapart" value="/SAPART <nick> <channel>
 +<helpop key="sapart" value="/SAPART <nick> <channel>[,<channel>]
  
 -Forces the user to part the channel.">
 +Forces the user to part the channel(s).">
  
  <helpop key="samode" value="/SAMODE <target> (+|-)<modes> [<parameters for modes>]
  
@@@ -731,7 -732,7 +731,7 @@@ The duration may be specified in second
  
  <helpop key="eline" value="/ELINE <user@host> [<duration> :<reason>]
  
- Sets or removes a e-line (local ban exception) on host mask.
+ Sets or removes a e-line (global ban exception) on host mask.
  You must specify at least 3 parameters to add an exception, and one
  parameter to remove an exception (just the user@host section).
  
@@@ -767,16 -768,12 +767,22 @@@ server is specified, the local server'
  
  Closes all unregistered connections to the local server.">
  
 +<helpop key="clearchan" value="/CLEARCHAN <channel> [<KILL|KICK|G|Z>] [<reason>]
 +
 +Quits or kicks all non-opers from a channel, optionally G/Z-Lines them.
 +Useful for quickly nuking bot channels.
 +
 +The default method, KILL, simply disconnects the victims from the server,
 +while methods G and Z also add G/Z-Lines for all the targets.
 +
 +When used, the victims won't see each other getting kicked or quitting.">
 +
+ <helpop key="modenotice" value="/MODENOTICE <modeletters> <message>
+ Sends a notice to all users who have the given mode(s) set.
+ If multiple mode letters are given, the notice is only sent to users
+ who have all of them set.">
  ######################
  # User/Channel Modes #
  ######################
  
   v <nickname>       Gives voice to <nickname>, allowing them to speak
                      while the channel is +m.
 - h <nickname>       Gives halfop status to <nickname> (this mode can
 -                    be disabled).
 + h <nickname>       Gives halfop status to <nickname> (requires
 +                    customprefix module).
   o <nickname>       Gives op status to <nickname>.
   a <nickname>       Gives protected status to <nickname>, preventing
                      them from them from being kicked (+q only,
 -                    requires chanprotect module).
 +                    requires customprefix module).
   q <nickname>       Gives owner status to <nickname>, preventing them
                      from being kicked (Services or only, requires
 -                    chanprotect module).
 +                    customprefix module).
  
   b <hostmask>       Bans <hostmask> from the channel.
   e <hostmask>       Excepts <hostmask> from bans (requires
                      module).
   D                  Delays join messages from users until they
                      message the channel (requires delayjoin module).
 + E [~*][lines]:[sec]{[:difference]}{[:backlog]} Allows blocking of similiar messages.
 +                    Kicks as default, blocks with ~ and bans with *
 +                    The last two parameters are optional.
   F <changes>:<sec>  Blocks nick changes when they equal or exceed the
                      specified rate (requires nickflood module).
   G                  Censors messages to the channel based on the
                      network configuration (requires censor module).
+  H <num>:<duration> Displays the last <num> lines of chat to joining
+                     users. <duration> is the maximum time to keep
+                     lines in the history buffer (requires chanhistory
+                     module).
   J <seconds>        Prevents rejoin after kick for the specified
                      number of seconds. This prevents auto-rejoin
                      (requires kicknorejoin module).
                      module).
   M                  Blocks unregistered users from speaking (requires
                      services account module).
-  N                  Prevents users on the channel from chainging nick
+  N                  Prevents users on the channel from changing nick
                      (requires nonicks module).
   O                  Channel is IRCops only (can only be set by IRCops,
                      requires operchans module).
@@@ -1039,8 -1037,8 +1049,8 @@@ Matching extbans
                 module).
   s:<server>    Matches users on a matching server (requires serverban
                 module).
 - z:<certfp>    Matches users having the given SSL certificate
 -               fingerprint (requires sslmodes module).
 + z:<certfp>    Matches users with a matching SSL certificate fingerprint
 +               (requires sslmodes module)
   O:<opertype>  Matches IRCops of a matching type, mostly useful as an
                 an invite exception (requires operchans module).
   R:<account>   Matches users logged into a matching account (requires
@@@ -1060,8 -1058,6 +1070,6 @@@ Acting extbans
                 matching users (requires blockcaps module).
   C:<banmask>   Blocks CTCPs from matching users (requires noctcp
                 module).
-  M:<account>   Blocks messages from users logged into a matching
-                account (requires services account module).
   N:<banmask>   Blocks nick changes from matching users (requires
                 nonicks module).
   Q:<banmask>   Blocks kicks by matching users (requires nokicks
                 (requires services account).
  
  A ban given to an Acting extban may either be a nick!user@host mask
- (unless stated otherwise, for example M: taking an account name),
matched against users as for a normal ban, or a Matching extban.
+ (unless stated otherwise), matched against users as for a normal ban,
+ or a Matching extban.
  
  There is an additional special type of extended ban, a redirect ban:
  
index c17ae87a59f86ff66036441fbf18b5e451be2479,32884ea16c25ccd6c0b47f4a8ce27b195d3214ca..84219dd94b0e9f4eadee4531707adf3867f6a4fc
@@@ -37,7 -37,7 +37,7 @@@ UNINVITE  AWAY     DCCALLOW  SILENCE   
  MKPASSWD  VHOST    TITLE     SETNAME
  
  WHOIS     WHOWAS   ISON      USERHOST  WATCH
 -LIST      NAMES    WHO       MOTD      RULES
 +LIST      NAMES    WHO       MOTD
  ADMIN     MAP      LINKS     LUSERS    TIME
  STATS     VERSION  INFO      MODULES   COMMANDS
  SSLINFO
@@@ -50,7 -50,7 +50,7 @@@ OPER"
  -------------
  
  OPERMOTD  CHECK     CLONES      USERIP   TLINE
- ALLTIME   WALLOPS   GLOBOPS
+ ALLTIME   WALLOPS   GLOBOPS     MODENOTICE
  
  SETHOST   SETIDENT  CHGHOST     CHGIDENT CHGNAME
  SETIDLE   SWHOIS
@@@ -61,7 -61,7 +61,7 @@@ SAJOIN    SAPART    SAMODE      SATOPI
  
  KILL      SAQUIT    GLINE       ZLINE    QLINE
  KLINE     RLINE     ELINE       CBAN     SHUN
 -FILTER
 +FILTER    CLEARCHAN
  
  CONNECT   SQUIT     RCONNECT    RSQUIT
  
@@@ -114,15 -114,15 +114,15 @@@ LOCKSERV       UNLOCKSERV"
  
   v <nickname>       Gives voice to <nickname>, allowing them to speak
                      while the channel is +m.
 - h <nickname>       Gives halfop status to <nickname> (this mode can
 -                    be disabled).
 + h <nickname>       Gives halfop status to <nickname> (requires
 +                    customprefix module).
   o <nickname>       Gives op status to <nickname>.
   a <nickname>       Gives protected status to <nickname>, preventing
                      them from them from being kicked (+q only,
 -                    requires chanprotect module).
 +                    requires customprefix module).
   q <nickname>       Gives owner status to <nickname>, preventing them
                      from being kicked (Services or only, requires
 -                    chanprotect module).
 +                    customprefix module).
  
   b <hostmask>       Bans <hostmask> from the channel.
   e <hostmask>       Excepts <hostmask> from bans (requires
                      module).
   D                  Delays join messages from users until they
                      message the channel (requires delayjoin module).
 + E [~*][lines]:[sec]{[:difference]}{[:backlog]} Allows blocking of similiar messages.
 +                    Kicks as default, blocks with ~ and bans with *
 +                    The last two parameters are optional.
   F <changes>:<sec>  Blocks nick changes when they equal or exceed the
                      specified rate (requires nickflood module).
   G                  Censors messages to the channel based on the
                      network configuration (requires censor module).
+  H <num>:<duration> Displays the last <num> lines of chat to joining
+                     users. <duration> is the maximum time to keep
+                     lines in the history buffer (requires chanhistory
+                     module).
   J <seconds>        Prevents rejoin after kick for the specified
                      number of seconds. This prevents auto-rejoin
                      (requires kicknorejoin module).
                      module).
   M                  Blocks unregistered users from speaking (requires
                      services account module).
-  N                  Prevents users on the channel from chainging nick
+  N                  Prevents users on the channel from changing nick
                      (requires nonicks module).
   O                  Channel is IRCops only (can only be set by IRCops,
                      requires operchans module).
@@@ -299,8 -300,6 +303,6 @@@ Acting extbans
                 matching users (requires blockcaps module).
   C:<banmask>   Blocks CTCPs from matching users (requires noctcp
                 module).
-  M:<account>   Blocks messages from users logged into a matching
-                account (requires services account module).
   N:<banmask>   Blocks nick changes from matching users (requires
                 nonicks module).
   Q:<banmask>   Blocks kicks by matching users (requires nokicks
                 (requires services account).
  
  A ban given to an Acting extban may either be a nick!user@host mask
- (unless stated otherwise, for example M: taking an account name),
matched against users as for a normal ban, or a Matching extban.
+ (unless stated otherwise), matched against users as for a normal ban,
+ or a Matching extban.
  
  There is an additional special type of extended ban, a redirect ban:
  
index d2f70cb22e3dd9bd68fe85eadb65b27e47ad74f3,9fd0adfd34d1e17e43c3bd379d3eb3c954d5bb54..123ebfd3162c352a1520dfef248e8fe8635f6a07
  #                                                                      #
  ########################################################################
  
 +#-#-#-#-#-#-#-#-#-#  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         #
@@@ -62,7 -53,7 +62,7 @@@
  #<include executable="/path/to/executable parameters">                #
  #                                                                     #
  # Executable include example:                                         #
- #<include executable="/usr/bin/wget -q -O - http://mynet.net/inspircd.conf">
+ #<include executable="/usr/bin/wget -q -O - http://example.com/inspircd.conf">
  #                                                                     #
  
  
  #                                                                     #
  # 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">
  
@@@ -85,7 -81,7 +85,7 @@@
  <server
          # name: Hostname of your server. Does not need to resolve, but
          # does need to be correct syntax (something.somethingelse.tld).
-         name="penguin.omega.org.za"
+         name="penguin.omega.example.org"
  
          # description: Server description. Spaces are allowed.
          description="Waddle World"
        # 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="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 number of seconds 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"
  >
  
  <bind address="" port="6660-6669" type="clients">
  #-#-#-#-#-#-#-#-#-#-  DIE/RESTART CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-
  #                                                                     #
  #   You can configure the passwords here which you wish to use for    #
- #   the /DIE and /RESTART commands. Only trusted IRCop's who will     #
+ #   the /DIE and /RESTART commands. Only trusted ircops who will      #
  #   need this ability should know the die and restart password.       #
  #                                                                     #
  
           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.
           # globalmax: Maximum global (network-wide) connections per IP (or CIDR mask, see below).
           globalmax="3"
  
-          # maxconnwarn: Enable warnings when localmax or globalmax is hit (defaults to on)
+          # 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.
           #usednsbl="yes"
           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.
           # 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"
  
  
  # 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
  # matched, the banlist size defaults to 64 entries.                   #
  #                                                                     #
  
- <banlist chan="#morons" limit="128">
+ <banlist chan="#largechan" limit="128">
  <banlist chan="*" limit="69">
  
  #-#-#-#-#-#-#-#-#-#-#-  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"
 -
           # 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.
  
           # allowmismatch: Setting this option to yes will allow servers to link even
           # if they don't have the same "optionally common" modules loaded. Setting this to
-          # yes may introduce some desyncs and weirdness.
+          # yes may introduce some desyncs and unwanted behaviour.
           allowmismatch="no"
  
           # defaultbind: Sets the default for <bind> tags without an address. Choices are
  
           # 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. cmd_privmsg.so).
 +          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:
          # maxident: Maximum length of a ident/username.
          maxident="11"
  
 +        # 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
  # 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.
  #
  # 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   -#-#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  <badhost
           # host: ident@hostname to ban.
           # Wildcards and CIDR (if you specify an IP) can be used.
-          host="*@hundredz.n.hundredz.o.1337.kiddies.example.net"
+          host="*@banneduser.example.net"
  
           # reason: Reason to display when user is disconnected
-          reason="Too many 1337 kiddiots">
+          reason="Evading Bans">
  
  <badhost host="root@*" reason="Don't IRC as root!">
  <badhost host="*@198.51.100.0/24" reason="This subnet is bad.">
  #                                                                     #
  #   You should already know what to do here :)                        #
  
- <die value="User error. Insert new user and press any key. (you didn't edit your config properly.)">
+ <die value="User error. You didn't edit your config properly. Go back and try again.">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# MODULES #-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #    ____                _   _____ _     _       ____  _ _   _        #
  # 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">
  
  
  #########################################################################
index 13754edbd03bcff24821430bcd788e53fd856d2a,a1bab2b3aaebe9241df556136f896d8abc7748ff..0b8b234383a8f7162e101e81d3d020d5adffca0e
  <link
        # name: The name of the remote server. This must match
        # the <server:name> value of the remote server.
-       name="hub.penguin.org"
+       name="hub.example.org"
  
        # ipaddr: The IP address of the remote server.
        # Can also be a hostname, but hostname must resolve.
-       ipaddr="penguin.box.com"
+       ipaddr="penguin.example.org"
  
        # port: The port to connect to the server on.
        # It must be bound as a server port on the other server.
@@@ -29,7 -29,7 +29,7 @@@
  
        # 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
@@@ -46,9 -46,9 +46,9 @@@
        ssl="gnutls"
  
        # fingerprint: If defined, this option will force servers to be
 -      # authenticated using SSL Fingerprints. See http://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
 +      # require an SSL link for both inbound and outbound connections.
        #fingerprint=""
  
        # bind: Local IP address to bind to.
@@@ -71,8 -71,8 +71,8 @@@
  
  # A duplicate of the first link block without comments
  # if you like copying & pasting.
- <link name="hub.penguin.org"
-       ipaddr="penguin.box.com"
+ <link name="hub.example.org"
+       ipaddr="penguin.example.org"
        port="7000"
        allowmask="203.0.113.0/24"
        timeout="300"
@@@ -85,7 -85,7 +85,7 @@@
  
  # Link block for services. Options are the same as for the first
  # link block (depending on what your services package supports).
- <link name="services.antarctic.com"
+ <link name="services.example.com"
        ipaddr="localhost"
        port="7000"
        allowmask="127.0.0.0/8"
@@@ -95,7 -95,7 +95,7 @@@
  # 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="10m" server="hub.penguin.org">
 -<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
  # robin fashion until one succeeds. Period defines the time for restarting
  # a single loop.
  <autoconnect period="120"
-       server="hub.us.penguin.org hub.eu.penguin.org leaf.eu.penguin.org">
+       server="hub.us.example.org hub.eu.example.org leaf.eu.example.org">
  
  
  #-#-#-#-#-#-#-#-#-#-#-#- ULINES CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
  # not generate quit and connect notices, which can cut down on noise  #
  # to opers on the network.                                            #
  #                                                                     #
- <uline server="services.antarctic.com" silent="yes">
+ <uline server="services.example.com" silent="yes">
index 881c5f07730a090a477ed6bae7f7b3881da847ee,71a0fb079b8b58b33c07198987d355abc4f10628..64c9ab0a1557dfab2951b95e01f36697e4022dfd
@@@ -22,7 -22,8 +22,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.    #
  # This is by design, to allow for the implementation of other linking #
- # protocols in modules in the future.                                 #
+ # protocols in modules in the future. This module is at the bottom of #
+ # this file.                                                          #
  #                                                                     #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # channel. e.g. +b nick!ident@host#channelbanneduserissentto
  #<module name="m_banredirect.so">
  
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# bcrypt module: Allows other modules to generate bcrypt hashes,
 +# usually for cryptographic uses and security.
 +#<module name="m_bcrypt.so">
 +#
 +# 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">
  #          cooldown="60">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # CAP module: Provides the CAP negotiation mechanism seen in
- # ratbox-derived ircds.
+ # 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.
++# It is also recommended for the STARTTLS support in m_starttls.
  #<module name="m_cap.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # specify some censor tags. See also:                                 #
  # http://wiki.inspircd.org/Modules/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).
 +# Adds snomask +w for monitoring CGI:IRC connections.
  #<module name="m_cgiirc.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-# CGIIRC  CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
  # CGI:IRC documentation.
  #
  # Old style:
- # <cgihost type="pass" mask="www.mysite.com">       # Get IP from PASS
- # <cgihost type="ident" mask="otherbox.mysite.com"> # Get IP from ident
- # <cgihost type="passfirst" mask="www.mysite.com">  # See the docs
+ # <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.mysite.com">                      # Get IP from WEBIRC
+ #   mask="somebox.example.com">                      # Get IP from WEBIRC
  #
  # IMPORTANT NOTE:
  # ---------------
  # 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
  # in a channel matching a ban like +b j:#channel*mask from joining.
  #<module name="m_channelban.so">
  
 -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 -# Chanprotect module: Gives +q and +a channel modes.
 -#<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">
 -
 -
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Check module: Adds the /CHECK command.
  # Check is useful for looking up information on channels, users,
  # To use, CHGNAME must be in one of your oper class blocks.
  #<module name="m_chgname.so">
  
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# Clear chan module: Allows opers to masskick, masskill or mass-G/ZLine
 +# all users on a channel using /CLEARCHAN.
 +#<module name="m_clearchan.so">
 +
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Cloaking module: Adds usermode +x and cloaking support.
  # Relies on the module m_md5.so being loaded.
  # 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 #
  #   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.              #
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #
  #<cloak mode="half"
  #       key="secret"
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Auto join on connect module: Allows you to force users to join one
 -# or more channels automatically upon connecting to the server.
 +# 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="m_conn_join.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-#- 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.
  #
 +# 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>
  #
  # 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.
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Custom prefixes: Allows for channel prefixes to be added.
 -# This replaces m_chanprotect and m_halfop.
  #<module name="m_customprefix.so">
  #
  # name       The name of the mode, must be unique from other modes.
  #  vhost    - Displayed host (optional).
  #
  #<title name="foo" password="bar" title="Official Chat Helper">
- #<title name="bar" password="foo" host="ident@host.name" title="Official Chat Helper" vhost="helper.network.chat">
+ #<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">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # redirect    -      Redirect the user to a different channel.        #
  #                                                                     #
  #<badchan name="#gods*" allowopers="yes" reason="Tortoises!">         #
- #<badchan name="#heaven" redirect="#hell" reason="Nice try!">         #
+ #<badchan name="#chan1" redirect="#chan2" reason="Chan1 is closed">   #
  #                                                                     #
  # Redirects will not work if the target channel is set +L.            #
  #                                                                     #
  # Additionally, you may specify channels which are allowed, even if   #
  # a badchan tag specifies it would be denied:                         #
- #<goodchan name="#godsleeps">                                         #
+ #<goodchan name="#funtimes">                                          #
  # Glob masks are accepted here also.                                  #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # 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  -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # 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="m_flashpolicyd.so">                                    #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Gecos ban: Implements extended ban 'r', which stops anyone matching
  #<module name="m_globalload.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 -# Halfop module: Provides the +h (halfops) channel status mode.
 -#<module name="m_halfop.so">
 -
 -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 -# HELPOP module: Provides the /HELPOP command.
 +# HELPOP module: Provides the /HELPOP command
  #<module name="m_helpop.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-#-  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">
 +#                                                                     #
 +#<include file="examples/inspircd.helpop-full.example">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Hide chans module: Allows users to hide their channels list from non-
  # 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="m_hidelist.so">
 +#
 +# 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.
  #                                                                     #
  # See http://wiki.inspircd.org/Modules/hostchange for help.           #
  #                                                                     #
- #<host suffix="polarbears.org" separator="." prefix="">
- #<hostchange mask="*@fbi.gov" action="addnick">
- #<hostchange mask="*r00t@*" action="suffix">
- #<hostchange mask="a@b.com" action="set" value="blah.blah.blah">
+ #<host suffix="invalid.org" separator="." prefix="">
+ #<hostchange mask="*@42.theanswer.example.org" action="addnick">
+ #<hostchange mask="*root@*" action="suffix">
+ #<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.
 +#<module name="m_hostcycle.so">
 +
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # httpd module: Provides HTTP server support for InspIRCd.
  #<module name="m_httpd.so">
  # Restrict access to the m_httpd_stats module to all but the local
  # network and when the correct password is specified:
  # <httpdacl path="/stats*" types="password,whitelist"
- #    username="secretstuff" password="mypasshere" whitelist="127.0.0.*,10.*">
+ #    username="secrets" password="mypasshere" whitelist="127.0.0.*,10.*">
  #
  # Deny all connections to all but the main index page:
  # <httpdacl path="/*" types="blacklist" blacklist="*">
  # 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">
- # Does a +I bypass channel +k in addition to +i?
+ # bypasskey: If this is enabled, exceptions will bypass +k as well as +i
  #<inviteexception bypasskey="yes">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # 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">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Knock module: Adds the /KNOCK command and channel mode +K.
  # 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="m_ldap.so">
 +#<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.                   #
 +# via LDAP.                                                           #
  #<module name="m_ldapauth.so">
  #                                                                     #
  # 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">                             #
  #                                                                     #
  # The attribute value indicates the attribute which is used to locate #
  # a user account by name. On POSIX systems this is usually 'uid'.     #
  #                                                                     #
 -# 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.                                   #
 +# 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.                                                              #
  #                                                                     #
  # 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.      #
 +# opers via LDAP.                                                     #
  #<module name="m_ldapoper.so">
  #                                                                     #
  # 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    #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Map hiding module: replaces /MAP and /LINKS output to users with a  #
- # message to see a website, set by maphide="http://link.to/site" in   #
+ # message to see a website, set by maphide="http://test.org/map" in   #
  # the <security> tag, instead.                                        #
  #<module name="m_maphide.so">
  
  # would likely be immediately bounced by services.
  #<module name="m_mlock.so">
  
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# Modenotice module: Adds the /MODENOTICE command that allows opers to
 +# send notices to all users having the given user mode(s) set.
 +#<module name="m_modenotice.so">
 +
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # MsSQL module: Allows other SQL modules to access MS SQL Server
  # through a unified API.
  #                 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.
  # password you want to hash. For example:
  #
  #     <oper name="Brain"
- #           host="ident@dialup15.isp.com"
+ #           host="ident@dialup15.isp.test.com"
  #           hash="sha256"
  #           password="01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"
  #           type="NetAdmin">
  # 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="m_pbkdf2.so">
 +#
 +# 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
  #
  # 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.
  # m_rline.
  #<module name="m_regex_pcre.so">
  
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# 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="m_regex_re2.so">
 +
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Regular expression provider for POSIX regular expressions.
  # You shouldn't need any additional libraries on a POSIX-compatible
  # Remove module: Adds the /REMOVE command which is a peaceful
  # alternative to /KICK.
  #<module name="m_remove.so">
 +#
 +# 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 similiar 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.
 +# maxsecs - Maximum value of seconds 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" maxsecs="0" size="512">
 +#<module name="m_repeat.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Restricted channels module: Allows only opers to create channels.
  # 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="m_rmode.so">
 +
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # 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.
 +# Opers need the users/sajoin-others priv to be able to /SAJOIN users
 +# other than themselves.
  #<module name="m_sajoin.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # as identified separately from the idea of a master account, which
  # can be useful for services which are heavily nick-as-account centric.
  #
- # Also of note is that this module implements three extbans:
+ # Also of note is that this module implements two extbans:
  # +b R: (stop matching account names from joining)
- # +b M: (stop matching account names from speaking)
  # +b U:n!u@h (blocks matching unregistered users)
  #
  #<module name="m_services_account.so">
  # to a server matching a mask like +b s:server.mask.here from joining.
  #<module name="m_serverban.so">
  
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# 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="m_showfile.so">                                        #
 +#                                                                     #
 +#-#-#-#-#-#-#-#-#-#-# 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                  #
 +#             (similiar 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.
  #<module name="m_showwhois.so">
  #
  # 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,
  # 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
  # "* <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.
  #<module name="m_silence.so">
  #
  # 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          #
  #                                                                     #
  #<sqloper dbid="1" hash="md5">
  
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# StartTLS module: Implements STARTTLS, which allows clients          #
 +# connected to non SSL enabled ports to enable SSL, if a proper SSL   #
 +# module is loaded (either m_ssl_gnutls or m_ssl_openssl).            #
 +#<module name="m_starttls.so">
 +
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # 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.
 +# SVSHOLD does not generate server notices by default, you can turn
 +# notices on by uncommenting the next line.
  #<svshold silent="false">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # host       - Vhost to set.                                          #
  #
- #<vhost user="some_username" pass="some_password" host="some.host">
- #<vhost user="foo" password="fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9" hash="sha256" host="some.other.host">
+ #<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">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Watch module: Adds the WATCH command, which is used by clients to
index 996fded6d7b6d0d38bea67ed73c0891a3c70f3b1,75b54faf049066889c11c10004f63c5dcfe9a3b9..3a6158f2c821ac7a1d1d61607224f41c208f8e06
@@@ -15,7 -15,7 +15,7 @@@
       name="Shutdown"
  
       # commands: Oper-only commands that opers of this class can run.
-      commands="DIE RESTART REHASH LOADMODULE UNLOADMODULE RELOADMODULE GUNLOADMODULE GRELOADMODULE"
+      commands="DIE RESTART REHASH LOADMODULE UNLOADMODULE RELOADMODULE GLOADMODULE GUNLOADMODULE GRELOADMODULE"
  
       # privs: Special privileges that users with this class may utilise.
       #  VIEWING:
       #   - servers/auspex: allows opers with this priv to see more detail about server information than normal users.
       # ACTIONS:
       #   - users/mass-message: allows opers with this priv to PRIVMSG and NOTICE to a server mask (e.g. NOTICE $*)
 -     #   - channels/high-join-limit: allows opers with this priv to join <channels:opers> total channels instead of <channels:users> total channels.
 +     #   - users/samode-usermodes: allows opers with this priv to change the user modes of any other user using /SAMODE
       # PERMISSIONS:
       #   - users/flood/no-fakelag: prevents opers from being penalized with fake lag for flooding (*NOTE)
       #   - users/flood/no-throttle: allows opers with this priv to send commands without being throttled (*NOTE)
       #   - users/flood/increased-buffers: allows opers with this priv to send and receive data without worrying about being disconnected for exceeding limits (*NOTE)
       #
       # *NOTE: These privs are potentially dangerous, as they grant users with them the ability to hammer your server's CPU/RAM as much as they want, essentially.
 -     privs="users/auspex channels/auspex servers/auspex users/mass-message channels/high-join-limit users/flood/no-throttle users/flood/increased-buffers"
 +     privs="users/auspex channels/auspex servers/auspex users/mass-message users/flood/no-throttle users/flood/increased-buffers"
  
       # usermodes: Oper-only usermodes that opers with this class can use.
       usermodes="*"
       # chanmodes: Oper-only channel modes that opers with this class can use.
       chanmodes="*">
  
- <class name="SACommands" commands="SAJOIN SAPART SANICK SAQUIT SATOPIC SAKICK SAMODE">
+ <class name="SACommands" commands="SAJOIN SAPART SANICK SAQUIT SATOPIC SAKICK SAMODE OJOIN">
  <class name="ServerLink" commands="CONNECT SQUIT RCONNECT RSQUIT MKPASSWD ALLTIME SWHOIS JUMPSERVER LOCKSERV UNLOCKSERV" usermodes="*" chanmodes="*" privs="servers/auspex">
  <class name="BanControl" commands="KILL GLINE KLINE ZLINE QLINE ELINE TLINE RLINE CHECK NICKLOCK NICKUNLOCK SHUN CLONES CBAN CLOSE" usermodes="*" chanmodes="*">
  <class name="OperChat" commands="WALLOPS GLOBOPS" usermodes="*" chanmodes="*" privs="users/mass-message">
- <class name="HostCloak" commands="SETHOST SETIDENT CHGNAME CHGHOST CHGIDENT SETIDLE" usermodes="*" chanmodes="*" privs="users/auspex">
+ <class name="HostCloak" commands="SETHOST SETIDENT SETIDLE CHGNAME CHGHOST CHGIDENT" usermodes="*" chanmodes="*" privs="users/auspex">
  
  
  #-#-#-#-#-#-#-#-#-#-#-#-  OPERATOR COMPOSITION   -#-#-#-#-#-#-#-#-#-#-#
@@@ -55,6 -55,8 +55,6 @@@
  
  <type
      # name: Name of type. Used in actual server operator accounts below.
 -    # Cannot contain spaces. If you would like a space, use
 -    # the _ character instead and it will translate to a space on whois.
      name="NetAdmin"
  
      # classes: Classes (blocks above) that this type belongs to.
@@@ -63,9 -65,6 +63,9 @@@
      # vhost: Host opers of this type get when they log in (oper up). This is optional.
      vhost="netadmin.omega.example.org"
  
 +    # maxchans: Maximum number of channels opers of this type can be in at once.
 +    maxchans="60"
 +
      # modes: User modes besides +o that are set on an oper of this type
      # when they oper up. Used for snomasks and other things.
      # Requires that m_opermodes.so be loaded.
        # If m_sslinfo isn't loaded, this option will be ignored.
        #fingerprint="67cb9dc013248a829bb2171ed11becd4"
  
 -      # autologin: If an SSL fingerprint for this oper is specified, you can
 -      # have the oper block automatically log in. This moves all security of the
 -      # oper block to the protection of the client certificate, so be sure that
 -      # the private key is well-protected! Requires m_sslinfo.
 +      # autologin: If an SSL certificate fingerprint for this oper is specified,
 +      # you can have the oper block automatically log in. This moves all security
 +      # of the oper block to the protection of the client certificate, so be sure
 +      # that the private key is well-protected! Requires m_sslinfo.
        #autologin="on"
  
        # sslonly: If on, this oper can only oper up if they're using a SSL connection.
  # Operator with a plaintext password and no comments, for easy copy & paste.
  <oper
        name="Brain"
-       password="s3cret"
-       host="brain@dialup15.isp.com *@localhost *@example.com *@2001:db8::/32"
+       password="youshouldhashthis"
+       host="brain@dialup15.isp.test.com *@localhost *@example.com *@2001:db8::/32"
        #fingerprint="67cb9dc013248a829bb2171ed11becd4"
        type="NetAdmin">
  
diff --combined include/modules.h
index 41dec1194c4046a54e49462fea4e61f38b3597bb,9857012fcfc0ac1c71793198e15f5b0a902d5e0c..fc2aa6324b9dbdf8d0f6a52ec616548b936dcd8e
@@@ -23,7 -23,8 +23,7 @@@
   */
  
  
 -#ifndef MODULES_H
 -#define MODULES_H
 +#pragma once
  
  #include "dynamic.h"
  #include "base.h"
@@@ -34,6 -35,7 +34,6 @@@
  #include <sstream>
  #include "timer.h"
  #include "mode.h"
 -#include "dns.h"
  
  /** Used to define a set of behavior bits for a module
   */
@@@ -107,33 -109,35 +107,33 @@@ struct ModResult 
  /** InspIRCd major version.
   * 1.2 -> 102; 2.1 -> 201; 2.12 -> 212
   */
 -#define INSPIRCD_VERSION_MAJ 200
 +#define INSPIRCD_VERSION_MAJ 202
  /** InspIRCd API version.
   * If you change any API elements, increment this value. This counter should be
   * reset whenever the major version is changed. Modules can use these two values
   * and numerical comparisons in preprocessor macros if they wish to support
   * multiple versions of InspIRCd in one file.
   */
 -#define INSPIRCD_VERSION_API 9
 +#define INSPIRCD_VERSION_API 1
  
  /**
   * This #define allows us to call a method in all
   * loaded modules in a readable simple way, e.g.:
 - * 'FOREACH_MOD(I_OnConnect,OnConnect(user));'
 + * 'FOREACH_MOD(OnConnect,(user));'
   */
  #define FOREACH_MOD(y,x) do { \
 -      EventHandlerIter safei; \
 -      for (EventHandlerIter _i = ServerInstance->Modules->EventHandlers[y].begin(); _i != ServerInstance->Modules->EventHandlers[y].end(); ) \
 +      const IntModuleList& _handlers = ServerInstance->Modules->EventHandlers[I_ ## y]; \
 +      for (IntModuleList::const_reverse_iterator _i = _handlers.rbegin(), _next; _i != _handlers.rend(); _i = _next) \
        { \
 -              safei = _i; \
 -              ++safei; \
 +              _next = _i+1; \
                try \
                { \
 -                      (*_i)->x ; \
 +                      (*_i)->x ; \
                } \
                catch (CoreException& modexcept) \
                { \
 -                      ServerInstance->Logs->Log("MODULE",DEFAULT,"Exception caught: %s",modexcept.GetReason()); \
 +                      ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Exception caught: " + modexcept.GetReason()); \
                } \
 -              _i = safei; \
        } \
  } while (0);
  
   */
  #define DO_EACH_HOOK(n,v,args) \
  do { \
 -      EventHandlerIter iter_ ## n = ServerInstance->Modules->EventHandlers[I_ ## n].begin(); \
 -      while (iter_ ## n != ServerInstance->Modules->EventHandlers[I_ ## n].end()) \
 +      const IntModuleList& _handlers = ServerInstance->Modules->EventHandlers[I_ ## n]; \
 +      for (IntModuleList::const_reverse_iterator _i = _handlers.rbegin(), _next; _i != _handlers.rend(); _i = _next) \
        { \
 -              Module* mod_ ## n = *iter_ ## n; \
 -              iter_ ## n ++; \
 +              _next = _i+1; \
                try \
                { \
 -                      v = (mod_ ## n)->n args;
 +                      v = (*_i)->n args;
  
  #define WHILE_EACH_HOOK(n) \
                } \
                catch (CoreException& except_ ## n) \
                { \
 -                      ServerInstance->Logs->Log("MODULE",DEFAULT,"Exception caught: %s", (except_ ## n).GetReason()); \
 -                      (void) mod_ ## n; /* catch mismatched pairs */ \
 +                      ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Exception caught: " + (except_ ## n).GetReason()); \
                } \
        } \
  } while(0)
@@@ -206,6 -212,69 +206,6 @@@ class CoreExport Versio
        virtual ~Version() {}
  };
  
 -/** The Request class is a unicast message directed at a given module.
 - * When this class is properly instantiated it may be sent to a module
 - * using the Send() method, which will call the given module's OnRequest
 - * method with this class as its parameter.
 - */
 -class CoreExport Request : public classbase
 -{
 - public:
 -      /** This should be a null-terminated string identifying the type of request,
 -       * all modules should define this and use it to determine the nature of the
 -       * request before they attempt to cast the Request in any way.
 -       */
 -      const char* const id;
 -      /** This is a pointer to the sender of the message, which can be used to
 -       * directly trigger events, or to create a reply.
 -       */
 -      ModuleRef source;
 -      /** The single destination of the Request
 -       */
 -      ModuleRef dest;
 -
 -      /** Create a new Request
 -       * This is for the 'new' way of defining a subclass
 -       * of Request and defining it in a common header,
 -       * passing an object of your Request subclass through
 -       * as a Request* and using the ID string to determine
 -       * what to cast it back to and the other end.
 -       */
 -      Request(Module* src, Module* dst, const char* idstr);
 -      /** Send the Request.
 -       */
 -      void Send();
 -};
 -
 -
 -/** The Event class is a unicast message directed at all modules.
 - * When the class is properly instantiated it may be sent to all modules
 - * using the Send() method, which will trigger the OnEvent method in
 - * all modules passing the object as its parameter.
 - */
 -class CoreExport Event : public classbase
 -{
 - public:
 -      /** This is a pointer to the sender of the message, which can be used to
 -       * directly trigger events, or to create a reply.
 -       */
 -      ModuleRef source;
 -      /** The event identifier.
 -       * This is arbitary text which should be used to distinguish
 -       * one type of event from another.
 -       */
 -      const std::string id;
 -
 -      /** Create a new Event
 -       */
 -      Event(Module* src, const std::string &eventid);
 -      /** Send the Event.
 -       * The return result of an Event::Send() will always be NULL as
 -       * no replies are expected.
 -       */
 -      void Send();
 -};
 -
  class CoreExport DataProvider : public ServiceProvider
  {
   public:
                : ServiceProvider(Creator, Name, SERVICE_DATA) {}
  };
  
 -class CoreExport dynamic_reference_base : public interfacebase
 -{
 - private:
 -      std::string name;
 - protected:
 -      DataProvider* value;
 - public:
 -      ModuleRef creator;
 -      dynamic_reference_base(Module* Creator, const std::string& Name);
 -      ~dynamic_reference_base();
 -      inline void ClearCache() { value = NULL; }
 -      inline const std::string& GetProvider() { return name; }
 -      void SetProvider(const std::string& newname);
 -      void lookup();
 -      operator bool();
 -      static void reset_all();
 -};
 -
 -template<typename T>
 -class dynamic_reference : public dynamic_reference_base
 -{
 - public:
 -      dynamic_reference(Module* Creator, const std::string& Name)
 -              : dynamic_reference_base(Creator, Name) {}
 -      inline T* operator->()
 -      {
 -              if (!value)
 -                      lookup();
 -              return static_cast<T*>(value);
 -      }
 -};
 -
  /** Priority types which can be used by Module::Prioritize()
   */
  enum Priority { PRIORITY_FIRST, PRIORITY_LAST, PRIORITY_BEFORE, PRIORITY_AFTER };
   */
  enum Implementation
  {
 -      I_BEGIN,
 -      I_OnUserConnect, I_OnUserQuit, I_OnUserDisconnect, I_OnUserJoin, I_OnUserPart, I_OnRehash,
 +      I_OnUserConnect, I_OnUserQuit, I_OnUserDisconnect, I_OnUserJoin, I_OnUserPart,
        I_OnSendSnotice, I_OnUserPreJoin, I_OnUserPreKick, I_OnUserKick, I_OnOper, I_OnInfo, I_OnWhois,
 -      I_OnUserPreInvite, I_OnUserInvite, I_OnUserPreMessage, I_OnUserPreNotice, I_OnUserPreNick,
 -      I_OnUserMessage, I_OnUserNotice, I_OnMode, I_OnGetServerDescription, I_OnSyncUser,
 -      I_OnSyncChannel, I_OnDecodeMetaData, I_OnWallops, I_OnAcceptConnection, I_OnUserInit,
 +      I_OnUserPreInvite, I_OnUserInvite, I_OnUserPreMessage, I_OnUserPreNick,
 +      I_OnUserMessage, I_OnMode, I_OnSyncUser,
 +      I_OnSyncChannel, I_OnDecodeMetaData, I_OnAcceptConnection, I_OnUserInit,
        I_OnChangeHost, I_OnChangeName, I_OnAddLine, I_OnDelLine, I_OnExpireLine,
 -      I_OnUserPostNick, I_OnPreMode, I_On005Numeric, I_OnKill, I_OnRemoteKill, I_OnLoadModule,
 +      I_OnUserPostNick, I_OnPreMode, I_On005Numeric, I_OnKill, I_OnLoadModule,
        I_OnUnloadModule, I_OnBackgroundTimer, I_OnPreCommand, I_OnCheckReady, I_OnCheckInvite,
        I_OnRawMode, I_OnCheckKey, I_OnCheckLimit, I_OnCheckBan, I_OnCheckChannelBan, I_OnExtBanCheck,
        I_OnStats, I_OnChangeLocalUserHost, I_OnPreTopicChange,
 -      I_OnPostTopicChange, I_OnEvent, I_OnGlobalOper, I_OnPostConnect, I_OnAddBan,
 -      I_OnDelBan, I_OnChangeLocalUserGECOS, I_OnUserRegister, I_OnChannelPreDelete, I_OnChannelDelete,
 +      I_OnPostTopicChange, I_OnPostConnect,
 +      I_OnChangeLocalUserGECOS, I_OnUserRegister, I_OnChannelPreDelete, I_OnChannelDelete,
        I_OnPostOper, I_OnSyncNetwork, I_OnSetAway, I_OnPostCommand, I_OnPostJoin,
        I_OnWhoisLine, I_OnBuildNeighborList, I_OnGarbageCollect, I_OnSetConnectClass,
 -      I_OnText, I_OnPassCompare, I_OnRunTestSuite, I_OnNamesListItem, I_OnNumeric, I_OnHookIO,
 +      I_OnText, I_OnPassCompare, I_OnNamesListItem, I_OnNumeric,
        I_OnPreRehash, I_OnModuleRehash, I_OnSendWhoLine, I_OnChangeIdent, I_OnSetUserIP,
        I_END
  };
   */
  class CoreExport Module : public classbase, public usecountbase
  {
 +      /** Detach an event from this module
 +       * @param i Event type to detach
 +       */
 +      void DetachEvent(Implementation i);
 +
   public:
        /** File that this module was loaded from
         */
        {
        }
  
 +      /** This method is called when you should reload module specific configuration:
 +       * on boot, on a /REHASH and on module load.
 +       * @param status The current status, can be inspected for more information;
 +       * also used for reporting configuration errors and warnings.
 +       */
 +      virtual void ReadConfig(ConfigStatus& status);
 +
        /** Returns the version number of a Module.
         * The method should return a Version object with its version information assigned via
         * Version::Version
         */
        virtual void OnModuleRehash(User* user, const std::string &parameter);
  
 -      /** Called on rehash.
 -       * This method is called after a rehash has completed. You should use it to reload any module
 -       * configuration from the main configuration file.
 -       * @param user The user that performed the rehash, if it was initiated by a user and that user
 -       * is still connected.
 -       */
 -      virtual void OnRehash(User* user);
 -
        /** Called whenever a snotice is about to be sent to a snomask.
         * snomask and type may both be modified; the message may not.
         * @param snomask The snomask the message is going to (e.g. 'A')
         * @param keygiven The key given to join the channel, or an empty string if none was provided
         * @return 1 To prevent the join, 0 to allow it.
         */
 -      virtual ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven);
 +      virtual ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven);
  
        /** Called whenever a user is about to be kicked.
         * Returning a value of 1 from this function stops the process immediately, causing no
         * @param status The status being used, e.g. PRIVMSG @#chan has status== '@', 0 to send to everyone.
         * @param exempt_list A list of users not to send to. For channel messages, this will usually contain just the sender.
         * It will be ignored for private messages.
 +       * @param msgtype The message type, MSG_PRIVMSG for PRIVMSGs, MSG_NOTICE for NOTICEs
         * @return 1 to deny the message, 0 to allow it
         */
 -      virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list);
 -
 -      /** Called whenever a user is about to NOTICE A user or a channel, before any processing is done.
 -       * Returning any nonzero value from this function stops the process immediately, causing no
 -       * output to be sent to the user by the core. If you do this you must produce your own numerics,
 -       * notices etc. This is useful for modules which may want to filter or redirect messages.
 -       * target_type can be one of TYPE_USER or TYPE_CHANNEL. If the target_type value is a user,
 -       * you must cast dest to a User* otherwise you must cast it to a Channel*, this is the details
 -       * of where the message is destined to be sent.
 -       * You may alter the message text as you wish before relinquishing control to the next module
 -       * in the chain, and if no other modules block the text this altered form of the text will be sent out
 -       * to the user and possibly to other servers.
 -       * @param user The user sending the message
 -       * @param dest The target of the message (Channel* or User*)
 -       * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL)
 -       * @param text Changeable text being sent by the user
 -       * @param status The status being used, e.g. PRIVMSG @#chan has status== '@', 0 to send to everyone.
 -       * @param exempt_list A list of users not to send to. For channel notices, this will usually contain just the sender.
 -       * It will be ignored for private notices.
 -       * @return 1 to deny the NOTICE, 0 to allow it
 -       */
 -      virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list);
 +      virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list, MessageType msgtype);
  
        /** Called when sending a message to all "neighbors" of a given user -
         * that is, all users that share a common channel. This is used in
         *
         * Set exceptions[user] = true to include, exceptions[user] = false to exclude
         */
 -      virtual void OnBuildNeighborList(User* source, UserChanList &include_c, std::map<User*,bool> &exceptions);
 +      virtual void OnBuildNeighborList(User* source, IncludeChanList& include_c, std::map<User*, bool>& exceptions);
  
 -      /** Called before any nickchange, local or remote. This can be used to implement Q-lines etc.
 -       * Please note that although you can see remote nickchanges through this function, you should
 -       * NOT make any changes to the User if the user is a remote user as this may cause a desnyc.
 -       * check user->server before taking any action (including returning nonzero from the method).
 +      /** Called before local nickname changes. This can be used to implement Q-lines etc.
         * If your method returns nonzero, the nickchange is silently forbidden, and it is down to your
         * module to generate some meaninful output.
         * @param user The username changing their nick
         * @param newnick Their new nickname
         * @return 1 to deny the change, 0 to allow
         */
 -      virtual ModResult OnUserPreNick(User* user, const std::string &newnick);
 +      virtual ModResult OnUserPreNick(LocalUser* user, const std::string& newnick);
  
        /** Called after any PRIVMSG sent from a user.
         * The dest variable contains a User* if target_type is TYPE_USER and a Channel*
         * @param text the text being sent by the user
         * @param status The status being used, e.g. PRIVMSG @#chan has status== '@', 0 to send to everyone.
         * @param exempt_list A list of users to not send to.
 +       * @param msgtype The message type, MSG_PRIVMSG for PRIVMSGs, MSG_NOTICE for NOTICEs
         */
 -      virtual void OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
 -
 -      /** Called after any NOTICE sent from a user.
 -       * The dest variable contains a User* if target_type is TYPE_USER and a Channel*
 -       * if target_type is TYPE_CHANNEL.
 -       * @param user The user sending the message
 -       * @param dest The target of the message
 -       * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL)
 -       * @param text the text being sent by the user
 -       * @param status The status being used, e.g. NOTICE @#chan has status== '@', 0 to send to everyone.
 -       * @param exempt_list A list of users to not send to.
 -       */
 -      virtual void OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
 +      virtual void OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list, MessageType msgtype);
  
        /** Called immediately before any NOTICE or PRIVMSG sent from a user, local or remote.
         * The dest variable contains a User* if target_type is TYPE_USER and a Channel*
         * if target_type is TYPE_CHANNEL.
 -       * The difference between this event and OnUserPreNotice/OnUserPreMessage is that delivery is gauranteed,
 +       * The difference between this event and OnUserPreMessage is that delivery is gauranteed,
         * the message has already been vetted. In the case of the other two methods, a later module may stop your
         * message. This also differs from OnUserMessage which occurs AFTER the message has been sent.
         * @param user The user sending the message
        virtual void OnText(User* user, void* dest, int target_type, const std::string &text, char status, CUList &exempt_list);
  
        /** Called after every MODE command sent from a user
 -       * The dest variable contains a User* if target_type is TYPE_USER and a Channel*
 -       * if target_type is TYPE_CHANNEL. The text variable contains the remainder of the
 -       * mode string after the target, e.g. "+wsi" or "+ooo nick1 nick2 nick3".
 +       * Either the usertarget or the chantarget variable contains the target of the modes,
 +       * the actual target will have a non-NULL pointer.
 +       * All changed modes are available in the changelist object.
         * @param user The user sending the MODEs
 -       * @param dest The target of the modes (User* or Channel*)
 -       * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL)
 -       * @param text The actual modes and their parameters if any
 -       * @param translate The translation types of the mode parameters
 +       * @param usertarget The target user of the modes, NULL if the target is a channel
 +       * @param chantarget The target channel of the modes, NULL if the target is a user
 +       * @param changelist The changed modes.
 +       * @param processflags Flags passed to ModeParser::Process(), see ModeParser::ModeProcessFlags
 +       * for the possible flags.
 +       * @param output_mode Changed modes, including '+' and '-' characters, not including any parameters
         */
 -      virtual void OnMode(User* user, void* dest, int target_type, const std::vector<std::string> &text, const std::vector<TranslateType> &translate);
 -
 -      /** Allows modules to alter or create server descriptions
 -       * Whenever a module requires a server description, for example for display in
 -       * WHOIS, this function is called in all modules. You may change or define the
 -       * description given in std::string &description. If you do, this description
 -       * will be shown in the WHOIS fields.
 -       * @param servername The servername being searched for
 -       * @param description Alterable server description for this server
 -       */
 -      virtual void OnGetServerDescription(const std::string &servername,std::string &description);
 +      virtual void OnMode(User* user, User* usertarget, Channel* chantarget, const Modes::ChangeList& changelist, ModeParser::ModeProcessFlag processflags, const std::string& output_mode);
  
        /** Allows modules to synchronize data which relates to users during a netburst.
         * When this function is called, it will be called from the module which implements
 -       * the linking protocol. This currently is m_spanningtree.so. A pointer to this module
 -       * is given in Module* proto, so that you may call its methods such as ProtoSendMode
 -       * (see below). This function will be called for every user visible on your side
 -       * of the burst, allowing you to for example set modes, etc. Do not use this call to
 -       * synchronize data which you have stored using class Extensible -- There is a specialist
 -       * function OnSyncUserMetaData and OnSyncChannelMetaData for this!
 +       * the linking protocol. This currently is m_spanningtree.so.
 +       * This function will be called for every user visible on your side
 +       * of the burst, allowing you to for example set modes, etc.
         * @param user The user being syncronized
 -       * @param proto A pointer to the module handling network protocol
 -       * @param opaque An opaque pointer set by the protocol module, should not be modified!
 +       * @param server The target of the burst
         */
 -      virtual void OnSyncUser(User* user, Module* proto, void* opaque);
 +      virtual void OnSyncUser(User* user, ProtocolServer& server);
  
        /** Allows modules to synchronize data which relates to channels during a netburst.
         * When this function is called, it will be called from the module which implements
 -       * the linking protocol. This currently is m_spanningtree.so. A pointer to this module
 -       * is given in Module* proto, so that you may call its methods such as ProtoSendMode
 -       * (see below). This function will be called for every user visible on your side
 -       * of the burst, allowing you to for example set modes, etc.
 -       *
 -       * For a good example of how to use this function, please see src/modules/m_chanprotect.cpp
 +       * the linking protocol. This currently is m_spanningtree.so.
 +       * This function will be called for every channel visible on your side of the burst,
 +       * allowing you to for example set modes, etc.
         *
         * @param chan The channel being syncronized
 -       * @param proto A pointer to the module handling network protocol
 -       * @param opaque An opaque pointer set by the protocol module, should not be modified!
 +       * @param server The target of the burst
         */
 -      virtual void OnSyncChannel(Channel* chan, Module* proto, void* opaque);
 +      virtual void OnSyncChannel(Channel* chan, ProtocolServer& server);
  
 -      /* Allows modules to syncronize metadata not related to users or channels, over the network during a netburst.
 -       * Whenever the linking module wants to send out data, but doesnt know what the data
 -       * represents (e.g. it is Extensible metadata, added to a User or Channel by a module) then
 -       * this method is called. You should use the ProtoSendMetaData function after you've
 -       * correctly decided how the data should be represented, to send the metadata on its way if
 -       * if it belongs to your module.
 -       * @param proto A pointer to the module handling network protocol
 -       * @param opaque An opaque pointer set by the protocol module, should not be modified!
 -       * @param displayable If this value is true, the data is going to be displayed to a user,
 -       * and not sent across the network. Use this to determine wether or not to show sensitive data.
 +      /** Allows modules to syncronize metadata not related to users or channels, over the network during a netburst.
 +       * When the linking module has finished sending all data it wanted to send during a netburst, then
 +       * this method is called. You should use the SendMetaData() function after you've
 +       * correctly decided how the data should be represented, to send the data.
 +       * @param server The target of the burst
         */
 -      virtual void OnSyncNetwork(Module* proto, void* opaque);
 +      virtual void OnSyncNetwork(ProtocolServer& server);
  
        /** Allows module data, sent via ProtoSendMetaData, to be decoded again by a receiving module.
         * Please see src/modules/m_swhois.cpp for a working example of how to use this method call.
         */
        virtual void OnDecodeMetaData(Extensible* target, const std::string &extname, const std::string &extdata);
  
 -      /** Implemented by modules which provide the ability to link servers.
 -       * These modules will implement this method, which allows transparent sending of servermodes
 -       * down the network link as a broadcast, without a module calling it having to know the format
 -       * of the MODE command before the actual mode string.
 -       *
 -       * More documentation to follow soon. Please see src/modules/m_chanprotect.cpp for examples
 -       * of how to use this function.
 -       *
 -       * @param opaque An opaque pointer set by the protocol module, should not be modified!
 -       * @param target_type The type of item to decode data for, TYPE_USER or TYPE_CHANNEL
 -       * @param target The Channel* or User* that modes should be sent for
 -       * @param modeline The modes and parameters to be sent
 -       * @param translate The translation types of the mode parameters
 -       */
 -      virtual void ProtoSendMode(void* opaque, TargetTypeFlags target_type, void* target, const std::vector<std::string> &modeline, const std::vector<TranslateType> &translate);
 -
 -      /** Implemented by modules which provide the ability to link servers.
 -       * These modules will implement this method, which allows metadata (extra data added to
 -       * user and channel records using class Extensible, Extensible::Extend, etc) to be sent
 -       * to other servers on a netburst and decoded at the other end by the same module on a
 -       * different server.
 -       *
 -       * More documentation to follow soon. Please see src/modules/m_swhois.cpp for example of
 -       * how to use this function.
 -       * @param opaque An opaque pointer set by the protocol module, should not be modified!
 -       * @param target The Channel* or User* that metadata should be sent for
 -       * @param extname The extension name to send metadata for
 -       * @param extdata Encoded data for this extension name, which will be encoded at the oppsite end by an identical module using OnDecodeMetaData
 -       */
 -      virtual void ProtoSendMetaData(void* opaque, Extensible* target, const std::string &extname, const std::string &extdata);
 -
 -      /** Called after every WALLOPS command.
 -       * @param user The user sending the WALLOPS
 -       * @param text The content of the WALLOPS message
 -       */
 -      virtual void OnWallops(User* user, const std::string &text);
 -
        /** Called whenever a user's hostname is changed.
         * This event triggers after the host has been set.
         * @param user The user whos host is being changed
         */
        virtual void OnUserPostNick(User* user, const std::string &oldnick);
  
 -      /** Called before any mode change, to allow a single access check for
 +      /** Called before a mode change via the MODE command, to allow a single access check for
         * a full mode change (use OnRawMode to check individual modes)
         *
         * Returning MOD_RES_ALLOW will skip prefix level checks, but can be overridden by
         * @param source the user making the mode change
         * @param dest the user destination of the umode change (NULL if a channel mode)
         * @param channel the channel destination of the mode change
 -       * @param parameters raw mode parameters; parameters[0] is the user/channel being changed
 +       * @param modes Modes being changed, can be edited
         */
 -      virtual ModResult OnPreMode(User* source, User* dest, Channel* channel, const std::vector<std::string>& parameters);
 +      virtual ModResult OnPreMode(User* source, User* dest, Channel* channel, Modes::ChangeList& modes);
  
        /** Called when a 005 numeric is about to be output.
         * The module should modify the 005 numeric if needed to indicate its features.
 -       * @param output The 005 string to be modified if neccessary.
 -       */
 -      virtual void On005Numeric(std::string &output);
 +      * @param tokens The 005 map to be modified if neccessary.
 +      */
 +      virtual void On005Numeric(std::map<std::string, std::string>& tokens);
  
        /** Called when a client is disconnected by KILL.
         * If a client is killed by a server, e.g. a nickname collision or protocol error,
         */
        virtual ModResult OnKill(User* source, User* dest, const std::string &reason);
  
 -      /** Called when an oper wants to disconnect a remote user via KILL
 -       * @param source The user sending the KILL
 -       * @param dest The user being killed
 -       * @param reason The kill reason
 -       * @param operreason The oper kill reason
 -       */
 -      virtual void OnRemoteKill(User* source, User* dest, const std::string &reason, const std::string &operreason);
 -
        /** Called whenever a module is loaded.
         * mod will contain a pointer to the module, and string will contain its name,
         * for example m_widgets.so. This function is primary for dependency checking,
         * @param result The return code given by the command handler, one of CMD_SUCCESS or CMD_FAILURE
         * @param original_line The entire original line as passed to the parser from the user
         */
 -      virtual void OnPostCommand(const std::string &command, const std::vector<std::string>& parameters, LocalUser *user, CmdResult result, const std::string &original_line);
 +      virtual void OnPostCommand(Command* command, const std::vector<std::string>& parameters, LocalUser* user, CmdResult result, const std::string& original_line);
  
        /** Called when a user is first connecting, prior to starting DNS lookups, checking initial
         * connect class, or accepting any commands.
         * Return 1 from this function to block the mode character from being processed entirely.
         * @param user The user who is sending the mode
         * @param chan The channel the mode is being sent to (or NULL if a usermode)
 -       * @param mode The mode character being set
 +       * @param mh The mode handler for the mode being changed
         * @param param The parameter for the mode or an empty string
         * @param adding true of the mode is being added, false if it is being removed
 -       * @param pcnt The parameter count for the mode (0 or 1)
         * @return ACR_DENY to deny the mode, ACR_DEFAULT to do standard mode checking, and ACR_ALLOW
         * to skip all permission checking. Please note that for remote mode changes, your return value
         * will be ignored!
         */
 -      virtual ModResult OnRawMode(User* user, Channel* chan, const char mode, const std::string &param, bool adding, int pcnt);
 +      virtual ModResult OnRawMode(User* user, Channel* chan, ModeHandler* mh, const std::string& param, bool adding);
  
        /** Called whenever a user joins a channel, to determine if key checks should go ahead or not.
         * This method will always be called for each join, wether or not the channel is actually +k, and
         */
        virtual void OnPostTopicChange(User* user, Channel* chan, const std::string &topic);
  
 -      /** Called whenever an Event class is sent to all modules by another module.
 -       * You should *always* check the value of Event::id to determine the event type.
 -       * @param event The Event class being received
 -       */
 -      virtual void OnEvent(Event& event);
 -
 -      /** Called whenever a Request class is sent to your module by another module.
 -       * The value of Request::id should be used to determine the type of request.
 -       * @param request The Request class being received
 -       */
 -      virtual void OnRequest(Request& request);
 -
        /** Called whenever a password check is to be made. Replaces the old OldOperCompare API.
         * The password field (from the config file) is in 'password' and is to be compared against
         * 'input'. This method allows for encryption of passwords (oper, connect:allow, die/restart, etc).
         */
        virtual ModResult OnPassCompare(Extensible* ex, const std::string &password, const std::string &input, const std::string& hashtype);
  
 -      /** Called whenever a user is given usermode +o, anywhere on the network.
 -       * You cannot override this and prevent it from happening as it is already happened and
 -       * such a task must be performed by another server. You can however bounce modes by sending
 -       * servermodes out to reverse mode changes.
 -       * @param user The user who is opering
 -       */
 -      virtual void OnGlobalOper(User* user);
 -
        /** Called after a user has fully connected and all modules have executed OnUserConnect
         * This event is informational only. You should not change any user information in this
         * event. To do so, use the OnUserConnect method to change the state of local users.
         */
        virtual void OnPostConnect(User* user);
  
 -      /** Called whenever a ban is added to a channel's list.
 -       * Return a non-zero value to 'eat' the mode change and prevent the ban from being added.
 -       * @param source The user adding the ban
 -       * @param channel The channel the ban is being added to
 -       * @param banmask The ban mask being added
 -       * @return 1 to block the ban, 0 to continue as normal
 -       */
 -      virtual ModResult OnAddBan(User* source, Channel* channel,const std::string &banmask);
 -
 -      /** Called whenever a ban is removed from a channel's list.
 -       * Return a non-zero value to 'eat' the mode change and prevent the ban from being removed.
 -       * @param source The user deleting the ban
 -       * @param channel The channel the ban is being deleted from
 -       * @param banmask The ban mask being deleted
 -       * @return 1 to block the unban, 0 to continue as normal
 -       */
 -      virtual ModResult OnDelBan(User* source, Channel* channel,const std::string &banmask);
 -
 -      /** Called to install an I/O hook on an event handler
 -       * @param user The socket to possibly install the I/O hook on
 -       * @param via The port that the user connected on
 -       */
 -      virtual void OnHookIO(StreamSocket* user, ListenSocket* via);
 -
        /** Called when a port accepts a connection
         * Return MOD_RES_ACCEPT if you have used the file descriptor.
         * @param fd The file descriptor returned from accept()
         */
        virtual ModResult OnAcceptConnection(int fd, ListenSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
  
 -      /** Called immediately after any connection is accepted. This is intended for raw socket
 -       * processing (e.g. modules which wrap the tcp connection within another library) and provides
 -       * no information relating to a user record as the connection has not been assigned yet.
 -       * There are no return values from this call as all modules get an opportunity if required to
 -       * process the connection.
 -       * @param sock The socket in question
 -       * @param client The client IP address and port
 -       * @param server The server IP address and port
 -       */
 -      virtual void OnStreamSocketAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
 -
 -      /**
 -       * Called when a hooked stream has data to write, or when the socket
 -       * engine returns it as writable
 -       * @param sock The socket in question
 -       * @param sendq Data to send to the socket
 -       * @return 1 if the sendq has been completely emptied, 0 if there is
 -       *  still data to send, and -1 if there was an error
 -       */
 -      virtual int OnStreamSocketWrite(StreamSocket* sock, std::string& sendq);
 -
 -      /** Called immediately before any socket is closed. When this event is called, shutdown()
 -       * has not yet been called on the socket.
 -       * @param sock The socket in question
 -       */
 -      virtual void OnStreamSocketClose(StreamSocket* sock);
 -
 -      /** Called immediately upon connection of an outbound BufferedSocket which has been hooked
 -       * by a module.
 -       * @param sock The socket in question
 -       */
 -      virtual void OnStreamSocketConnect(StreamSocket* sock);
 -
 -      /**
 -       * Called when the stream socket has data to read
 -       * @param sock The socket that is ready
 -       * @param recvq The receive queue that new data should be appended to
 -       * @return 1 if new data has been read, 0 if no new data is ready (but the
 -       *  socket is still connected), -1 if there was an error or close
 -       */
 -      virtual int OnStreamSocketRead(StreamSocket* sock, std::string& recvq);
 -
        /** Called whenever a user sets away or returns from being away.
         * The away message is available as a parameter, but should not be modified.
         * At this stage, it has already been copied into the user record.
         */
        virtual ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass);
  
 +#ifdef INSPIRCD_ENABLE_TESTSUITE
        /** Add test suite hooks here. These are used for testing functionality of a module
         * via the --testsuite debugging parameter.
         */
        virtual void OnRunTestSuite();
 +#endif
  
        /** Called for every item in a NAMES list, so that modules may reformat portions of it as they see fit.
 -       * For example NAMESX, channel mode +u and +I, and UHNAMES. If the nick is set to an empty string by any
 -       * module, then this will cause the nickname not to be displayed at all.
 +       * For example NAMESX, channel mode +u and +I, and UHNAMES.
 +       * @param issuer The user who is going to receive the NAMES list being built
 +       * @param item The channel member being considered for inclusion
 +       * @param prefixes The prefix character(s) to display, initially set to the prefix char of the most powerful
 +       * prefix mode the member has, can be changed
 +       * @param nick The nick to display, initially set to the member's nick, can be changed
 +       * @return Return MOD_RES_PASSTHRU to allow the member to be displayed, MOD_RES_DENY to cause them to be
 +       * excluded from this NAMES list
         */
 -      virtual void OnNamesListItem(User* issuer, Membership* item, std::string &prefixes, std::string &nick);
 +      virtual ModResult OnNamesListItem(User* issuer, Membership* item, std::string& prefixes, std::string& nick);
  
        virtual ModResult OnNumeric(User* user, unsigned int numeric, const std::string &text);
  
         * @param source The user running the /WHO query
         * @param params The parameters to the /WHO query
         * @param user The user that this line of the query is about
 +       * @param memb The member shown in this line, NULL if no channel is in this line
         * @param line The raw line to send; modifiable, if empty no line will be returned.
         */
 -      virtual void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, std::string& line);
 +      virtual void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, std::string& line);
  
        /** Called whenever a local user's IP is set for the first time, or when a local user's IP changes due to
         * a module like m_cgiirc changing it.
        virtual void OnSetUserIP(LocalUser* user);
  };
  
 -
 -#define CONF_NO_ERROR         0x000000
 -#define CONF_NOT_A_NUMBER     0x000010
 -#define CONF_INT_NEGATIVE     0x000080
 -#define CONF_VALUE_NOT_FOUND  0x000100
 -#define CONF_FILE_NOT_FOUND   0x000200
 -
 -
 -/** Allows reading of values from configuration files
 - * This class allows a module to read from either the main configuration file (inspircd.conf) or from
 - * a module-specified configuration file. It may either be instantiated with one parameter or none.
 - * Constructing the class using one parameter allows you to specify a path to your own configuration
 - * file, otherwise, inspircd.conf is read.
 - */
 -class CoreExport ConfigReader : public interfacebase
 -{
 -  protected:
 -      /** Error code
 -       */
 -      long error;
 -
 -  public:
 -      /** Default constructor.
 -       * This constructor initialises the ConfigReader class to read the inspircd.conf file
 -       * as specified when running ./configure.
 -       */
 -      ConfigReader();
 -      /** Default destructor.
 -       * This method destroys the ConfigReader class.
 -       */
 -      ~ConfigReader();
 -
 -      /** Retrieves a value from the config file.
 -       * This method retrieves a value from the config file. Where multiple copies of the tag
 -       * exist in the config file, index indicates which of the values to retrieve.
 -       */
 -      std::string ReadValue(const std::string &tag, const std::string &name, int index, bool allow_linefeeds = false);
 -      /** Retrieves a value from the config file.
 -       * This method retrieves a value from the config file. Where multiple copies of the tag
 -       * exist in the config file, index indicates which of the values to retrieve. If the
 -       * tag is not found the default value is returned instead.
 -       */
 -      std::string ReadValue(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool allow_linefeeds = false);
 -
 -      /** Retrieves a boolean value from the config file.
 -       * This method retrieves a boolean value from the config file. Where multiple copies of the tag
 -       * exist in the config file, index indicates which of the values to retrieve. The values "1", "yes"
 -       * and "true" in the config file count as true to ReadFlag, and any other value counts as false.
 -       */
 -      bool ReadFlag(const std::string &tag, const std::string &name, int index);
 -      /** Retrieves a boolean value from the config file.
 -       * This method retrieves a boolean value from the config file. Where multiple copies of the tag
 -       * exist in the config file, index indicates which of the values to retrieve. The values "1", "yes"
 -       * and "true" in the config file count as true to ReadFlag, and any other value counts as false.
 -       * If the tag is not found, the default value is used instead.
 -       */
 -      bool ReadFlag(const std::string &tag, const std::string &name, const std::string &default_value, int index);
 -
 -      /** Retrieves an integer value from the config file.
 -       * This method retrieves an integer value from the config file. Where multiple copies of the tag
 -       * exist in the config file, index indicates which of the values to retrieve. Any invalid integer
 -       * values in the tag will cause the objects error value to be set, and any call to GetError() will
 -       * return CONF_INVALID_NUMBER to be returned. need_positive is set if the number must be non-negative.
 -       * If a negative number is placed into a tag which is specified positive, 0 will be returned and GetError()
 -       * will return CONF_INT_NEGATIVE. Note that need_positive is not suitable to get an unsigned int - you
 -       * should cast the result to achieve that effect.
 -       */
 -      int ReadInteger(const std::string &tag, const std::string &name, int index, bool need_positive);
 -      /** Retrieves an integer value from the config file.
 -       * This method retrieves an integer value from the config file. Where multiple copies of the tag
 -       * exist in the config file, index indicates which of the values to retrieve. Any invalid integer
 -       * values in the tag will cause the objects error value to be set, and any call to GetError() will
 -       * return CONF_INVALID_NUMBER to be returned. needs_unsigned is set if the number must be unsigned.
 -       * If a signed number is placed into a tag which is specified unsigned, 0 will be returned and GetError()
 -       * will return CONF_NOT_UNSIGNED. If the tag is not found, the default value is used instead.
 -       */
 -      int ReadInteger(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool need_positive);
 -
 -      /** Returns the last error to occur.
 -       * Valid errors can be found by looking in modules.h. Any nonzero value indicates an error condition.
 -       * A call to GetError() resets the error flag back to 0.
 -       */
 -      long GetError();
 -
 -      /** Counts the number of times a given tag appears in the config file.
 -       * This method counts the number of times a tag appears in a config file, for use where
 -       * there are several tags of the same kind, e.g. with opers and connect types. It can be
 -       * used with the index value of ConfigReader::ReadValue to loop through all copies of a
 -       * multiple instance tag.
 -       */
 -      int Enumerate(const std::string &tag);
 -};
 -
 -
 -
 -/** Caches a text file into memory and can be used to retrieve lines from it.
 - * This class contains methods for read-only manipulation of a text file in memory.
 - * Either use the constructor type with one parameter to load a file into memory
 - * at construction, or use the LoadFile method to load a file.
 - */
 -class CoreExport FileReader : public classbase
 -{
 -      /** The file contents
 -       */
 -      std::vector<std::string> fc;
 -
 -      /** Content size in bytes
 -       */
 -      unsigned long contentsize;
 -
 -      /** Calculate content size in bytes
 -       */
 -      void CalcSize();
 -
 - public:
 -      /** Default constructor.
 -       * This method does not load any file into memory, you must use the LoadFile method
 -       * after constructing the class this way.
 -       */
 -      FileReader();
 -
 -      /** Secondary constructor.
 -       * This method initialises the class with a file loaded into it ready for GetLine and
 -       * and other methods to be called. If the file could not be loaded, FileReader::FileSize
 -       * returns 0.
 -       */
 -      FileReader(const std::string &filename);
 -
 -      /** Default destructor.
 -       * This deletes the memory allocated to the file.
 -       */
 -      ~FileReader();
 -
 -      /** Used to load a file.
 -       * This method loads a file into the class ready for GetLine and
 -       * and other methods to be called. If the file could not be loaded, FileReader::FileSize
 -       * returns 0.
 -       */
 -      void LoadFile(const std::string &filename);
 -
 -      /** Returns the whole content of the file as std::string
 -       */
 -      std::string Contents();
 -
 -      /** Returns the entire size of the file as std::string
 -       */
 -      unsigned long ContentSize();
 -
 -      /** Returns true if the file exists
 -       * This function will return false if the file could not be opened.
 -       */
 -      bool Exists();
 -
 -      /** Retrieve one line from the file.
 -       * This method retrieves one line from the text file. If an empty non-NULL string is returned,
 -       * the index was out of bounds, or the line had no data on it.
 -       */
 -      std::string GetLine(int x);
 -
 -      /** Returns the size of the file in lines.
 -       * This method returns the number of lines in the read file. If it is 0, no lines have been
 -       * read into memory, either because the file is empty or it does not exist, or cannot be
 -       * opened due to permission problems.
 -       */
 -      int FileSize();
 -};
 -
  /** A list of modules
   */
  typedef std::vector<Module*> IntModuleList;
@@@ -1036,16 -1479,17 +1036,16 @@@ typedef IntModuleList::iterator EventHa
  /** ModuleManager takes care of all things module-related
   * in the core.
   */
 -class CoreExport ModuleManager
 +class CoreExport ModuleManager : public fakederef<ModuleManager>
  {
 + public:
 +      typedef std::vector<ServiceProvider*> ServiceList;
 +
   private:
        /** Holds a string describing the last module error to occur
         */
        std::string LastModuleError;
  
 -      /** Total number of modules loaded into the ircd
 -       */
 -      int ModCount;
 -
        /** List of loaded modules and shared object/dll handles
         * keyed by module name
         */
  
        /** Internal unload module hook */
        bool CanUnload(Module*);
 +
 +      /** Loads all core modules (cmd_*)
 +       */
 +      void LoadCoreModules(std::map<std::string, ServiceList>& servicemap);
 +
 +      /** Calls the Prioritize() method in all loaded modules
 +       * @return True if all went well, false if a dependency loop was detected
 +       */
 +      bool PrioritizeHooks();
 +
   public:
 +      typedef std::map<std::string, Module*> ModuleMap;
  
        /** Event handler hooks.
         * This needs to be public to be used by FOREACH_MOD and friends.
        /** List of data services keyed by name */
        std::multimap<std::string, ServiceProvider*> DataProviders;
  
 +      /** A list of ServiceProviders waiting to be registered.
 +       * Non-NULL when constructing a Module, NULL otherwise.
 +       * When non-NULL ServiceProviders add themselves to this list on creation and the core
 +       * automatically registers them (that is, call AddService()) after the Module is constructed,
 +       * and before Module::init() is called.
 +       * If a service is created after the construction of the Module (for example in init()) it
 +       * has to be registered manually.
 +       */
 +      ServiceList* NewServices;
 +
        /** Simple, bog-standard, boring constructor.
         */
        ModuleManager();
         */
        bool SetPriority(Module* mod, Implementation i, Priority s, Module* which = NULL);
  
 -      /** Backwards compat interface */
 -      inline bool SetPriority(Module* mod, Implementation i, Priority s, Module** dptr)
 -      {
 -              return SetPriority(mod, i, s, *dptr);
 -      }
 -
        /** Change the priority of all events in a module.
         * @param mod The module to set the priority of
         * @param s The priority of all events in the module.
         * SetPriority method for this, where you may specify other modules to
         * be prioritized against.
         */
 -      bool SetPriority(Module* mod, Priority s);
 +      void SetPriority(Module* mod, Priority s);
  
        /** Attach an event to a module.
         * You may later detatch the event with ModuleManager::Detach().
         */
        void DetachAll(Module* mod);
  
 +      /** Attach all events to a module (used on module load)
 +       * @param mod Module to attach to all events
 +       */
 +      void AttachAll(Module* mod);
 +
        /** Returns text describing the last module error
         * @return The last error message to occur
         */
        void UnloadAll();
        void DoSafeUnload(Module*);
  
 -      /** Get the total number of currently loaded modules
 -       * @return The number of loaded modules
 -       */
 -      int GetCount()
 -      {
 -              return this->ModCount;
 -      }
 -
        /** Find a module by name, and return a Module* to it.
         * This is preferred over iterating the module lists yourself.
         * @param name The module name to look up
        /** Unregister a service provided by a module */
        void DelService(ServiceProvider&);
  
 +      /** Register all services in a given ServiceList
 +       * @param list The list containing the services to register
 +       */
 +      void AddServices(const ServiceList& list);
 +
        inline void AddServices(ServiceProvider** list, int count)
        {
                for(int i=0; i < count; i++)
                return static_cast<T*>(FindService(SERVICE_DATA, name));
        }
  
 -      /** Return a list of all modules matching the given filter
 -       * @param filter This int is a bitmask of flags set in Module::Flags,
 -       * such as VF_VENDOR or VF_STATIC. If you wish to receive a list of
 -       * all modules with no filtering, set this to 0.
 -       * @return The list of module names
 +      /** Get a map of all loaded modules keyed by their name
 +       * @return A ModuleMap containing all loaded modules
         */
 -      const std::vector<std::string> GetAllModuleNames(int filter);
 +      const ModuleMap& GetModules() const { return Modules; }
  };
  
  /** Do not mess with these functions unless you know the C preprocessor
@@@ -1259,7 -1689,11 +1259,7 @@@ struct AllModuleList 
  };
  
  #define MODULE_INIT(x) static Module* MK_ ## x() { return new x; } \
 -      static const AllModuleList PREP_ ## x(&MK_ ## x, MODNAMESTR);
 -
 -#define MODNAMESTR MODNAMESTR_FN_2(MODNAME)
 -#define MODNAMESTR_FN_2(x) MODNAMESTR_FN_1(x)
 -#define MODNAMESTR_FN_1(x) #x
 +      static const AllModuleList PREP_ ## x(&MK_ ## x, MODNAME ".so");
  
  #else
  
                                break; \
                } \
                return TRUE; \
-       }
+       } \
 -      extern "C" DllExport const char inspircd_src_version[] = VERSION " r" REVISION;
++      extern "C" DllExport const char inspircd_src_version[] = INSPIRCD_VERSION " " INSPIRCD_REVISION;
  
  #else
  
        { \
                return new y; \
        } \
 -      extern "C" const char inspircd_src_version[] = VERSION " r" REVISION;
 +      extern "C" DllExport const char inspircd_src_version[] = INSPIRCD_VERSION " " INSPIRCD_REVISION;
  #endif
  
  #define COMMAND_INIT(c) MODULE_INIT(CommandModule<c>)
  
  #endif
 -
 -#endif
diff --combined include/usermanager.h
index 3671e8907e6de89101afa39d9581f15d39a9bb9d,2a9d6b47b2b9ca54c893ba75a07bbe296a74f9e9..a67f90224d628839eda4c05a6f7feac53e08cd11
   */
  
  
 -#ifndef USERMANAGER_H
 -#define USERMANAGER_H
 +#pragma once
  
  #include <list>
  
 -/** A list of ip addresses cross referenced against clone counts */
 -typedef std::map<irc::sockets::cidr_mask, unsigned int> clonemap;
 -
 -class CoreExport UserManager
 +class CoreExport UserManager : public fakederef<UserManager>
  {
 + public:
 +      struct CloneCounts
 +      {
 +              unsigned int global;
 +              unsigned int local;
 +              CloneCounts() : global(0), local(0) { }
 +      };
 +
 +      /** Container that maps IP addresses to clone counts
 +       */
 +      typedef std::map<irc::sockets::cidr_mask, CloneCounts> CloneMap;
 +
 +      /** Sequence container in which each element is a User*
 +       */
 +      typedef std::vector<User*> OperList;
 +
 +      /** A list holding local users
 +      */
 +      typedef insp::intrusive_list<LocalUser> LocalList;
 +
   private:
 -      /** Map of local ip addresses for clone counting
 +      /** Map of IP addresses for clone counting
         */
 -      clonemap local_clones;
 +      CloneMap clonemap;
 +
 +      /** A CloneCounts that contains zero for both local and global
 +       */
 +      const CloneCounts zeroclonecounts;
 +
 +      /** Local client list, a list containing only local clients
 +       */
 +      LocalList local_users;
 +
   public:
 +      /** Constructor, initializes variables
 +       */
        UserManager();
  
 -      ~UserManager()
 -      {
 -              for (user_hash::iterator i = clientlist->begin();i != clientlist->end();i++)
 -              {
 -                      delete i->second;
 -              }
 -              clientlist->clear();
 -              delete clientlist;
 -              delete uuidlist;
 -      }
 +      /** Destructor, destroys all users in clientlist
 +       */
 +      ~UserManager();
  
        /** Client list, a hash_map containing all clients, local and remote
         */
 -      user_hash* clientlist;
 +      user_hash clientlist;
  
        /** Client list stored by UUID. Contains all clients, and is updated
         * automatically by the constructor and destructor of User.
         */
 -      user_hash* uuidlist;
 -
 -      /** Local client list, a list containing only local clients
 -       */
 -      LocalUserList local_users;
 +      user_hash uuidlist;
  
        /** Oper list, a vector containing all local and remote opered users
         */
 -      std::list<User*> all_opers;
 +      OperList all_opers;
  
        /** Number of unregistered users online right now.
         * (Unregistered means before USER/NICK/dns)
         */
        unsigned int unregistered_count;
  
 -      /** Number of elements in local_users
 +      /**
 +       * Reset the already_sent IDs so we don't wrap it around and drop a message
 +       * Also removes all expired invites
 +     */
 +      void GarbageCollect();
 +
 +      /** Perform background user events such as PING checks
         */
 -      unsigned int local_count;
 +      void DoBackgroundUserStuff();
  
 -      /** Map of global ip addresses for clone counting
 -       * XXX - this should be private, but m_clones depends on it currently.
 +      /** Returns true when all modules have done pre-registration checks on a user
 +       * @param user The user to verify
 +       * @return True if all modules have finished checking this user
         */
 -      clonemap global_clones;
 +      bool AllModulesReportReady(LocalUser* user);
  
        /** Add a client to the system.
         * This will create a new User, insert it into the user_hash,
        /** Disconnect a user gracefully
         * @param user The user to remove
         * @param quitreason The quit reason to show to normal users
 -       * @param operreason The quit reason to show to opers
 +       * @param operreason The quit reason to show to opers, can be NULL if same as quitreason
         * @return Although this function has no return type, on exit the user provided will no longer exist.
         */
 -      void QuitUser(User *user, const std::string &quitreason, const char* operreason = "");
 -
 -      /** Add a user to the local clone map
 -       * @param user The user to add
 -       */
 -      void AddLocalClone(User *user);
 +      void QuitUser(User* user, const std::string& quitreason, const std::string* operreason = NULL);
  
 -      /** Add a user to the global clone map
 +      /** Add a user to the clone map
         * @param user The user to add
         */
 -      void AddGlobalClone(User *user);
 +      void AddClone(User* user);
  
        /** Remove all clone counts from the user, you should
         * use this if you change the user's IP address
         */
        void RemoveCloneCounts(User *user);
  
 -      /** Return the number of global clones of this user
 -       * @param user The user to get a count for
 -       * @return The global clone count of this user
+       /** Rebuild clone counts
+        */
+       void RehashCloneCounts();
 +      /** Return the number of local and global clones of this user
 +       * @param user The user to get the clone counts for
 +       * @return The clone counts of this user. The returned reference is volatile - you
 +       * must assume that it becomes invalid as soon as you call any function other than
 +       * your own.
         */
 -      unsigned long GlobalCloneCount(User *user);
 +      const CloneCounts& GetCloneCounts(User* user) const;
  
 -      /** Return the number of local clones of this user
 -       * @param user The user to get a count for
 -       * @return The local clone count of this user
 +      /** Return a map containg IP addresses and their clone counts
 +       * @return The clone count map
         */
 -      unsigned long LocalCloneCount(User *user);
 +      const CloneMap& GetCloneMap() const { return clonemap; }
  
 -      /** Return a count of users, unknown and known connections
 -       * @return The number of users
 +      /** Return a count of all global users, unknown and known connections
 +       * @return The number of users on the network, including local unregistered users
         */
 -      unsigned int UserCount();
 +      unsigned int UserCount() const { return this->clientlist.size(); }
  
 -      /** Return a count of fully registered connections only
 -       * @return The number of registered users
 +      /** Return a count of fully registered connections on the network
 +       * @return The number of registered users on the network
         */
 -      unsigned int RegisteredUserCount();
 +      unsigned int RegisteredUserCount() { return this->clientlist.size() - this->UnregisteredUserCount(); }
  
 -      /** Return a count of opered (umode +o) users only
 -       * @return The number of opers
 +      /** Return a count of opered (umode +o) users on the network
 +       * @return The number of opers on the network
         */
 -      unsigned int OperCount();
 +      unsigned int OperCount() const { return this->all_opers.size(); }
  
 -      /** Return a count of unregistered (before NICK/USER) users only
 -       * @return The number of unregistered (unknown) connections
 +      /** Return a count of local unregistered (before NICK/USER) users
 +       * @return The number of local unregistered (unknown) connections
         */
 -      unsigned int UnregisteredUserCount();
 +      unsigned int UnregisteredUserCount() const { return this->unregistered_count; }
  
 -      /** Return a count of local users on this server only
 -       * @return The number of local users
 +      /** Return a count of local registered users
 +       * @return The number of registered local users
         */
 -      unsigned int LocalUserCount();
 -
 -
 +      unsigned int LocalUserCount() const { return (this->local_users.size() - this->UnregisteredUserCount()); }
  
 +      /** Get a hash map containing all users, keyed by their nickname
 +       * @return A hash map mapping nicknames to User pointers
 +       */
 +      user_hash& GetUsers() { return clientlist; }
  
 -      /** Number of users with a certain mode set on them
 +      /** Get a list containing all local users
 +       * @return A const list of local users
         */
 -      int ModeCount(const char mode);
 +      const LocalList& GetLocalUsers() const { return local_users; }
  
        /** Send a server notice to all local users
         * @param text The text format string to send
         * @param ... The format arguments
         */
        void ServerNoticeAll(const char* text, ...) CUSTOM_PRINTF(2, 3);
 -
 -      /** Send a server message (PRIVMSG) to all local users
 -       * @param text The text format string to send
 -       * @param ... The format arguments
 -       */
 -      void ServerPrivmsgAll(const char* text, ...) CUSTOM_PRINTF(2, 3);
  };
 -
 -#endif
diff --combined make/template/main.mk
index 9ac43e3bbc14a72a8deda0774cd61b40ff2f9c21,d5705d92876f063424c35c991a829a0c6b342d9c..9630905b19370289c8fbbc649984a88cb7022514
@@@ -1,5 -1,3 +1,5 @@@
 +%target BSD_MAKE BSDmakefile
 +%target GNU_MAKE GNUmakefile
  #
  # InspIRCd -- Internet Relay Chat Daemon
  #
  #
  
  
 -CC = @CC@
 -SYSTEM = @SYSTEM@
 -BUILDPATH = @BUILD_DIR@
 +CXX = @CXX@
 +COMPILER = @COMPILER_NAME@
 +SYSTEM = @SYSTEM_NAME@
 +BUILDPATH ?= $(PWD)/build
  SOCKETENGINE = @SOCKETENGINE@
 -CXXFLAGS = -pipe -fPIC -DPIC
 -LDLIBS = -pthread -lstdc++
 -LDFLAGS = 
 +CORECXXFLAGS = -fPIC -fvisibility=hidden -fvisibility-inlines-hidden -pipe -Iinclude -Wall -Wextra -Wfatal-errors -Wno-unused-parameter -Wshadow
 +LDLIBS = -lstdc++
  CORELDFLAGS = -rdynamic -L. $(LDFLAGS)
  PICLDFLAGS = -fPIC -shared -rdynamic $(LDFLAGS)
  BASE = "$(DESTDIR)@BASE_DIR@"
  CONPATH = "$(DESTDIR)@CONFIG_DIR@"
 +MANPATH = "$(DESTDIR)@MANUAL_DIR@"
  MODPATH = "$(DESTDIR)@MODULE_DIR@"
  DATPATH = "$(DESTDIR)@DATA_DIR@"
  BINPATH = "$(DESTDIR)@BINARY_DIR@"
  INSTALL = install
  INSTUID = @UID@
 -INSTMODE_DIR = 0755
 -INSTMODE_BIN = 0755
 -INSTMODE_LIB = 0644
 -
 -@IFEQ $(CC) icpc
 -  CXXFLAGS += -Wshadow
 -@ELSE
 -  CXXFLAGS += -pedantic -Woverloaded-virtual -Wshadow -Wformat=2 -Wmissing-format-attribute -Wall
 +INSTMODE_DIR = 0750
 +INSTMODE_BIN = 0750
 +INSTMODE_LIB = 0640
 +
 +@IFNEQ $(COMPILER) ICC
 +  CORECXXFLAGS += -Woverloaded-virtual -Wshadow
 +@IFNEQ $(SYSTEM) openbsd
 +    CORECXXFLAGS += -pedantic -Wformat=2 -Wmissing-format-attribute
 +@ENDIF
  @ENDIF
  
 +@IFNEQ $(SYSTEM) darwin
 +  LDLIBS += -pthread
 +@ENDIF
  
  @IFEQ $(SYSTEM) linux
    LDLIBS += -ldl -lrt
    LDLIBS += -lsocket -lnsl -lrt -lresolv
    INSTALL = ginstall
  @ENDIF
 -@IFEQ $(SYSTEM) sunos
 -  LDLIBS += -lsocket -lnsl -lrt -lresolv
 -      INSTALL = ginstall
 -@ENDIF
  @IFEQ $(SYSTEM) darwin
 -  CXXFLAGS += -DDARWIN -frtti
    LDLIBS += -ldl
    CORELDFLAGS = -dynamic -bind_at_load -L. $(LDFLAGS)
    PICLDFLAGS = -fPIC -shared -twolevel_namespace -undefined dynamic_lookup $(LDFLAGS)
  @ENDIF
 -@IFEQ $(SYSTEM) interix
 -  CXXFLAGS += -D_ALL_SOURCE -I/usr/local/include
 -@ENDIF
  
  @IFNDEF D
    D=0
  
  DBGOK=0
  @IFEQ $(D) 0
 -  CXXFLAGS += -O2
 -@IFEQ $(CC) g++
 -    CXXFLAGS += -g1
 +  CORECXXFLAGS += -fno-rtti -O2
 +@IFEQ $(COMPILER) GCC
 +    CORECXXFLAGS += -g1
  @ENDIF
    HEADER = std-header
    DBGOK=1
  @ENDIF
  @IFEQ $(D) 1
 -  CXXFLAGS += -O0 -g3 -Werror
 +  CORECXXFLAGS += -O0 -g3 -Werror -DINSPIRCD_ENABLE_RTTI
    HEADER = debug-header
    DBGOK=1
  @ENDIF
  @IFEQ $(D) 2
 -  CXXFLAGS += -O2 -g3
 +  CORECXXFLAGS += -fno-rtti -O2 -g3
    HEADER = debug-header
    DBGOK=1
  @ENDIF
  FOOTER = finishmessage
  
 -CXXFLAGS += -Iinclude
 +@TARGET GNU_MAKE MAKEFLAGS += --no-print-directory
  
 -@GNU_ONLY MAKEFLAGS += --no-print-directory
 -
 -@GNU_ONLY SOURCEPATH = $(shell /bin/pwd)
 -@BSD_ONLY SOURCEPATH != /bin/pwd
 +@TARGET GNU_MAKE SOURCEPATH = $(shell /bin/pwd)
 +@TARGET BSD_MAKE SOURCEPATH != /bin/pwd
  
  @IFDEF V
 -  RUNCC = $(CC)
 -  RUNLD = $(CC)
 +  RUNCC = $(CXX)
 +  RUNLD = $(CXX)
    VERBOSE = -v
  @ELSE
 -  @GNU_ONLY MAKEFLAGS += --silent
 -  @BSD_ONLY MAKE += -s
 -  RUNCC = perl $(SOURCEPATH)/make/run-cc.pl $(CC)
 -  RUNLD = perl $(SOURCEPATH)/make/run-cc.pl $(CC)
 +  @TARGET GNU_MAKE MAKEFLAGS += --silent
 +  @TARGET BSD_MAKE MAKE += -s
 +  RUNCC = perl $(SOURCEPATH)/make/run-cc.pl $(CXX)
 +  RUNLD = perl $(SOURCEPATH)/make/run-cc.pl $(CXX)
+   VERBOSE =
  @ENDIF
  
  @IFDEF PURE_STATIC
 -  CXXFLAGS += -DPURE_STATIC
 +  CORECXXFLAGS += -DPURE_STATIC
  @ENDIF
  
 -@DO_EXPORT RUNCC RUNLD CXXFLAGS LDLIBS PICLDFLAGS VERBOSE SOCKETENGINE CORELDFLAGS
 -@DO_EXPORT SOURCEPATH BUILDPATH PURE_STATIC SPLIT_CC
 +# Add the users CXXFLAGS to the base ones to allow them to override
 +# things like -Wfatal-errors if they wish to.
 +CORECXXFLAGS += $(CXXFLAGS)
 +
 +@DO_EXPORT RUNCC RUNLD CORECXXFLAGS LDLIBS PICLDFLAGS VERBOSE SOCKETENGINE CORELDFLAGS
 +@DO_EXPORT SOURCEPATH BUILDPATH PURE_STATIC
  
  # Default target
  TARGET = all
  @IFDEF M
      HEADER = mod-header
      FOOTER = mod-footer
 -    @BSD_ONLY TARGET = modules/${M:S/.so$//}.so
 -    @GNU_ONLY TARGET = modules/$(M:.so=).so
 +    @TARGET BSD_MAKE TARGET = modules/${M:S/.so$//}.so
 +    @TARGET GNU_MAKE TARGET = modules/$(M:.so=).so
  @ENDIF
  
  @IFDEF T
@@@ -225,25 -225,14 +226,25 @@@ install: targe
        @-$(INSTALL) -d -m $(INSTMODE_DIR) $(BINPATH)
        @-$(INSTALL) -d -m $(INSTMODE_DIR) $(CONPATH)/examples/aliases
        @-$(INSTALL) -d -m $(INSTMODE_DIR) $(CONPATH)/examples/modules
 +      @-$(INSTALL) -d -m $(INSTMODE_DIR) $(MANPATH)
        @-$(INSTALL) -d -m $(INSTMODE_DIR) $(MODPATH)
        [ $(BUILDPATH)/bin/ -ef $(BINPATH) ] || $(INSTALL) -m $(INSTMODE_BIN) $(BUILDPATH)/bin/inspircd $(BINPATH)
  @IFNDEF PURE_STATIC
        [ $(BUILDPATH)/modules/ -ef $(MODPATH) ] || $(INSTALL) -m $(INSTMODE_LIB) $(BUILDPATH)/modules/*.so $(MODPATH)
  @ENDIF
 -      -$(INSTALL) -m $(INSTMODE_BIN) @STARTSCRIPT@ $(BASE) 2>/dev/null
 -      -$(INSTALL) -m $(INSTMODE_LIB) tools/gdbargs $(BASE)/.gdbargs 2>/dev/null
 +      -$(INSTALL) -m $(INSTMODE_BIN) inspircd $(BASE) 2>/dev/null
 +      -$(INSTALL) -m $(INSTMODE_LIB) .gdbargs $(BASE)/.gdbargs 2>/dev/null
 +@IFEQ $(SYSTEM) darwin
 +      -$(INSTALL) -m $(INSTMODE_BIN) org.inspircd.plist $(BASE) 2>/dev/null
 +@ENDIF
 +@IFEQ $(SYSTEM) linux
 +      -$(INSTALL) -m $(INSTMODE_LIB) inspircd.service $(BASE) 2>/dev/null
 +@ENDIF
 +      -$(INSTALL) -m $(INSTMODE_LIB) inspircd.1 $(MANPATH) 2>/dev/null
 +      -$(INSTALL) -m $(INSTMODE_LIB) inspircd-genssl.1 $(MANPATH) 2>/dev/null
 +      -$(INSTALL) -m $(INSTMODE_BIN) tools/genssl $(BINPATH)/inspircd-genssl 2>/dev/null
        -$(INSTALL) -m $(INSTMODE_LIB) docs/conf/*.example $(CONPATH)/examples
 +      -$(INSTALL) -m $(INSTMODE_LIB) *.pem $(CONPATH) 2>/dev/null
        -$(INSTALL) -m $(INSTMODE_LIB) docs/conf/aliases/*.example $(CONPATH)/examples/aliases
        -$(INSTALL) -m $(INSTMODE_LIB) docs/conf/modules/*.example $(CONPATH)/examples/modules
        @echo ""
        @echo 'Remember to create your config file:' $(CONPATH)/inspircd.conf
        @echo 'Examples are available at:' $(CONPATH)/examples/
  
 -@GNU_ONLY RCS_FILES = $(wildcard .git/index src/version.sh)
 -@BSD_ONLY RCS_FILES = src/version.sh
 -GNUmakefile BSDmakefile: make/template/main.mk configure $(RCS_FILES)
 -      ./configure -update
 -@BSD_ONLY .MAKEFILEDEPS: BSDmakefile
 +GNUmakefile BSDmakefile: make/template/main.mk src/version.sh configure @CONFIGURE_CACHE_FILE@
 +      ./configure --update
 +@TARGET BSD_MAKE .MAKEFILEDEPS: BSDmakefile
  
  clean:
        @echo Cleaning...
  deinstall:
        -rm -f $(BINPATH)/inspircd
        -rm -rf $(CONPATH)/examples
 +      -rm -f $(MANPATH)/inspircd.1
 +      -rm -f $(MANPATH)/inspircd-genssl.1
        -rm -f $(MODPATH)/*.so
        -rm -f $(BASE)/.gdbargs
 +      -rm -f $(BASE)/inspircd.service
        -rm -f $(BASE)/org.inspircd.plist
  
 -squeakyclean: distclean
 -
  configureclean:
 -      rm -f .config.cache
        rm -f BSDmakefile
        rm -f GNUmakefile
 -      rm -f include/inspircd_config.h
 -      rm -f include/inspircd_version.h
 +      rm -f include/config.h
        rm -f inspircd
 +      rm -f inspircd.1
 +      rm -f inspircd-genssl.1
 +      -rm -f inspircd.service
        -rm -f org.inspircd.plist
 +      -rm -f @CONFIGURE_CACHE_FILE@
  
  distclean: clean configureclean
        -rm -rf $(SOURCEPATH)/run
@@@ -325,4 -313,4 +326,4 @@@ help
        @echo ' deinstall Removes the files created by "make install"'
        @echo
  
 -.PHONY: all target debug debug-header mod-header mod-footer std-header finishmessage install clean deinstall squeakyclean configureclean help
 +.PHONY: all target debug debug-header mod-header mod-footer std-header finishmessage install clean deinstall configureclean help
diff --combined modulemanager
index ee281d7fac0bd1a3c6efe07977910ceeb3827897,af5bf113caa6f75b1d16b43bfbb5c8b74d193bb0..e859f683be0e043f195937122e991fb54a69a897
@@@ -22,7 -22,7 +22,7 @@@
  use strict;
  use warnings FATAL => qw(all);
  
 -use make::configure;
 +use make::common;
  
  BEGIN {
        unless (module_installed("LWP::Simple")) {
        unless (module_installed("Crypt::SSLeay") || module_installed("IO::Socket::SSL")) {
                die "Your system is missing the Crypt::SSLeay or IO::Socket::SSL Perl modules!";
        }
 +
  }
  
 +use File::Basename;
  use LWP::Simple;
  
 -our @modlist;
 -
  my %installed;
  # $installed{name} = $version
  
@@@ -131,6 -131,8 +131,6 @@@ while (<SRC>) 
  }
  close SRC;
  
 -getmodules(1);
 -
  # determine core version
  `./src/version.sh` =~ /InspIRCd-([0-9.]+)/ or die "Cannot determine inspircd version";
  $installed{core} = $1;
@@@ -154,8 -156,9 +154,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;
@@@ -345,11 -348,16 +345,16 @@@ for my $mod (sort keys %todo) 
        }
        $mod_versions{$mod} = $ver;
  
-       my $stat = getstore($url, "src/modules/$mod.cpp");
-       if ($stat == 200) {
+       my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 });
+       my $response = $ua->get($url);
+       if ($response->is_success) {
+               open(MF, ">src/modules/$mod.cpp") or die "\nFilesystem not writable: $!";
+               print MF $response->decoded_content;
+               close(MF);
                print " - done\n";
        } else {
-               print " - HTTP $stat\n";
+               printf "\nHTTP %s: %s\n", $response->code, $response->message;
        }
  }
  
diff --combined src/command_parse.cpp
index c93dac65fd386c05bff39ae57307c87dde5117e0,76dfc06ce05eabf887794b76981ef8410c36f992..7998d9cc3a982f0f71732a2f7dfe4af2b831e9f3
  
  #include "inspircd.h"
  
 -int InspIRCd::PassCompare(Extensible* ex, const std::string &data, const std::string &input, const std::string &hashtype)
 +bool InspIRCd::PassCompare(Extensible* ex, const std::string& data, const std::string& input, const std::string& hashtype)
  {
        ModResult res;
        FIRST_MOD_RESULT(OnPassCompare, res, (ex, data, input, hashtype));
  
        /* Module matched */
        if (res == MOD_RES_ALLOW)
 -              return 0;
 +              return true;
  
        /* Module explicitly didnt match */
        if (res == MOD_RES_DENY)
 -              return 1;
 +              return false;
  
        /* We dont handle any hash types except for plaintext - Thanks tra26 */
        if (!hashtype.empty() && hashtype != "plaintext")
 -              /* See below. 1 because they dont match */
 -              return 1;
 +              return false;
  
 -      return (data != input); // this seems back to front, but returns 0 if they *match*, 1 else
 +      return TimingSafeCompare(data, input);
  }
  
 -/* 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,
 - * 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.
 - */
 -int CommandParser::LoopCall(User* user, Command* CommandObj, const std::vector<std::string>& parameters, unsigned int splithere, int extra, bool usemax)
 +bool CommandParser::LoopCall(User* user, Command* handler, const std::vector<std::string>& parameters, unsigned int splithere, int extra, bool usemax)
  {
        if (splithere >= parameters.size())
 -              return 0;
 -
 -      if (extra >= (signed)parameters.size())
 -              extra = -1;
 +              return false;
  
 -      /* First check if we have more than one item in the list, if we don't we return zero here and the handler
 +      /* First check if we have more than one item in the list, if we don't we return false here and the handler
         * which called us just carries on as it was.
         */
        if (parameters[splithere].find(',') == std::string::npos)
 -              return 0;
 +              return false;
  
        /** Some lame ircds will weed out dupes using some shitty O(n^2) algorithm.
         * By using std::set (thanks for the idea w00t) we can cut this down a ton.
         * ...VOOODOOOO!
 +       *
 +       * Only check for duplicates if there is one list (allow them in JOIN).
         */
 -      std::set<irc::string> dupes;
 +      insp::flat_set<irc::string> dupes;
 +      bool check_dupes = (extra < 0);
  
 -      /* Create two lists, one for channel names, one for keys
 +      /* Create two sepstreams, if we have only one list, then initialize the second sepstream with
 +       * an empty string. The second parameter of the constructor of the sepstream tells whether
 +       * or not to allow empty tokens.
 +       * We allow empty keys, so "JOIN #a,#b ,bkey" will be interpreted as "JOIN #a", "JOIN #b bkey"
         */
        irc::commasepstream items1(parameters[splithere]);
 -      irc::commasepstream items2(extra >= 0 ? parameters[extra] : "");
 -      std::string extrastuff;
 +      irc::commasepstream items2(extra >= 0 ? parameters[extra] : "", true);
        std::string item;
        unsigned int max = 0;
 +      LocalUser* localuser = IS_LOCAL(user);
  
 -      /* Attempt to iterate these lists and call the command objech
 -       * which called us, for every parameter pair until there are
 -       * no more left to parse.
 +      /* Attempt to iterate these lists and call the command handler
 +       * for every parameter or parameter pair until there are no more
 +       * left to parse.
         */
        while (items1.GetToken(item) && (!usemax || max++ < ServerInstance->Config->MaxTargets))
        {
 -              if (dupes.find(item.c_str()) == dupes.end())
 +              if ((!check_dupes) || (dupes.insert(item.c_str()).second))
                {
                        std::vector<std::string> new_parameters(parameters);
 -
 -                      if (!items2.GetToken(extrastuff))
 -                              extrastuff.clear();
 -
                        new_parameters[splithere] = item;
 -                      if (extra >= 0)
 -                              new_parameters[extra] = extrastuff;
 -
 -                      CommandObj->Handle(new_parameters, user);
  
 -                      dupes.insert(item.c_str());
 -              }
 -      }
 -      return 1;
 -}
 -
 -bool CommandParser::IsValidCommand(const std::string &commandname, unsigned int pcnt, User * user)
 -{
 -      Commandtable::iterator n = cmdlist.find(commandname);
 -
 -      if (n != cmdlist.end())
 -      {
 -              if ((pcnt >= n->second->min_params))
 -              {
 -                      if (IS_LOCAL(user) && n->second->flags_needed)
 +                      if (extra >= 0)
                        {
 -                              if (user->IsModeSet(n->second->flags_needed))
 -                              {
 -                                      return (user->HasPermission(commandname));
 -                              }
 +                              // If we have two lists then get the next item from the second list.
 +                              // In case it runs out of elements then 'item' will be an empty string.
 +                              items2.GetToken(item);
 +                              new_parameters[extra] = item;
                        }
 -                      else
 +
 +                      CmdResult result = handler->Handle(new_parameters, user);
 +                      if (localuser)
                        {
 -                              return true;
 +                              // Run the OnPostCommand hook with the last parameter (original line) being empty
 +                              // to indicate that the command had more targets in its original form.
 +                              item.clear();
 +                              FOREACH_MOD(OnPostCommand, (handler, new_parameters, localuser, result, item));
                        }
                }
        }
 -      return false;
 +
 +      return true;
  }
  
  Command* CommandParser::GetHandler(const std::string &commandname)
  {
 -      Commandtable::iterator n = cmdlist.find(commandname);
 +      CommandMap::iterator n = cmdlist.find(commandname);
        if (n != cmdlist.end())
                return n->second;
  
  
  // calls a handler function for a command
  
 -CmdResult CommandParser::CallHandler(const std::string &commandname, const std::vector<std::string>& parameters, User *user)
 +CmdResult CommandParser::CallHandler(const std::string& commandname, const std::vector<std::string>& parameters, User* user, Command** cmd)
  {
 -      Commandtable::iterator n = cmdlist.find(commandname);
 +      CommandMap::iterator n = cmdlist.find(commandname);
  
        if (n != cmdlist.end())
        {
  
                        if (bOkay)
                        {
 +                              if (cmd)
 +                                      *cmd = n->second;
                                return n->second->Handle(parameters,user);
                        }
                }
        return CMD_INVALID;
  }
  
 -bool CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
 +void CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
  {
        std::vector<std::string> command_p;
        irc::tokenstream tokens(cmd);
        if (command[0] == ':')
                tokens.GetToken(command);
  
 -      while (tokens.GetToken(token) && (command_p.size() <= MAXPARAMETERS))
 +      while (tokens.GetToken(token))
                command_p.push_back(token);
  
        std::transform(command.begin(), command.end(), command.begin(), ::toupper);
  
        /* find the command, check it exists */
 -      Commandtable::iterator cm = cmdlist.find(command);
 +      Command* handler = GetHandler(command);
  
+       // Penalty to give if the command fails before the handler is executed
+       unsigned int failpenalty = 0;
        /* Modify the user's penalty regardless of whether or not the command exists */
 -      bool do_more = true;
        if (!user->HasPrivPermission("users/flood/no-throttle"))
        {
                // If it *doesn't* exist, give it a slightly heftier penalty than normal to deter flooding us crap
-               user->CommandFloodPenalty += handler ? handler->Penalty * 1000 : 2000;
 -              unsigned int penalty = (cm != cmdlist.end() ? cm->second->Penalty * 1000 : 2000);
++              unsigned int penalty = (handler ? handler->Penalty * 1000 : 2000);
+               user->CommandFloodPenalty += penalty;
+               // Increase their penalty later if we fail and the command has 0 penalty by default (i.e. in Command::Penalty) to
+               // throttle sending ERR_* from the command parser. If the command does have a non-zero penalty then this is not
+               // needed because we've increased their penalty above.
+               if (penalty == 0)
+                       failpenalty = 1000;
        }
  
 -
 -      if (cm == cmdlist.end())
 +      if (!handler)
        {
                ModResult MOD_RESULT;
                FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, false, cmd));
                if (MOD_RESULT == MOD_RES_DENY)
 -                      return true;
 +                      return;
  
                /*
                 * This double lookup is in case a module (abbreviation) wishes to change a command.
                 * Thanks dz for making me actually understand why this is necessary!
                 * -- w00t
                 */
 -              cm = cmdlist.find(command);
 -              if (cm == cmdlist.end())
 +              handler = GetHandler(command);
 +              if (!handler)
                {
                        if (user->registered == REG_ALL)
 -                              user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :Unknown command",user->nick.c_str(),command.c_str());
 -                      ServerInstance->stats->statsUnknown++;
 -                      return true;
 +                              user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :Unknown command",command.c_str());
 +                      ServerInstance->stats.Unknown++;
 +                      return;
                }
        }
  
 -      if (cm->second->max_params && command_p.size() > cm->second->max_params)
 +      // If we were given more parameters than max_params then append the excess parameter(s)
 +      // to command_p[maxparams-1], i.e. to the last param that is still allowed
 +      if (handler->max_params && command_p.size() > handler->max_params)
        {
                /*
                 * command_p input (assuming max_params 1):
                 *      a
                 *      test
                 */
 -              std::string lparam;
  
 -              /*
 -               * The '-1' here is a clever trick, we'll go backwards throwing everything into a temporary param
 -               * and then just toss that into the array.
 -               * -- w00t
 -               */
 -              while (command_p.size() > (cm->second->max_params - 1))
 -              {
 -                      // BE CAREFUL: .end() returns past the end of the vector, hence decrement.
 -                      std::vector<std::string>::iterator it = command_p.end() - 1;
 +              // Iterator to the last parameter that will be kept
 +              const std::vector<std::string>::iterator lastkeep = command_p.begin() + (handler->max_params - 1);
 +              // Iterator to the first excess parameter
 +              const std::vector<std::string>::iterator firstexcess = lastkeep + 1;
  
 -                      lparam.insert(0, " " + *(it));
 -                      command_p.erase(it); // remove last element
 +              // Append all excess parameter(s) to the last parameter, seperated by spaces
 +              for (std::vector<std::string>::const_iterator i = firstexcess; i != command_p.end(); ++i)
 +              {
 +                      lastkeep->push_back(' ');
 +                      lastkeep->append(*i);
                }
  
 -              /* we now have (each iteration):
 -               *      ' test'
 -               *      ' a test'
 -               *      ' is a test' <-- final string
 -               * ...now remove the ' ' at the start...
 -               */
 -              lparam.erase(lparam.begin());
 -
 -              /* param is now 'is a test', which is exactly what we wanted! */
 -              command_p.push_back(lparam);
 +              // Erase the excess parameter(s)
 +              command_p.erase(firstexcess, command_p.end());
        }
  
        /*
        ModResult MOD_RESULT;
        FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, false, cmd));
        if (MOD_RESULT == MOD_RES_DENY)
 -              return true;
 +              return;
  
        /* activity resets the ping pending timer */
        user->nping = ServerInstance->Time() + user->MyClass->GetPingTime();
  
 -      if (cm->second->flags_needed)
 +      if (handler->flags_needed)
        {
 -              if (!user->IsModeSet(cm->second->flags_needed))
 +              if (!user->IsModeSet(handler->flags_needed))
                {
 -                      user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - You do not have the required operator privileges",user->nick.c_str());
 -                      return do_more;
+                       user->CommandFloodPenalty += failpenalty;
 +                      user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - You do not have the required operator privileges");
 +                      return;
                }
 +
                if (!user->HasPermission(command))
                {
 -                      user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - Oper type %s does not have access to command %s",
 -                              user->nick.c_str(), user->oper->NameStr(), command.c_str());
 -                      return do_more;
+                       user->CommandFloodPenalty += failpenalty;
 +                      user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - Oper type %s does not have access to command %s",
 +                              user->oper->name.c_str(), command.c_str());
 +                      return;
                }
        }
 -      if ((user->registered == REG_ALL) && (!IS_OPER(user)) && (cm->second->IsDisabled()))
 +
 +      if ((user->registered == REG_ALL) && (!user->IsOper()) && (handler->IsDisabled()))
        {
                /* command is disabled! */
+               user->CommandFloodPenalty += failpenalty;
                if (ServerInstance->Config->DisabledDontExist)
                {
 -                      user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :Unknown command",user->nick.c_str(),command.c_str());
 +                      user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :Unknown command", command.c_str());
                }
                else
                {
 -                      user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :This command has been disabled.",
 -                                                                              user->nick.c_str(), command.c_str());
 +                      user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :This command has been disabled.", command.c_str());
                }
  
                ServerInstance->SNO->WriteToSnoMask('a', "%s denied for %s (%s@%s)",
                                command.c_str(), user->nick.c_str(), user->ident.c_str(), user->host.c_str());
 -              return do_more;
 +              return;
        }
  
 -      if ((!command_p.empty()) && (command_p.back().empty()) && (!cm->second->allow_empty_last_param))
 +      if ((!command_p.empty()) && (command_p.back().empty()) && (!handler->allow_empty_last_param))
                command_p.pop_back();
  
 -      if (command_p.size() < cm->second->min_params)
 +      if (command_p.size() < handler->min_params)
        {
 -              user->WriteNumeric(ERR_NEEDMOREPARAMS, "%s %s :Not enough parameters.", user->nick.c_str(), command.c_str());
 -              if ((ServerInstance->Config->SyntaxHints) && (user->registered == REG_ALL) && (cm->second->syntax.length()))
 -                      user->WriteNumeric(RPL_SYNTAX, "%s :SYNTAX %s %s", user->nick.c_str(), cm->second->name.c_str(), cm->second->syntax.c_str());
 -              return do_more;
+               user->CommandFloodPenalty += failpenalty;
 +              user->WriteNumeric(ERR_NEEDMOREPARAMS, "%s :Not enough parameters.", command.c_str());
 +              if ((ServerInstance->Config->SyntaxHints) && (user->registered == REG_ALL) && (handler->syntax.length()))
 +                      user->WriteNumeric(RPL_SYNTAX, ":SYNTAX %s %s", handler->name.c_str(), handler->syntax.c_str());
 +              return;
        }
 -      if ((user->registered != REG_ALL) && (!cm->second->WorksBeforeReg()))
 +
 +      if ((user->registered != REG_ALL) && (!handler->WorksBeforeReg()))
        {
 -              user->WriteNumeric(ERR_NOTREGISTERED, "%s %s :You have not registered", user->nick.c_str(), command.c_str());
 -              return do_more;
+               user->CommandFloodPenalty += failpenalty;
 +              user->WriteNumeric(ERR_NOTREGISTERED, "%s :You have not registered",command.c_str());
        }
        else
        {
                /* passed all checks.. first, do the (ugly) stats counters. */
 -              cm->second->use_count++;
 -              cm->second->total_bytes += cmd.length();
 +              handler->use_count++;
  
                /* module calls too */
                FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, true, cmd));
                if (MOD_RESULT == MOD_RES_DENY)
 -                      return do_more;
 +                      return;
  
                /*
                 * WARNING: be careful, the user may be deleted soon
                 */
 -              CmdResult result = cm->second->Handle(command_p, user);
 +              CmdResult result = handler->Handle(command_p, user);
  
 -              FOREACH_MOD(I_OnPostCommand,OnPostCommand(command, command_p, user, result,cmd));
 -              return do_more;
 +              FOREACH_MOD(OnPostCommand, (handler, command_p, user, result, cmd));
        }
  }
  
  void CommandParser::RemoveCommand(Command* x)
  {
 -      Commandtable::iterator n = cmdlist.find(x->name);
 +      CommandMap::iterator n = cmdlist.find(x->name);
        if (n != cmdlist.end() && n->second == x)
                cmdlist.erase(n);
  }
  
 +CommandBase::CommandBase(Module* mod, const std::string& cmd, unsigned int minpara, unsigned int maxpara)
 +      : ServiceProvider(mod, cmd, SERVICE_COMMAND)
 +      , flags_needed(0)
 +      , min_params(minpara)
 +      , max_params(maxpara)
 +      , use_count(0)
 +      , disabled(false)
 +      , works_before_reg(false)
 +      , allow_empty_last_param(true)
 +      , Penalty(1)
 +{
 +}
 +
 +CommandBase::~CommandBase()
 +{
 +}
 +
 +void CommandBase::EncodeParameter(std::string& parameter, int index)
 +{
 +}
 +
 +RouteDescriptor CommandBase::GetRouting(User* user, const std::vector<std::string>& parameters)
 +{
 +      return ROUTE_LOCALONLY;
 +}
 +
 +Command::Command(Module* mod, const std::string& cmd, unsigned int minpara, unsigned int maxpara)
 +      : CommandBase(mod, cmd, minpara, maxpara)
 +      , force_manual_route(false)
 +{
 +}
 +
  Command::~Command()
  {
 -      ServerInstance->Parser->RemoveCommand(this);
 +      ServerInstance->Parser.RemoveCommand(this);
  }
  
 -bool CommandParser::ProcessBuffer(std::string &buffer,LocalUser *user)
 +void CommandParser::ProcessBuffer(std::string &buffer,LocalUser *user)
  {
 -      if (!user || buffer.empty())
 -              return true;
 +      if (buffer.empty())
 +              return;
  
 -      ServerInstance->Logs->Log("USERINPUT", RAWIO, "C[%s] I :%s %s",
 +      ServerInstance->Logs->Log("USERINPUT", LOG_RAWIO, "C[%s] I :%s %s",
                user->uuid.c_str(), user->nick.c_str(), buffer.c_str());
 -      return ProcessCommand(user,buffer);
 +      ProcessCommand(user,buffer);
  }
  
  bool CommandParser::AddCommand(Command *f)
@@@ -389,64 -406,88 +404,64 @@@ CommandParser::CommandParser(
  {
  }
  
 -int CommandParser::TranslateUIDs(const std::vector<TranslateType> to, const std::vector<std::string> &source, std::string &dest, bool prefix_final, Command* custom_translator)
 +std::string CommandParser::TranslateUIDs(const std::vector<TranslateType>& to, const std::vector<std::string>& source, bool prefix_final, CommandBase* custom_translator)
  {
        std::vector<TranslateType>::const_iterator types = to.begin();
 -      User* user = NULL;
 -      unsigned int i;
 -      int translations = 0;
 -      dest.clear();
 +      std::string dest;
  
 -      for(i=0; i < source.size(); i++)
 +      for (unsigned int i = 0; i < source.size(); i++)
        {
 -              TranslateType t;
 -              std::string item = source[i];
 -
 -              if (types == to.end())
 -                      t = TR_TEXT;
 -              else
 +              TranslateType t = TR_TEXT;
 +              // They might supply less translation types than parameters,
 +              // in that case pretend that all remaining types are TR_TEXT
 +              if (types != to.end())
                {
                        t = *types;
                        types++;
                }
  
 -              if (prefix_final && i == source.size() - 1)
 -                      dest.append(":");
 +              bool last = (i == (source.size() - 1));
 +              if (prefix_final && last)
 +                      dest.push_back(':');
  
 -              switch (t)
 -              {
 -                      case TR_NICK:
 -                              /* Translate single nickname */
 -                              user = ServerInstance->FindNick(item);
 -                              if (user)
 -                              {
 -                                      dest.append(user->uuid);
 -                                      translations++;
 -                              }
 -                              else
 -                                      dest.append(item);
 -                      break;
 -                      case TR_CUSTOM:
 -                              if (custom_translator)
 -                                      custom_translator->EncodeParameter(item, i);
 -                              dest.append(item);
 -                      break;
 -                      case TR_END:
 -                      case TR_TEXT:
 -                      default:
 -                              /* Do nothing */
 -                              dest.append(item);
 -                      break;
 -              }
 -              if (i != source.size() - 1)
 -                      dest.append(" ");
 +              TranslateSingleParam(t, source[i], dest, custom_translator, i);
 +
 +              if (!last)
 +                      dest.push_back(' ');
        }
  
 -      return translations;
 +      return dest;
  }
  
 -int CommandParser::TranslateUIDs(TranslateType to, const std::string &source, std::string &dest)
 +void CommandParser::TranslateSingleParam(TranslateType to, const std::string& item, std::string& dest, CommandBase* custom_translator, unsigned int paramnumber)
  {
 -      User* user = NULL;
 -      int translations = 0;
 -      dest.clear();
 -
        switch (to)
        {
                case TR_NICK:
 +              {
                        /* Translate single nickname */
 -                      user = ServerInstance->FindNick(source);
 +                      User* user = ServerInstance->FindNick(item);
                        if (user)
 +                              dest.append(user->uuid);
 +                      else
 +                              dest.append(item);
 +                      break;
 +              }
 +              case TR_CUSTOM:
 +              {
 +                      if (custom_translator)
                        {
 -                              dest = user->uuid;
 -                              translations++;
 +                              std::string translated = item;
 +                              custom_translator->EncodeParameter(translated, paramnumber);
 +                              dest.append(translated);
 +                              break;
                        }
 -                      else
 -                              dest = source;
 -              break;
 -              case TR_END:
 +                      // If no custom translator was given, fall through
 +              }
                case TR_TEXT:
                default:
                        /* Do nothing */
 -                      dest = source;
 +                      dest.append(item);
                break;
        }
 -
 -      return translations;
  }
diff --combined src/configreader.cpp
index d52f3de13fdc3f03690947555f0f4a6d92b6337e,bcee938d55208e1aefc3dcc5d0db93cf81820175..68495623c778b1a03ad9a02fad1f3e7ac3f87e01
  
  
  #include "inspircd.h"
 -#include <fstream>
  #include "xline.h"
 +#include "listmode.h"
  #include "exitcodes.h"
 -#include "commands/cmd_whowas.h"
  #include "configparser.h"
  #include <iostream>
 -#ifdef _WIN32
 -#include <Iphlpapi.h>
 -#pragma comment(lib, "Iphlpapi.lib")
 -#endif
 +
 +ServerLimits::ServerLimits(ConfigTag* tag)
 +      : NickMax(tag->getInt("maxnick", 32))
 +      , ChanMax(tag->getInt("maxchan", 64))
 +      , MaxModes(tag->getInt("maxmodes", 20))
 +      , IdentMax(tag->getInt("maxident", 11))
 +      , MaxQuit(tag->getInt("maxquit", 255))
 +      , MaxTopic(tag->getInt("maxtopic", 307))
 +      , MaxKick(tag->getInt("maxkick", 255))
 +      , MaxGecos(tag->getInt("maxgecos", 128))
 +      , MaxAway(tag->getInt("maxaway", 200))
 +      , MaxLine(tag->getInt("maxline", 512))
 +      , MaxHost(tag->getInt("maxhost", 64))
 +{
 +}
 +
 +static ConfigTag* CreateEmptyTag()
 +{
 +      std::vector<KeyVal>* items;
 +      return ConfigTag::create("empty", "<auto>", 0, items);
 +}
  
  ServerConfig::ServerConfig()
 -      : NoSnoticeStack(false)
 +      : EmptyTag(CreateEmptyTag())
 +      , Limits(EmptyTag)
++      , NoSnoticeStack(false)
  {
 -      WhoWasGroupSize = WhoWasMaxGroups = WhoWasMaxKeep = 0;
 -      RawLog = NoUserDns = HideBans = HideSplits = UndernetMsgPrefix = false;
 -      WildcardIPv6 = CycleHosts = InvBypassModes = true;
 +      RawLog = HideBans = HideSplits = UndernetMsgPrefix = false;
 +      WildcardIPv6 = InvBypassModes = true;
        dns_timeout = 5;
        MaxTargets = 20;
        NetBufferSize = 10240;
 -      SoftLimit = ServerInstance->SE->GetMaxFds();
        MaxConn = SOMAXCONN;
        MaxChans = 20;
        OperMaxChans = 30;
        c_ipv4_range = 32;
        c_ipv6_range = 128;
 -
 -      std::vector<KeyVal>* items;
 -      EmptyTag = ConfigTag::create("empty", "<auto>", 0, items);
  }
  
  ServerConfig::~ServerConfig()
        delete EmptyTag;
  }
  
 -void ServerConfig::Update005()
 -{
 -      std::stringstream out(data005);
 -      std::vector<std::string> data;
 -      std::string token;
 -      while (out >> token)
 -              data.push_back(token);
 -      sort(data.begin(), data.end());
 -
 -      std::string line5;
 -      isupport.clear();
 -      for(unsigned int i=0; i < data.size(); i++)
 -      {
 -              token = data[i];
 -              line5 = line5 + token + " ";
 -              if (i % 13 == 12)
 -              {
 -                      line5.append(":are supported by this server");
 -                      isupport.push_back(line5);
 -                      line5.clear();
 -              }
 -      }
 -      if (!line5.empty())
 -      {
 -              line5.append(":are supported by this server");
 -              isupport.push_back(line5);
 -      }
 -}
 -
 -void ServerConfig::Send005(User* user)
 -{
 -      for (std::vector<std::string>::iterator line = ServerInstance->Config->isupport.begin(); line != ServerInstance->Config->isupport.end(); line++)
 -              user->WriteNumeric(RPL_ISUPPORT, "%s %s", user->nick.c_str(), line->c_str());
 -}
 -
 -template<typename T, typename V>
 -static void range(T& value, V min, V max, V def, const char* msg)
 -{
 -      if (value >= (T)min && value <= (T)max)
 -              return;
 -      ServerInstance->Logs->Log("CONFIG", DEFAULT,
 -              "WARNING: %s value of %ld is not between %ld and %ld; set to %ld.",
 -              msg, (long)value, (long)min, (long)max, (long)def);
 -      value = def;
 -}
 -
 -
 -static void ValidIP(const std::string& ip, const std::string& key)
 -{
 -      irc::sockets::sockaddrs dummy;
 -      if (!irc::sockets::aptosa(ip, 0, dummy))
 -              throw CoreException("The value of "+key+" is not an IP address");
 -}
 -
  static void ValidHost(const std::string& p, const std::string& msg)
  {
        int num_dots = 0;
@@@ -97,20 -139,79 +98,20 @@@ bool ServerConfig::ApplyDisabledCommand
        std::string thiscmd;
  
        /* Enable everything first */
 -      for (Commandtable::iterator x = ServerInstance->Parser->cmdlist.begin(); x != ServerInstance->Parser->cmdlist.end(); x++)
 +      const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
 +      for (CommandParser::CommandMap::const_iterator x = commands.begin(); x != commands.end(); ++x)
                x->second->Disable(false);
  
        /* Now disable all the ones which the user wants disabled */
        while (dcmds >> thiscmd)
        {
 -              Commandtable::iterator cm = ServerInstance->Parser->cmdlist.find(thiscmd);
 -              if (cm != ServerInstance->Parser->cmdlist.end())
 -              {
 -                      cm->second->Disable(true);
 -              }
 +              Command* handler = ServerInstance->Parser.GetHandler(thiscmd);
 +              if (handler)
 +                      handler->Disable(true);
        }
        return true;
  }
  
 -static void FindDNS(std::string& server)
 -{
 -      if (!server.empty())
 -              return;
 -#ifdef _WIN32
 -      // attempt to look up their nameserver from the system
 -      ServerInstance->Logs->Log("CONFIG",DEFAULT,"WARNING: <dns:server> not defined, attempting to find a working server in the system settings...");
 -
 -      PFIXED_INFO pFixedInfo;
 -      DWORD dwBufferSize = sizeof(FIXED_INFO);
 -      pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, sizeof(FIXED_INFO));
 -
 -      if(pFixedInfo)
 -      {
 -              if (GetNetworkParams(pFixedInfo, &dwBufferSize) == ERROR_BUFFER_OVERFLOW) {
 -                      HeapFree(GetProcessHeap(), 0, pFixedInfo);
 -                      pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, dwBufferSize);
 -              }
 -
 -              if(pFixedInfo) {
 -                      if (GetNetworkParams(pFixedInfo, &dwBufferSize) == NO_ERROR)
 -                              server = pFixedInfo->DnsServerList.IpAddress.String;
 -
 -                      HeapFree(GetProcessHeap(), 0, pFixedInfo);
 -              }
 -
 -              if(!server.empty())
 -              {
 -                      ServerInstance->Logs->Log("CONFIG",DEFAULT,"<dns:server> set to '%s' as first active resolver in the system settings.", server.c_str());
 -                      return;
 -              }
 -      }
 -
 -      ServerInstance->Logs->Log("CONFIG",DEFAULT,"No viable nameserver found! Defaulting to nameserver '127.0.0.1'!");
 -#else
 -      // attempt to look up their nameserver from /etc/resolv.conf
 -      ServerInstance->Logs->Log("CONFIG",DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
 -
 -      std::ifstream resolv("/etc/resolv.conf");
 -
 -      while (resolv >> server)
 -      {
 -              if (server == "nameserver")
 -              {
 -                      resolv >> server;
 -                      if (server.find_first_not_of("0123456789.") == std::string::npos)
 -                      {
 -                              ServerInstance->Logs->Log("CONFIG",DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",server.c_str());
 -                              return;
 -                      }
 -              }
 -      }
 -
 -      ServerInstance->Logs->Log("CONFIG",DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
 -#endif
 -      server = "127.0.0.1";
 -}
 -
  static void ReadXLine(ServerConfig* conf, const std::string& tag, const std::string& key, XLineFactory* make)
  {
        ConfigTagList tags = conf->ConfTags(tag);
@@@ -149,11 -250,13 +150,11 @@@ void ServerConfig::CrossCheckOperClassT
                std::string name = tag->getString("name");
                if (name.empty())
                        throw CoreException("<type:name> is missing from tag at " + tag->getTagLocation());
 -              if (!ServerInstance->IsNick(name.c_str(), Limits.NickMax))
 -                      throw CoreException("<type:name> is invalid (value '" + name + "')");
 -              if (oper_blocks.find(" " + name) != oper_blocks.end())
 +              if (OperTypes.find(name) != OperTypes.end())
                        throw CoreException("Duplicate type block with name " + name + " at " + tag->getTagLocation());
  
                OperInfo* ifo = new OperInfo;
 -              oper_blocks[" " + name] = ifo;
 +              OperTypes[name] = ifo;
                ifo->name = name;
                ifo->type_block = tag;
  
                        throw CoreException("<oper:name> missing from tag at " + tag->getTagLocation());
  
                std::string type = tag->getString("type");
 -              OperIndex::iterator tblk = oper_blocks.find(" " + type);
 -              if (tblk == oper_blocks.end())
 +              OperIndex::iterator tblk = OperTypes.find(type);
 +              if (tblk == OperTypes.end())
                        throw CoreException("Oper block " + name + " has missing type " + type);
                if (oper_blocks.find(name) != oper_blocks.end())
                        throw CoreException("Duplicate oper block with name " + name + " at " + tag->getTagLocation());
@@@ -202,7 -305,7 +203,7 @@@ void ServerConfig::CrossCheckConnectBlo
                for(ClassVector::iterator i = current->Classes.begin(); i != current->Classes.end(); ++i)
                {
                        ConnectClass* c = *i;
 -                      if (c->name.substr(0, 8) != "unnamed-")
 +                      if (c->name.compare(0, 8, "unnamed-", 8))
                        {
                                oldBlocksByMask["n" + c->name] = c;
                        }
                        me->maxchans = tag->getInt("maxchans", me->maxchans);
                        me->maxconnwarn = tag->getBool("maxconnwarn", me->maxconnwarn);
                        me->limit = tag->getInt("limit", me->limit);
 +                      me->resolvehostnames = tag->getBool("resolvehostnames", me->resolvehostnames);
  
                        ClassMap::iterator oldMask = oldBlocksByMask.find(typeMask);
                        if (oldMask != oldBlocksByMask.end())
  
  /** Represents a deprecated configuration tag.
   */
 -struct Deprecated
 +struct DeprecatedConfig
  {
 -      /** Tag name
 -       */
 -      const char* tag;
 -      /** Tag value
 -       */
 -      const char* value;
 -      /** Reason for deprecation
 -       */
 -      const char* reason;
 +      /** Tag name. */
 +      std::string tag;
 +
 +      /** Attribute key. */
 +      std::string key;
 +
 +      /** Attribute value. */
 +      std::string value;
 +
 +      /** Reason for deprecation. */
 +      std::string reason;
  };
  
 -static const Deprecated ChangedConfig[] = {
 -      {"options", "hidelinks",                "has been moved to <security:hidelinks> as of 1.2a3"},
 -      {"options", "hidewhois",                "has been moved to <security:hidewhois> as of 1.2a3"},
 -      {"options", "userstats",                "has been moved to <security:userstats> as of 1.2a3"},
 -      {"options", "customversion",    "has been moved to <security:customversion> as of 1.2a3"},
 -      {"options", "hidesplits",               "has been moved to <security:hidesplits> as of 1.2a3"},
 -      {"options", "hidebans",         "has been moved to <security:hidebans> as of 1.2a3"},
 -      {"options", "hidekills",                "has been moved to <security:hidekills> as of 1.2a3"},
 -      {"options", "operspywhois",             "has been moved to <security:operspywhois> as of 1.2a3"},
 -      {"options", "announceinvites",  "has been moved to <security:announceinvites> as of 1.2a3"},
 -      {"options", "hidemodes",                "has been moved to <security:hidemodes> as of 1.2a3"},
 -      {"options", "maxtargets",               "has been moved to <security:maxtargets> as of 1.2a3"},
 -      {"options",     "nouserdns",            "has been moved to <performance:nouserdns> as of 1.2a3"},
 -      {"options",     "maxwho",               "has been moved to <performance:maxwho> as of 1.2a3"},
 -      {"options",     "softlimit",            "has been moved to <performance:softlimit> as of 1.2a3"},
 -      {"options", "somaxconn",                "has been moved to <performance:somaxconn> as of 1.2a3"},
 -      {"options", "netbuffersize",    "has been moved to <performance:netbuffersize> as of 1.2a3"},
 -      {"options", "maxwho",           "has been moved to <performance:maxwho> as of 1.2a3"},
 -      {"options",     "loglevel",             "1.2+ does not use the loglevel value. Please define <log> tags instead."},
 -      {"die",     "value",            "you need to reread your config"},
 -      {"bind",    "transport",                "has been moved to <bind:ssl> as of 2.0a1"},
 -      {"link",    "transport",                "has been moved to <link:ssl> as of 2.0a1"},
 -      {"link",        "autoconnect",          "2.0+ does not use the autoconnect value. Please define <autoconnect> tags instead."},
 +static const DeprecatedConfig ChangedConfig[] = {
 +      { "bind",        "transport",   "",                 "has been moved to <bind:ssl> as of 2.0" },
 +      { "die",         "value",       "",                 "you need to reread your config" },
 +      { "gnutls",      "starttls",    "",                 "has been replaced with m_starttls as of 2.2" },
 +      { "link",        "autoconnect", "",                 "2.0+ does not use this attribute - define <autoconnect> tags instead" },
 +      { "link",        "transport",   "",                 "has been moved to <link:ssl> as of 2.0" },
 +      { "module",      "name",        "m_chanprotect.so", "has been replaced with m_customprefix as of 2.2" },
 +      { "module",      "name",        "m_halfop.so",      "has been replaced with m_customprefix as of 2.2" },
 +      { "options",     "cyclehosts",  "",                 "has been replaced with m_hostcycle as of 2.2" },
 +      { "performance", "nouserdns",   "",                 "has been moved to <connect:resolvehostnames> as of 2.2" }
  };
  
  void ServerConfig::Fill()
        ConfigTag* security = ConfValue("security");
        if (sid.empty())
        {
 -              ServerName = ConfValue("server")->getString("name");
 -              sid = ConfValue("server")->getString("id");
 +              ServerName = ConfValue("server")->getString("name", "irc.example.com");
                ValidHost(ServerName, "<server:name>");
 -              if (!sid.empty() && !ServerInstance->IsSID(sid))
 +
 +              sid = ConfValue("server")->getString("id");
 +              if (!sid.empty() && !InspIRCd::IsSID(sid))
                        throw CoreException(sid + " is not a valid server ID. A server ID must be 3 characters long, with the first character a digit and the next two characters a digit or letter.");
        }
        else
        {
                if (ServerName != ConfValue("server")->getString("name"))
 -                      throw CoreException("You must restart to change the server name or SID");
 +                      throw CoreException("You must restart to change the server name");
 +
                std::string nsid = ConfValue("server")->getString("id");
                if (!nsid.empty() && nsid != sid)
 -                      throw CoreException("You must restart to change the server name or SID");
 -      }
 -      diepass = ConfValue("power")->getString("diepass");
 -      restartpass = ConfValue("power")->getString("restartpass");
 -      powerhash = ConfValue("power")->getString("hash");
 -      PrefixQuit = options->getString("prefixquit");
 -      SuffixQuit = options->getString("suffixquit");
 -      FixedQuit = options->getString("fixedquit");
 -      PrefixPart = options->getString("prefixpart");
 -      SuffixPart = options->getString("suffixpart");
 -      FixedPart = options->getString("fixedpart");
 -      SoftLimit = ConfValue("performance")->getInt("softlimit", ServerInstance->SE->GetMaxFds());
 +                      throw CoreException("You must restart to change the server id");
 +      }
 +      SoftLimit = ConfValue("performance")->getInt("softlimit", (SocketEngine::GetMaxFds() > 0 ? SocketEngine::GetMaxFds() : LONG_MAX), 10);
 +      CCOnConnect = ConfValue("performance")->getBool("clonesonconnect", true);
        MaxConn = ConfValue("performance")->getInt("somaxconn", SOMAXCONN);
 -      MoronBanner = options->getString("moronbanner", "You're banned!");
 +      XLineMessage = options->getString("xlinemessage", options->getString("moronbanner", "You're banned!"));
        ServerDesc = ConfValue("server")->getString("description", "Configure Me");
        Network = ConfValue("server")->getString("network", "Network");
 -      AdminName = ConfValue("admin")->getString("name", "");
 -      AdminEmail = ConfValue("admin")->getString("email", "null@example.com");
 -      AdminNick = ConfValue("admin")->getString("nick", "admin");
 -      ModPath = ConfValue("path")->getString("moduledir", MOD_PATH);
 -      NetBufferSize = ConfValue("performance")->getInt("netbuffersize", 10240);
 +      NetBufferSize = ConfValue("performance")->getInt("netbuffersize", 10240, 1024, 65534);
        dns_timeout = ConfValue("dns")->getInt("timeout", 5);
        DisabledCommands = ConfValue("disabled")->getString("commands", "");
        DisabledDontExist = ConfValue("disabled")->getBool("fakenonexistant");
        UserStats = security->getString("userstats");
 -      CustomVersion = security->getString("customversion", Network + " IRCd");
 +      CustomVersion = security->getString("customversion");
        HideSplits = security->getBool("hidesplits");
        HideBans = security->getBool("hidebans");
        HideWhoisServer = security->getString("hidewhois");
        HideKillsServer = security->getString("hidekills");
        RestrictBannedUsers = security->getBool("restrictbannedusers", true);
        GenericOper = security->getBool("genericoper");
 -      NoUserDns = ConfValue("performance")->getBool("nouserdns");
        SyntaxHints = options->getBool("syntaxhints");
 -      CycleHosts = options->getBool("cyclehosts");
        CycleHostsFromUser = options->getBool("cyclehostsfromuser");
        UndernetMsgPrefix = options->getBool("ircumsgprefix");
        FullHostInTopic = options->getBool("hostintopic");
 -      MaxTargets = security->getInt("maxtargets", 20);
 -      DefaultModes = options->getString("defaultmodes", "nt");
 +      MaxTargets = security->getInt("maxtargets", 20, 1, 31);
 +      DefaultModes = options->getString("defaultmodes", "not");
        PID = ConfValue("pid")->getString("file");
 -      WhoWasGroupSize = ConfValue("whowas")->getInt("groupsize");
 -      WhoWasMaxGroups = ConfValue("whowas")->getInt("maxgroups");
 -      WhoWasMaxKeep = ServerInstance->Duration(ConfValue("whowas")->getString("maxkeep"));
        MaxChans = ConfValue("channels")->getInt("users", 20);
 -      OperMaxChans = ConfValue("channels")->getInt("opers", 60);
 +      OperMaxChans = ConfValue("channels")->getInt("opers");
        c_ipv4_range = ConfValue("cidr")->getInt("ipv4clone", 32);
        c_ipv6_range = ConfValue("cidr")->getInt("ipv6clone", 128);
 -      Limits.NickMax = ConfValue("limits")->getInt("maxnick", 32);
 -      Limits.ChanMax = ConfValue("limits")->getInt("maxchan", 64);
 -      Limits.MaxModes = ConfValue("limits")->getInt("maxmodes", 20);
 -      Limits.IdentMax = ConfValue("limits")->getInt("maxident", 11);
 -      Limits.MaxQuit = ConfValue("limits")->getInt("maxquit", 255);
 -      Limits.MaxTopic = ConfValue("limits")->getInt("maxtopic", 307);
 -      Limits.MaxKick = ConfValue("limits")->getInt("maxkick", 255);
 -      Limits.MaxGecos = ConfValue("limits")->getInt("maxgecos", 128);
 -      Limits.MaxAway = ConfValue("limits")->getInt("maxaway", 200);
 +      Limits = ServerLimits(ConfValue("limits"));
 +      Paths.Config = ConfValue("path")->getString("configdir", INSPIRCD_CONFIG_PATH);
 +      Paths.Data = ConfValue("path")->getString("datadir", INSPIRCD_DATA_PATH);
 +      Paths.Log = ConfValue("path")->getString("logdir", INSPIRCD_LOG_PATH);
 +      Paths.Module = ConfValue("path")->getString("moduledir", INSPIRCD_MODULE_PATH);
        InvBypassModes = options->getBool("invitebypassmodes", true);
        NoSnoticeStack = options->getBool("nosnoticestack", false);
 -      WelcomeNotice = options->getBool("welcomenotice", true);
 -
 -      range(SoftLimit, 10, ServerInstance->SE->GetMaxFds(), ServerInstance->SE->GetMaxFds(), "<performance:softlimit>");
 -      if (ConfValue("performance")->getBool("limitsomaxconn", true))
 -              range(MaxConn, 0, SOMAXCONN, SOMAXCONN, "<performance:somaxconn>");
 -      range(MaxTargets, 1, 31, 20, "<security:maxtargets>");
 -      range(NetBufferSize, 1024, 65534, 10240, "<performance:netbuffersize>");
 -      range(WhoWasGroupSize, 0, 10000, 10, "<whowas:groupsize>");
 -      range(WhoWasMaxGroups, 0, 1000000, 10240, "<whowas:maxgroups>");
 -      range(WhoWasMaxKeep, 3600, INT_MAX, 3600, "<whowas:maxkeep>");
  
 -      ValidIP(DNSServer, "<dns:server>");
 +      if (Network.find(' ') != std::string::npos)
 +              throw CoreException(Network + " is not a valid network name. A network name must not contain spaces.");
  
        std::string defbind = options->getString("defaultbind");
        if (assign(defbind) == "ipv4")
                if (socktest < 0)
                        WildcardIPv6 = false;
                else
 -                      ServerInstance->SE->Close(socktest);
 -      }
 -      ConfigTagList tags = ConfTags("uline");
 -      for(ConfigIter i = tags.first; i != tags.second; ++i)
 -      {
 -              ConfigTag* tag = i->second;
 -              std::string server;
 -              if (!tag->readString("server", server))
 -                      throw CoreException("<uline> tag missing server at " + tag->getTagLocation());
 -              ulines[assign(server)] = tag->getBool("silent");
 -      }
 -
 -      tags = ConfTags("banlist");
 -      for(ConfigIter i = tags.first; i != tags.second; ++i)
 -      {
 -              ConfigTag* tag = i->second;
 -              std::string chan;
 -              if (!tag->readString("chan", chan))
 -                      throw CoreException("<banlist> tag missing chan at " + tag->getTagLocation());
 -              maxbans[chan] = tag->getInt("limit");
 +                      SocketEngine::Close(socktest);
        }
  
        ReadXLine(this, "badip", "ipmask", ServerInstance->XLines->GetFactory("Z"));
                DisabledCModes[*p - 'A'] = 1;
        }
  
 -      memset(HideModeLists, 0, sizeof(HideModeLists));
 -      modes = ConfValue("security")->getString("hidemodes");
 -      for (std::string::const_iterator p = modes.begin(); p != modes.end(); ++p)
 -              HideModeLists[(unsigned char) *p] = true;
 -
        std::string v = security->getString("announceinvites");
  
        if (v == "ops")
@@@ -509,7 -674,12 +510,7 @@@ void ServerConfig::Read(
        catch (CoreException& err)
        {
                valid = false;
 -              errstr << err.GetReason();
 -      }
 -      if (valid)
 -      {
 -              DNSServer = ConfValue("dns")->getString("server");
 -              FindDNS(DNSServer);
 +              errstr << err.GetReason() << std::endl;
        }
  }
  
@@@ -529,26 -699,16 +530,26 @@@ void ServerConfig::Apply(ServerConfig* 
        /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */
        try
        {
 -              for (int Index = 0; Index * sizeof(Deprecated) < sizeof(ChangedConfig); Index++)
 +              for (int index = 0; index * sizeof(DeprecatedConfig) < sizeof(ChangedConfig); index++)
                {
 -                      std::string dummy;
 -                      ConfigTagList tags = ConfTags(ChangedConfig[Index].tag);
 +                      std::string value;
 +                      ConfigTagList tags = ConfTags(ChangedConfig[index].tag);
                        for(ConfigIter i = tags.first; i != tags.second; ++i)
                        {
 -                              if (i->second->readString(ChangedConfig[Index].value, dummy, true))
 -                                      errstr << "Your configuration contains a deprecated value: <"
 -                                              << ChangedConfig[Index].tag << ":" << ChangedConfig[Index].value << "> - " << ChangedConfig[Index].reason
 -                                              << " (at " << i->second->getTagLocation() << ")\n";
 +                              if (i->second->readString(ChangedConfig[index].key, value, true)
 +                                      && (ChangedConfig[index].value.empty() || value == ChangedConfig[index].value))
 +                              {
 +                                      errstr << "Your configuration contains a deprecated value: <"  << ChangedConfig[index].tag;
 +                                      if (ChangedConfig[index].value.empty())
 +                                      {
 +                                              errstr << ':' << ChangedConfig[index].key;
 +                                      }
 +                                      else
 +                                      {
 +                                              errstr << ' ' << ChangedConfig[index].key << "=\"" << ChangedConfig[index].value << "\"";
 +                                      }
 +                                      errstr << "> - " << ChangedConfig[index].reason << " (at " << i->second->getTagLocation() << ")" << std::endl;
 +                              }
                        }
                }
  
        if (valid)
                ServerInstance->WritePID(this->PID);
  
-       if (old)
 +      ConfigTagList binds = ConfTags("bind");
 +      if (binds.first == binds.second)
 +               errstr << "Possible configuration error: you have not defined any <bind> blocks." << std::endl
 +                       << "You will need to do this if you want clients to be able to connect!" << std::endl;
 +
+       if (old && valid)
        {
                // On first run, ports are bound later on
                FailedPortList pl;
                ServerInstance->BindPorts(pl);
                if (pl.size())
                {
 -                      errstr << "Not all your client ports could be bound.\nThe following port(s) failed to bind:\n";
 +                      errstr << "Not all your client ports could be bound." << std::endl
 +                              << "The following port(s) failed to bind:" << std::endl;
  
                        int j = 1;
                        for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
                        {
 -                              char buf[MAXBUF];
 -                              snprintf(buf, MAXBUF, "%d.   Address: %s   Reason: %s\n", j, i->first.empty() ? "<all>" : i->first.c_str(), i->second.c_str());
 -                              errstr << buf;
 +                              errstr << j << ".\tAddress: " << (i->first.empty() ? "<all>" : i->first.c_str()) << "\tReason: "
 +                                      << i->second << std::endl;
                        }
                }
        }
  
        if (!valid)
        {
 -              ServerInstance->Logs->Log("CONFIG",DEFAULT, "There were errors in your configuration file:");
 +              ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "There were errors in your configuration file:");
                Classes.clear();
        }
  
                ConfigFileCache::iterator file = this->Files.find(tag->getString("motd", "motd"));
                if (file != this->Files.end())
                      InspIRCd::ProcessColors(file->second);
 -
 -              file = this->Files.find(tag->getString("rules", "rules"));
 -              if (file != this->Files.end())
 -                    InspIRCd::ProcessColors(file->second);
        }
  
        /* No old configuration -> initial boot, nothing more to do here */
  
  void ServerConfig::ApplyModules(User* user)
  {
 -      Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
 -      if (whowas)
 -              WhowasRequest(NULL, whowas, WhowasRequest::WHOWAS_PRUNE).Send();
 -
 -      const std::vector<std::string> v = ServerInstance->Modules->GetAllModuleNames(0);
        std::vector<std::string> added_modules;
 -      std::set<std::string> removed_modules(v.begin(), v.end());
 +      ModuleManager::ModuleMap removed_modules = ServerInstance->Modules->GetModules();
  
        ConfigTagList tags = ConfTags("module");
        for(ConfigIter i = tags.first; i != tags.second; ++i)
                }
        }
  
 -      if (ConfValue("options")->getBool("allowhalfop") && removed_modules.erase("m_halfop.so") == 0)
 -              added_modules.push_back("m_halfop.so");
 -
 -      for (std::set<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
 +      for (ModuleManager::ModuleMap::iterator i = removed_modules.begin(); i != removed_modules.end(); ++i)
        {
 -              // Don't remove cmd_*.so, just remove m_*.so
 -              if (removing->c_str()[0] == 'c')
 +              const std::string& modname = i->first;
 +              // Don't remove core_*.so, just remove m_*.so
 +              if (modname.c_str()[0] == 'c')
                        continue;
 -              Module* m = ServerInstance->Modules->Find(*removing);
 -              if (m && ServerInstance->Modules->Unload(m))
 +              if (ServerInstance->Modules->Unload(i->second))
                {
 -                      ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH UNLOADED MODULE: %s",removing->c_str());
 +                      ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH UNLOADED MODULE: %s", modname.c_str());
  
                        if (user)
 -                              user->WriteNumeric(RPL_UNLOADEDMODULE, "%s %s :Module %s successfully unloaded.",user->nick.c_str(), removing->c_str(), removing->c_str());
 +                              user->WriteNumeric(RPL_UNLOADEDMODULE, "%s :Module %s successfully unloaded.", modname.c_str(), modname.c_str());
                        else
 -                              ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully unloaded.", removing->c_str());
 +                              ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully unloaded.", modname.c_str());
                }
                else
                {
                        if (user)
 -                              user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s %s :Failed to unload module %s: %s",user->nick.c_str(), removing->c_str(), removing->c_str(), ServerInstance->Modules->LastError().c_str());
 +                              user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :Failed to unload module %s: %s", modname.c_str(), modname.c_str(), ServerInstance->Modules->LastError().c_str());
                        else
 -                               ServerInstance->SNO->WriteGlobalSno('a', "Failed to unload module %s: %s", removing->c_str(), ServerInstance->Modules->LastError().c_str());
 +                               ServerInstance->SNO->WriteGlobalSno('a', "Failed to unload module %s: %s", modname.c_str(), ServerInstance->Modules->LastError().c_str());
                }
        }
  
                {
                        ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH LOADED MODULE: %s",adding->c_str());
                        if (user)
 -                              user->WriteNumeric(RPL_LOADEDMODULE, "%s %s :Module %s successfully loaded.",user->nick.c_str(), adding->c_str(), adding->c_str());
 +                              user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module %s successfully loaded.", adding->c_str(), adding->c_str());
                        else
                                ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully loaded.", adding->c_str());
                }
                else
                {
                        if (user)
 -                              user->WriteNumeric(ERR_CANTLOADMODULE, "%s %s :Failed to load module %s: %s",user->nick.c_str(), adding->c_str(), adding->c_str(), ServerInstance->Modules->LastError().c_str());
 +                              user->WriteNumeric(ERR_CANTLOADMODULE, "%s :Failed to load module %s: %s", adding->c_str(), adding->c_str(), ServerInstance->Modules->LastError().c_str());
                        else
                                ServerInstance->SNO->WriteGlobalSno('a', "Failed to load module %s: %s", adding->c_str(), ServerInstance->Modules->LastError().c_str());
                }
        }
  }
  
 -bool ServerConfig::StartsWithWindowsDriveLetter(const std::string &path)
 -{
 -      return (path.length() > 2 && isalpha(path[0]) && path[1] == ':');
 -}
 -
  ConfigTag* ServerConfig::ConfValue(const std::string &tag)
  {
        ConfigTagList found = config_data.equal_range(tag);
        ConfigTag* rv = found.first->second;
        found.first++;
        if (found.first != found.second)
 -              ServerInstance->Logs->Log("CONFIG",DEFAULT, "Multiple <" + tag + "> tags found; only first will be used "
 +              ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Multiple <" + tag + "> tags found; only first will be used "
                        "(first at " + rv->getTagLocation() + "; second at " + found.first->second->getTagLocation() + ")");
        return rv;
  }
@@@ -739,28 -911,35 +740,28 @@@ ConfigTagList ServerConfig::ConfTags(co
        return config_data.equal_range(tag);
  }
  
 -bool ServerConfig::FileExists(const char* file)
 +std::string ServerConfig::Escape(const std::string& str, bool xml)
  {
 -      struct stat sb;
 -      if (stat(file, &sb) == -1)
 -              return false;
 -
 -      if ((sb.st_mode & S_IFDIR) > 0)
 -              return false;
 -
 -      FILE *input = fopen(file, "r");
 -      if (input == NULL)
 -              return false;
 -      else
 +      std::string escaped;
 +      for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
        {
 -              fclose(input);
 -              return true;
 +              switch (*it)
 +              {
 +                      case '"':
 +                              escaped += xml ? "&quot;" : "\"";
 +                              break;
 +                      case '&':
 +                              escaped += xml ? "&amp;" : "&";
 +                              break;
 +                      case '\\':
 +                              escaped += xml ? "\\" : "\\\\";
 +                              break;
 +                      default:
 +                              escaped += *it;
 +                              break;
 +              }
        }
 -}
 -
 -const char* ServerConfig::CleanFilename(const char* name)
 -{
 -      const char* p = name + strlen(name);
 -      while ((p != name) && (*p != '/') && (*p != '\\')) p--;
 -      return (p != name ? ++p : p);
 -}
 -
 -const std::string& ServerConfig::GetSID()
 -{
 -      return sid;
 +      return escaped;
  }
  
  void ConfigReaderThread::Run()
  void ConfigReaderThread::Finish()
  {
        ServerConfig* old = ServerInstance->Config;
 -      ServerInstance->Logs->Log("CONFIG",DEBUG,"Switching to new configuration...");
 +      ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "Switching to new configuration...");
        ServerInstance->Config = this->Config;
        Config->Apply(old, TheUserUID);
  
                 * XXX: The order of these is IMPORTANT, do not reorder them without testing
                 * thoroughly!!!
                 */
 -              ServerInstance->Users->RehashCloneCounts();
++              ServerInstance->Users.RehashCloneCounts();
                ServerInstance->XLines->CheckELines();
                ServerInstance->XLines->ApplyLines();
 -              ServerInstance->Res->Rehash();
 -              ServerInstance->ResetMaxBans();
 +              ChanModeReference ban(NULL, "ban");
 +              static_cast<ListModeBase*>(*ban)->DoRehash();
                Config->ApplyDisabledCommands(Config->DisabledCommands);
                User* user = ServerInstance->FindNick(TheUserUID);
 -              FOREACH_MOD(I_OnRehash, OnRehash(user));
 -              ServerInstance->BuildISupport();
 +
 +              ConfigStatus status(user);
 +              const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules();
 +              for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i)
 +                      i->second->ReadConfig(status);
 +
 +              // The description of this server may have changed - update it for WHOIS etc.
 +              ServerInstance->FakeClient->server->description = Config->ServerDesc;
 +
 +              ServerInstance->ISupport.Build();
  
                ServerInstance->Logs->CloseLogs();
                ServerInstance->Logs->OpenFileLogs();
index de8dedd4ac41e260eb0f773d25ce261020275137,0000000000000000000000000000000000000000..9aca8b3384aacf8a83a5d73fdfc5864fe7f7a292
mode 100644,000000..100644
--- /dev/null
@@@ -1,832 -1,0 +1,840 @@@
 +/*
 + * InspIRCd -- Internet Relay Chat Daemon
 + *
 + *   Copyright (C) 2013 Adam <Adam@anope.org>
 + *   Copyright (C) 2003-2013 Anope Team <team@anope.org>
 + *
 + * 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 "modules/dns.h"
 +#include <iostream>
 +#include <fstream>
 +
 +#ifdef _WIN32
 +#include <Iphlpapi.h>
 +#pragma comment(lib, "Iphlpapi.lib")
 +#endif
 +
 +using namespace DNS;
 +
 +/** A full packet sent or recieved to/from the nameserver
 + */
 +class Packet : public Query
 +{
++      static bool IsValidName(const std::string& name)
++      {
++              return (name.find_first_not_of("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-") == std::string::npos);
++      }
++
 +      void PackName(unsigned char* output, unsigned short output_size, unsigned short& pos, const std::string& name)
 +      {
 +              if (pos + name.length() + 2 > output_size)
 +                      throw Exception("Unable to pack name");
 +
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Packing name " + name);
 +
 +              irc::sepstream sep(name, '.');
 +              std::string token;
 +
 +              while (sep.GetToken(token))
 +              {
 +                      output[pos++] = token.length();
 +                      memcpy(&output[pos], token.data(), token.length());
 +                      pos += token.length();
 +              }
 +
 +              output[pos++] = 0;
 +      }
 +
 +      std::string UnpackName(const unsigned char* input, unsigned short input_size, unsigned short& pos)
 +      {
 +              std::string name;
 +              unsigned short pos_ptr = pos, lowest_ptr = input_size;
 +              bool compressed = false;
 +
 +              if (pos_ptr >= input_size)
 +                      throw Exception("Unable to unpack name - no input");
 +
 +              while (input[pos_ptr] > 0)
 +              {
 +                      unsigned short offset = input[pos_ptr];
 +
 +                      if (offset & POINTER)
 +                      {
 +                              if ((offset & POINTER) != POINTER)
 +                                      throw Exception("Unable to unpack name - bogus compression header");
 +                              if (pos_ptr + 1 >= input_size)
 +                                      throw Exception("Unable to unpack name - bogus compression header");
 +
 +                              /* Place pos at the second byte of the first (farthest) compression pointer */
 +                              if (compressed == false)
 +                              {
 +                                      ++pos;
 +                                      compressed = true;
 +                              }
 +
 +                              pos_ptr = (offset & LABEL) << 8 | input[pos_ptr + 1];
 +
 +                              /* Pointers can only go back */
 +                              if (pos_ptr >= lowest_ptr)
 +                                      throw Exception("Unable to unpack name - bogus compression pointer");
 +                              lowest_ptr = pos_ptr;
 +                      }
 +                      else
 +                      {
 +                              if (pos_ptr + offset + 1 >= input_size)
 +                                      throw Exception("Unable to unpack name - offset too large");
 +                              if (!name.empty())
 +                                      name += ".";
 +                              for (unsigned i = 1; i <= offset; ++i)
 +                                      name += input[pos_ptr + i];
 +
 +                              pos_ptr += offset + 1;
 +                              if (compressed == false)
 +                                      /* Move up pos */
 +                                      pos = pos_ptr;
 +                      }
 +              }
 +
 +              /* +1 pos either to one byte after the compression pointer or one byte after the ending \0 */
 +              ++pos;
 +
 +              if (name.empty())
 +                      throw Exception("Unable to unpack name - no name");
 +
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Unpack name " + name);
 +
 +              return name;
 +      }
 +
 +      Question UnpackQuestion(const unsigned char* input, unsigned short input_size, unsigned short& pos)
 +      {
 +              Question question;
 +
 +              question.name = this->UnpackName(input, input_size, pos);
 +
 +              if (pos + 4 > input_size)
 +                      throw Exception("Unable to unpack question");
 +
 +              question.type = static_cast<QueryType>(input[pos] << 8 | input[pos + 1]);
 +              pos += 2;
 +
 +              question.qclass = input[pos] << 8 | input[pos + 1];
 +              pos += 2;
 +
 +              return question;
 +      }
 +
 +      ResourceRecord UnpackResourceRecord(const unsigned char* input, unsigned short input_size, unsigned short& pos)
 +      {
 +              ResourceRecord record = static_cast<ResourceRecord>(this->UnpackQuestion(input, input_size, pos));
 +
 +              if (pos + 6 > input_size)
 +                      throw Exception("Unable to unpack resource record");
 +
 +              record.ttl = (input[pos] << 24) | (input[pos + 1] << 16) | (input[pos + 2] << 8) | input[pos + 3];
 +              pos += 4;
 +
 +              //record.rdlength = input[pos] << 8 | input[pos + 1];
 +              pos += 2;
 +
 +              switch (record.type)
 +              {
 +                      case QUERY_A:
 +                      {
 +                              if (pos + 4 > input_size)
 +                                      throw Exception("Unable to unpack resource record");
 +
 +                              irc::sockets::sockaddrs addrs;
 +                              memset(&addrs, 0, sizeof(addrs));
 +
 +                              addrs.in4.sin_family = AF_INET;
 +                              addrs.in4.sin_addr.s_addr = input[pos] | (input[pos + 1] << 8) | (input[pos + 2] << 16)  | (input[pos + 3] << 24);
 +                              pos += 4;
 +
 +                              record.rdata = addrs.addr();
 +                              break;
 +                      }
 +                      case QUERY_AAAA:
 +                      {
 +                              if (pos + 16 > input_size)
 +                                      throw Exception("Unable to unpack resource record");
 +
 +                              irc::sockets::sockaddrs addrs;
 +                              memset(&addrs, 0, sizeof(addrs));
 +
 +                              addrs.in6.sin6_family = AF_INET6;
 +                              for (int j = 0; j < 16; ++j)
 +                                      addrs.in6.sin6_addr.s6_addr[j] = input[pos + j];
 +                              pos += 16;
 +
 +                              record.rdata = addrs.addr();
 +
 +                              break;
 +                      }
 +                      case QUERY_CNAME:
 +                      case QUERY_PTR:
 +                      {
 +                              record.rdata = this->UnpackName(input, input_size, pos);
++                              if (!IsValidName(record.rdata))
++                                      throw Exception("Invalid name"); // XXX: Causes the request to time out
++
 +                              break;
 +                      }
 +                      default:
 +                              break;
 +              }
 +
 +              if (!record.name.empty() && !record.rdata.empty())
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, record.name + " -> " + record.rdata);
 +
 +              return record;
 +      }
 +
 + public:
 +      static const int POINTER = 0xC0;
 +      static const int LABEL = 0x3F;
 +      static const int HEADER_LENGTH = 12;
 +
 +      /* ID for this packet */
 +      unsigned short id;
 +      /* Flags on the packet */
 +      unsigned short flags;
 +
 +      Packet() : id(0), flags(0)
 +      {
 +      }
 +
 +      void Fill(const unsigned char* input, const unsigned short len)
 +      {
 +              if (len < HEADER_LENGTH)
 +                      throw Exception("Unable to fill packet");
 +
 +              unsigned short packet_pos = 0;
 +
 +              this->id = (input[packet_pos] << 8) | input[packet_pos + 1];
 +              packet_pos += 2;
 +
 +              if (this->id >= MAX_REQUEST_ID)
 +                      throw Exception("Query ID too large?");
 +
 +              this->flags = (input[packet_pos] << 8) | input[packet_pos + 1];
 +              packet_pos += 2;
 +
 +              unsigned short qdcount = (input[packet_pos] << 8) | input[packet_pos + 1];
 +              packet_pos += 2;
 +
 +              unsigned short ancount = (input[packet_pos] << 8) | input[packet_pos + 1];
 +              packet_pos += 2;
 +
 +              unsigned short nscount = (input[packet_pos] << 8) | input[packet_pos + 1];
 +              packet_pos += 2;
 +
 +              unsigned short arcount = (input[packet_pos] << 8) | input[packet_pos + 1];
 +              packet_pos += 2;
 +
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "qdcount: " + ConvToStr(qdcount) + " ancount: " + ConvToStr(ancount) + " nscount: " + ConvToStr(nscount) + " arcount: " + ConvToStr(arcount));
 +
 +              for (unsigned i = 0; i < qdcount; ++i)
 +                      this->questions.push_back(this->UnpackQuestion(input, len, packet_pos));
 +
 +              for (unsigned i = 0; i < ancount; ++i)
 +                      this->answers.push_back(this->UnpackResourceRecord(input, len, packet_pos));
 +      }
 +
 +      unsigned short Pack(unsigned char* output, unsigned short output_size)
 +      {
 +              if (output_size < HEADER_LENGTH)
 +                      throw Exception("Unable to pack packet");
 +
 +              unsigned short pos = 0;
 +
 +              output[pos++] = this->id >> 8;
 +              output[pos++] = this->id & 0xFF;
 +              output[pos++] = this->flags >> 8;
 +              output[pos++] = this->flags & 0xFF;
 +              output[pos++] = this->questions.size() >> 8;
 +              output[pos++] = this->questions.size() & 0xFF;
 +              output[pos++] = this->answers.size() >> 8;
 +              output[pos++] = this->answers.size() & 0xFF;
 +              output[pos++] = 0;
 +              output[pos++] = 0;
 +              output[pos++] = 0;
 +              output[pos++] = 0;
 +
 +              for (unsigned i = 0; i < this->questions.size(); ++i)
 +              {
 +                      Question& q = this->questions[i];
 +
 +                      if (q.type == QUERY_PTR)
 +                      {
 +                              irc::sockets::sockaddrs ip;
 +                              irc::sockets::aptosa(q.name, 0, ip);
 +
 +                              if (q.name.find(':') != std::string::npos)
 +                              {
 +                                      static const char* const hex = "0123456789abcdef";
 +                                      char reverse_ip[128];
 +                                      unsigned reverse_ip_count = 0;
 +                                      for (int j = 15; j >= 0; --j)
 +                                      {
 +                                              reverse_ip[reverse_ip_count++] = hex[ip.in6.sin6_addr.s6_addr[j] & 0xF];
 +                                              reverse_ip[reverse_ip_count++] = '.';
 +                                              reverse_ip[reverse_ip_count++] = hex[ip.in6.sin6_addr.s6_addr[j] >> 4];
 +                                              reverse_ip[reverse_ip_count++] = '.';
 +                                      }
 +                                      reverse_ip[reverse_ip_count++] = 0;
 +
 +                                      q.name = reverse_ip;
 +                                      q.name += "ip6.arpa";
 +                              }
 +                              else
 +                              {
 +                                      unsigned long forward = ip.in4.sin_addr.s_addr;
 +                                      ip.in4.sin_addr.s_addr = forward << 24 | (forward & 0xFF00) << 8 | (forward & 0xFF0000) >> 8 | forward >> 24;
 +
 +                                      q.name = ip.addr() + ".in-addr.arpa";
 +                              }
 +                      }
 +
 +                      this->PackName(output, output_size, pos, q.name);
 +
 +                      if (pos + 4 >= output_size)
 +                              throw Exception("Unable to pack packet");
 +
 +                      short s = htons(q.type);
 +                      memcpy(&output[pos], &s, 2);
 +                      pos += 2;
 +
 +                      s = htons(q.qclass);
 +                      memcpy(&output[pos], &s, 2);
 +                      pos += 2;
 +              }
 +
 +              for (unsigned int i = 0; i < answers.size(); i++)
 +              {
 +                      ResourceRecord& rr = answers[i];
 +
 +                      this->PackName(output, output_size, pos, rr.name);
 +
 +                      if (pos + 8 >= output_size)
 +                              throw Exception("Unable to pack packet");
 +
 +                      short s = htons(rr.type);
 +                      memcpy(&output[pos], &s, 2);
 +                      pos += 2;
 +
 +                      s = htons(rr.qclass);
 +                      memcpy(&output[pos], &s, 2);
 +                      pos += 2;
 +
 +                      long l = htonl(rr.ttl);
 +                      memcpy(&output[pos], &l, 4);
 +                      pos += 4;
 +
 +                      switch (rr.type)
 +                      {
 +                              case QUERY_A:
 +                              {
 +                                      if (pos + 6 > output_size)
 +                                              throw Exception("Unable to pack packet");
 +
 +                                      irc::sockets::sockaddrs a;
 +                                      irc::sockets::aptosa(rr.rdata, 0, a);
 +
 +                                      s = htons(4);
 +                                      memcpy(&output[pos], &s, 2);
 +                                      pos += 2;
 +
 +                                      memcpy(&output[pos], &a.in4.sin_addr, 4);
 +                                      pos += 4;
 +                                      break;
 +                              }
 +                              case QUERY_AAAA:
 +                              {
 +                                      if (pos + 18 > output_size)
 +                                              throw Exception("Unable to pack packet");
 +
 +                                      irc::sockets::sockaddrs a;
 +                                      irc::sockets::aptosa(rr.rdata, 0, a);
 +
 +                                      s = htons(16);
 +                                      memcpy(&output[pos], &s, 2);
 +                                      pos += 2;
 +
 +                                      memcpy(&output[pos], &a.in6.sin6_addr, 16);
 +                                      pos += 16;
 +                                      break;
 +                              }
 +                              case QUERY_CNAME:
 +                              case QUERY_PTR:
 +                              {
 +                                      if (pos + 2 >= output_size)
 +                                              throw Exception("Unable to pack packet");
 +
 +                                      unsigned short packet_pos_save = pos;
 +                                      pos += 2;
 +
 +                                      this->PackName(output, output_size, pos, rr.rdata);
 +
 +                                      s = htons(pos - packet_pos_save - 2);
 +                                      memcpy(&output[packet_pos_save], &s, 2);
 +                                      break;
 +                              }
 +                              default:
 +                                      break;
 +                      }
 +              }
 +
 +              return pos;
 +      }
 +};
 +
 +class MyManager : public Manager, public Timer, public EventHandler
 +{
 +      typedef TR1NS::unordered_map<Question, Query, Question::hash> cache_map;
 +      cache_map cache;
 +
 +      irc::sockets::sockaddrs myserver;
 +
 +      static bool IsExpired(const Query& record, time_t now = ServerInstance->Time())
 +      {
 +              const ResourceRecord& req = record.answers[0];
 +              return (req.created + static_cast<time_t>(req.ttl) < now);
 +      }
 +
 +      /** Check the DNS cache to see if request can be handled by a cached result
 +       * @return true if a cached result was found.
 +       */
 +      bool CheckCache(DNS::Request* req, const DNS::Question& question)
 +      {
 +              ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "cache: Checking cache for " + question.name);
 +
 +              cache_map::iterator it = this->cache.find(question);
 +              if (it == this->cache.end())
 +                      return false;
 +
 +              Query& record = it->second;
 +              if (IsExpired(record))
 +              {
 +                      this->cache.erase(it);
 +                      return false;
 +              }
 +
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "cache: Using cached result for " + question.name);
 +              record.cached = true;
 +              req->OnLookupComplete(&record);
 +              return true;
 +      }
 +
 +      /** Add a record to the dns cache
 +       * @param r The record
 +       */
 +      void AddCache(Query& r)
 +      {
 +              const ResourceRecord& rr = r.answers[0];
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "cache: added cache for " + rr.name + " -> " + rr.rdata + " ttl: " + ConvToStr(rr.ttl));
 +              this->cache[r.questions[0]] = r;
 +      }
 +
 + public:
 +      DNS::Request* requests[MAX_REQUEST_ID];
 +
 +      MyManager(Module* c) : Manager(c), Timer(3600, true)
 +      {
 +              for (int i = 0; i < MAX_REQUEST_ID; ++i)
 +                      requests[i] = NULL;
 +              ServerInstance->Timers.AddTimer(this);
 +      }
 +
 +      ~MyManager()
 +      {
 +              for (int i = 0; i < MAX_REQUEST_ID; ++i)
 +              {
 +                      DNS::Request* request = requests[i];
 +                      if (!request)
 +                              continue;
 +
 +                      Query rr(*request);
 +                      rr.error = ERROR_UNKNOWN;
 +                      request->OnError(&rr);
 +
 +                      delete request;
 +              }
 +      }
 +
 +      void Process(DNS::Request* req)
 +      {
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Processing request to lookup " + req->name + " of type " + ConvToStr(req->type) + " to " + this->myserver.addr());
 +
 +              /* Create an id */
 +              unsigned int tries = 0;
 +              do
 +              {
 +                      req->id = ServerInstance->GenRandomInt(DNS::MAX_REQUEST_ID);
 +
 +                      if (++tries == DNS::MAX_REQUEST_ID*5)
 +                      {
 +                              // If we couldn't find an empty slot this many times, do a sequential scan as a last
 +                              // resort. If an empty slot is found that way, go on, otherwise throw an exception
 +                              req->id = 0;
 +                              for (int i = 1; i < DNS::MAX_REQUEST_ID; i++)
 +                              {
 +                                      if (!this->requests[i])
 +                                      {
 +                                              req->id = i;
 +                                              break;
 +                                      }
 +                              }
 +
 +                              if (req->id == 0)
 +                                      throw Exception("DNS: All ids are in use");
 +
 +                              break;
 +                      }
 +              }
 +              while (!req->id || this->requests[req->id]);
 +
 +              this->requests[req->id] = req;
 +
 +              Packet p;
 +              p.flags = QUERYFLAGS_RD;
 +              p.id = req->id;
 +              p.questions.push_back(*req);
 +
 +              unsigned char buffer[524];
 +              unsigned short len = p.Pack(buffer, sizeof(buffer));
 +
 +              /* Note that calling Pack() above can actually change the contents of p.questions[0].name, if the query is a PTR,
 +               * to contain the value that would be in the DNS cache, which is why this is here.
 +               */
 +              if (req->use_cache && this->CheckCache(req, p.questions[0]))
 +              {
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Using cached result");
 +                      delete req;
 +                      return;
 +              }
 +
 +              if (SocketEngine::SendTo(this, buffer, len, 0, &this->myserver.sa, this->myserver.sa_size()) != len)
 +                      throw Exception("DNS: Unable to send query");
 +      }
 +
 +      void RemoveRequest(DNS::Request* req)
 +      {
 +              this->requests[req->id] = NULL;
 +      }
 +
 +      std::string GetErrorStr(Error e)
 +      {
 +              switch (e)
 +              {
 +                      case ERROR_UNLOADED:
 +                              return "Module is unloading";
 +                      case ERROR_TIMEDOUT:
 +                              return "Request timed out";
 +                      case ERROR_NOT_AN_ANSWER:
 +                      case ERROR_NONSTANDARD_QUERY:
 +                      case ERROR_FORMAT_ERROR:
 +                              return "Malformed answer";
 +                      case ERROR_SERVER_FAILURE:
 +                      case ERROR_NOT_IMPLEMENTED:
 +                      case ERROR_REFUSED:
 +                      case ERROR_INVALIDTYPE:
 +                              return "Nameserver failure";
 +                      case ERROR_DOMAIN_NOT_FOUND:
 +                      case ERROR_NO_RECORDS:
 +                              return "Domain not found";
 +                      case ERROR_NONE:
 +                      case ERROR_UNKNOWN:
 +                      default:
 +                              return "Unknown error";
 +              }
 +      }
 +
 +      void OnEventHandlerError(int errcode) CXX11_OVERRIDE
 +      {
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "UDP socket got an error event");
 +      }
 +
 +      void OnEventHandlerRead() CXX11_OVERRIDE
 +      {
 +              unsigned char buffer[524];
 +              irc::sockets::sockaddrs from;
 +              socklen_t x = sizeof(from);
 +
 +              int length = SocketEngine::RecvFrom(this, buffer, sizeof(buffer), 0, &from.sa, &x);
 +
 +              if (length < Packet::HEADER_LENGTH)
 +                      return;
 +
 +              Packet recv_packet;
 +
 +              try
 +              {
 +                      recv_packet.Fill(buffer, length);
 +              }
 +              catch (Exception& ex)
 +              {
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, ex.GetReason());
 +                      return;
 +              }
 +
 +              if (myserver != from)
 +              {
 +                      std::string server1 = from.str();
 +                      std::string server2 = myserver.str();
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Got a result from the wrong server! Bad NAT or DNS forging attempt? '%s' != '%s'",
 +                              server1.c_str(), server2.c_str());
 +                      return;
 +              }
 +
 +              DNS::Request* request = this->requests[recv_packet.id];
 +              if (request == NULL)
 +              {
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Received an answer for something we didn't request");
 +                      return;
 +              }
 +
 +              if (recv_packet.flags & QUERYFLAGS_OPCODE)
 +              {
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Received a nonstandard query");
 +                      ServerInstance->stats.DnsBad++;
 +                      recv_packet.error = ERROR_NONSTANDARD_QUERY;
 +                      request->OnError(&recv_packet);
 +              }
 +              else if (recv_packet.flags & QUERYFLAGS_RCODE)
 +              {
 +                      Error error = ERROR_UNKNOWN;
 +
 +                      switch (recv_packet.flags & QUERYFLAGS_RCODE)
 +                      {
 +                              case 1:
 +                                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "format error");
 +                                      error = ERROR_FORMAT_ERROR;
 +                                      break;
 +                              case 2:
 +                                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "server error");
 +                                      error = ERROR_SERVER_FAILURE;
 +                                      break;
 +                              case 3:
 +                                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "domain not found");
 +                                      error = ERROR_DOMAIN_NOT_FOUND;
 +                                      break;
 +                              case 4:
 +                                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "not implemented");
 +                                      error = ERROR_NOT_IMPLEMENTED;
 +                                      break;
 +                              case 5:
 +                                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "refused");
 +                                      error = ERROR_REFUSED;
 +                                      break;
 +                              default:
 +                                      break;
 +                      }
 +
 +                      ServerInstance->stats.DnsBad++;
 +                      recv_packet.error = error;
 +                      request->OnError(&recv_packet);
 +              }
 +              else if (recv_packet.questions.empty() || recv_packet.answers.empty())
 +              {
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "No resource records returned");
 +                      ServerInstance->stats.DnsBad++;
 +                      recv_packet.error = ERROR_NO_RECORDS;
 +                      request->OnError(&recv_packet);
 +              }
 +              else
 +              {
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Lookup complete for " + request->name);
 +                      ServerInstance->stats.DnsGood++;
 +                      request->OnLookupComplete(&recv_packet);
 +                      this->AddCache(recv_packet);
 +              }
 +
 +              ServerInstance->stats.Dns++;
 +
 +              /* Request's destructor removes it from the request map */
 +              delete request;
 +      }
 +
 +      bool Tick(time_t now)
 +      {
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "cache: purging DNS cache");
 +
 +              for (cache_map::iterator it = this->cache.begin(); it != this->cache.end(); )
 +              {
 +                      const Query& query = it->second;
 +                      if (IsExpired(query, now))
 +                              this->cache.erase(it++);
 +                      else
 +                              ++it;
 +              }
 +              return true;
 +      }
 +
 +      void Rehash(const std::string& dnsserver)
 +      {
 +              if (this->GetFd() > -1)
 +              {
 +                      SocketEngine::Shutdown(this, 2);
 +                      SocketEngine::Close(this);
 +
 +                      /* Remove expired entries from the cache */
 +                      this->Tick(ServerInstance->Time());
 +              }
 +
 +              irc::sockets::aptosa(dnsserver, DNS::PORT, myserver);
 +
 +              /* Initialize mastersocket */
 +              int s = socket(myserver.sa.sa_family, SOCK_DGRAM, 0);
 +              this->SetFd(s);
 +
 +              /* Have we got a socket? */
 +              if (this->GetFd() != -1)
 +              {
 +                      SocketEngine::SetReuse(s);
 +                      SocketEngine::NonBlocking(s);
 +
 +                      irc::sockets::sockaddrs bindto;
 +                      memset(&bindto, 0, sizeof(bindto));
 +                      bindto.sa.sa_family = myserver.sa.sa_family;
 +
 +                      if (SocketEngine::Bind(this->GetFd(), bindto) < 0)
 +                      {
 +                              /* Failed to bind */
 +                              ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Error binding dns socket - hostnames will NOT resolve");
 +                              SocketEngine::Close(this->GetFd());
 +                              this->SetFd(-1);
 +                      }
 +                      else if (!SocketEngine::AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE))
 +                      {
 +                              ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Internal error starting DNS - hostnames will NOT resolve.");
 +                              SocketEngine::Close(this->GetFd());
 +                              this->SetFd(-1);
 +                      }
 +              }
 +              else
 +              {
 +                      ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Error creating DNS socket - hostnames will NOT resolve");
 +              }
 +      }
 +};
 +
 +class ModuleDNS : public Module
 +{
 +      MyManager manager;
 +      std::string DNSServer;
 +
 +      void FindDNSServer()
 +      {
 +#ifdef _WIN32
 +              // attempt to look up their nameserver from the system
 +              ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: <dns:server> not defined, attempting to find a working server in the system settings...");
 +
 +              PFIXED_INFO pFixedInfo;
 +              DWORD dwBufferSize = sizeof(FIXED_INFO);
 +              pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, sizeof(FIXED_INFO));
 +
 +              if (pFixedInfo)
 +              {
 +                      if (GetNetworkParams(pFixedInfo, &dwBufferSize) == ERROR_BUFFER_OVERFLOW)
 +                      {
 +                              HeapFree(GetProcessHeap(), 0, pFixedInfo);
 +                              pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, dwBufferSize);
 +                      }
 +
 +                      if (pFixedInfo)
 +                      {
 +                              if (GetNetworkParams(pFixedInfo, &dwBufferSize) == NO_ERROR)
 +                                      DNSServer = pFixedInfo->DnsServerList.IpAddress.String;
 +
 +                              HeapFree(GetProcessHeap(), 0, pFixedInfo);
 +                      }
 +
 +                      if (!DNSServer.empty())
 +                      {
 +                              ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "<dns:server> set to '%s' as first active resolver in the system settings.", DNSServer.c_str());
 +                              return;
 +                      }
 +              }
 +
 +              ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "No viable nameserver found! Defaulting to nameserver '127.0.0.1'!");
 +#else
 +              // attempt to look up their nameserver from /etc/resolv.conf
 +              ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
 +
 +              std::ifstream resolv("/etc/resolv.conf");
 +
 +              while (resolv >> DNSServer)
 +              {
 +                      if (DNSServer == "nameserver")
 +                      {
 +                              resolv >> DNSServer;
 +                              if (DNSServer.find_first_not_of("0123456789.") == std::string::npos)
 +                              {
 +                                      ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",DNSServer.c_str());
 +                                      return;
 +                              }
 +                      }
 +              }
 +
 +              ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
 +#endif
 +              DNSServer = "127.0.0.1";
 +      }
 +
 + public:
 +      ModuleDNS() : manager(this)
 +      {
 +      }
 +
 +      void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
 +      {
 +              std::string oldserver = DNSServer;
 +              DNSServer = ServerInstance->Config->ConfValue("dns")->getString("server");
 +              if (DNSServer.empty())
 +                      FindDNSServer();
 +
 +              if (oldserver != DNSServer)
 +                      this->manager.Rehash(DNSServer);
 +      }
 +
 +      void OnUnloadModule(Module* mod)
 +      {
 +              for (int i = 0; i < MAX_REQUEST_ID; ++i)
 +              {
 +                      DNS::Request* req = this->manager.requests[i];
 +                      if (!req)
 +                              continue;
 +
 +                      if (req->creator == mod)
 +                      {
 +                              Query rr(*req);
 +                              rr.error = ERROR_UNLOADED;
 +                              req->OnError(&rr);
 +
 +                              delete req;
 +                      }
 +              }
 +      }
 +
 +      Version GetVersion()
 +      {
 +              return Version("DNS support", VF_CORE|VF_VENDOR);
 +      }
 +};
 +
 +MODULE_INIT(ModuleDNS)
 +
index 2d396858f536dbc20280d8bd9d0d4e51dcb13733,0000000000000000000000000000000000000000..57616094ede4c9976291e24cd0c94bfcd83e275d
mode 100644,000000..100644
--- /dev/null
@@@ -1,66 -1,0 +1,72 @@@
 +/*
 + * InspIRCd -- Internet Relay Chat Daemon
 + *
 + *   Copyright (C) 2009-2010 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_info.h"
 +
 +CommandMotd::CommandMotd(Module* parent)
 +      : Command(parent, "MOTD", 0, 1)
 +{
 +      syntax = "[<servername>]";
 +}
 +
 +/** Handle /MOTD
 + */
 +CmdResult CommandMotd::Handle (const std::vector<std::string>& parameters, User *user)
 +{
 +      if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
++      {
++              // Give extra penalty if a non-oper queries the /MOTD of a remote server
++              LocalUser* localuser = IS_LOCAL(user);
++              if ((localuser) && (!user->IsOper()))
++                      localuser->CommandFloodPenalty += 2000;
 +              return CMD_SUCCESS;
++      }
 +
 +      ConfigTag* tag = ServerInstance->Config->EmptyTag;
 +      LocalUser* localuser = IS_LOCAL(user);
 +      if (localuser)
 +              tag = localuser->GetClass()->config;
 +      std::string motd_name = tag->getString("motd", "motd");
 +      ConfigFileCache::iterator motd = ServerInstance->Config->Files.find(motd_name);
 +      if (motd == ServerInstance->Config->Files.end())
 +      {
 +              user->SendText(":%s %03d %s :Message of the day file is missing.",
 +                      ServerInstance->Config->ServerName.c_str(), ERR_NOMOTD, user->nick.c_str());
 +              return CMD_SUCCESS;
 +      }
 +
 +      user->SendText(":%s %03d %s :%s message of the day", ServerInstance->Config->ServerName.c_str(),
 +              RPL_MOTDSTART, user->nick.c_str(), ServerInstance->Config->ServerName.c_str());
 +
 +      for (file_cache::iterator i = motd->second.begin(); i != motd->second.end(); i++)
 +              user->SendText(":%s %03d %s :- %s", ServerInstance->Config->ServerName.c_str(), RPL_MOTD, user->nick.c_str(), i->c_str());
 +
 +      user->SendText(":%s %03d %s :End of message of the day.", ServerInstance->Config->ServerName.c_str(), RPL_ENDOFMOTD, user->nick.c_str());
 +
 +      return CMD_SUCCESS;
 +}
 +
 +RouteDescriptor CommandMotd::GetRouting(User* user, const std::vector<std::string>& parameters)
 +{
 +      if (parameters.size() > 0)
 +              return ROUTE_UNICAST(parameters[0]);
 +      return ROUTE_LOCALONLY;
 +}
index 745f019f2274c518fe6e2f2df216bb25b1b9758b,0000000000000000000000000000000000000000..278e6044d0e31fd2792d14a6ad89229849b0bbe6
mode 100644,000000..100644
--- /dev/null
@@@ -1,117 -1,0 +1,118 @@@
-               if ((!n) && (chan->IsModeSet(privatemode)))
 +/*
 + * 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"
 +
 +/** Handle /LIST.
 + */
 +class CommandList : public Command
 +{
 +      ChanModeReference secretmode;
 +      ChanModeReference privatemode;
 +
 + public:
 +      /** Constructor for list.
 +       */
 +      CommandList(Module* parent)
 +              : Command(parent,"LIST", 0, 0)
 +              , secretmode(creator, "secret")
 +              , privatemode(creator, "private")
 +      {
 +              Penalty = 5;
 +      }
 +
 +      /** Handle command.
 +       * @param parameters The parameters to the command
 +       * @param user The user issuing the command
 +       * @return A value from CmdResult to indicate command success or failure.
 +       */
 +      CmdResult Handle(const std::vector<std::string>& parameters, User *user);
 +};
 +
 +
 +/** Handle /LIST
 + */
 +CmdResult CommandList::Handle (const std::vector<std::string>& parameters, User *user)
 +{
 +      int minusers = 0, maxusers = 0;
 +
 +      user->WriteNumeric(RPL_LISTSTART, "Channel :Users Name");
 +
 +      /* Work around mIRC suckyness. YOU SUCK, KHALED! */
 +      if (parameters.size() == 1)
 +      {
 +              if (parameters[0][0] == '<')
 +              {
 +                      maxusers = atoi((parameters[0].c_str())+1);
 +              }
 +              else if (parameters[0][0] == '>')
 +              {
 +                      minusers = atoi((parameters[0].c_str())+1);
 +              }
 +      }
 +
 +      const bool has_privs = user->HasPrivPermission("channels/auspex");
 +      const bool match_name_topic = ((!parameters.empty()) && (!parameters[0].empty()) && (parameters[0][0] != '<') && (parameters[0][0] != '>'));
 +
 +      const chan_hash& chans = ServerInstance->GetChans();
 +      for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
 +      {
 +              Channel* const chan = i->second;
 +
 +              // attempt to match a glob pattern
 +              long users = chan->GetUserCounter();
 +
 +              bool too_few = (minusers && (users <= minusers));
 +              bool too_many = (maxusers && (users >= maxusers));
 +
 +              if (too_many || too_few)
 +                      continue;
 +
 +              if (match_name_topic)
 +              {
 +                      if (!InspIRCd::Match(chan->name, parameters[0]) && !InspIRCd::Match(chan->topic, parameters[0]))
 +                              continue;
 +              }
 +
 +              // if the channel is not private/secret, OR the user is on the channel anyway
 +              bool n = (has_privs || chan->HasUser(user));
 +
-                       /* Channel is +p and user is outside/not privileged */
-                       user->WriteNumeric(RPL_LIST, "* %ld :", users);
-               }
-               else
-               {
-                       if ((n) || (!chan->IsModeSet(secretmode)))
++              // If we're not in the channel and +s is set on it, we want to ignore it
++              if ((n) || (!chan->IsModeSet(secretmode)))
 +              {
++                      if ((!n) && (chan->IsModeSet(privatemode)))
++                      {
++                              // Channel is private (+p) and user is outside/not privileged
++                              user->WriteNumeric(RPL_LIST, "* %ld :", users);
++                      }
++                      else
 +                      {
 +                              /* User is in the channel/privileged, channel is not +s */
 +                              user->WriteNumeric(RPL_LIST, "%s %ld :[+%s] %s", chan->name.c_str(), users, chan->ChanModes(n), chan->topic.c_str());
 +                      }
 +              }
 +      }
 +      user->WriteNumeric(RPL_LISTEND, ":End of channel list.");
 +
 +      return CMD_SUCCESS;
 +}
 +
 +
 +COMMAND_INIT(CommandList)
index 1561131dc71365ff43375530c8fc51bbd8db5e24,0000000000000000000000000000000000000000..7f0f15e7735169817acbf2823e73cf98f526a146
mode 100644,000000..100644
--- /dev/null
@@@ -1,78 -1,0 +1,78 @@@
-               ServerInstance->Modules->Reload(m, new ReloadModuleWorker(user->uuid, parameters[0]));
 +/*
 + * 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"
 +
 +class CommandReloadmodule : public Command
 +{
 + public:
 +      /** Constructor for reloadmodule.
 +       */
 +      CommandReloadmodule ( Module* parent) : Command( parent, "RELOADMODULE",1) { flags_needed = 'o'; syntax = "<modulename>"; }
 +      /** Handle command.
 +       * @param parameters The parameters to the command
 +       * @param user The user issuing the command
 +       * @return A value from CmdResult to indicate command success or failure.
 +       */
 +      CmdResult Handle(const std::vector<std::string>& parameters, User *user);
 +};
 +
 +class ReloadModuleWorker : public HandlerBase1<void, bool>
 +{
 + public:
 +      const std::string name;
 +      const std::string uid;
 +      ReloadModuleWorker(const std::string& uuid, const std::string& modn)
 +              : name(modn), uid(uuid) {}
 +      void Call(bool result)
 +      {
 +              ServerInstance->SNO->WriteGlobalSno('a', "RELOAD MODULE: %s %ssuccessfully reloaded",
 +                      name.c_str(), result ? "" : "un");
 +              User* user = ServerInstance->FindNick(uid);
 +              if (user)
 +                      user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module %ssuccessfully reloaded.",
 +                              name.c_str(), result ? "" : "un");
 +              ServerInstance->GlobalCulls.AddItem(this);
 +      }
 +};
 +
 +CmdResult CommandReloadmodule::Handle (const std::vector<std::string>& parameters, User *user)
 +{
 +      Module* m = ServerInstance->Modules->Find(parameters[0]);
 +      if (m == creator)
 +      {
 +              user->WriteNumeric(RPL_LOADEDMODULE, "%s :You cannot reload core_reloadmodule.so (unload and load it)",
 +                      parameters[0].c_str());
 +              return CMD_FAILURE;
 +      }
 +
 +      if (m)
 +      {
++              ServerInstance->Modules->Reload(m, (creator->dying ? NULL : new ReloadModuleWorker(user->uuid, parameters[0])));
 +              return CMD_SUCCESS;
 +      }
 +      else
 +      {
 +              user->WriteNumeric(RPL_LOADEDMODULE, "%s :Could not find module by that name", parameters[0].c_str());
 +              return CMD_FAILURE;
 +      }
 +}
 +
 +COMMAND_INIT(CommandReloadmodule)
index 997dd3afee71cd628ef48593f01484fdca7696a3,0000000000000000000000000000000000000000..180ece9b3b1f56ee883bc19aa42d35792524f85d
mode 100644,000000..100644
--- /dev/null
@@@ -1,393 -1,0 +1,399 @@@
-               results.push_back("211 "+user->nick+" "+u->nick+"["+u->ident+"@"+(c == 'l' ? u->dhost : u->GetIPString())+"] "+ConvToStr(u->eh.getSendQSize())+" "+ConvToStr(u->cmds_out)+" "+ConvToStr(u->bytes_out)+" "+ConvToStr(u->cmds_in)+" "+ConvToStr(u->bytes_in)+" "+ConvToStr(ServerInstance->Time() - u->age));
 +/*
 + * InspIRCd -- Internet Relay Chat Daemon
 + *
 + *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
 + *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
 + *   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 "xline.h"
 +
 +#ifdef _WIN32
 +#include <psapi.h>
 +#pragma comment(lib, "psapi.lib") // For GetProcessMemoryInfo()
 +#endif
 +
 +/** Handle /STATS.
 + */
 +class CommandStats : public Command
 +{
 +      void DoStats(char statschar, User* user, string_list &results);
 + public:
 +      /** Constructor for stats.
 +       */
 +      CommandStats ( Module* parent) : Command(parent,"STATS",1,2) { syntax = "<stats-symbol> [<servername>]"; }
 +      /** Handle command.
 +       * @param parameters The parameters to the command
 +       * @param user The user issuing the command
 +       * @return A value from CmdResult to indicate command success or failure.
 +       */
 +      CmdResult Handle(const std::vector<std::string>& parameters, User *user);
 +      RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
 +      {
 +              if (parameters.size() > 1)
 +                      return ROUTE_UNICAST(parameters[1]);
 +              return ROUTE_LOCALONLY;
 +      }
 +};
 +
 +static void GenerateStatsLl(User* user, string_list& results, char c)
 +{
 +      results.push_back(InspIRCd::Format("211 %s nick[ident@%s] sendq cmds_out bytes_out cmds_in bytes_in time_open", user->nick.c_str(), (c == 'l' ? "host" : "ip")));
 +
 +      const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
 +      for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
 +      {
 +              LocalUser* u = *i;
++              results.push_back("211 "+user->nick+" "+u->nick+"["+u->ident+"@"+(c == 'l' ? u->dhost : u->GetIPString())+"] "+ConvToStr(u->eh.getSendQSize())+" "+ConvToStr(u->cmds_out)+" "+ConvToStr(u->bytes_out)+" "+ConvToStr(u->cmds_in)+" "+ConvToStr(u->bytes_in)+" "+ConvToStr(ServerInstance->Time() - u->signon));
 +      }
 +}
 +
 +void CommandStats::DoStats(char statschar, User* user, string_list &results)
 +{
 +      bool isPublic = ServerInstance->Config->UserStats.find(statschar) != std::string::npos;
 +      bool isRemoteOper = IS_REMOTE(user) && (user->IsOper());
 +      bool isLocalOperWithPrivs = IS_LOCAL(user) && user->HasPrivPermission("servers/auspex");
 +
 +      if (!isPublic && !isRemoteOper && !isLocalOperWithPrivs)
 +      {
 +              ServerInstance->SNO->WriteToSnoMask('t',
 +                              "%s '%c' denied for %s (%s@%s)",
 +                              (IS_LOCAL(user) ? "Stats" : "Remote stats"),
 +                              statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
 +              results.push_back("481 " + user->nick + " :Permission Denied - STATS " + statschar + " requires the servers/auspex priv.");
 +              return;
 +      }
 +
 +      ModResult MOD_RESULT;
 +      FIRST_MOD_RESULT(OnStats, MOD_RESULT, (statschar, user, results));
 +      if (MOD_RESULT == MOD_RES_DENY)
 +      {
 +              results.push_back("219 "+user->nick+" "+statschar+" :End of /STATS report");
 +              ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",
 +                      (IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
 +              return;
 +      }
 +
 +      switch (statschar)
 +      {
 +              /* stats p (show listening ports) */
 +              case 'p':
 +              {
 +                      for (std::vector<ListenSocket*>::const_iterator i = ServerInstance->ports.begin(); i != ServerInstance->ports.end(); ++i)
 +                      {
 +                              ListenSocket* ls = *i;
 +                              std::string ip = ls->bind_addr;
 +                              if (ip.empty())
 +                                      ip.assign("*");
 +                              std::string type = ls->bind_tag->getString("type", "clients");
 +                              std::string hook = ls->bind_tag->getString("ssl", "plaintext");
 +
 +                              results.push_back("249 "+user->nick+" :"+ ip + ":"+ConvToStr(ls->bind_port)+
 +                                      " (" + type + ", " + hook + ")");
 +                      }
 +              }
 +              break;
 +
 +              /* These stats symbols must be handled by a linking module */
 +              case 'n':
 +              case 'c':
 +              break;
 +
 +              case 'i':
 +              {
 +                      for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); ++i)
 +                      {
 +                              ConnectClass* c = *i;
 +                              std::stringstream res;
 +                              res << "215 " << user->nick << " I " << c->name << ' ';
 +                              if (c->type == CC_ALLOW)
 +                                      res << '+';
 +                              if (c->type == CC_DENY)
 +                                      res << '-';
 +
 +                              if (c->type == CC_NAMED)
 +                                      res << '*';
 +                              else
 +                                      res << c->host;
 +
 +                              res << ' ' << c->config->getString("port", "*") << ' ';
 +
 +                              res << c->GetRecvqMax() << ' ' << c->GetSendqSoftMax() << ' ' << c->GetSendqHardMax()
 +                                      << ' ' << c->GetCommandRate() << ' ' << c->GetPenaltyThreshold();
 +                              if (c->fakelag)
 +                                      res << '*';
 +                              results.push_back(res.str());
 +                      }
 +              }
 +              break;
 +
 +              case 'Y':
 +              {
 +                      int idx = 0;
 +                      for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
 +                      {
 +                              ConnectClass* c = *i;
 +                              results.push_back("215 "+user->nick+" i NOMATCH * "+c->GetHost()+" "+ConvToStr(c->limit ? c->limit : SocketEngine::GetMaxFds())+" "+ConvToStr(idx)+" "+ServerInstance->Config->ServerName+" *");
 +                              results.push_back("218 "+user->nick+" Y "+ConvToStr(idx)+" "+ConvToStr(c->GetPingTime())+" 0 "+ConvToStr(c->GetSendqHardMax())+" :"+
 +                                              ConvToStr(c->GetRecvqMax())+" "+ConvToStr(c->GetRegTimeout()));
 +                              idx++;
 +                      }
 +              }
 +              break;
 +
 +              case 'P':
 +              {
 +                      unsigned int idx = 0;
 +                      const UserManager::OperList& opers = ServerInstance->Users->all_opers;
 +                      for (UserManager::OperList::const_iterator i = opers.begin(); i != opers.end(); ++i)
 +                      {
 +                              User* oper = *i;
 +                              if (!oper->server->IsULine())
 +                              {
 +                                      LocalUser* lu = IS_LOCAL(oper);
 +                                      results.push_back("249 " + user->nick + " :" + oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " +
 +                                                      (lu ? ConvToStr(ServerInstance->Time() - lu->idle_lastmsg) + " secs" : "unavailable"));
 +                                      idx++;
 +                              }
 +                      }
 +                      results.push_back("249 "+user->nick+" :"+ConvToStr(idx)+" OPER(s)");
 +              }
 +              break;
 +
 +              case 'k':
 +                      ServerInstance->XLines->InvokeStats("K",216,user,results);
 +              break;
 +              case 'g':
 +                      ServerInstance->XLines->InvokeStats("G",223,user,results);
 +              break;
 +              case 'q':
 +                      ServerInstance->XLines->InvokeStats("Q",217,user,results);
 +              break;
 +              case 'Z':
 +                      ServerInstance->XLines->InvokeStats("Z",223,user,results);
 +              break;
 +              case 'e':
 +                      ServerInstance->XLines->InvokeStats("E",223,user,results);
 +              break;
 +              case 'E':
 +              {
 +                      const SocketEngine::Statistics& stats = SocketEngine::GetStats();
 +                      results.push_back("249 "+user->nick+" :Total events: "+ConvToStr(stats.TotalEvents));
 +                      results.push_back("249 "+user->nick+" :Read events:  "+ConvToStr(stats.ReadEvents));
 +                      results.push_back("249 "+user->nick+" :Write events: "+ConvToStr(stats.WriteEvents));
 +                      results.push_back("249 "+user->nick+" :Error events: "+ConvToStr(stats.ErrorEvents));
 +                      break;
 +              }
 +
 +              /* stats m (list number of times each command has been used, plus bytecount) */
 +              case 'm':
 +              {
 +                      const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
 +                      for (CommandParser::CommandMap::const_iterator i = commands.begin(); i != commands.end(); ++i)
 +                      {
 +                              if (i->second->use_count)
 +                              {
 +                                      /* RPL_STATSCOMMANDS */
 +                                      results.push_back("212 "+user->nick+" "+i->second->name+" "+ConvToStr(i->second->use_count));
 +                              }
 +                      }
 +              }
 +              break;
 +
 +              /* stats z (debug and memory info) */
 +              case 'z':
 +              {
 +                      results.push_back("249 "+user->nick+" :Users: "+ConvToStr(ServerInstance->Users->GetUsers().size()));
 +                      results.push_back("249 "+user->nick+" :Channels: "+ConvToStr(ServerInstance->GetChans().size()));
 +                      results.push_back("249 "+user->nick+" :Commands: "+ConvToStr(ServerInstance->Parser.GetCommands().size()));
 +
 +                      float kbitpersec_in, kbitpersec_out, kbitpersec_total;
 +                      char kbitpersec_in_s[30], kbitpersec_out_s[30], kbitpersec_total_s[30];
 +
 +                      SocketEngine::GetStats().GetBandwidth(kbitpersec_in, kbitpersec_out, kbitpersec_total);
 +
 +                      snprintf(kbitpersec_total_s, 30, "%03.5f", kbitpersec_total);
 +                      snprintf(kbitpersec_out_s, 30, "%03.5f", kbitpersec_out);
 +                      snprintf(kbitpersec_in_s, 30, "%03.5f", kbitpersec_in);
 +
 +                      results.push_back("249 "+user->nick+" :Bandwidth total:  "+ConvToStr(kbitpersec_total_s)+" kilobits/sec");
 +                      results.push_back("249 "+user->nick+" :Bandwidth out:    "+ConvToStr(kbitpersec_out_s)+" kilobits/sec");
 +                      results.push_back("249 "+user->nick+" :Bandwidth in:     "+ConvToStr(kbitpersec_in_s)+" kilobits/sec");
 +
 +#ifndef _WIN32
 +                      /* Moved this down here so all the not-windows stuff (look w00tie, I didn't say win32!) is in one ifndef.
 +                       * Also cuts out some identical code in both branches of the ifndef. -- Om
 +                       */
 +                      rusage R;
 +
 +                      /* Not sure why we were doing '0' with a RUSAGE_SELF comment rather than just using RUSAGE_SELF -- Om */
 +                      if (!getrusage(RUSAGE_SELF,&R)) /* RUSAGE_SELF */
 +                      {
 +                              results.push_back("249 "+user->nick+" :Total allocation: "+ConvToStr(R.ru_maxrss)+"K");
 +                              results.push_back("249 "+user->nick+" :Signals:          "+ConvToStr(R.ru_nsignals));
 +                              results.push_back("249 "+user->nick+" :Page faults:      "+ConvToStr(R.ru_majflt));
 +                              results.push_back("249 "+user->nick+" :Swaps:            "+ConvToStr(R.ru_nswap));
 +                              results.push_back("249 "+user->nick+" :Context Switches: Voluntary; "+ConvToStr(R.ru_nvcsw)+" Involuntary; "+ConvToStr(R.ru_nivcsw));
 +
 +                              char percent[30];
 +
 +                              float n_elapsed = (ServerInstance->Time() - ServerInstance->stats.LastSampled.tv_sec) * 1000000
 +                                      + (ServerInstance->Time_ns() - ServerInstance->stats.LastSampled.tv_nsec) / 1000;
 +                              float n_eaten = ((R.ru_utime.tv_sec - ServerInstance->stats.LastCPU.tv_sec) * 1000000 + R.ru_utime.tv_usec - ServerInstance->stats.LastCPU.tv_usec);
 +                              float per = (n_eaten / n_elapsed) * 100;
 +
 +                              snprintf(percent, 30, "%03.5f%%", per);
 +                              results.push_back("249 "+user->nick+" :CPU Use (now):    "+percent);
 +
 +                              n_elapsed = ServerInstance->Time() - ServerInstance->startup_time;
 +                              n_eaten = (float)R.ru_utime.tv_sec + R.ru_utime.tv_usec / 100000.0;
 +                              per = (n_eaten / n_elapsed) * 100;
 +                              snprintf(percent, 30, "%03.5f%%", per);
 +                              results.push_back("249 "+user->nick+" :CPU Use (total):  "+percent);
 +                      }
 +#else
 +                      PROCESS_MEMORY_COUNTERS MemCounters;
 +                      if (GetProcessMemoryInfo(GetCurrentProcess(), &MemCounters, sizeof(MemCounters)))
 +                      {
 +                              results.push_back("249 "+user->nick+" :Total allocation: "+ConvToStr((MemCounters.WorkingSetSize + MemCounters.PagefileUsage) / 1024)+"K");
 +                              results.push_back("249 "+user->nick+" :Pagefile usage:   "+ConvToStr(MemCounters.PagefileUsage / 1024)+"K");
 +                              results.push_back("249 "+user->nick+" :Page faults:      "+ConvToStr(MemCounters.PageFaultCount));
 +                      }
 +
 +                      FILETIME CreationTime;
 +                      FILETIME ExitTime;
 +                      FILETIME KernelTime;
 +                      FILETIME UserTime;
 +                      LARGE_INTEGER ThisSample;
 +                      if(GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime) &&
 +                              QueryPerformanceCounter(&ThisSample))
 +                      {
 +                              KernelTime.dwHighDateTime += UserTime.dwHighDateTime;
 +                              KernelTime.dwLowDateTime += UserTime.dwLowDateTime;
 +                              double n_eaten = (double)( ( (uint64_t)(KernelTime.dwHighDateTime - ServerInstance->stats.LastCPU.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime - ServerInstance->stats.LastCPU.dwLowDateTime) )/100000;
 +                              double n_elapsed = (double)(ThisSample.QuadPart - ServerInstance->stats.LastSampled.QuadPart) / ServerInstance->stats.QPFrequency.QuadPart;
 +                              double per = (n_eaten/n_elapsed);
 +
 +                              char percent[30];
 +
 +                              snprintf(percent, 30, "%03.5f%%", per);
 +                              results.push_back("249 "+user->nick+" :CPU Use (now):    "+percent);
 +
 +                              n_elapsed = ServerInstance->Time() - ServerInstance->startup_time;
 +                              n_eaten = (double)(( (uint64_t)(KernelTime.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime))/100000;
 +                              per = (n_eaten / n_elapsed);
 +                              snprintf(percent, 30, "%03.5f%%", per);
 +                              results.push_back("249 "+user->nick+" :CPU Use (total):  "+percent);
 +                      }
 +#endif
 +              }
 +              break;
 +
 +              case 'T':
 +              {
 +                      results.push_back("249 "+user->nick+" :accepts "+ConvToStr(ServerInstance->stats.Accept)+" refused "+ConvToStr(ServerInstance->stats.Refused));
 +                      results.push_back("249 "+user->nick+" :unknown commands "+ConvToStr(ServerInstance->stats.Unknown));
 +                      results.push_back("249 "+user->nick+" :nick collisions "+ConvToStr(ServerInstance->stats.Collisions));
 +                      results.push_back("249 "+user->nick+" :dns requests "+ConvToStr(ServerInstance->stats.DnsGood+ServerInstance->stats.DnsBad)+" succeeded "+ConvToStr(ServerInstance->stats.DnsGood)+" failed "+ConvToStr(ServerInstance->stats.DnsBad));
 +                      results.push_back("249 "+user->nick+" :connection count "+ConvToStr(ServerInstance->stats.Connects));
 +                      results.push_back(InspIRCd::Format("249 %s :bytes sent %5.2fK recv %5.2fK", user->nick.c_str(),
 +                              ServerInstance->stats.Sent / 1024.0, ServerInstance->stats.Recv / 1024.0));
 +              }
 +              break;
 +
 +              /* stats o */
 +              case 'o':
 +              {
 +                      ConfigTagList tags = ServerInstance->Config->ConfTags("oper");
 +                      for(ConfigIter i = tags.first; i != tags.second; ++i)
 +                      {
 +                              ConfigTag* tag = i->second;
 +                              results.push_back("243 "+user->nick+" O "+tag->getString("host")+" * "+
 +                                      tag->getString("name") + " " + tag->getString("type")+" 0");
 +                      }
 +              }
 +              break;
 +              case 'O':
 +              {
 +                      for (ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->OperTypes.begin(); i != ServerInstance->Config->OperTypes.end(); ++i)
 +                      {
 +                              OperInfo* tag = i->second;
 +                              tag->init();
 +                              std::string umodes;
 +                              std::string cmodes;
 +                              for(char c='A'; c <= 'z'; c++)
 +                              {
 +                                      ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_USER);
 +                                      if (mh && mh->NeedsOper() && tag->AllowedUserModes[c - 'A'])
 +                                              umodes.push_back(c);
 +                                      mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL);
 +                                      if (mh && mh->NeedsOper() && tag->AllowedChanModes[c - 'A'])
 +                                              cmodes.push_back(c);
 +                              }
 +                              results.push_back("243 "+user->nick+" O "+tag->name.c_str() + " " + umodes + " " + cmodes);
 +                      }
 +              }
 +              break;
 +
 +              /* stats l (show user I/O stats) */
 +              case 'l':
 +              /* stats L (show user I/O stats with IP addresses) */
 +              case 'L':
 +                      GenerateStatsLl(user, results, statschar);
 +              break;
 +
 +              /* stats u (show server uptime) */
 +              case 'u':
 +              {
 +                      unsigned int up = static_cast<unsigned int>(ServerInstance->Time() - ServerInstance->startup_time);
 +                      results.push_back(InspIRCd::Format("242 %s :Server up %u days, %.2u:%.2u:%.2u", user->nick.c_str(),
 +                              up / 86400, (up / 3600) % 24, (up / 60) % 60, up % 60));
 +              }
 +              break;
 +
 +              default:
 +              break;
 +      }
 +
 +      results.push_back("219 "+user->nick+" "+statschar+" :End of /STATS report");
 +      ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",
 +              (IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
 +      return;
 +}
 +
 +CmdResult CommandStats::Handle (const std::vector<std::string>& parameters, User *user)
 +{
 +      if (parameters.size() > 1 && parameters[1] != ServerInstance->Config->ServerName)
++      {
++              // Give extra penalty if a non-oper does /STATS <remoteserver>
++              LocalUser* localuser = IS_LOCAL(user);
++              if ((localuser) && (!user->IsOper()))
++                      localuser->CommandFloodPenalty += 2000;
 +              return CMD_SUCCESS;
++      }
 +      string_list values;
 +      char search = parameters[0][0];
 +      DoStats(search, user, values);
 +
 +      const std::string p = ":" + ServerInstance->Config->ServerName + " ";
 +      for (size_t i = 0; i < values.size(); i++)
 +              user->SendText(p + values[i]);
 +
 +      return CMD_SUCCESS;
 +}
 +
 +COMMAND_INIT(CommandStats)
index d593d7f4b8f7974f6f2f341b5b1f8ff84b4b5d82,0000000000000000000000000000000000000000..cbf4f5e08b97ae1487561c28946d96304ca06ec2
mode 100644,000000..100644
--- /dev/null
@@@ -1,81 -1,0 +1,82 @@@
 +/*
 + * 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_user.h"
 +
 +CommandUser::CommandUser(Module* parent)
 +      : SplitCommand(parent, "USER", 4, 4)
 +{
 +      works_before_reg = true;
 +      Penalty = 0;
 +      syntax = "<username> <localhost> <remotehost> <GECOS>";
 +}
 +
 +CmdResult CommandUser::HandleLocal(const std::vector<std::string>& parameters, LocalUser *user)
 +{
 +      /* A user may only send the USER command once */
 +      if (!(user->registered & REG_USER))
 +      {
 +              if (!ServerInstance->IsIdent(parameters[0]))
 +              {
 +                      /*
 +                       * RFC says we must use this numeric, so we do. Let's make it a little more nub friendly though. :)
 +                       *  -- Craig, and then w00t.
 +                       */
 +                      user->WriteNumeric(ERR_NEEDMOREPARAMS, "USER :Your username is not valid");
 +                      return CMD_FAILURE;
 +              }
 +              else
 +              {
 +                      /*
 +                       * The ident field is IDENTMAX+2 in size to account for +1 for the optional
 +                       * ~ character, and +1 for null termination, therefore we can safely use up to
 +                       * IDENTMAX here.
 +                       */
 +                      user->ChangeIdent(parameters[0]);
 +                      user->fullname.assign(parameters[3].empty() ? "No info" : parameters[3], 0, ServerInstance->Config->Limits.MaxGecos);
 +                      user->registered = (user->registered | REG_USER);
 +              }
 +      }
 +      else
 +      {
 +              user->WriteNumeric(ERR_ALREADYREGISTERED, ":You may not reregister");
++              user->CommandFloodPenalty += 1000;
 +              return CMD_FAILURE;
 +      }
 +
 +      /* parameters 2 and 3 are local and remote hosts, and are ignored */
 +      return CheckRegister(user);
 +}
 +
 +CmdResult CommandUser::CheckRegister(LocalUser* user)
 +{
 +      // If the user is registered, return CMD_SUCCESS/CMD_FAILURE depending on what modules say, otherwise just
 +      // return CMD_SUCCESS without doing anything, knowing the other handler will call us again
 +      if (user->registered == REG_NICKUSER)
 +      {
 +              ModResult MOD_RESULT;
 +              FIRST_MOD_RESULT(OnUserRegister, MOD_RESULT, (user));
 +              if (MOD_RESULT == MOD_RES_DENY)
 +                      return CMD_FAILURE;
 +      }
 +
 +      return CMD_SUCCESS;
 +}
index ffa6aa2ff3de81c2ef37a3c6b976a3a8761b50c7,0000000000000000000000000000000000000000..dd778548ab95514a37e87157c92756ff70b9e9e6
mode 100644,000000..100644
--- /dev/null
@@@ -1,163 -1,0 +1,170 @@@
-               Penalty = 0;
 +/*
 + * InspIRCd -- Internet Relay Chat Daemon
 + *
 + *   Copyright (C) 2014 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
 + * 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_user.h"
 +
 +/** Handle /PASS.
 + */
 +class CommandPass : public SplitCommand
 +{
 + public:
 +      /** Constructor for pass.
 +       */
 +      CommandPass(Module* parent)
 +              : SplitCommand(parent, "PASS", 1, 1)
 +      {
 +              works_before_reg = true;
 +              Penalty = 0;
 +              syntax = "<password>";
 +      }
 +
 +      /** Handle command.
 +       * @param parameters The parameters to the command
 +       * @param user The user issuing the command
 +       * @return A value from CmdResult to indicate command success or failure.
 +       */
 +      CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser *user)
 +      {
 +              // Check to make sure they haven't registered -- Fix by FCS
 +              if (user->registered == REG_ALL)
 +              {
++                      user->CommandFloodPenalty += 1000;
 +                      user->WriteNumeric(ERR_ALREADYREGISTERED, ":You may not reregister");
 +                      return CMD_FAILURE;
 +              }
 +              user->password = parameters[0];
 +
 +              return CMD_SUCCESS;
 +      }
 +};
 +
 +/** Handle /PING.
 + */
 +class CommandPing : public Command
 +{
 + public:
 +      /** Constructor for ping.
 +       */
 +      CommandPing(Module* parent)
 +              : Command(parent, "PING", 1, 2)
 +      {
-               if (IS_LOCAL(user))
-                       IS_LOCAL(user)->lastping = 1;
 +              syntax = "<servername> [:<servername>]";
 +      }
 +
 +      /** Handle command.
 +       * @param parameters The parameters to the command
 +       * @param user The user issuing the command
 +       * @return A value from CmdResult to indicate command success or failure.
 +       */
 +      CmdResult Handle(const std::vector<std::string>& parameters, User* user)
 +      {
 +              user->WriteServ("PONG %s :%s", ServerInstance->Config->ServerName.c_str(), parameters[0].c_str());
 +              return CMD_SUCCESS;
 +      }
 +};
 +
 +/** Handle /PONG.
 + */
 +class CommandPong : public Command
 +{
 + public:
 +      /** Constructor for pong.
 +       */
 +      CommandPong(Module* parent)
 +              : Command(parent, "PONG", 0, 1)
 +      {
 +              Penalty = 0;
 +              syntax = "<ping-text>";
 +      }
 +
 +      /** Handle command.
 +       * @param parameters The parameters to the command
 +       * @param user The user issuing the command
 +       * @return A value from CmdResult to indicate command success or failure.
 +       */
 +      CmdResult Handle(const std::vector<std::string>& parameters, User* user)
 +      {
 +              // set the user as alive so they survive to next ping
++              LocalUser* localuser = IS_LOCAL(user);
++              if (localuser)
++              {
++                      // Increase penalty unless we've sent a PING and this is the reply
++                      if (localuser->lastping)
++                              localuser->CommandFloodPenalty += 1000;
++                      else
++                              localuser->lastping = 1;
++              }
 +              return CMD_SUCCESS;
 +      }
 +};
 +
 +void MessageWrapper::Wrap(const std::string& message, std::string& out)
 +{
 +      // If there is a fixed message, it is stored in prefix. Otherwise prefix contains
 +      // only the prefix, so append the message and the suffix
 +      out.assign(prefix);
 +      if (!fixed)
 +              out.append(message).append(suffix);
 +}
 +
 +void MessageWrapper::ReadConfig(const char* prefixname, const char* suffixname, const char* fixedname)
 +{
 +      ConfigTag* tag = ServerInstance->Config->ConfValue("options");
 +      prefix = tag->getString(fixedname);
 +      fixed = (!prefix.empty());
 +      if (!fixed)
 +      {
 +              prefix = tag->getString(prefixname);
 +              suffix = tag->getString(suffixname);
 +      }
 +}
 +
 +class CoreModUser : public Module
 +{
 +      CommandAway cmdaway;
 +      CommandMode cmdmode;
 +      CommandNick cmdnick;
 +      CommandPart cmdpart;
 +      CommandPass cmdpass;
 +      CommandPing cmdping;
 +      CommandPong cmdpong;
 +      CommandQuit cmdquit;
 +      CommandUser cmduser;
 +
 + public:
 +      CoreModUser()
 +              : cmdaway(this), cmdmode(this), cmdnick(this), cmdpart(this), cmdpass(this), cmdping(this)
 +              , cmdpong(this), cmdquit(this), cmduser(this)
 +      {
 +      }
 +
 +      void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
 +      {
 +              cmdpart.msgwrap.ReadConfig("prefixpart", "suffixpart", "fixedpart");
 +              cmdquit.msgwrap.ReadConfig("prefixquit", "suffixquit", "fixedquit");
 +      }
 +
 +      Version GetVersion() CXX11_OVERRIDE
 +      {
 +              return Version("Provides the AWAY, MODE, NICK, PART, PASS, PING, PONG, QUIT and USER commands", VF_VENDOR|VF_CORE);
 +      }
 +};
 +
 +MODULE_INIT(CoreModUser)
index a6782419496a0710f13bc8480a4248d7e07c87d7,0000000000000000000000000000000000000000..eae6e51ce3f0f3a1eaa2b61f343d57e9edce3652
mode 100644,000000..100644
--- /dev/null
@@@ -1,72 -1,0 +1,85 @@@
-       CommandUserhost ( Module* parent) : Command(parent,"USERHOST", 1, 5) {
 +/*
 + * 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"
 +
 +/** Handle /USERHOST.
 + */
 +class CommandUserhost : public Command
 +{
++      UserModeReference hideopermode;
++
 + public:
 +      /** Constructor for userhost.
 +       */
-       for (unsigned int i = 0; i < parameters.size(); i++)
++      CommandUserhost(Module* parent)
++              : Command(parent,"USERHOST", 1)
++              , hideopermode(parent, "hideoper")
++      {
 +              syntax = "<nick> [<nick> ...]";
 +      }
 +      /** Handle command.
 +       * @param parameters The parameters to the command
 +       * @param user The user issuing the command
 +       * @return A value from CmdResult to indicate command success or failure.
 +       */
 +      CmdResult Handle(const std::vector<std::string>& parameters, User *user);
 +};
 +
 +CmdResult CommandUserhost::Handle (const std::vector<std::string>& parameters, User *user)
 +{
 +      const bool has_privs = user->HasPrivPermission("users/auspex");
 +
 +      std::string retbuf = "302 " + user->nick + " :";
 +
-                               retbuf += '*';
++      unsigned int max = parameters.size();
++      if (max > 5)
++              max = 5;
++
++      for (unsigned int i = 0; i < max; i++)
 +      {
 +              User *u = ServerInstance->FindNickOnly(parameters[i]);
 +
 +              if ((u) && (u->registered == REG_ALL))
 +              {
 +                      retbuf += u->nick;
 +
 +                      if (u->IsOper())
++                      {
++                              // XXX: +H hidden opers must not be shown as opers
++                              if ((u == user) || (has_privs) || (!u->IsModeSet(hideopermode)))
++                                      retbuf += '*';
++                      }
 +
 +                      retbuf += '=';
 +                      retbuf += (u->IsAway() ? '-' : '+');
 +                      retbuf += u->ident;
 +                      retbuf += '@';
 +                      retbuf += (((u == user) || (has_privs)) ? u->host : u->dhost);
 +                      retbuf += ' ';
 +              }
 +      }
 +
 +      user->WriteServ(retbuf);
 +
 +      return CMD_SUCCESS;
 +}
 +
 +COMMAND_INIT(CommandUserhost)
index 39ea347dc8292b5c2b92f7cd908ddaa9c88e7acf,0000000000000000000000000000000000000000..8b9258d71d374160f2bbb8a3e792a93697436ad6
mode 100644,000000..100644
--- /dev/null
@@@ -1,397 -1,0 +1,397 @@@
-                       if (user->age >= ServerInstance->Time() - seconds)
 +/*
 + * InspIRCd -- Internet Relay Chat Daemon
 + *
 + *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
 + *   Copyright (C) 2007-2008 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"
 +
 +/** Handle /WHO.
 + */
 +class CommandWho : public Command
 +{
 +      bool CanView(Channel* chan, User* user);
 +      bool opt_viewopersonly;
 +      bool opt_showrealhost;
 +      bool opt_realname;
 +      bool opt_mode;
 +      bool opt_ident;
 +      bool opt_metadata;
 +      bool opt_port;
 +      bool opt_away;
 +      bool opt_local;
 +      bool opt_far;
 +      bool opt_time;
 +      ChanModeReference secretmode;
 +      ChanModeReference privatemode;
 +      UserModeReference invisiblemode;
 +
 +      Membership* get_first_visible_channel(User* u)
 +      {
 +              for (User::ChanList::iterator i = u->chans.begin(); i != u->chans.end(); ++i)
 +              {
 +                      Membership* memb = *i;
 +                      if (!memb->chan->IsModeSet(secretmode))
 +                              return memb;
 +              }
 +              return NULL;
 +      }
 +
 + public:
 +      /** Constructor for who.
 +       */
 +      CommandWho(Module* parent)
 +              : Command(parent, "WHO", 1)
 +              , secretmode(parent, "secret")
 +              , privatemode(parent, "private")
 +              , invisiblemode(parent, "invisible")
 +      {
 +              syntax = "<server>|<nickname>|<channel>|<realname>|<host>|0 [ohurmMiaplf]";
 +      }
 +
 +      void SendWhoLine(User* user, const std::vector<std::string>& parms, const std::string& initial, Membership* memb, User* u, std::vector<std::string>& whoresults);
 +      /** Handle command.
 +       * @param parameters The parameters to the command
 +       * @param user The user issuing the command
 +       * @return A value from CmdResult to indicate command success or failure.
 +       */
 +      CmdResult Handle(const std::vector<std::string>& parameters, User *user);
 +      bool whomatch(User* cuser, User* user, const char* matchtext);
 +};
 +
 +bool CommandWho::whomatch(User* cuser, User* user, const char* matchtext)
 +{
 +      bool match = false;
 +      bool positive = false;
 +
 +      if (user->registered != REG_ALL)
 +              return false;
 +
 +      if (opt_local && !IS_LOCAL(user))
 +              return false;
 +      else if (opt_far && IS_LOCAL(user))
 +              return false;
 +
 +      if (opt_mode)
 +      {
 +              for (const char* n = matchtext; *n; n++)
 +              {
 +                      if (*n == '+')
 +                      {
 +                              positive = true;
 +                              continue;
 +                      }
 +                      else if (*n == '-')
 +                      {
 +                              positive = false;
 +                              continue;
 +                      }
 +                      if (user->IsModeSet(*n) != positive)
 +                              return false;
 +              }
 +              return true;
 +      }
 +      else
 +      {
 +              /*
 +               * This was previously one awesome pile of ugly nested if, when really, it didn't need
 +               * to be, since only one condition was ever checked, a chained if works just fine.
 +               * -- w00t
 +               */
 +              if (opt_metadata)
 +              {
 +                      match = false;
 +                      const Extensible::ExtensibleStore& list = user->GetExtList();
 +                      for(Extensible::ExtensibleStore::const_iterator i = list.begin(); i != list.end(); ++i)
 +                              if (InspIRCd::Match(i->first->name, matchtext))
 +                                      match = true;
 +              }
 +              else if (opt_realname)
 +                      match = InspIRCd::Match(user->fullname, matchtext);
 +              else if (opt_showrealhost)
 +                      match = InspIRCd::Match(user->host, matchtext, ascii_case_insensitive_map);
 +              else if (opt_ident)
 +                      match = InspIRCd::Match(user->ident, matchtext, ascii_case_insensitive_map);
 +              else if (opt_port)
 +              {
 +                      irc::portparser portrange(matchtext, false);
 +                      long portno = -1;
 +                      while ((portno = portrange.GetToken()))
 +                              if (IS_LOCAL(user) && portno == IS_LOCAL(user)->GetServerPort())
 +                              {
 +                                      match = true;
 +                                      break;
 +                              }
 +              }
 +              else if (opt_away)
 +                      match = InspIRCd::Match(user->awaymsg, matchtext);
 +              else if (opt_time)
 +              {
 +                      long seconds = InspIRCd::Duration(matchtext);
 +
 +                      // Okay, so time matching, we want all users connected `seconds' ago
++                      if (user->signon >= ServerInstance->Time() - seconds)
 +                              match = true;
 +              }
 +
 +              /*
 +               * Once the conditionals have been checked, only check dhost/nick/server
 +               * if they didn't match this user -- and only match if we don't find a match.
 +               *
 +               * This should make things minutely faster, and again, less ugly.
 +               * -- w00t
 +               */
 +              if (!match)
 +                      match = InspIRCd::Match(user->dhost, matchtext, ascii_case_insensitive_map);
 +
 +              if (!match)
 +                      match = InspIRCd::Match(user->nick, matchtext);
 +
 +              /* Don't allow server name matches if HideWhoisServer is enabled, unless the command user has the priv */
 +              if (!match && (ServerInstance->Config->HideWhoisServer.empty() || cuser->HasPrivPermission("users/auspex")))
 +                      match = InspIRCd::Match(user->server->GetName(), matchtext);
 +
 +              return match;
 +      }
 +}
 +
 +bool CommandWho::CanView(Channel* chan, User* user)
 +{
 +      /* Bug #383 - moved higher up the list, because if we are in the channel
 +       * we can see all its users
 +       */
 +      if (chan->HasUser(user))
 +              return true;
 +      /* Opers see all */
 +      if (user->HasPrivPermission("users/auspex"))
 +              return true;
 +      /* Cant see inside a +s or a +p channel unless we are a member (see above) */
 +      else if (!chan->IsModeSet(secretmode) && !chan->IsModeSet(privatemode))
 +              return true;
 +
 +      return false;
 +}
 +
 +void CommandWho::SendWhoLine(User* user, const std::vector<std::string>& parms, const std::string& initial, Membership* memb, User* u, std::vector<std::string>& whoresults)
 +{
 +      if (!memb)
 +              memb = get_first_visible_channel(u);
 +
 +      std::string wholine = initial + (memb ? memb->chan->name : "*") + " " + u->ident + " " +
 +              (opt_showrealhost ? u->host : u->dhost) + " ";
 +      if (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
 +              wholine.append(ServerInstance->Config->HideWhoisServer);
 +      else
 +              wholine.append(u->server->GetName());
 +
 +      wholine.append(" " + u->nick + " ");
 +
 +      /* away? */
 +      if (u->IsAway())
 +      {
 +              wholine.append("G");
 +      }
 +      else
 +      {
 +              wholine.append("H");
 +      }
 +
 +      /* oper? */
 +      if (u->IsOper())
 +      {
 +              wholine.push_back('*');
 +      }
 +
 +      if (memb)
 +      {
 +              char prefix = memb->GetPrefixChar();
 +              if (prefix)
 +                      wholine.push_back(prefix);
 +      }
 +
 +      wholine.append(" :0 " + u->fullname);
 +
 +      FOREACH_MOD(OnSendWhoLine, (user, parms, u, memb, wholine));
 +
 +      if (!wholine.empty())
 +              whoresults.push_back(wholine);
 +}
 +
 +CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User *user)
 +{
 +      /*
 +       * XXX - RFC says:
 +       *   The <name> passed to WHO is matched against users' host, server, real
 +       *   name and nickname
 +       * Currently, we support WHO #chan, WHO nick, WHO 0, WHO *, and the addition of a 'o' flag, as per RFC.
 +       */
 +
 +      /* WHO options */
 +      opt_viewopersonly = false;
 +      opt_showrealhost = false;
 +      opt_realname = false;
 +      opt_mode = false;
 +      opt_ident = false;
 +      opt_metadata = false;
 +      opt_port = false;
 +      opt_away = false;
 +      opt_local = false;
 +      opt_far = false;
 +      opt_time = false;
 +
 +      std::vector<std::string> whoresults;
 +      std::string initial = "352 " + user->nick + " ";
 +
 +      /* Change '0' into '*' so the wildcard matcher can grok it */
 +      std::string matchtext = ((parameters[0] == "0") ? "*" : parameters[0]);
 +
 +      // WHO flags count as a wildcard
 +      bool usingwildcards = ((parameters.size() > 1) || (matchtext.find_first_of("*?.") != std::string::npos));
 +
 +      if (parameters.size() > 1)
 +      {
 +              for (std::string::const_iterator iter = parameters[1].begin(); iter != parameters[1].end(); ++iter)
 +              {
 +                      switch (*iter)
 +                      {
 +                              case 'o':
 +                                      opt_viewopersonly = true;
 +                                      break;
 +                              case 'h':
 +                                      if (user->HasPrivPermission("users/auspex"))
 +                                              opt_showrealhost = true;
 +                                      break;
 +                              case 'r':
 +                                      opt_realname = true;
 +                                      break;
 +                              case 'm':
 +                                      if (user->HasPrivPermission("users/auspex"))
 +                                              opt_mode = true;
 +                                      break;
 +                              case 'M':
 +                                      if (user->HasPrivPermission("users/auspex"))
 +                                              opt_metadata = true;
 +                                      break;
 +                              case 'i':
 +                                      opt_ident = true;
 +                                      break;
 +                              case 'p':
 +                                      if (user->HasPrivPermission("users/auspex"))
 +                                              opt_port = true;
 +                                      break;
 +                              case 'a':
 +                                      opt_away = true;
 +                                      break;
 +                              case 'l':
 +                                      if (user->HasPrivPermission("users/auspex") || ServerInstance->Config->HideWhoisServer.empty())
 +                                              opt_local = true;
 +                                      break;
 +                              case 'f':
 +                                      if (user->HasPrivPermission("users/auspex") || ServerInstance->Config->HideWhoisServer.empty())
 +                                              opt_far = true;
 +                                      break;
 +                              case 't':
 +                                      opt_time = true;
 +                                      break;
 +                      }
 +              }
 +      }
 +
 +
 +      /* who on a channel? */
 +      Channel* ch = ServerInstance->FindChan(matchtext);
 +
 +      if (ch)
 +      {
 +              if (CanView(ch,user))
 +              {
 +                      bool inside = ch->HasUser(user);
 +
 +                      /* who on a channel. */
 +                      const Channel::MemberMap& cu = ch->GetUsers();
 +                      for (Channel::MemberMap::const_iterator i = cu.begin(); i != cu.end(); ++i)
 +                      {
 +                              /* None of this applies if we WHO ourselves */
 +                              if (user != i->first)
 +                              {
 +                                      /* opers only, please */
 +                                      if (opt_viewopersonly && !i->first->IsOper())
 +                                              continue;
 +
 +                                      /* If we're not inside the channel, hide +i users */
 +                                      if (i->first->IsModeSet(invisiblemode) && !inside && !user->HasPrivPermission("users/auspex"))
 +                                              continue;
 +                              }
 +
 +                              SendWhoLine(user, parameters, initial, i->second, i->first, whoresults);
 +                      }
 +              }
 +      }
 +      else
 +      {
 +              /* Match against wildcard of nick, server or host */
 +              if (opt_viewopersonly)
 +              {
 +                      /* Showing only opers */
 +                      const UserManager::OperList& opers = ServerInstance->Users->all_opers;
 +                      for (UserManager::OperList::const_iterator i = opers.begin(); i != opers.end(); ++i)
 +                      {
 +                              User* oper = *i;
 +
 +                              if (whomatch(user, oper, matchtext.c_str()))
 +                              {
 +                                      if (!user->SharesChannelWith(oper))
 +                                      {
 +                                              if (usingwildcards && (oper->IsModeSet(invisiblemode)) && (!user->HasPrivPermission("users/auspex")))
 +                                                      continue;
 +                                      }
 +
 +                                      SendWhoLine(user, parameters, initial, NULL, oper, whoresults);
 +                              }
 +                      }
 +              }
 +              else
 +              {
 +                      const user_hash& users = ServerInstance->Users->GetUsers();
 +                      for (user_hash::const_iterator i = users.begin(); i != users.end(); ++i)
 +                      {
 +                              if (whomatch(user, i->second, matchtext.c_str()))
 +                              {
 +                                      if (!user->SharesChannelWith(i->second))
 +                                      {
 +                                              if (usingwildcards && (i->second->IsModeSet(invisiblemode)) && (!user->HasPrivPermission("users/auspex")))
 +                                                      continue;
 +                                      }
 +
 +                                      SendWhoLine(user, parameters, initial, NULL, i->second, whoresults);
 +                              }
 +                      }
 +              }
 +      }
 +      /* Send the results out */
 +      for (std::vector<std::string>::const_iterator n = whoresults.begin(); n != whoresults.end(); n++)
 +              user->WriteServ(*n);
 +      user->WriteNumeric(RPL_ENDOFWHO, "%s :End of /WHO list.", *parameters[0].c_str() ? parameters[0].c_str() : "*");
 +
 +      // Penalize the user a bit for large queries
 +      // (add one unit of penalty per 200 results)
 +      if (IS_LOCAL(user))
 +              IS_LOCAL(user)->CommandFloodPenalty += whoresults.size() * 5;
 +      return CMD_SUCCESS;
 +}
 +
 +COMMAND_INIT(CommandWho)
diff --combined src/hashcomp.cpp
index 42e25f8f6a6248bfb0efc19294857cd409d34e19,e0347421b29d01fb89e7cedd13d89aacde04fb74..35e5f3671cec15aa37c1387bfc460a6eaf54eb76
   */
  
  
 -/* $Core */
 -
  #include "inspircd.h"
 -#include "hashcomp.h"
 -#include "hash_map.h"
  
  /******************************************************
   *
@@@ -31,7 -35,7 +31,7 @@@
   * scene spend a lot of time debating (arguing) about
   * the best way to write hash functions to hash irc
   * nicknames, channels etc.
 - * We are lucky as C++ developers as hash_map does
 + * We are lucky as C++ developers as unordered_map does
   * a lot of this for us. It does intellegent memory
   * requests, bucketing, search functions, insertion
   * and deletion etc. All we have to do is write some
   *
   ******************************************************/
  
 -/** A mapping of uppercase to lowercase, including scandinavian
 - * 'oddities' as specified by RFC1459, e.g. { -> [, and | -> \
 - */
 -unsigned const char rfc_case_insensitive_map[256] = {
 -      0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,                                   /* 0-19 */
 -      20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,                         /* 20-39 */
 -      40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,                         /* 40-59 */
 -      60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,             /* 60-79 */
 -      112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 94, 95, 96, 97, 98, 99,           /* 80-99 */
 -      100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,     /* 100-119 */
 -      120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,     /* 120-139 */
 -      140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,     /* 140-159 */
 -      160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,     /* 160-179 */
 -      180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,     /* 180-199 */
 -      200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,     /* 200-219 */
 -      220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,     /* 220-239 */
 -      240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255                          /* 240-255 */
 -};
  
 -/** Case insensitive map, ASCII rules.
 - * That is;
 - * [ != {, but A == a.
 +/**
 + * A case insensitive mapping of characters from upper case to lower case for
 + * the ASCII character set.
   */
  unsigned const char ascii_case_insensitive_map[256] = {
 -        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,                                   /* 0-19 */
 -        20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,                         /* 20-39 */
 -        40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,                         /* 40-59 */
 -        60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,             /* 60-79 */
 -        112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,              /* 80-99 */
 -        100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,     /* 100-119 */
 -        120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,     /* 120-139 */
 -        140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,     /* 140-159 */
 -        160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,     /* 160-179 */
 -        180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,     /* 180-199 */
 -        200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,     /* 200-219 */
 -        220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,     /* 220-239 */
 -        240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255                          /* 240-255 */
 +      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   // 0-9
 +      10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  // 10-19
 +      20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  // 20-29
 +      30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  // 30-39
 +      40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  // 40-49
 +      50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  // 50-59
 +      60,  61,  62,  63,  64,  97,  98,  99,  100, 101, // 60-69
 +      102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 70-79
 +      112, 113, 114, 115, 116, 117, 118, 119, 120, 121, // 80-89
 +      122, 91,  92,  93,  94,  95,  96,  97,  98,  99,  // 90-99
 +      100, 101, 102, 103, 104, 105, 106, 107, 108, 109, // 100-109
 +      110, 111, 112, 113, 114, 115, 116, 117, 118, 119, // 110-119
 +      120, 121, 122, 123, 124, 125, 126, 127, 128, 129, // 120-129
 +      130, 131, 132, 133, 134, 135, 136, 137, 138, 139, // 130-139
 +      140, 141, 142, 143, 144, 145, 146, 147, 148, 149, // 140-149
 +      150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 150-159
 +      160, 161, 162, 163, 164, 165, 166, 167, 168, 169, // 160-169
 +      170, 171, 172, 173, 174, 175, 176, 177, 178, 179, // 170-179
 +      180, 181, 182, 183, 184, 185, 186, 187, 188, 189, // 180-189
 +      190, 191, 192, 193, 194, 195, 196, 197, 198, 199, // 190-199
 +      200, 201, 202, 203, 204, 205, 206, 207, 208, 209, // 200-209
 +      210, 211, 212, 213, 214, 215, 216, 217, 218, 219, // 210-219
 +      220, 221, 222, 223, 224, 225, 226, 227, 228, 229, // 220-229
 +      230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 230-249
 +      240, 241, 242, 243, 244, 245, 246, 247, 248, 249, // 240-249
 +      250, 251, 252, 253, 254, 255,                     // 250-255
  };
  
 -/** Case sensitive map.
 - * Can technically also be used for ASCII case sensitive comparisons, as [ != {, etc.
 +
 +
 +/**
 + * A case insensitive mapping of characters from upper case to lower case for
 + * the character set of RFC 1459. This is identical to ASCII with the small
 + * exception of {}| being considered to be the lower case equivalents of the
 + * characters []\ respectively.
   */
 -unsigned const char rfc_case_sensitive_map[256] = {
 -      0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
 -        21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
 -        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
 -        61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
 -        81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
 -        101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
 -        121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
 -        141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
 -        161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
 -        181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200,
 -        201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220,
 -        221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240,
 -        241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
 +unsigned const char rfc_case_insensitive_map[256] = {
 +      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   // 0-9
 +      10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  // 10-19
 +      20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  // 20-29
 +      30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  // 30-39
 +      40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  // 40-49
 +      50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  // 50-59
 +      60,  61,  62,  63,  64,  97,  98,  99,  100, 101, // 60-69
 +      102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 70-79
 +      112, 113, 114, 115, 116, 117, 118, 119, 120, 121, // 80-89
 +      122, 123, 124, 125, 94,  95,  96,  97,  98,  99,  // 90-99
 +      100, 101, 102, 103, 104, 105, 106, 107, 108, 109, // 100-109
 +      110, 111, 112, 113, 114, 115, 116, 117, 118, 119, // 110-119
 +      120, 121, 122, 123, 124, 125, 126, 127, 128, 129, // 120-129
 +      130, 131, 132, 133, 134, 135, 136, 137, 138, 139, // 130-139
 +      140, 141, 142, 143, 144, 145, 146, 147, 148, 149, // 140-149
 +      150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 150-159
 +      160, 161, 162, 163, 164, 165, 166, 167, 168, 169, // 160-169
 +      170, 171, 172, 173, 174, 175, 176, 177, 178, 179, // 170-179
 +      180, 181, 182, 183, 184, 185, 186, 187, 188, 189, // 180-189
 +      190, 191, 192, 193, 194, 195, 196, 197, 198, 199, // 190-199
 +      200, 201, 202, 203, 204, 205, 206, 207, 208, 209, // 200-209
 +      210, 211, 212, 213, 214, 215, 216, 217, 218, 219, // 210-219
 +      220, 221, 222, 223, 224, 225, 226, 227, 228, 229, // 220-229
 +      230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 230-239
 +      240, 241, 242, 243, 244, 245, 246, 247, 248, 249, // 240-249
 +      250, 251, 252, 253, 254, 255,                     // 250-255
  };
  
 -/* convert a string to lowercase. Note following special circumstances
 - * taken from RFC 1459. Many "official" server branches still hold to this
 - * rule so i will too;
 - *
 - *  Because of IRC's scandanavian origin, the characters {}| are
 - *  considered to be the lower case equivalents of the characters []\,
 - *  respectively. This is a critical issue when determining the
 - *  equivalence of two nicknames.
 +/**
 + * A case sensitive mapping of characters from upper case to lower case for the
 + * character set of RFC 1459. This is identical to ASCII.
   */
 -void nspace::strlower(char *n)
 -{
 -      if (n)
 -      {
 -              for (char* t = n; *t; t++)
 -                      *t = national_case_insensitive_map[(unsigned char)*t];
 -      }
 -}
 -
 -#ifdef HASHMAP_DEPRECATED
 -      size_t CoreExport nspace::insensitive::operator()(const std::string &s) const
 -#else
 -      size_t nspace::hash<std::string>::operator()(const std::string &s) const
 -#endif
 -
 -{
 -      /* XXX: NO DATA COPIES! :)
 -       * The hash function here is practically
 -       * a copy of the one in STL's hash_fun.h,
 -       * only with *x replaced with national_case_insensitive_map[*x].
 -       * This avoids a copy to use hash<const char*>
 -       */
 -      size_t t = 0;
 -      for (std::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */
 -              t = 5 * t + national_case_insensitive_map[(unsigned char)*x];
 -      return t;
 -}
 -
 +unsigned const char rfc_case_sensitive_map[256] = {
 +      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   // 0-9
 +      10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  // 10-19
 +      20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  // 20-29
 +      30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  // 30-39
 +      40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  // 40-49
 +      50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  // 50-59
 +      60,  61,  62,  63,  64,  65,  66,  67,  68,  69,  // 60-69
 +      70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  // 70-79
 +      80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  // 80-89
 +      90,  91,  92,  93,  94,  95,  96,  97,  98,  99,  // 90-99
 +      100, 101, 102, 103, 104, 105, 106, 107, 108, 109, // 100-109
 +      110, 111, 112, 113, 114, 115, 116, 117, 118, 119, // 110-119
 +      120, 121, 122, 123, 124, 125, 126, 127, 128, 129, // 120-129
 +      130, 131, 132, 133, 134, 135, 136, 137, 138, 139, // 130-139
 +      140, 141, 142, 143, 144, 145, 146, 147, 148, 149, // 140-149
 +      150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 150-159
 +      160, 161, 162, 163, 164, 165, 166, 167, 168, 169, // 160-169
 +      170, 171, 172, 173, 174, 175, 176, 177, 178, 179, // 170-179
 +      180, 181, 182, 183, 184, 185, 186, 187, 188, 189, // 180-189
 +      190, 191, 192, 193, 194, 195, 196, 197, 198, 199, // 190-199
 +      200, 201, 202, 203, 204, 205, 206, 207, 208, 209, // 200-209
 +      210, 211, 212, 213, 214, 215, 216, 217, 218, 219, // 210-219
 +      220, 221, 222, 223, 224, 225, 226, 227, 228, 229, // 220-229
 +      230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 230-239
 +      240, 241, 242, 243, 244, 245, 246, 247, 248, 249, // 240-249
 +      250, 251, 252, 253, 254, 255,                     // 250-255
 +};
  
  size_t CoreExport irc::hash::operator()(const irc::string &s) const
  {
-       register size_t t = 0;
+       size_t t = 0;
        for (irc::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */
                t = 5 * t + national_case_insensitive_map[(unsigned char)*x];
        return t;
@@@ -169,39 -165,6 +169,39 @@@ bool irc::StrHashComp::operator()(cons
        return (national_case_insensitive_map[*n1] == national_case_insensitive_map[*n2]);
  }
  
-       register size_t t = 0;
 +bool irc::insensitive_swo::operator()(const std::string& a, const std::string& b) const
 +{
 +      const unsigned char* charmap = national_case_insensitive_map;
 +      std::string::size_type asize = a.size();
 +      std::string::size_type bsize = b.size();
 +      std::string::size_type maxsize = std::min(asize, bsize);
 +
 +      for (std::string::size_type i = 0; i < maxsize; i++)
 +      {
 +              unsigned char A = charmap[(unsigned char)a[i]];
 +              unsigned char B = charmap[(unsigned char)b[i]];
 +              if (A > B)
 +                      return false;
 +              else if (A < B)
 +                      return true;
 +      }
 +      return (asize < bsize);
 +}
 +
 +size_t irc::insensitive::operator()(const std::string &s) const
 +{
 +      /* XXX: NO DATA COPIES! :)
 +       * The hash function here is practically
 +       * a copy of the one in STL's hash_fun.h,
 +       * only with *x replaced with national_case_insensitive_map[*x].
 +       * This avoids a copy to use hash<const char*>
 +       */
++      size_t t = 0;
 +      for (std::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */
 +              t = 5 * t + national_case_insensitive_map[(unsigned char)*x];
 +      return t;
 +}
 +
  /******************************************************
   *
   * This is the implementation of our special irc::string
@@@ -254,30 -217,60 +254,30 @@@ const char* irc::irc_char_traits::find(
        return (n >= 0) ? s1 : NULL;
  }
  
 -irc::tokenstream::tokenstream(const std::string &source) : tokens(source), last_pushed(false)
 -{
 -      /* Record starting position and current position */
 -      last_starting_position = tokens.begin();
 -      n = tokens.begin();
 -}
 -
 -irc::tokenstream::~tokenstream()
 +irc::tokenstream::tokenstream(const std::string &source) : spacesepstream(source)
  {
  }
  
  bool irc::tokenstream::GetToken(std::string &token)
  {
 -      std::string::iterator lsp = last_starting_position;
 -
 -      while (n != tokens.end())
 -      {
 -              /** Skip multi space, converting "  " into " "
 -               */
 -              while ((n+1 != tokens.end()) && (*n == ' ') && (*(n+1) == ' '))
 -                      n++;
 -
 -              if ((last_pushed) && (*n == ':'))
 -              {
 -                      /* If we find a token thats not the first and starts with :,
 -                       * this is the last token on the line
 -                       */
 -                      std::string::iterator curr = ++n;
 -                      n = tokens.end();
 -                      token = std::string(curr, tokens.end());
 -                      return true;
 -              }
 +      bool first = !pos;
  
 -              last_pushed = false;
 +      if (!spacesepstream::GetToken(token))
 +              return false;
  
 -              if ((*n == ' ') || (n+1 == tokens.end()))
 +      /* This is the last parameter */
 +      if (token[0] == ':' && !first)
 +      {
 +              token.erase(token.begin());
 +              if (!StreamEnd())
                {
 -                      /* If we find a space, or end of string, this is the end of a token.
 -                       */
 -                      last_starting_position = n+1;
 -                      last_pushed = *n == ' ';
 -
 -                      std::string strip(lsp, n+1 == tokens.end() ? n+1  : n++);
 -                      while ((strip.length()) && (strip.find_last_of(' ') == strip.length() - 1))
 -                              strip.erase(strip.end() - 1);
 -
 -                      token = strip;
 -                      return !token.empty();
 +                      token += ' ';
 +                      token += GetRemaining();
                }
 -
 -              n++;
 +              pos = tokens.length() + 1;
        }
 -      token.clear();
 -      return false;
 +
 +      return true;
  }
  
  bool irc::tokenstream::GetToken(irc::string &token)
@@@ -304,59 -297,182 +304,59 @@@ bool irc::tokenstream::GetToken(long &t
        return returnval;
  }
  
 -irc::sepstream::sepstream(const std::string &source, char seperator) : tokens(source), sep(seperator)
 +irc::sepstream::sepstream(const std::string& source, char separator, bool allowempty)
 +      : tokens(source), sep(separator), pos(0), allow_empty(allowempty)
  {
 -      last_starting_position = tokens.begin();
 -      n = tokens.begin();
  }
  
  bool irc::sepstream::GetToken(std::string &token)
  {
 -      std::string::iterator lsp = last_starting_position;
 -
 -      while (n != tokens.end())
 +      if (this->StreamEnd())
        {
 -              if ((*n == sep) || (n+1 == tokens.end()))
 -              {
 -                      last_starting_position = n+1;
 -                      token = std::string(lsp, n+1 == tokens.end() ? n+1  : n++);
 -
 -                      while ((token.length()) && (token.find_last_of(sep) == token.length() - 1))
 -                              token.erase(token.end() - 1);
 -
 -                      if (token.empty())
 -                              n++;
 -
 -                      return n == tokens.end() ? false : true;
 -              }
 -
 -              n++;
 -      }
 -
 -      token.clear();
 -      return false;
 -}
 -
 -const std::string irc::sepstream::GetRemaining()
 -{
 -      return std::string(n, tokens.end());
 -}
 -
 -bool irc::sepstream::StreamEnd()
 -{
 -      return ((n + 1) == tokens.end());
 -}
 -
 -irc::sepstream::~sepstream()
 -{
 -}
 -
 -std::string irc::hex(const unsigned char *raw, size_t rawsz)
 -{
 -      if (!rawsz)
 -              return "";
 -
 -      /* EWW! This used to be using sprintf, which is WAY inefficient. -Special */
 -
 -      const char *hex = "0123456789abcdef";
 -      static char hexbuf[MAXBUF];
 -
 -      size_t i, j;
 -      for (i = 0, j = 0; j < rawsz; ++j)
 -      {
 -              hexbuf[i++] = hex[raw[j] / 16];
 -              hexbuf[i++] = hex[raw[j] % 16];
 -      }
 -      hexbuf[i] = 0;
 -
 -      return hexbuf;
 -}
 -
 -CoreExport const char* irc::Spacify(const char* n)
 -{
 -      static char x[MAXBUF];
 -      strlcpy(x,n,MAXBUF);
 -      for (char* y = x; *y; y++)
 -              if (*y == '_')
 -                      *y = ' ';
 -      return x;
 -}
 -
 -
 -irc::modestacker::modestacker(bool add) : adding(add)
 -{
 -      sequence.clear();
 -      sequence.push_back("");
 -}
 -
 -void irc::modestacker::Push(char modeletter, const std::string &parameter)
 -{
 -      *(sequence.begin()) += modeletter;
 -      sequence.push_back(parameter);
 -}
 -
 -void irc::modestacker::Push(char modeletter)
 -{
 -      this->Push(modeletter,"");
 -}
 -
 -void irc::modestacker::PushPlus()
 -{
 -      this->Push('+',"");
 -}
 -
 -void irc::modestacker::PushMinus()
 -{
 -      this->Push('-',"");
 -}
 -
 -int irc::modestacker::GetStackedLine(std::vector<std::string> &result, int max_line_size)
 -{
 -      if (sequence.empty())
 -      {
 -              return 0;
 +              token.clear();
 +              return false;
        }
  
 -      unsigned int n = 0;
 -      int size = 1; /* Account for initial +/- char */
 -      int nextsize = 0;
 -      int start = result.size();
 -      std::string modeline = adding ? "+" : "-";
 -      result.push_back(modeline);
 -
 -      if (sequence.size() > 1)
 -              nextsize = sequence[1].length() + 2;
 -
 -      while (!sequence[0].empty() && (sequence.size() > 1) && (n < ServerInstance->Config->Limits.MaxModes) && ((size + nextsize) < max_line_size))
 +      if (!this->allow_empty)
        {
 -              modeline += *(sequence[0].begin());
 -              if (!sequence[1].empty())
 +              this->pos = this->tokens.find_first_not_of(this->sep, this->pos);
 +              if (this->pos == std::string::npos)
                {
 -                      result.push_back(sequence[1]);
 -                      size += nextsize; /* Account for mode character and whitespace */
 +                      this->pos = this->tokens.length() + 1;
 +                      token.clear();
 +                      return false;
                }
 -              sequence[0].erase(sequence[0].begin());
 -              sequence.erase(sequence.begin() + 1);
 -
 -              if (sequence.size() > 1)
 -                      nextsize = sequence[1].length() + 2;
 -
 -              n++;
        }
 -      result[start] = modeline;
  
 -      return n;
 -}
 +      size_t p = this->tokens.find(this->sep, this->pos);
 +      if (p == std::string::npos)
 +              p = this->tokens.length();
  
 -irc::stringjoiner::stringjoiner(const std::string &seperator, const std::vector<std::string> &sequence, int begin, int end)
 -{
 -      if (end < begin)
 -              return; // nothing to do here
 +      token.assign(tokens, this->pos, p - this->pos);
 +      this->pos = p + 1;
  
 -      for (int v = begin; v < end; v++)
 -              joined.append(sequence[v]).append(seperator);
 -      joined.append(sequence[end]);
 +      return true;
  }
  
 -irc::stringjoiner::stringjoiner(const std::string &seperator, const std::deque<std::string> &sequence, int begin, int end)
 +const std::string irc::sepstream::GetRemaining()
  {
 -      if (end < begin)
 -              return; // nothing to do here
 -
 -      for (int v = begin; v < end; v++)
 -              joined.append(sequence[v]).append(seperator);
 -      joined.append(sequence[end]);
 +      return !this->StreamEnd() ? this->tokens.substr(this->pos) : "";
  }
  
 -irc::stringjoiner::stringjoiner(const std::string &seperator, const char* const* sequence, int begin, int end)
 +bool irc::sepstream::StreamEnd()
  {
 -      if (end < begin)
 -              return; // nothing to do here
 -
 -      for (int v = begin; v < end; v++)
 -              joined.append(sequence[v]).append(seperator);
 -      joined.append(sequence[end]);
 +      return this->pos > this->tokens.length();
  }
  
 -std::string& irc::stringjoiner::GetJoined()
 +std::string irc::stringjoiner(const std::vector<std::string>& sequence, char separator)
  {
 +      std::string joined;
 +      if (sequence.empty())
 +              return joined; // nothing to do here
 +
 +      for (std::vector<std::string>::const_iterator i = sequence.begin(); i != sequence.end(); ++i)
 +              joined.append(*i).push_back(separator);
 +      joined.erase(joined.end()-1);
        return joined;
  }
  
@@@ -412,9 -528,10 +412,9 @@@ long irc::portparser::GetToken(
        std::string::size_type dash = x.rfind('-');
        if (dash != std::string::npos)
        {
 -              std::string sbegin = x.substr(0, dash);
 -              std::string send = x.substr(dash+1, x.length());
 +              std::string sbegin(x, 0, dash);
                range_begin = atoi(sbegin.c_str());
 -              range_end = atoi(send.c_str());
 +              range_end = atoi(x.c_str()+dash+1);
  
                if ((range_begin > 0) && (range_end > 0) && (range_begin < 65536) && (range_end < 65536) && (range_begin < range_end))
                {
                return atoi(x.c_str());
        }
  }
 -
 -/*const std::basic_string& SearchAndReplace(std::string& text, const std::string& pattern, const std::string& replace)
 -{
 -      std::string replacement;
 -      if ((!pattern.empty()) && (!text.empty()))
 -      {
 -              for (std::string::size_type n = 0; n != text.length(); ++n)
 -              {
 -                      if (text.length() >= pattern.length() && text.substr(n, pattern.length()) == pattern)
 -                      {
 -                              replacement.append(replace);
 -                              n = n + pattern.length() - 1;
 -                      }
 -                      else
 -                      {
 -                              replacement += text[n];
 -                      }
 -              }
 -      }
 -      text = replacement;
 -      return text;
 -}*/
index 184013a4e0db3777957a85c9a636fe3fb6fbe5e5,050f41c758379e7581fea0054287a3532d885c7e..fc6161e3175f2320d22e331bd3d52c22a6369295
  
  
  #include "inspircd.h"
 -#include "xline.h"
 -#include "socket.h"
 -#include "socketengine.h"
 -#include "command_parse.h"
 -#include "dns.h"
  #include "exitcodes.h"
  #include <iostream>
  
@@@ -31,34 -36,34 +31,37 @@@ bool ModuleManager::Load(const std::str
  {
        /* Don't allow people to specify paths for modules, it doesn't work as expected */
        if (filename.find('/') != std::string::npos)
+       {
+               LastModuleError = "You can't load modules with a path: " + filename;
                return false;
+       }
  
 -      char modfile[MAXBUF];
 -      snprintf(modfile,MAXBUF,"%s/%s",ServerInstance->Config->ModPath.c_str(),filename.c_str());
 +      const std::string moduleFile = ServerInstance->Config->Paths.PrependModule(filename);
  
 -      if (!ServerConfig::FileExists(modfile))
 +      if (!FileSystem::FileExists(moduleFile))
        {
                LastModuleError = "Module file could not be found: " + filename;
 -              ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
 +              ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
                return false;
        }
  
        if (Modules.find(filename) != Modules.end())
        {
                LastModuleError = "Module " + filename + " is already loaded, cannot load a module twice!";
 -              ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
 +              ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
                return false;
        }
  
        Module* newmod = NULL;
 -      DLLManager* newhandle = new DLLManager(modfile);
 +      DLLManager* newhandle = new DLLManager(moduleFile.c_str());
 +      ServiceList newservices;
 +      if (!defer)
 +              this->NewServices = &newservices;
  
        try
        {
                newmod = newhandle->CallInit();
 +              this->NewServices = NULL;
  
                if (newmod)
                {
                        std::string version = newhandle->GetVersion();
                        if (defer)
                        {
 -                              ServerInstance->Logs->Log("MODULE", DEFAULT,"New module introduced: %s (Module version %s)",
 +                              ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "New module introduced: %s (Module version %s)",
                                        filename.c_str(), version.c_str());
                        }
                        else
                        {
 +                              ConfigStatus confstatus;
 +
 +                              AttachAll(newmod);
 +                              AddServices(newservices);
                                newmod->init();
 +                              newmod->ReadConfig(confstatus);
  
                                Version v = newmod->GetVersion();
 -                              ServerInstance->Logs->Log("MODULE", DEFAULT,"New module introduced: %s (Module version %s)%s",
 +                              ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "New module introduced: %s (Module version %s)%s",
                                        filename.c_str(), version.c_str(), (!(v.Flags & VF_VENDOR) ? " [3rd Party]" : " [Vendor]"));
                        }
                }
                else
                {
                        LastModuleError = "Unable to load " + filename + ": " + newhandle->LastError();
 -                      ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
 +                      ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
                        delete newhandle;
                        return false;
                }
        }
        catch (CoreException& modexcept)
        {
 +              this->NewServices = NULL;
 +
                // failure in module constructor
                if (newmod)
                {
                else
                        delete newhandle;
                LastModuleError = "Unable to load " + filename + ": " + modexcept.GetReason();
 -              ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
 +              ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
                return false;
        }
  
 -      this->ModCount++;
        if (defer)
                return true;
  
 -      FOREACH_MOD(I_OnLoadModule,OnLoadModule(newmod));
 -      /* We give every module a chance to re-prioritize when we introduce a new one,
 -       * not just the one thats loading, as the new module could affect the preference
 -       * of others
 -       */
 -      for(int tries = 0; tries < 20; tries++)
 -      {
 -              prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
 -              for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
 -                      n->second->Prioritize();
 -
 -              if (prioritizationState == PRIO_STATE_LAST)
 -                      break;
 -              if (tries == 19)
 -                      ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected while loading " + filename);
 -      }
 -
 -      ServerInstance->BuildISupport();
 -      return true;
 -}
 -
 -namespace {
 -      struct UnloadAction : public HandlerBase0<void>
 -      {
 -              Module* const mod;
 -              UnloadAction(Module* m) : mod(m) {}
 -              void Call()
 -              {
 -                      DLLManager* dll = mod->ModuleDLLManager;
 -                      ServerInstance->Modules->DoSafeUnload(mod);
 -                      ServerInstance->GlobalCulls.Apply();
 -                      delete dll;
 -                      ServerInstance->GlobalCulls.AddItem(this);
 -              }
 -      };
 -
 -      struct ReloadAction : public HandlerBase0<void>
 -      {
 -              Module* const mod;
 -              HandlerBase1<void, bool>* const callback;
 -              ReloadAction(Module* m, HandlerBase1<void, bool>* c)
 -                      : mod(m), callback(c) {}
 -              void Call()
 -              {
 -                      DLLManager* dll = mod->ModuleDLLManager;
 -                      std::string name = mod->ModuleSourceFile;
 -                      ServerInstance->Modules->DoSafeUnload(mod);
 -                      ServerInstance->GlobalCulls.Apply();
 -                      delete dll;
 -                      bool rv = ServerInstance->Modules->Load(name.c_str());
 -                      if (callback)
 -                              callback->Call(rv);
 -                      ServerInstance->GlobalCulls.AddItem(this);
 -              }
 -      };
 -}
 -
 -bool ModuleManager::Unload(Module* mod)
 -{
 -      if (!CanUnload(mod))
 -              return false;
 -      ServerInstance->AtomicActions.AddAction(new UnloadAction(mod));
 +      FOREACH_MOD(OnLoadModule, (newmod));
 +      PrioritizeHooks();
 +      ServerInstance->ISupport.Build();
        return true;
  }
  
 -void ModuleManager::Reload(Module* mod, HandlerBase1<void, bool>* callback)
 -{
 -      if (CanUnload(mod))
 -              ServerInstance->AtomicActions.AddAction(new ReloadAction(mod, callback));
 -      else if (callback)
 -              callback->Call(false);
 -}
 -
  /* We must load the modules AFTER initializing the socket engine, now */
 -void ModuleManager::LoadAll()
 +void ModuleManager::LoadCoreModules(std::map<std::string, ServiceList>& servicemap)
  {
 -      ModCount = 0;
 -
        std::cout << std::endl << "Loading core commands";
        fflush(stdout);
  
 -      DIR* library = opendir(ServerInstance->Config->ModPath.c_str());
 +      DIR* library = opendir(ServerInstance->Config->Paths.Module.c_str());
        if (library)
        {
                dirent* entry = NULL;
                while (0 != (entry = readdir(library)))
                {
 -                      if (InspIRCd::Match(entry->d_name, "cmd_*.so", ascii_case_insensitive_map))
 +                      if (InspIRCd::Match(entry->d_name, "core_*.so", ascii_case_insensitive_map))
                        {
                                std::cout << ".";
                                fflush(stdout);
  
 +                              this->NewServices = &servicemap[entry->d_name];
 +
                                if (!Load(entry->d_name, true))
                                {
 -                                      ServerInstance->Logs->Log("MODULE", DEFAULT, this->LastError());
 +                                      ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, this->LastError());
                                        std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << this->LastError() << std::endl << std::endl;
                                        ServerInstance->Exit(EXIT_STATUS_MODULE);
                                }
                closedir(library);
                std::cout << std::endl;
        }
 -
 -      ConfigTagList tags = ServerInstance->Config->ConfTags("module");
 -      for(ConfigIter i = tags.first; i != tags.second; ++i)
 -      {
 -              ConfigTag* tag = i->second;
 -              std::string name = tag->getString("name");
 -              std::cout << "[" << con_green << "*" << con_reset << "] Loading module:\t" << con_green << name << con_reset << std::endl;
 -
 -              if (!this->Load(name, true))
 -              {
 -                      ServerInstance->Logs->Log("MODULE", DEFAULT, this->LastError());
 -                      std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << this->LastError() << std::endl << std::endl;
 -                      ServerInstance->Exit(EXIT_STATUS_MODULE);
 -              }
 -      }
 -
 -      for(std::map<std::string, Module*>::iterator i = Modules.begin(); i != Modules.end(); i++)
 -      {
 -              Module* mod = i->second;
 -              try
 -              {
 -                      ServerInstance->Logs->Log("MODULE", DEBUG, "Initializing %s", i->first.c_str());
 -                      mod->init();
 -              }
 -              catch (CoreException& modexcept)
 -              {
 -                      LastModuleError = "Unable to initialize " + mod->ModuleSourceFile + ": " + modexcept.GetReason();
 -                      ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
 -                      std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << LastModuleError << std::endl << std::endl;
 -                      ServerInstance->Exit(EXIT_STATUS_MODULE);
 -              }
 -      }
 -
 -      /* We give every module a chance to re-prioritize when we introduce a new one,
 -       * not just the one thats loading, as the new module could affect the preference
 -       * of others
 -       */
 -      for(int tries = 0; tries < 20; tries++)
 -      {
 -              prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
 -              for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
 -                      n->second->Prioritize();
 -
 -              if (prioritizationState == PRIO_STATE_LAST)
 -                      break;
 -              if (tries == 19)
 -              {
 -                      ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected");
 -                      ServerInstance->Exit(EXIT_STATUS_MODULE);
 -              }
 -      }
  }
  
  #endif
index 77d86cb3167ba5de06b8ed1a37197c8b88e3718b,1e8f7117669083465047b6094584acb5233e5628..d2fa09c4ebad1106a5f37776c50c85a5832ea6e7
  
  #include "inspircd.h"
  
 -/* $ModDesc: Provides the ability to abbreviate commands a-la BBC BASIC keywords. */
 -
  class ModuleAbbreviation : public Module
  {
   public:
 -      void init()
 -      {
 -              ServerInstance->Modules->Attach(I_OnPreCommand, this);
 -      }
 -
        void Prioritize()
        {
                ServerInstance->Modules->SetPriority(this, I_OnPreCommand, PRIORITY_FIRST);
        }
  
 -      virtual Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides the ability to abbreviate commands a-la BBC BASIC keywords.",VF_VENDOR);
        }
  
 -      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
        {
                /* Command is already validated, has a length of 0, or last character is not a . */
                if (validated || command.empty() || *command.rbegin() != '.')
                        return MOD_RES_PASSTHRU;
  
 -              /* Whack the . off the end */
 -              command.erase(command.end() - 1);
 -
                /* Look for any command that starts with the same characters, if it does, replace the command string with it */
 -              size_t clen = command.length();
 +              size_t clen = command.length() - 1;
                std::string foundcommand, matchlist;
                bool foundmatch = false;
 -              for (Commandtable::iterator n = ServerInstance->Parser->cmdlist.begin(); n != ServerInstance->Parser->cmdlist.end(); ++n)
 +              const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
 +              for (CommandParser::CommandMap::const_iterator n = commands.begin(); n != commands.end(); ++n)
                {
 -                      if (n->first.length() < clen)
 -                              continue;
 -
 -                      if (command == n->first.substr(0, clen))
 +                      if (!command.compare(0, clen, n->first, 0, clen))
                        {
                                if (matchlist.length() > 450)
                                {
 -                                      user->WriteNumeric(420, "%s :Ambiguous abbreviation and too many possible matches.", user->nick.c_str());
 +                                      user->WriteNumeric(420, ":Ambiguous abbreviation and too many possible matches.");
                                        return MOD_RES_DENY;
                                }
  
                /* Ambiguous command, list the matches */
                if (!matchlist.empty())
                {
-                       user->WriteNumeric(420, ":Ambiguous abbreviation, posssible matches: %s%s", foundcommand.c_str(), matchlist.c_str());
 -                      user->WriteNumeric(420, "%s :Ambiguous abbreviation, possible matches: %s%s", user->nick.c_str(), foundcommand.c_str(), matchlist.c_str());
++                      user->WriteNumeric(420, ":Ambiguous abbreviation, possible matches: %s%s", foundcommand.c_str(), matchlist.c_str());
                        return MOD_RES_DENY;
                }
  
 -              if (foundcommand.empty())
 -              {
 -                      /* No match, we have to put the . back again so that the invalid command numeric looks correct. */
 -                      command += '.';
 -              }
 -              else
 +              if (!foundcommand.empty())
                {
                        command = foundcommand;
                }
diff --combined src/modules/m_check.cpp
index 8ae30bfed0da5b3e76e823a7e8522aaf93a97b57,9c5c414f195473386d4b7bf2b2ad95061a694d10..6f9c32fb1290e05b1637bcbe9a1c553603d88037
   */
  
  
 -/* $ModDesc: Provides the /CHECK command to retrieve information on a user, channel, hostname or IP address */
 -
  #include "inspircd.h"
 +#include "listmode.h"
  
  /** Handle /CHECK
   */
  class CommandCheck : public Command
  {
 +      UserModeReference snomaskmode;
 +
 +      std::string GetSnomasks(User* user)
 +      {
 +              std::string ret;
 +              if (snomaskmode)
 +                      ret = snomaskmode->GetUserParameter(user);
 +
 +              if (ret.empty())
 +                      ret = "+";
 +              return ret;
 +      }
 +
 +      static void dumpListMode(User* user, const std::string& checkstr, const ListModeBase::ModeList* list)
 +      {
 +              if (!list)
 +                      return;
 +
 +              std::string buf = checkstr + " modelist";
 +              const std::string::size_type headlen = buf.length();
 +              const size_t maxline = ServerInstance->Config->Limits.MaxLine;
 +              for (ListModeBase::ModeList::const_iterator i = list->begin(); i != list->end(); ++i)
 +              {
 +                      if (buf.size() + i->mask.size() + 1 > maxline)
 +                      {
 +                              user->SendText(buf);
 +                              buf.erase(headlen);
 +                      }
 +                      buf.append(" ").append(i->mask);
 +              }
 +              if (buf.length() > headlen)
 +                      user->SendText(buf);
 +      }
 +
   public:
 -      CommandCheck(Module* parent) : Command(parent,"CHECK", 1)
 +      CommandCheck(Module* parent)
 +              : Command(parent,"CHECK", 1)
 +              , snomaskmode(parent, "snomask")
        {
                flags_needed = 'o'; syntax = "<nickname>|<ip>|<hostmask>|<channel> <server>";
        }
        {
                char timebuf[60];
                struct tm *mytime = gmtime(&time);
-               strftime(timebuf, 59, "%Y-%m-%d %H:%M:%S UTC (%s)", mytime);
-               return std::string(timebuf);
+               strftime(timebuf, 59, "%Y-%m-%d %H:%M:%S UTC (", mytime);
+               std::string ret(timebuf);
+               ret.append(ConvToStr(time)).push_back(')');
+               return ret;
        }
  
        void dumpExt(User* user, const std::string& checkstr, Extensible* ext)
                        user->SendText(checkstr + " realnuh " + targuser->GetFullRealHost());
                        user->SendText(checkstr + " realname " + targuser->fullname);
                        user->SendText(checkstr + " modes +" + targuser->FormatModes());
 -                      user->SendText(checkstr + " snomasks +" + targuser->FormatNoticeMasks());
 -                      user->SendText(checkstr + " server " + targuser->server);
 +                      user->SendText(checkstr + " snomasks " + GetSnomasks(targuser));
 +                      user->SendText(checkstr + " server " + targuser->server->GetName());
                        user->SendText(checkstr + " uid " + targuser->uuid);
                        user->SendText(checkstr + " signon " + timestring(targuser->signon));
                        user->SendText(checkstr + " nickts " + timestring(targuser->age));
                        if (loctarg)
 -                              user->SendText(checkstr + " lastmsg " + timestring(targuser->idle_lastmsg));
 +                              user->SendText(checkstr + " lastmsg " + timestring(loctarg->idle_lastmsg));
  
 -                      if (IS_AWAY(targuser))
 +                      if (targuser->IsAway())
                        {
                                /* user is away */
                                user->SendText(checkstr + " awaytime " + timestring(targuser->awaytime));
                                user->SendText(checkstr + " awaymsg " + targuser->awaymsg);
                        }
  
 -                      if (IS_OPER(targuser))
 +                      if (targuser->IsOper())
                        {
                                OperInfo* oper = targuser->oper;
                                /* user is an oper of type ____ */
 -                              user->SendText(checkstr + " opertype " + oper->NameStr());
 +                              user->SendText(checkstr + " opertype " + oper->name);
                                if (loctarg)
                                {
                                        std::string umodes;
                                        }
                                        user->SendText(checkstr + " modeperms user=" + umodes + " channel=" + cmodes);
                                        std::string opcmds;
 -                                      for(std::set<std::string>::iterator i = oper->AllowedOperCommands.begin(); i != oper->AllowedOperCommands.end(); i++)
 +                                      for (OperInfo::PrivSet::const_iterator i = oper->AllowedOperCommands.begin(); i != oper->AllowedOperCommands.end(); ++i)
                                        {
                                                opcmds.push_back(' ');
                                                opcmds.append(*i);
                                        std::stringstream opcmddump(opcmds);
                                        user->SendText(checkstr + " commandperms", opcmddump);
                                        std::string privs;
 -                                      for(std::set<std::string>::iterator i = oper->AllowedPrivs.begin(); i != oper->AllowedPrivs.end(); i++)
 +                                      for (OperInfo::PrivSet::const_iterator i = oper->AllowedPrivs.begin(); i != oper->AllowedPrivs.end(); ++i)
                                        {
                                                privs.push_back(' ');
                                                privs.append(*i);
  
                        if (loctarg)
                        {
 -                              user->SendText(checkstr + " clientaddr " + irc::sockets::satouser(loctarg->client_sa));
 -                              user->SendText(checkstr + " serveraddr " + irc::sockets::satouser(loctarg->server_sa));
 +                              user->SendText(checkstr + " clientaddr " + loctarg->client_sa.str());
 +                              user->SendText(checkstr + " serveraddr " + loctarg->server_sa.str());
  
                                std::string classname = loctarg->GetClass()->name;
                                if (!classname.empty())
                        else
                                user->SendText(checkstr + " onip " + targuser->GetIPString());
  
 -                      for (UCListIter i = targuser->chans.begin(); i != targuser->chans.end(); i++)
 +                      for (User::ChanList::iterator i = targuser->chans.begin(); i != targuser->chans.end(); i++)
                        {
 -                              Channel* c = *i;
 -                              chliststr.append(c->GetPrefixChar(targuser)).append(c->name).append(" ");
 +                              Membership* memb = *i;
 +                              Channel* c = memb->chan;
 +                              char prefix = memb->GetPrefixChar();
 +                              if (prefix)
 +                                      chliststr.push_back(prefix);
 +                              chliststr.append(c->name).push_back(' ');
                        }
  
                        std::stringstream dump(chliststr);
  
                        /* now the ugly bit, spool current members of a channel. :| */
  
 -                      const UserMembList *ulist= targchan->GetUsers();
 +                      const Channel::MemberMap& ulist = targchan->GetUsers();
  
                        /* note that unlike /names, we do NOT check +i vs in the channel */
 -                      for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
 +                      for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
                        {
 -                              char tmpbuf[MAXBUF];
                                /*
 -                               * Unlike Asuka, I define a clone as coming from the same host. --w00t
 -                               */
 -                              snprintf(tmpbuf, MAXBUF, "%-3lu %s%s (%s@%s) %s ", ServerInstance->Users->GlobalCloneCount(i->first), targchan->GetAllPrefixChars(i->first), i->first->nick.c_str(), i->first->ident.c_str(), i->first->dhost.c_str(), i->first->fullname.c_str());
 -                              user->SendText(checkstr + " member " + tmpbuf);
 +                               * Unlike Asuka, I define a clone as coming from the same host. --w00t
 +                               */
 +                              const UserManager::CloneCounts& clonecount = ServerInstance->Users->GetCloneCounts(i->first);
 +                              user->SendText("%s member %-3u %s%s (%s@%s) %s ",
 +                                      checkstr.c_str(), clonecount.global,
 +                                      i->second->GetAllPrefixChars(), i->first->nick.c_str(),
 +                                      i->first->ident.c_str(), i->first->dhost.c_str(), i->first->fullname.c_str());
                        }
  
 -                      irc::modestacker modestack(true);
 -                      for(BanList::iterator b = targchan->bans.begin(); b != targchan->bans.end(); ++b)
 -                      {
 -                              modestack.Push('b', b->data);
 -                      }
 -                      std::vector<std::string> stackresult;
 -                      std::vector<TranslateType> dummy;
 -                      while (modestack.GetStackedLine(stackresult))
 -                      {
 -                              creator->ProtoSendMode(user, TYPE_CHANNEL, targchan, stackresult, dummy);
 -                              stackresult.clear();
 -                      }
 -                      FOREACH_MOD(I_OnSyncChannel,OnSyncChannel(targchan,creator,user));
 +                      const ModeParser::ListModeList& listmodes = ServerInstance->Modes->GetListModes();
 +                      for (ModeParser::ListModeList::const_iterator i = listmodes.begin(); i != listmodes.end(); ++i)
 +                              dumpListMode(user, checkstr, (*i)->GetList(targchan));
 +
                        dumpExt(user, checkstr, targchan);
                }
                else
                        long x = 0;
  
                        /* hostname or other */
 -                      for (user_hash::const_iterator a = ServerInstance->Users->clientlist->begin(); a != ServerInstance->Users->clientlist->end(); a++)
 +                      const user_hash& users = ServerInstance->Users->GetUsers();
 +                      for (user_hash::const_iterator a = users.begin(); a != users.end(); ++a)
                        {
                                if (InspIRCd::Match(a->second->host, parameters[0], ascii_case_insensitive_map) || InspIRCd::Match(a->second->dhost, parameters[0], ascii_case_insensitive_map))
                                {
        }
  };
  
 -
  class ModuleCheck : public Module
  {
 - private:
        CommandCheck mycommand;
   public:
        ModuleCheck() : mycommand(this)
        {
        }
  
 -      void init()
 -      {
 -              ServerInstance->Modules->AddService(mycommand);
 -      }
 -
 -      ~ModuleCheck()
 -      {
 -      }
 -
 -      void ProtoSendMode(void* uv, TargetTypeFlags, void*, const std::vector<std::string>& result, const std::vector<TranslateType>&)
 -      {
 -              User* user = (User*)uv;
 -              std::string checkstr(":");
 -              checkstr.append(ServerInstance->Config->ServerName);
 -              checkstr.append(" 304 ");
 -              checkstr.append(user->nick);
 -              checkstr.append(" :CHECK modelist");
 -              for(unsigned int i=0; i < result.size(); i++)
 -              {
 -                      checkstr.append(" ");
 -                      checkstr.append(result[i]);
 -              }
 -              user->SendText(checkstr);
 -      }
 -
 -      Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("CHECK command, view user, channel, IP address or hostname information", VF_VENDOR|VF_OPTCOMMON);
        }
index 2b8d1306cd5f544265f4106063eb895a03db2742,829c1d3378ce91a3229f4de51ef15a6fc7a47611..f011fa44929320dc8de13a79dc32296d8367fba7
@@@ -25,6 -25,8 +25,6 @@@
  
  #include "inspircd.h"
  
 -/* $ModDesc: Provides support for the /DCCALLOW command */
 -
  class BannedFileList
  {
   public:
@@@ -51,16 -53,12 +51,16 @@@ typedef std::vector<DCCAllow> dccallowl
  dccallowlist* dl;
  typedef std::vector<BannedFileList> bannedfilelist;
  bannedfilelist bfl;
 -SimpleExtItem<dccallowlist>* ext;
 +typedef SimpleExtItem<dccallowlist> DCCAllowExt;
  
  class CommandDccallow : public Command
  {
 +      DCCAllowExt& ext;
 +
   public:
 -      CommandDccallow(Module* parent) : Command(parent, "DCCALLOW", 0)
 +      CommandDccallow(Module* parent, DCCAllowExt& Ext)
 +              : Command(parent, "DCCALLOW", 0)
 +              , ext(Ext)
        {
                syntax = "[(+|-)<nick> [<time>]]|[LIST|HELP]";
                /* XXX we need to fix this so it can work with translation stuff (i.e. move +- into a seperate param */
                                }
                                else
                                {
 -                                      user->WriteNumeric(998, "%s :DCCALLOW command not understood. For help on DCCALLOW, type /DCCALLOW HELP", user->nick.c_str());
 +                                      user->WriteNumeric(998, ":DCCALLOW command not understood. For help on DCCALLOW, type /DCCALLOW HELP");
                                        return CMD_FAILURE;
                                }
                        }
  
 -                      std::string nick = parameters[0].substr(1);
 +                      std::string nick(parameters[0], 1);
                        User *target = ServerInstance->FindNickOnly(nick);
  
                        if ((target) && (!IS_SERVER(target)) && (!target->quitting) && (target->registered == REG_ALL))
                                if (action == '-')
                                {
                                        // check if it contains any entries
 -                                      dl = ext->get(user);
 +                                      dl = ext.get(user);
                                        if (dl)
                                        {
                                                for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
                                                        if (i->nickname == target->nick)
                                                        {
                                                                dl->erase(i);
 -                                                              user->WriteNumeric(995, "%s %s :Removed %s from your DCCALLOW list", user->nick.c_str(), user->nick.c_str(), target->nick.c_str());
 +                                                              user->WriteNumeric(995, "%s :Removed %s from your DCCALLOW list", user->nick.c_str(), target->nick.c_str());
                                                                break;
                                                        }
                                                }
                                {
                                        if (target == user)
                                        {
 -                                              user->WriteNumeric(996, "%s %s :You cannot add yourself to your own DCCALLOW list!", user->nick.c_str(), user->nick.c_str());
 +                                              user->WriteNumeric(996, "%s :You cannot add yourself to your own DCCALLOW list!", user->nick.c_str());
                                                return CMD_FAILURE;
                                        }
  
 -                                      dl = ext->get(user);
 +                                      dl = ext.get(user);
                                        if (!dl)
                                        {
                                                dl = new dccallowlist;
 -                                              ext->set(user, dl);
 +                                              ext.set(user, dl);
                                                // add this user to the userlist
                                                ul.push_back(user);
                                        }
                                        {
                                                if (k->nickname == target->nick)
                                                {
 -                                                      user->WriteNumeric(996, "%s %s :%s is already on your DCCALLOW list", user->nick.c_str(), user->nick.c_str(), target->nick.c_str());
 +                                                      user->WriteNumeric(996, "%s :%s is already on your DCCALLOW list", user->nick.c_str(), target->nick.c_str());
                                                        return CMD_FAILURE;
                                                }
                                        }
                                        std::string mask = target->nick+"!"+target->ident+"@"+target->dhost;
                                        std::string default_length = ServerInstance->Config->ConfValue("dccallow")->getString("length");
  
 -                                      long length;
 +                                      unsigned long length;
                                        if (parameters.size() < 2)
                                        {
 -                                              length = ServerInstance->Duration(default_length);
 +                                              length = InspIRCd::Duration(default_length);
                                        }
                                        else if (!atoi(parameters[1].c_str()))
                                        {
                                        }
                                        else
                                        {
 -                                              length = ServerInstance->Duration(parameters[1]);
 +                                              length = InspIRCd::Duration(parameters[1]);
                                        }
  
 -                                      if (!ServerInstance->IsValidMask(mask))
 +                                      if (!InspIRCd::IsValidMask(mask))
                                        {
                                                return CMD_FAILURE;
                                        }
  
                                        if (length > 0)
                                        {
 -                                              user->WriteNumeric(993, "%s %s :Added %s to DCCALLOW list for %ld seconds", user->nick.c_str(), user->nick.c_str(), target->nick.c_str(), length);
 +                                              user->WriteNumeric(993, "%s :Added %s to DCCALLOW list for %ld seconds", user->nick.c_str(), target->nick.c_str(), length);
                                        }
                                        else
                                        {
 -                                              user->WriteNumeric(994, "%s %s :Added %s to DCCALLOW list for this session", user->nick.c_str(), user->nick.c_str(), target->nick.c_str());
 +                                              user->WriteNumeric(994, "%s :Added %s to DCCALLOW list for this session", user->nick.c_str(), target->nick.c_str());
                                        }
  
                                        /* route it. */
                        else
                        {
                                // nick doesn't exist
 -                              user->WriteNumeric(401, "%s %s :No such nick/channel", user->nick.c_str(), nick.c_str());
 +                              user->WriteNumeric(401, "%s :No such nick/channel", nick.c_str());
                                return CMD_FAILURE;
                        }
                }
  
        void DisplayHelp(User* user)
        {
 -              user->WriteNumeric(998, "%s :DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP]", user->nick.c_str());
 -              user->WriteNumeric(998, "%s :You may allow DCCs from specific users by specifying a", user->nick.c_str());
 -              user->WriteNumeric(998, "%s :DCC allow for the user you want to receive DCCs from.", user->nick.c_str());
 -              user->WriteNumeric(998, "%s :For example, to allow the user Brain to send you inspircd.exe", user->nick.c_str());
 -              user->WriteNumeric(998, "%s :you would type:", user->nick.c_str());
 -              user->WriteNumeric(998, "%s :/DCCALLOW +Brain", user->nick.c_str());
 -              user->WriteNumeric(998, "%s :Brain would then be able to send you files. They would have to", user->nick.c_str());
 -              user->WriteNumeric(998, "%s :resend the file again if the server gave them an error message", user->nick.c_str());
 -              user->WriteNumeric(998, "%s :before you added them to your DCCALLOW list.", user->nick.c_str());
 -              user->WriteNumeric(998, "%s :DCCALLOW entries will be temporary by default, if you want to add", user->nick.c_str());
 -              user->WriteNumeric(998, "%s :them to your DCCALLOW list until you leave IRC, type:", user->nick.c_str());
 -              user->WriteNumeric(998, "%s :/DCCALLOW +Brain 0", user->nick.c_str());
 -              user->WriteNumeric(998, "%s :To remove the user from your DCCALLOW list, type:", user->nick.c_str());
 -              user->WriteNumeric(998, "%s :/DCCALLOW -Brain", user->nick.c_str());
 -              user->WriteNumeric(998, "%s :To see the users in your DCCALLOW list, type:", user->nick.c_str());
 -              user->WriteNumeric(998, "%s :/DCCALLOW LIST", user->nick.c_str());
 -              user->WriteNumeric(998, "%s :NOTE: If the user leaves IRC or changes their nickname", user->nick.c_str());
 -              user->WriteNumeric(998, "%s :  they will be removed from your DCCALLOW list.", user->nick.c_str());
 -              user->WriteNumeric(998, "%s :  your DCCALLOW list will be deleted when you leave IRC.", user->nick.c_str());
 -              user->WriteNumeric(999, "%s :End of DCCALLOW HELP", user->nick.c_str());
 +              user->WriteNumeric(998, ":DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP]");
 +              user->WriteNumeric(998, ":You may allow DCCs from specific users by specifying a");
 +              user->WriteNumeric(998, ":DCC allow for the user you want to receive DCCs from.");
 +              user->WriteNumeric(998, ":For example, to allow the user Brain to send you inspircd.exe");
 +              user->WriteNumeric(998, ":you would type:");
 +              user->WriteNumeric(998, ":/DCCALLOW +Brain");
 +              user->WriteNumeric(998, ":Brain would then be able to send you files. They would have to");
 +              user->WriteNumeric(998, ":resend the file again if the server gave them an error message");
 +              user->WriteNumeric(998, ":before you added them to your DCCALLOW list.");
 +              user->WriteNumeric(998, ":DCCALLOW entries will be temporary by default, if you want to add");
 +              user->WriteNumeric(998, ":them to your DCCALLOW list until you leave IRC, type:");
 +              user->WriteNumeric(998, ":/DCCALLOW +Brain 0");
 +              user->WriteNumeric(998, ":To remove the user from your DCCALLOW list, type:");
 +              user->WriteNumeric(998, ":/DCCALLOW -Brain");
 +              user->WriteNumeric(998, ":To see the users in your DCCALLOW list, type:");
 +              user->WriteNumeric(998, ":/DCCALLOW LIST");
 +              user->WriteNumeric(998, ":NOTE: If the user leaves IRC or changes their nickname");
 +              user->WriteNumeric(998, ":  they will be removed from your DCCALLOW list.");
 +              user->WriteNumeric(998, ":  your DCCALLOW list will be deleted when you leave IRC.");
 +              user->WriteNumeric(999, ":End of DCCALLOW HELP");
  
                LocalUser* localuser = IS_LOCAL(user);
                if (localuser)
        void DisplayDCCAllowList(User* user)
        {
                 // display current DCCALLOW list
 -              user->WriteNumeric(990, "%s :Users on your DCCALLOW list:", user->nick.c_str());
 +              user->WriteNumeric(990, ":Users on your DCCALLOW list:");
  
 -              dl = ext->get(user);
 +              dl = ext.get(user);
                if (dl)
                {
                        for (dccallowlist::const_iterator c = dl->begin(); c != dl->end(); ++c)
                        {
 -                              user->WriteNumeric(991, "%s %s :%s (%s)", user->nick.c_str(), user->nick.c_str(), c->nickname.c_str(), c->hostmask.c_str());
 +                              user->WriteNumeric(991, "%s :%s (%s)", user->nick.c_str(), c->nickname.c_str(), c->hostmask.c_str());
                        }
                }
  
 -              user->WriteNumeric(992, "%s :End of DCCALLOW list", user->nick.c_str());
 +              user->WriteNumeric(992, ":End of DCCALLOW list");
        }
  
  };
  
  class ModuleDCCAllow : public Module
  {
 +      DCCAllowExt ext;
        CommandDccallow cmd;
 - public:
  
 + public:
        ModuleDCCAllow()
 -              : cmd(this)
 -      {
 -              ext = NULL;
 -      }
 -
 -      void init()
 -      {
 -              ext = new SimpleExtItem<dccallowlist>("dccallow", this);
 -              ServerInstance->Modules->AddService(*ext);
 -              ServerInstance->Modules->AddService(cmd);
 -              ReadFileConf();
 -              Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_OnUserQuit, I_OnUserPostNick, I_OnRehash };
 -              ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
 -      }
 -
 -      virtual void OnRehash(User* user)
 +              : ext("dccallow", ExtensionItem::EXT_USER, this)
 +              , cmd(this, ext)
        {
 -              ReadFileConf();
        }
  
 -      virtual void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
 +      void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE
        {
 -              dccallowlist* udl = ext->get(user);
 +              dccallowlist* udl = ext.get(user);
  
                // remove their DCCALLOW list if they have one
                if (udl)
 -              {
 -                      userlist::iterator it = std::find(ul.begin(), ul.end(), user);
 -                      if (it != ul.end())
 -                              ul.erase(it);
 -              }
 +                      stdalgo::erase(ul, user);
  
                // remove them from any DCCALLOW lists
                // they are currently on
                RemoveNick(user);
        }
  
 -      virtual void OnUserPostNick(User* user, const std::string &oldnick)
 +      void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
        {
                RemoveNick(user);
        }
  
 -      virtual ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list)
 -      {
 -              return OnUserPreNotice(user, dest, target_type, text, status, exempt_list);
 -      }
 -
 -      virtual ModResult OnUserPreNotice(User* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list)
 +      ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
        {
                if (!IS_LOCAL(user))
                        return MOD_RES_PASSTHRU;
  
                                if (strncmp(text.c_str(), "\1DCC ", 5) == 0)
                                {
 -                                      dl = ext->get(u);
 +                                      dl = ext.get(u);
                                        if (dl && dl->size())
                                        {
                                                for (dccallowlist::const_iterator iter = dl->begin(); iter != dl->end(); ++iter)
                                        while (ss >> buf)
                                                tokens.push_back(buf);
  
+                                       if (tokens.size() < 2)
+                                               return MOD_RES_PASSTHRU;
                                        irc::string type = tokens[1].c_str();
  
                                        ConfigTag* conftag = ServerInstance->Config->ConfValue("dccallow");
  
                                        if (type == "SEND")
                                        {
+                                               if (tokens.size() < 3)
+                                                       return MOD_RES_PASSTHRU;
                                                std::string defaultaction = conftag->getString("action");
                                                std::string filename = tokens[2];
  
                                                if ((!found) && (defaultaction == "allow"))
                                                        return MOD_RES_PASSTHRU;
  
 -                                              user->WriteServ("NOTICE %s :The user %s is not accepting DCC SENDs from you. Your file %s was not sent.", user->nick.c_str(), u->nick.c_str(), filename.c_str());
 -                                              u->WriteServ("NOTICE %s :%s (%s@%s) attempted to send you a file named %s, which was blocked.", u->nick.c_str(), user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), filename.c_str());
 -                                              u->WriteServ("NOTICE %s :If you trust %s and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.", u->nick.c_str(), user->nick.c_str());
 +                                              user->WriteNotice("The user " + u->nick + " is not accepting DCC SENDs from you. Your file " + filename + " was not sent.");
 +                                              u->WriteNotice(user->nick + " (" + user->ident + "@" + user->dhost + ") attempted to send you a file named " + filename + ", which was blocked.");
 +                                              u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
                                                return MOD_RES_DENY;
                                        }
                                        else if ((type == "CHAT") && (blockchat))
                                        {
 -                                              user->WriteServ("NOTICE %s :The user %s is not accepting DCC CHAT requests from you.", user->nick.c_str(), u->nick.c_str());
 -                                              u->WriteServ("NOTICE %s :%s (%s@%s) attempted to initiate a DCC CHAT session, which was blocked.", u->nick.c_str(), user->nick.c_str(), user->ident.c_str(), user->dhost.c_str());
 -                                              u->WriteServ("NOTICE %s :If you trust %s and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.", u->nick.c_str(), user->nick.c_str());
 +                                              user->WriteNotice("The user " + u->nick + " is not accepting DCC CHAT requests from you.");
 +                                              u->WriteNotice(user->nick + " (" + user->ident + "@" + user->dhost + ") attempted to initiate a DCC CHAT session, which was blocked.");
 +                                              u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
                                                return MOD_RES_DENY;
                                        }
                                }
                for (userlist::iterator iter = ul.begin(); iter != ul.end();)
                {
                        User* u = (User*)(*iter);
 -                      dl = ext->get(u);
 +                      dl = ext.get(u);
                        if (dl)
                        {
                                if (dl->size())
                                        {
                                                if (iter2->length != 0 && (iter2->set_on + iter2->length) <= ServerInstance->Time())
                                                {
 -                                                      u->WriteNumeric(997, "%s %s :DCCALLOW entry for %s has expired", u->nick.c_str(), u->nick.c_str(), iter2->nickname.c_str());
 +                                                      u->WriteNumeric(997, "%s :DCCALLOW entry for %s has expired", u->nick.c_str(), iter2->nickname.c_str());
                                                        iter2 = dl->erase(iter2);
                                                }
                                                else
                for (userlist::iterator iter = ul.begin(); iter != ul.end();)
                {
                        User *u = (User*)(*iter);
 -                      dl = ext->get(u);
 +                      dl = ext.get(u);
                        if (dl)
                        {
                                if (dl->size())
                                                if (i->nickname == user->nick)
                                                {
  
 -                                                      u->WriteServ("NOTICE %s :%s left the network or changed their nickname and has been removed from your DCCALLOW list", u->nick.c_str(), i->nickname.c_str());
 -                                                      u->WriteNumeric(995, "%s %s :Removed %s from your DCCALLOW list", u->nick.c_str(), u->nick.c_str(), i->nickname.c_str());
 +                                                      u->WriteNotice(i->nickname + " left the network or changed their nickname and has been removed from your DCCALLOW list");
 +                                                      u->WriteNumeric(995, "%s :Removed %s from your DCCALLOW list", u->nick.c_str(), i->nickname.c_str());
                                                        dl->erase(i);
                                                        break;
                                                }
                }
        }
  
 -      void ReadFileConf()
 +      void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                bfl.clear();
                ConfigTagList tags = ServerInstance->Config->ConfTags("banfile");
                }
        }
  
 -      virtual ~ModuleDCCAllow()
 -      {
 -              delete ext;
 -      }
 -
 -      virtual Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for the /DCCALLOW command", VF_COMMON | VF_VENDOR);
        }
index 8ee8472e676fb28c2d81e34710a1678cacb6954a,aed65045f58d78c9d5577e3a66e6a6834844de64..a3f3242f060745ad7e6e3180fee84384ee0956d3
@@@ -22,6 -22,8 +22,6 @@@
   */
  
  
 -/* $ModDesc: Allows global loading of a module. */
 -
  #include "inspircd.h"
  
  /** Handle /GLOADMODULE
@@@ -33,6 -35,7 +33,6 @@@ class CommandGloadmodule : public Comma
        {
                flags_needed = 'o';
                syntax = "<modulename> [servermask]";
 -              TRANSLATE3(TR_TEXT, TR_TEXT, TR_END);
        }
  
        CmdResult Handle (const std::vector<std::string> &parameters, User *user)
                        if (ServerInstance->Modules->Load(parameters[0].c_str()))
                        {
                                ServerInstance->SNO->WriteToSnoMask('a', "NEW MODULE '%s' GLOBALLY LOADED BY '%s'",parameters[0].c_str(), user->nick.c_str());
 -                              user->WriteNumeric(975, "%s %s :Module successfully loaded.",user->nick.c_str(), parameters[0].c_str());
 +                              user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module successfully loaded.", parameters[0].c_str());
                        }
                        else
                        {
 -                              user->WriteNumeric(974, "%s %s :%s",user->nick.c_str(), parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
 +                              user->WriteNumeric(ERR_CANTLOADMODULE, "%s :%s", parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
                        }
                }
                else
@@@ -76,13 -79,6 +76,13 @@@ class CommandGunloadmodule : public Com
  
        CmdResult Handle (const std::vector<std::string> &parameters, User *user)
        {
 +              if (!ServerInstance->Config->ConfValue("security")->getBool("allowcoreunload") &&
 +                      InspIRCd::Match(parameters[0], "core_*.so", ascii_case_insensitive_map))
 +              {
 +                      user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :You cannot unload core commands!", parameters[0].c_str());
 +                      return CMD_FAILURE;
 +              }
 +
                std::string servername = parameters.size() > 1 ? parameters[1] : "*";
  
                if (InspIRCd::Match(ServerInstance->Config->ServerName.c_str(), servername))
                                }
                                else
                                {
 -                                      user->WriteNumeric(972, "%s %s :%s",user->nick.c_str(), parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
 +                                      user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :%s", parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
                                }
                        }
                        else
 -                              user->SendText(":%s 972 %s %s :No such module", ServerInstance->Config->ServerName.c_str(), user->nick.c_str(), parameters[0].c_str());
 +                              user->SendText(":%s %03d %s %s :No such module", ServerInstance->Config->ServerName.c_str(), ERR_CANTUNLOADMODULE, user->nick.c_str(), parameters[0].c_str());
                }
                else
                        ServerInstance->SNO->WriteToSnoMask('a', "MODULE '%s' GLOBAL UNLOAD BY '%s' (not unloaded here)",parameters[0].c_str(), user->nick.c_str());
@@@ -129,8 -125,8 +129,8 @@@ class GReloadModuleWorker : public Hand
                ServerInstance->SNO->WriteToSnoMask('a', "MODULE '%s' GLOBALLY RELOADED BY '%s'%s", name.c_str(), nick.c_str(), result ? "" : " (failed here)");
                User* user = ServerInstance->FindNick(uid);
                if (user)
 -                      user->WriteNumeric(975, "%s %s :Module %ssuccessfully reloaded.",
 -                              user->nick.c_str(), name.c_str(), result ? "" : "un");
 +                      user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module %ssuccessfully reloaded.",
 +                              name.c_str(), result ? "" : "un");
                ServerInstance->GlobalCulls.AddItem(this);
        }
  };
@@@ -155,13 -151,13 +155,13 @@@ class CommandGreloadmodule : public Com
                        if (m)
                        {
                                GReloadModuleWorker* worker = NULL;
-                               if (m != creator)
+                               if ((m != creator) && (!creator->dying))
                                        worker = new GReloadModuleWorker(user->nick, user->uuid, parameters[0]);
                                ServerInstance->Modules->Reload(m, worker);
                        }
                        else
                        {
 -                              user->WriteNumeric(975, "%s %s :Could not find module by that name", user->nick.c_str(), parameters[0].c_str());
 +                              user->WriteNumeric(RPL_LOADEDMODULE, "%s :Could not find module by that name", parameters[0].c_str());
                                return CMD_FAILURE;
                        }
                }
@@@ -189,10 -185,22 +189,10 @@@ class ModuleGlobalLoad : public Modul
        {
        }
  
 -      void init()
 -      {
 -              ServerInstance->Modules->AddService(cmd1);
 -              ServerInstance->Modules->AddService(cmd2);
 -              ServerInstance->Modules->AddService(cmd3);
 -      }
 -
 -      ~ModuleGlobalLoad()
 -      {
 -      }
 -
 -      Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Allows global loading of a module.", VF_COMMON | VF_VENDOR);
        }
  };
  
  MODULE_INIT(ModuleGlobalLoad)
 -
index 5b226f3b8baf733036314a2b814130ca552342b8,88b0c4cdffbf544f5ed12a1a5746162cdf152a6d..81b9b888f4fff7724b8e1c8acb0d31bcc0194ff7
@@@ -21,6 -21,8 +21,6 @@@
  
  #include "inspircd.h"
  
 -/* $ModDesc: Provides support for hiding oper status with user mode +H */
 -
  /** Handles user mode +H
   */
  class HideOper : public SimpleUserModeHandler
@@@ -41,12 -43,24 +41,12 @@@ class ModuleHideOper : public Modul
        {
        }
  
 -      void init()
 -      {
 -              ServerInstance->Modules->AddService(hm);
 -              Implementation eventlist[] = { I_OnWhoisLine, I_OnSendWhoLine, I_OnStats };
 -              ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
 -      }
 -
 -
 -      virtual ~ModuleHideOper()
 -      {
 -      }
 -
 -      virtual Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for hiding oper status with user mode +H", VF_VENDOR);
        }
  
 -      ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text)
 +      ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text) CXX11_OVERRIDE
        {
                /* Dont display numeric 313 (RPL_WHOISOPER) if they have +H set and the
                 * person doing the WHOIS is not an oper
@@@ -54,7 -68,7 +54,7 @@@
                if (numeric != 313)
                        return MOD_RES_PASSTHRU;
  
 -              if (!dest->IsModeSet('H'))
 +              if (!dest->IsModeSet(hm))
                        return MOD_RES_PASSTHRU;
  
                if (!user->HasPrivPermission("users/auspex"))
                return MOD_RES_PASSTHRU;
        }
  
 -      void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, std::string& line)
 +      void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, std::string& line) CXX11_OVERRIDE
        {
 -              if (user->IsModeSet('H') && !source->HasPrivPermission("users/auspex"))
 +              if (user->IsModeSet(hm) && !source->HasPrivPermission("users/auspex"))
                {
                        // hide the "*" that marks the user as an oper from the /WHO line
-                       std::string::size_type pos = line.find("*");
+                       std::string::size_type spcolon = line.find(" :");
+                       if (spcolon == std::string::npos)
+                               return; // Another module hid the user completely
+                       std::string::size_type sp = line.rfind(' ', spcolon-1);
+                       std::string::size_type pos = line.find('*', sp);
                        if (pos != std::string::npos)
                                line.erase(pos, 1);
                        // hide the line completely if doing a "/who * o" query
                }
        }
  
 -      ModResult OnStats(char symbol, User* user, string_list &results)
 +      ModResult OnStats(char symbol, User* user, string_list& results) CXX11_OVERRIDE
        {
                if (symbol != 'P')
                        return MOD_RES_PASSTHRU;
  
                unsigned int count = 0;
 -              for (std::list<User*>::const_iterator i = ServerInstance->Users->all_opers.begin(); i != ServerInstance->Users->all_opers.end(); ++i)
 +              const UserManager::OperList& opers = ServerInstance->Users->all_opers;
 +              for (UserManager::OperList::const_iterator i = opers.begin(); i != opers.end(); ++i)
                {
                        User* oper = *i;
 -                      if (!ServerInstance->ULine(oper->server) && (IS_OPER(user) || !oper->IsModeSet('H')))
 +                      if (!oper->server->IsULine() && (user->IsOper() || !oper->IsModeSet(hm)))
                        {
 -                              results.push_back(ServerInstance->Config->ServerName+" 249 " + user->nick + " :" + oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " +
 -                                              (IS_LOCAL(oper) ? ConvToStr(ServerInstance->Time() - oper->idle_lastmsg) + " secs" : "unavailable"));
 +                              LocalUser* lu = IS_LOCAL(oper);
 +                              results.push_back("249 " + user->nick + " :" + oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " +
 +                                              (lu ? ConvToStr(ServerInstance->Time() - lu->idle_lastmsg) + " secs" : "unavailable"));
                                count++;
                        }
                }
 -              results.push_back(ServerInstance->Config->ServerName+" 249 "+user->nick+" :"+ConvToStr(count)+" OPER(s)");
 +              results.push_back("249 "+user->nick+" :"+ConvToStr(count)+" OPER(s)");
  
                return MOD_RES_DENY;
        }
  };
  
 -
  MODULE_INIT(ModuleHideOper)
diff --combined src/modules/m_httpd.cpp
index e09ca3fa2c03c604f644736e7c839065ff08be00,2b079c6ff9fe19d017d8347332f38d4b0a66fe95..aa83b120cee1b6a51c97500bffb6c390389ce74b
  
  
  #include "inspircd.h"
 -#include "httpd.h"
 -
 -/* $ModDesc: Provides HTTP serving facilities to modules */
 -/* $ModDep: httpd.h */
 +#include "iohook.h"
 +#include "modules/httpd.h"
  
  class ModuleHttpServer;
  
  static ModuleHttpServer* HttpModule;
 -static bool claimed;
 -static std::set<HttpServerSocket*> sockets;
 +static insp::intrusive_list<HttpServerSocket> sockets;
 +static Events::ModuleEventProvider* aclevprov;
 +static Events::ModuleEventProvider* reqevprov;
  
  /** HTTP socket states
   */
@@@ -44,7 -45,7 +44,7 @@@ enum HttpStat
  
  /** A socket used for HTTP transport
   */
 -class HttpServerSocket : public BufferedSocket
 +class HttpServerSocket : public BufferedSocket, public Timer, public insp::intrusive_list_node<HttpServerSocket>
  {
        HttpState InternalState;
        std::string ip;
        std::string uri;
        std::string http_version;
  
 - public:
 -      const time_t createtime;
 +      /** True if this object is in the cull list
 +       */
 +      bool waitingcull;
 +
 +      bool Tick(time_t currtime) CXX11_OVERRIDE
 +      {
 +              AddToCull();
 +              return false;
 +      }
  
 -      HttpServerSocket(int newfd, const std::string& IP, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
 -              : BufferedSocket(newfd), ip(IP), postsize(0)
 -              , createtime(ServerInstance->Time())
 + public:
 +      HttpServerSocket(int newfd, const std::string& IP, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server, unsigned int timeoutsec)
 +              : BufferedSocket(newfd)
 +              , Timer(timeoutsec)
 +              , InternalState(HTTP_SERVE_WAIT_REQUEST)
 +              , ip(IP)
 +              , postsize(0)
 +              , waitingcull(false)
        {
 -              InternalState = HTTP_SERVE_WAIT_REQUEST;
 +              ServerInstance->Timers.AddTimer(this);
  
 -              FOREACH_MOD(I_OnHookIO, OnHookIO(this, via));
 -              if (GetIOHook())
 -                      GetIOHook()->OnStreamSocketAccept(this, client, server);
 +              if (via->iohookprov)
 +                      via->iohookprov->OnAccept(this, client, server);
        }
  
        ~HttpServerSocket()
@@@ -87,9 -77,9 +87,9 @@@
                sockets.erase(this);
        }
  
 -      virtual void OnError(BufferedSocketError)
 +      void OnError(BufferedSocketError) CXX11_OVERRIDE
        {
 -              ServerInstance->GlobalCulls.AddItem(this);
 +              AddToCull();
        }
  
        std::string Response(int response)
  
                WriteData(http_version + " "+ConvToStr(response)+" "+Response(response)+"\r\n");
  
 -              time_t local = ServerInstance->Time();
 -              struct tm *timeinfo = gmtime(&local);
 -              char *date = asctime(timeinfo);
 -              date[strlen(date) - 1] = '\0';
 -              rheaders.CreateHeader("Date", date);
 -
 -              rheaders.CreateHeader("Server", BRANCH);
 +              rheaders.CreateHeader("Date", InspIRCd::TimeString(ServerInstance->Time(), "%a, %d %b %Y %H:%M:%S GMT", true));
 +              rheaders.CreateHeader("Server", INSPIRCD_BRANCH);
                rheaders.SetHeader("Content-Length", ConvToStr(size));
  
                if (size)
  
                        if (reqbuffer.length() >= 8192)
                        {
 -                              ServerInstance->Logs->Log("m_httpd",DEBUG, "m_httpd dropped connection due to an oversized request buffer");
 +                              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "m_httpd dropped connection due to an oversized request buffer");
                                reqbuffer.clear();
                                SetError("Buffer");
                        }
                                continue;
                        }
  
 -                      std::string cheader = reqbuffer.substr(hbegin, hend - hbegin);
 +                      std::string cheader(reqbuffer, hbegin, hend - hbegin);
  
                        std::string::size_type fieldsep = cheader.find(':');
                        if ((fieldsep == std::string::npos) || (fieldsep == 0) || (fieldsep == cheader.length() - 1))
  
                        if (reqbuffer.length() >= postsize)
                        {
 -                              postdata = reqbuffer.substr(0, postsize);
 +                              postdata.assign(reqbuffer, 0, postsize);
                                reqbuffer.erase(0, postsize);
                        }
                        else if (!reqbuffer.empty())
        {
                InternalState = HTTP_SERVE_SEND_DATA;
  
 -              claimed = false;
 -              HTTPRequest acl((Module*)HttpModule, "httpd_acl", request_type, uri, &headers, this, ip, postdata);
 -              acl.Send();
 -              if (!claimed)
 +              ModResult MOD_RESULT;
 +              HTTPRequest acl(request_type, uri, &headers, this, ip, postdata);
 +              FIRST_MOD_RESULT_CUSTOM(*aclevprov, HTTPACLEventListener, OnHTTPACLCheck, MOD_RESULT, (acl));
 +              if (MOD_RESULT != MOD_RES_DENY)
                {
 -                      HTTPRequest url((Module*)HttpModule, "httpd_url", request_type, uri, &headers, this, ip, postdata);
 -                      url.Send();
 -                      if (!claimed)
 +                      HTTPRequest url(request_type, uri, &headers, this, ip, postdata);
 +                      FIRST_MOD_RESULT_CUSTOM(*reqevprov, HTTPRequestEventListener, OnHTTPRequest, MOD_RESULT, (url));
 +                      if (MOD_RESULT == MOD_RES_PASSTHRU)
                        {
                                SendHTTPError(404);
                        }
                SendHeaders(n->str().length(), response, *hheaders);
                WriteData(n->str());
        }
 +
 +      void AddToCull()
 +      {
 +              if (waitingcull)
 +                      return;
 +
 +              waitingcull = true;
 +              Close();
 +              ServerInstance->GlobalCulls.AddItem(this);
 +      }
 +};
 +
 +class HTTPdAPIImpl : public HTTPdAPIBase
 +{
 + public:
 +      HTTPdAPIImpl(Module* parent)
 +              : HTTPdAPIBase(parent)
 +      {
 +      }
 +
 +      void SendResponse(HTTPDocumentResponse& resp) CXX11_OVERRIDE
 +      {
 +              resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers);
 +      }
  };
  
  class ModuleHttpServer : public Module
  {
 +      HTTPdAPIImpl APIImpl;
        unsigned int timeoutsec;
 +      Events::ModuleEventProvider acleventprov;
 +      Events::ModuleEventProvider reqeventprov;
  
   public:
 -
 -      void init()
 +      ModuleHttpServer()
 +              : APIImpl(this)
 +              , acleventprov(this, "event/http-acl")
 +              , reqeventprov(this, "event/http-request")
        {
 -              HttpModule = this;
 -              Implementation eventlist[] = { I_OnAcceptConnection, I_OnBackgroundTimer, I_OnRehash, I_OnUnloadModule };
 -              ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
 -              OnRehash(NULL);
 +              aclevprov = &acleventprov;
 +              reqevprov = &reqeventprov;
        }
  
 -      void OnRehash(User* user)
 +      void init() CXX11_OVERRIDE
        {
 -              ConfigTag* tag = ServerInstance->Config->ConfValue("httpd");
 -              timeoutsec = tag->getInt("timeout");
 +              HttpModule = this;
        }
  
 -      void OnRequest(Request& request)
 +      void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
 -              if (strcmp(request.id, "HTTP-DOC") != 0)
 -                      return;
 -              HTTPDocumentResponse& resp = static_cast<HTTPDocumentResponse&>(request);
 -              claimed = true;
 -              resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers);
 +              ConfigTag* tag = ServerInstance->Config->ConfValue("httpd");
 +              timeoutsec = tag->getInt("timeout", 10, 1);
        }
  
 -      ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
 +      ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
        {
                if (from->bind_tag->getString("type") != "httpd")
                        return MOD_RES_PASSTHRU;
                int port;
                std::string incomingip;
                irc::sockets::satoap(*client, incomingip, port);
 -              sockets.insert(new HttpServerSocket(nfd, incomingip, from, client, server));
 +              sockets.push_front(new HttpServerSocket(nfd, incomingip, from, client, server, timeoutsec));
                return MOD_RES_ALLOW;
        }
  
 -      void OnBackgroundTimer(time_t curtime)
 -      {
 -              if (!timeoutsec)
 -                      return;
 -
 -              time_t oldest_allowed = curtime - timeoutsec;
 -              for (std::set<HttpServerSocket*>::const_iterator i = sockets.begin(); i != sockets.end(); )
 -              {
 -                      HttpServerSocket* sock = *i;
 -                      ++i;
 -                      if (sock->createtime < oldest_allowed)
 -                      {
 -                              sock->cull();
 -                              delete sock;
 -                      }
 -              }
 -      }
 -
+       void OnUnloadModule(Module* mod)
+       {
 -              for (std::set<HttpServerSocket*>::const_iterator i = sockets.begin(); i != sockets.end(); )
++              for (insp::intrusive_list<HttpServerSocket>::const_iterator i = sockets.begin(); i != sockets.end(); ++i)
+               {
+                       HttpServerSocket* sock = *i;
+                       ++i;
 -                      if (sock->GetIOHook() == mod)
++                      if (sock->GetIOHook() && sock->GetIOHook()->prov->creator == mod)
+                       {
+                               sock->cull();
+                               delete sock;
+                       }
+               }
+       }
 -      CullResult cull()
 +      CullResult cull() CXX11_OVERRIDE
        {
 -              std::set<HttpServerSocket*> local;
 -              local.swap(sockets);
 -              for (std::set<HttpServerSocket*>::const_iterator i = local.begin(); i != local.end(); ++i)
 +              for (insp::intrusive_list<HttpServerSocket>::const_iterator i = sockets.begin(); i != sockets.end(); ++i)
                {
                        HttpServerSocket* sock = *i;
 -                      sock->cull();
 -                      delete sock;
 +                      sock->AddToCull();
                }
                return Module::cull();
        }
  
 -      virtual Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides HTTP serving facilities to modules", VF_VENDOR);
        }
diff --combined src/modules/m_md5.cpp
index 6e6f5006fd552b50f1866af0428d49881936ef96,c902ee3cb753c8f9c527a0bfd3e381656261c766..6cec05a18e1b48efff4f012904178fcf00c620fe
   */
  
  
 -/* $ModDesc: Allows for MD5 encrypted oper passwords */
 -
  #include "inspircd.h"
 -#ifdef HAS_STDINT
 -#include <stdint.h>
 -#endif
 -#include "hash.h"
 +#include "modules/hash.h"
  
  /* The four core functions - F1 is optimized somewhat */
  #define F1(x, y, z) (z ^ (x & (y ^ z)))
  #define MD5STEP(f,w,x,y,z,in,s) \
        (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
  
 -#ifndef HAS_STDINT
 -typedef unsigned int uint32_t;
 -#endif
 -
  typedef uint32_t word32; /* NOT unsigned long. We don't support 16 bit platforms, anyway. */
  typedef unsigned char byte;
  
@@@ -154,7 -163,7 +154,7 @@@ class MD5Provider : public HashProvide
  
        void MD5Transform(word32 buf[4], word32 const in[16])
        {
-               register word32 a, b, c, d;
+               word32 a, b, c, d;
  
                a = buf[0];
                b = buf[1];
                MD5Final((unsigned char*)dest, &context);
        }
  
 -
 -      void GenHash(const char* src, char* dest, const char* xtab, unsigned int* ikey, size_t srclen)
 -      {
 -              unsigned char bytes[16];
 -
 -              MyMD5((char*)bytes, (void*)src, srclen, ikey);
 -
 -              for (int i = 0; i < 16; i++)
 -              {
 -                      *dest++ = xtab[bytes[i] / 16];
 -                      *dest++ = xtab[bytes[i] % 16];
 -              }
 -              *dest++ = 0;
 -      }
   public:
 -      std::string sum(const std::string& data)
 +      std::string GenerateRaw(const std::string& data)
        {
                char res[16];
                MyMD5(res, (void*)data.data(), data.length(), NULL);
                return std::string(res, 16);
        }
  
 -      std::string sumIV(unsigned int* IV, const char* HexMap, const std::string &sdata)
 -      {
 -              char res[33];
 -              GenHash(sdata.data(), res, HexMap, IV, sdata.length());
 -              return res;
 -      }
 -
 -      MD5Provider(Module* parent) : HashProvider(parent, "hash/md5", 16, 64) {}
 +      MD5Provider(Module* parent) : HashProvider(parent, "md5", 16, 64) {}
  };
  
  class ModuleMD5 : public Module
   public:
        ModuleMD5() : md5(this)
        {
 -              ServerInstance->Modules->AddService(md5);
        }
  
 -      Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Implements MD5 hashing",VF_VENDOR);
        }
index 4c63e53d1b109153d45560eacd2924c76a0c508b,9f1f6cc5e8f3f25863a940abde3513d133767d46..51281a528b6867e102c8ccd0dfdad43ed5835528
   * Originally by Chernov-Phoenix Alexey (Phoenix@RusNet) mailto:phoenix /email address separator/ pravmail.ru
   */
  
 -/* $ModDesc: Gives opers cmode +y which provides a staff prefix. */
 -
  #include "inspircd.h"
  
  #define OPERPREFIX_VALUE 1000000
  
 -class OperPrefixMode : public ModeHandler
 +class OperPrefixMode : public PrefixMode
  {
        public:
 -              OperPrefixMode(Module* Creator) : ModeHandler(Creator, "operprefix", 'y', PARAM_ALWAYS, MODETYPE_CHANNEL)
 +              OperPrefixMode(Module* Creator)
 +                      : PrefixMode(Creator, "operprefix", 'y', OPERPREFIX_VALUE)
                {
                        std::string pfx = ServerInstance->Config->ConfValue("operprefix")->getString("prefix", "!");
 -                      list = true;
                        prefix = pfx.empty() ? '!' : pfx[0];
 -                      levelrequired = OPERPREFIX_VALUE;
 -                      m_paramtype = TR_NICK;
 -              }
 -
 -              unsigned int GetPrefixRank()
 -              {
 -                      return OPERPREFIX_VALUE;
 +                      levelrequired = INT_MAX;
                }
 -
 -              ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
 -              {
 -                      if (IS_SERVER(source) || ServerInstance->ULine(source->server))
 -                              return MODEACTION_ALLOW;
 -                      else
 -                      {
 -                              if (channel)
 -                                      source->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :Only servers are permitted to change channel mode '%c'", source->nick.c_str(), channel->name.c_str(), 'y');
 -                              return MODEACTION_DENY;
 -                      }
 -              }
 -
 -              bool NeedsOper() { return true; }
  };
  
  class ModuleOperPrefixMode;
  class HideOperWatcher : public ModeWatcher
  {
        ModuleOperPrefixMode* parentmod;
 +
   public:
 -      HideOperWatcher(ModuleOperPrefixMode* parent) : ModeWatcher((Module*) parent, 'H', MODETYPE_USER), parentmod(parent) {}
 -      void AfterMode(User* source, User* dest, Channel* channel, const std::string &parameter, bool adding, ModeType type);
 +      HideOperWatcher(ModuleOperPrefixMode* parent);
 +      void AfterMode(User* source, User* dest, Channel* channel, const std::string &parameter, bool adding);
  };
  
  class ModuleOperPrefixMode : public Module
  {
 - private:
        OperPrefixMode opm;
 -      bool mw_added;
        HideOperWatcher hideoperwatcher;
 +      UserModeReference hideopermode;
 +
   public:
        ModuleOperPrefixMode()
 -              : opm(this), mw_added(false), hideoperwatcher(this)
 -      {
 -      }
 -
 -      void init()
 +              : opm(this), hideoperwatcher(this)
 +              , hideopermode(this, "hideoper")
        {
 -              ServerInstance->Modules->AddService(opm);
 -
 -              Implementation eventlist[] = { I_OnUserPreJoin, I_OnPostOper, I_OnLoadModule, I_OnUnloadModule, I_OnPostJoin };
 -              ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
 -
                /* To give clients a chance to learn about the new prefix we don't give +y to opers
                 * right now. That means if the module was loaded after opers have joined channels
                 * they need to rejoin them in order to get the oper prefix.
                 */
 -
 -              if (ServerInstance->Modules->Find("m_hideoper.so"))
 -                      mw_added = ServerInstance->Modes->AddModeWatcher(&hideoperwatcher);
        }
  
 -      ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string& privs, const std::string& keygiven)
 +      ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
        {
 -              /* The user may have the +H umode on himself, but +H does not necessarily correspond
 -               * to the +H of m_hideoper.
 -               * However we only add the modewatcher when m_hideoper is loaded, so these
 -               * conditions (mw_added and the user being +H) together mean the user is a hidden oper.
 -               */
 -
 -              if (IS_OPER(user) && (!mw_added || !user->IsModeSet('H')))
 +              if ((user->IsOper()) && (!user->IsModeSet(hideopermode)))
                        privs.push_back('y');
                return MOD_RES_PASSTHRU;
        }
  
 -              if ((!IS_LOCAL(memb->user)) || (!IS_OPER(memb->user)) || (((mw_added) && (memb->user->IsModeSet('H')))))
+       void OnPostJoin(Membership* memb)
+       {
 -              std::vector<std::string> modechange;
 -              modechange.push_back(memb->chan->name);
 -              modechange.push_back("+y");
 -              modechange.push_back(memb->user->nick);
 -              ServerInstance->SendGlobalMode(modechange, ServerInstance->FakeClient);
++              if ((!IS_LOCAL(memb->user)) || (!memb->user->IsOper()) || (memb->user->IsModeSet(hideopermode)))
+                       return;
+               if (memb->hasMode(opm.GetModeChar()))
+                       return;
+               // The user was force joined and OnUserPreJoin() did not run. Set the operprefix now.
++              Modes::ChangeList changelist;
++              changelist.push_add(&opm, memb->user->nick);
++              ServerInstance->Modes.Process(ServerInstance->FakeClient, memb->chan, NULL, changelist);
+       }
        void SetOperPrefix(User* user, bool add)
        {
 -              std::vector<std::string> modechange;
 -              modechange.push_back("");
 -              modechange.push_back(add ? "+y" : "-y");
 -              modechange.push_back(user->nick);
 -              for (UCListIter v = user->chans.begin(); v != user->chans.end(); v++)
 -              {
 -                      modechange[0] = (*v)->name;
 -                      ServerInstance->SendGlobalMode(modechange, ServerInstance->FakeClient);
 -              }
 +              Modes::ChangeList changelist;
 +              changelist.push(&opm, add, user->nick);
 +              for (User::ChanList::iterator v = user->chans.begin(); v != user->chans.end(); v++)
 +                      ServerInstance->Modes->Process(ServerInstance->FakeClient, (*v)->chan, NULL, changelist);
        }
  
 -      void OnPostOper(User* user, const std::string& opername, const std::string& opertype)
 +      void OnPostOper(User* user, const std::string& opername, const std::string& opertype) CXX11_OVERRIDE
        {
 -              if (IS_LOCAL(user) && (!mw_added || !user->IsModeSet('H')))
 +              if (IS_LOCAL(user) && (!user->IsModeSet(hideopermode)))
                        SetOperPrefix(user, true);
        }
  
 -      void OnLoadModule(Module* mod)
 -      {
 -              if ((!mw_added) && (mod->ModuleSourceFile == "m_hideoper.so"))
 -                      mw_added = ServerInstance->Modes->AddModeWatcher(&hideoperwatcher);
 -      }
 -
 -      void OnUnloadModule(Module* mod)
 -      {
 -              if ((mw_added) && (mod->ModuleSourceFile == "m_hideoper.so") && (ServerInstance->Modes->DelModeWatcher(&hideoperwatcher)))
 -                      mw_added = false;
 -      }
 -
 -      ~ModuleOperPrefixMode()
 -      {
 -              if (mw_added)
 -                      ServerInstance->Modes->DelModeWatcher(&hideoperwatcher);
 -      }
 -
 -      Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Gives opers cmode +y which provides a staff prefix.", VF_VENDOR);
        }
        }
  };
  
 -void HideOperWatcher::AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding, ModeType type)
 +HideOperWatcher::HideOperWatcher(ModuleOperPrefixMode* parent)
 +      : ModeWatcher(parent, "hideoper", MODETYPE_USER)
 +      , parentmod(parent)
 +{
 +}
 +
 +void HideOperWatcher::AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding)
  {
        // If hideoper is being unset because the user is deopering, don't set +y
 -      if (IS_LOCAL(dest) && IS_OPER(dest))
 +      if (IS_LOCAL(dest) && dest->IsOper())
                parentmod->SetOperPrefix(dest, !adding);
  }
  
diff --combined src/modules/m_sasl.cpp
index c96b8703400c75ebd3d4bc66ddf3fbff0730692f,32c9afc79ec05e36be5eeecd07f91f6f75e94ca3..341b3aea7166139068f75039e4ebc1b101e98bb4
  
  
  #include "inspircd.h"
 -#include "m_cap.h"
 -#include "account.h"
 -#include "sasl.h"
 -#include "ssl.h"
 -
 -/* $ModDesc: Provides support for IRC Authentication Layer (aka: atheme SASL) via AUTHENTICATE. */
 +#include "modules/cap.h"
 +#include "modules/account.h"
 +#include "modules/sasl.h"
 +#include "modules/ssl.h"
  
  enum SaslState { SASL_INIT, SASL_COMM, SASL_DONE };
  enum SaslResult { SASL_OK, SASL_FAIL, SASL_ABORT };
  
  static std::string sasl_target = "*";
 +static Events::ModuleEventProvider* saslevprov;
  
  static void SendSASL(const parameterlist& params)
  {
 -      if (!ServerInstance->PI->SendEncapsulatedData(params))
 +      if (!ServerInstance->PI->SendEncapsulatedData(sasl_target, "SASL", params))
        {
 -              SASLFallback(NULL, params);
 +              FOREACH_MOD_CUSTOM(*saslevprov, SASLEventListener, OnSASLAuth, (params));
        }
  }
  
@@@ -55,15 -56,17 +55,15 @@@ class SaslAuthenticato
                : user(user_), state(SASL_INIT), state_announced(false)
        {
                parameterlist params;
 -              params.push_back(sasl_target);
 -              params.push_back("SASL");
                params.push_back(user->uuid);
                params.push_back("*");
                params.push_back("S");
                params.push_back(method);
  
 -              if (method == "EXTERNAL" && IS_LOCAL(user_))
 +              LocalUser* localuser = IS_LOCAL(user);
 +              if (method == "EXTERNAL" && localuser)
                {
 -                      SocketCertificateRequest req(&((LocalUser*)user_)->eh, ServerInstance->Modules->Find("m_sasl.so"));
 -                      std::string fp = req.GetFingerprint();
 +                      std::string fp = SSLClientCert::GetFingerprint(&localuser->eh);
  
                        if (fp.size())
                                params.push_back(fp);
@@@ -96,6 -99,9 +96,9 @@@
                        if (msg[0] != this->agent)
                                return this->state;
  
+                       if (msg.size() < 4)
+                               return this->state;
                        if (msg[2] == "C")
                                this->user->Write("AUTHENTICATE %s", msg[3].c_str());
                        else if (msg[2] == "D")
                        else if (msg[2] == "M")
                                this->user->WriteNumeric(908, "%s %s :are available SASL mechanisms", this->user->nick.c_str(), msg[3].c_str());
                        else
 -                              ServerInstance->Logs->Log("m_sasl", DEFAULT, "Services sent an unknown SASL message \"%s\" \"%s\"", msg[2].c_str(), msg[3].c_str());
 +                              ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Services sent an unknown SASL message \"%s\" \"%s\"", msg[2].c_str(), msg[3].c_str());
  
                        break;
                 case SASL_DONE:
                        break;
                 default:
 -                      ServerInstance->Logs->Log("m_sasl", DEFAULT, "WTF: SaslState is not a known state (%d)", this->state);
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WTF: SaslState is not a known state (%d)", this->state);
                        break;
                }
  
                        return true;
  
                parameterlist params;
 -              params.push_back(sasl_target);
 -              params.push_back("SASL");
                params.push_back(this->user->uuid);
                params.push_back(this->agent);
                params.push_back("C");
                switch (this->result)
                {
                 case SASL_OK:
 -                      this->user->WriteNumeric(903, "%s :SASL authentication successful", this->user->nick.c_str());
 +                      this->user->WriteNumeric(903, ":SASL authentication successful");
                        break;
                 case SASL_ABORT:
 -                      this->user->WriteNumeric(906, "%s :SASL authentication aborted", this->user->nick.c_str());
 +                      this->user->WriteNumeric(906, ":SASL authentication aborted");
                        break;
                 case SASL_FAIL:
 -                      this->user->WriteNumeric(904, "%s :SASL authentication failed", this->user->nick.c_str());
 +                      this->user->WriteNumeric(904, ":SASL authentication failed");
                        break;
                 default:
                        break;
@@@ -218,7 -226,7 +221,7 @@@ class CommandSASL : public Comman
                User* target = ServerInstance->FindNick(parameters[1]);
                if ((!target) || (IS_SERVER(target)))
                {
 -                      ServerInstance->Logs->Log("m_sasl", DEBUG,"User not found in sasl ENCAP event: %s", parameters[1].c_str());
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User not found in sasl ENCAP event: %s", parameters[1].c_str());
                        return CMD_FAILURE;
                }
  
@@@ -247,31 -255,31 +250,31 @@@ class ModuleSASL : public Modul
        GenericCap cap;
        CommandAuthenticate auth;
        CommandSASL sasl;
 +      Events::ModuleEventProvider sasleventprov;
 +
   public:
        ModuleSASL()
 -              : authExt("sasl_auth", this), cap(this, "sasl"), auth(this, authExt, cap), sasl(this, authExt)
 +              : authExt("sasl_auth", ExtensionItem::EXT_USER, this)
 +              , cap(this, "sasl")
 +              , auth(this, authExt, cap)
 +              , sasl(this, authExt)
 +              , sasleventprov(this, "event/sasl")
        {
 +              saslevprov = &sasleventprov;
        }
  
 -      void init()
 +      void init() CXX11_OVERRIDE
        {
 -              OnRehash(NULL);
 -              Implementation eventlist[] = { I_OnEvent, I_OnUserRegister, I_OnRehash };
 -              ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
 -
 -              ServiceProvider* providelist[] = { &auth, &sasl, &authExt };
 -              ServerInstance->Modules->AddServices(providelist, 3);
 -
                if (!ServerInstance->Modules->Find("m_services_account.so") || !ServerInstance->Modules->Find("m_cap.so"))
 -                      ServerInstance->Logs->Log("m_sasl", DEFAULT, "WARNING: m_services_account.so and m_cap.so are not loaded! m_sasl.so will NOT function correctly until these two modules are loaded!");
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: m_services_account.so and m_cap.so are not loaded! m_sasl.so will NOT function correctly until these two modules are loaded!");
        }
  
 -      void OnRehash(User*)
 +      void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                sasl_target = ServerInstance->Config->ConfValue("sasl")->getString("target", "*");
        }
  
 -      ModResult OnUserRegister(LocalUser *user)
 +      ModResult OnUserRegister(LocalUser *user) CXX11_OVERRIDE
        {
                SaslAuthenticator *sasl_ = authExt.get(user);
                if (sasl_)
                return MOD_RES_PASSTHRU;
        }
  
 -      Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides support for IRC Authentication Layer (aka: atheme SASL) via AUTHENTICATE.",VF_VENDOR);
+               return Version("Provides support for IRC Authentication Layer (aka: SASL) via AUTHENTICATE.", VF_VENDOR);
        }
 -
 -      void OnEvent(Event &ev)
 -      {
 -              cap.HandleEvent(ev);
 -      }
  };
  
  MODULE_INIT(ModuleSASL)
index 25c1f66781f5716b1eabbbbe790f0fd8e5622cf1,47b39452268dfa8cc34923a98cfc36184726bb25..e29aa09d7ae5d9aa0c5fce8f59656dcd24c3707b
  #include "treeserver.h"
  #include "treesocket.h"
  
 +/** FJOIN builder for rebuilding incoming FJOINs and splitting them up into multiple messages if necessary
 + */
 +class FwdFJoinBuilder : public CommandFJoin::Builder
 +{
 +      TreeServer* const sourceserver;
 +
 + public:
 +      FwdFJoinBuilder(Channel* chan, TreeServer* server)
 +              : CommandFJoin::Builder(chan, server)
 +              , sourceserver(server)
 +      {
 +      }
 +
 +      void add(Membership* memb, std::string::const_iterator mbegin, std::string::const_iterator mend);
 +};
 +
  /** FJOIN, almost identical to TS6 SJOIN, except for nicklist handling. */
 -CmdResult CommandFJoin::Handle(const std::vector<std::string>& params, User *srcuser)
 +CmdResult CommandFJoin::Handle(User* srcuser, std::vector<std::string>& params)
  {
 -      SpanningTreeUtilities* Utils = ((ModuleSpanningTree*)(Module*)creator)->Utils;
 -      /* 1.1 FJOIN works as follows:
 +      /* 1.1+ FJOIN works as follows:
         *
         * Each FJOIN is sent along with a timestamp, and the side with the lowest
         * timestamp 'wins'. From this point on we will refer to this side as the
         * The winning side on the other hand will ignore all user modes from the
         * losing side, so only its own modes get applied. Life is simple for those
         * who succeed at internets. :-)
 +       *
 +       * Syntax:
 +       * :<sid> FJOIN <chan> <TS> <modes> :[<member> [<member> ...]]
 +       * The last parameter is a list consisting of zero or more channel members
 +       * (permanent channels may have zero users). Each entry on the list is in the
 +       * following format:
 +       * [[<modes>,]<uuid>[:<membid>]
 +       * <modes> is a concatenation of the mode letters the user has on the channel
 +       * (e.g.: "ov" if the user is opped and voiced). The order of the mode letters
 +       * are not important but if a server ecounters an unknown mode letter, it will
 +       * drop the link to avoid desync.
 +       *
 +       * InspIRCd 2.0 and older required a comma before the uuid even if the user
 +       * had no prefix modes on the channel, InspIRCd 2.2 and later does not require
 +       * a comma in this case anymore.
 +       *
 +       * <membid> is a positive integer representing the id of the membership.
 +       * If not present (in FJOINs coming from pre-1205 servers), 0 is assumed.
 +       *
 +       * Forwarding:
 +       * FJOIN messages are forwarded with the new TS and modes. Prefix modes of
 +       * members on the losing side are not forwarded.
 +       * This is required to only have one server on each side of the network who
 +       * decides the fate of a channel during a network merge. Otherwise, if the
 +       * clock of a server is slightly off it may make a different decision than
 +       * the rest of the network and desync.
 +       * The prefix modes are always forwarded as-is, or not at all.
 +       * One incoming FJOIN may result in more than one FJOIN being generated
 +       * and forwarded mainly due to compatibility reasons with non-InspIRCd
 +       * servers that don't handle more than 512 char long lines.
 +       *
 +       * Forwarding examples:
 +       * Existing channel #chan with TS 1000, modes +n.
 +       * Incoming:  :220 FJOIN #chan 1000 +t :o,220AAAAAB:0
 +       * Forwarded: :220 FJOIN #chan 1000 +nt :o,220AAAAAB:0
 +       * Merge modes and forward the result. Forward their prefix modes as well.
 +       *
 +       * Existing channel #chan with TS 1000, modes +nt.
 +       * Incoming:  :220 FJOIN #CHAN 2000 +i :ov,220AAAAAB:0 o,220AAAAAC:20
 +       * Forwarded: :220 FJOIN #chan 1000 +nt :,220AAAAAB:0 ,220AAAAAC:20
 +       * Drop their modes, forward our modes and TS, use our channel name
 +       * capitalization. Don't forward prefix modes.
 +       *
         */
  
 -      irc::modestacker modestack(true);                       /* Modes to apply from the users in the user list */
 -      User* who = NULL;                                               /* User we are currently checking */
 -      std::string channel = params[0];                                /* Channel name, as a string */
 -      time_t TS = atoi(params[1].c_str());                            /* Timestamp given to us for remote side */
 -      irc::tokenstream users((params.size() > 3) ? params[params.size() - 1] : "");   /* users from the user list */
 -      bool apply_other_sides_modes = true;                            /* True if we are accepting the other side's modes */
 -      Channel* chan = ServerInstance->FindChan(channel);              /* The channel we're sending joins to */
 -      bool created = !chan;                                           /* True if the channel doesnt exist here yet */
 -      std::string item;                                               /* One item in the list of nicks */
 -
 -      TreeServer* src_server = Utils->FindServer(srcuser->server);
 -      TreeSocket* src_socket = src_server->GetRoute()->GetSocket();
 +      time_t TS = ServerCommand::ExtractTS(params[1]);
  
 -      if (!TS)
 -      {
 -              ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"*** BUG? *** TS of 0 sent to FJOIN. Are some services authors smoking craq, or is it 1970 again?. Dropped.");
 -              ServerInstance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FJOIN with a TS of zero. Total craq. Command was dropped.", srcuser->server.c_str());
 -              return CMD_INVALID;
 -      }
 +      const std::string& channel = params[0];
 +      Channel* chan = ServerInstance->FindChan(channel);
 +      bool apply_other_sides_modes = true;
  
 -      if (created)
 +      if (!chan)
        {
                chan = new Channel(channel, TS);
 -              ServerInstance->SNO->WriteToSnoMask('d', "Creation FJOIN received for %s, timestamp: %lu", chan->name.c_str(), (unsigned long)TS);
        }
        else
        {
                time_t ourTS = chan->age;
 -
                if (TS != ourTS)
-                       ServerInstance->SNO->WriteToSnoMask('d', "Merge FJOIN received for %s, ourTS: %lu, TS: %lu, difference: %lu",
-                               chan->name.c_str(), (unsigned long)ourTS, (unsigned long)TS, (unsigned long)(ourTS - TS));
 +              {
 -              /* If our TS is less than theirs, we dont accept their modes */
 -              if (ourTS < TS)
 -              {
 -                      ServerInstance->SNO->WriteToSnoMask('d', "NOT Applying modes from other side");
 -                      apply_other_sides_modes = false;
 -              }
 -              else if (ourTS > TS)
 -              {
 -                      /* Our TS greater than theirs, clear all our modes from the channel, accept theirs. */
 -                      ServerInstance->SNO->WriteToSnoMask('d', "Removing our modes, accepting remote");
 -                      parameterlist param_list;
 -                      if (Utils->AnnounceTSChange)
 -                              chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name.c_str(), channel.c_str(), (unsigned long) ourTS, (unsigned long) TS);
 -                      // while the name is equal in case-insensitive compare, it might differ in case; use the remote version
 -                      chan->name = channel;
 -                      chan->age = TS;
 -                      chan->ClearInvites();
 -                      param_list.push_back(channel);
 -                      this->RemoveStatus(ServerInstance->FakeClient, param_list);
 -
 -                      // XXX: If the channel does not exist in the chan hash at this point, create it so the remote modes can be applied on it.
 -                      // This happens to 0-user permanent channels on the losing side, because those are removed (from the chan hash, then
 -                      // deleted later) as soon as the permchan mode is removed from them.
 -                      if (ServerInstance->FindChan(channel) == NULL)
+                       ServerInstance->SNO->WriteToSnoMask('d', "Merge FJOIN received for %s, ourTS: %lu, TS: %lu, difference: %ld",
+                               chan->name.c_str(), (unsigned long)ourTS, (unsigned long)TS, (long)(ourTS - TS));
 +                      /* If our TS is less than theirs, we dont accept their modes */
 +                      if (ourTS < TS)
 +                      {
 +                              apply_other_sides_modes = false;
 +                      }
 +                      else if (ourTS > TS)
                        {
 -                              chan = new Channel(channel, TS);
 +                              // Our TS is greater than theirs, remove all modes, extensions, etc. from the channel
 +                              LowerTS(chan, TS, channel);
 +
 +                              // XXX: If the channel does not exist in the chan hash at this point, create it so the remote modes can be applied on it.
 +                              // This happens to 0-user permanent channels on the losing side, because those are removed (from the chan hash, then
 +                              // deleted later) as soon as the permchan mode is removed from them.
 +                              if (ServerInstance->FindChan(channel) == NULL)
 +                              {
 +                                      chan = new Channel(channel, TS);
 +                              }
                        }
                }
 -              // The silent case here is ourTS == TS, we don't need to remove modes here, just to merge them later on.
        }
  
 -      /* First up, apply their modes if they won the TS war */
 +      /* First up, apply their channel modes if they won the TS war */
 +      Modes::ChangeList modechangelist;
        if (apply_other_sides_modes)
        {
 -              // Need to use a modestacker here due to maxmodes
 -              irc::modestacker stack(true);
 -              std::vector<std::string>::const_iterator paramit = params.begin() + 3;
 -              const std::vector<std::string>::const_iterator lastparamit = ((params.size() > 3) ? (params.end() - 1) : params.end());
 -              for (std::string::const_iterator i = params[2].begin(); i != params[2].end(); ++i)
 -              {
 -                      ModeHandler* mh = ServerInstance->Modes->FindMode(*i, MODETYPE_CHANNEL);
 -                      if (!mh)
 -                              continue;
 +              ServerInstance->Modes.ModeParamsToChangeList(srcuser, MODETYPE_CHANNEL, params, modechangelist, 2, params.size() - 1);
 +              ServerInstance->Modes->Process(srcuser, chan, NULL, modechangelist, ModeParser::MODE_LOCALONLY | ModeParser::MODE_MERGE);
 +              // Reuse for prefix modes
 +              modechangelist.clear();
 +      }
  
 -                      std::string modeparam;
 -                      if ((paramit != lastparamit) && (mh->GetNumParams(true)))
 -                      {
 -                              modeparam = *paramit;
 -                              ++paramit;
 -                      }
 +      TreeServer* const sourceserver = TreeServer::Get(srcuser);
  
 -                      stack.Push(*i, modeparam);
 -              }
 +      // Build a new FJOIN for forwarding. Put the correct TS in it and the current modes of the channel
 +      // after applying theirs. If they lost, the prefix modes from their message are not forwarded.
 +      FwdFJoinBuilder fwdfjoin(chan, sourceserver);
  
 -              std::vector<std::string> modelist;
 +      /* Now, process every 'modes,uuid' pair */
 +      irc::tokenstream users(params.back());
 +      std::string item;
 +      Modes::ChangeList* modechangelistptr = (apply_other_sides_modes ? &modechangelist : NULL);
 +      while (users.GetToken(item))
 +      {
 +              ProcessModeUUIDPair(item, sourceserver, chan, modechangelistptr, fwdfjoin);
 +      }
  
 -              // Mode parser needs to know what channel to act on.
 -              modelist.push_back(params[0]);
 +      fwdfjoin.finalize();
 +      fwdfjoin.Forward(sourceserver);
  
 -              while (stack.GetStackedLine(modelist))
 -              {
 -                      ServerInstance->Modes->Process(modelist, srcuser, true);
 -                      modelist.erase(modelist.begin() + 1, modelist.end());
 -              }
 +      // Set prefix modes on their users if we lost the FJOIN or had equal TS
 +      if (apply_other_sides_modes)
 +              ServerInstance->Modes->Process(srcuser, chan, NULL, modechangelist, ModeParser::MODE_LOCALONLY);
 +
 +      return CMD_SUCCESS;
 +}
  
 -              ServerInstance->Modes->Process(modelist, srcuser, true);
 +void CommandFJoin::ProcessModeUUIDPair(const std::string& item, TreeServer* sourceserver, Channel* chan, Modes::ChangeList* modechangelist, FwdFJoinBuilder& fwdfjoin)
 +{
 +      std::string::size_type comma = item.find(',');
 +
 +      // Comma not required anymore if the user has no modes
 +      const std::string::size_type ubegin = (comma == std::string::npos ? 0 : comma+1);
 +      std::string uuid(item, ubegin, UIDGenerator::UUID_LENGTH);
 +      User* who = ServerInstance->FindUUID(uuid);
 +      if (!who)
 +      {
 +              // Probably KILLed, ignore
 +              return;
        }
  
 -      /* Now, process every 'modes,nick' pair */
 -      while (users.GetToken(item))
 +      TreeSocket* src_socket = sourceserver->GetSocket();
 +      /* Check that the user's 'direction' is correct */
 +      TreeServer* route_back_again = TreeServer::Get(who);
 +      if (route_back_again->GetSocket() != src_socket)
 +      {
 +              return;
 +      }
 +
 +      std::string::const_iterator modeendit = item.begin(); // End of the "ov" mode string
 +      /* Check if the user received at least one mode */
 +      if ((modechangelist) && (comma != std::string::npos))
        {
 -              const char* usr = item.c_str();
 -              if (usr && *usr)
 +              modeendit += comma;
 +              /* Iterate through the modes and see if they are valid here, if so, apply */
 +              for (std::string::const_iterator i = item.begin(); i != modeendit; ++i)
                {
 -                      const char* unparsedmodes = usr;
 -                      std::string modes;
 +                      ModeHandler* mh = ServerInstance->Modes->FindMode(*i, MODETYPE_CHANNEL);
 +                      if (!mh)
 +                              throw ProtocolException("Unrecognised mode '" + std::string(1, *i) + "'");
  
 +                      /* Add any modes this user had to the mode stack */
 +                      modechangelist->push_add(mh, who->nick);
 +              }
 +      }
  
 -                      /* Iterate through all modes for this user and check they are valid. */
 -                      while ((*unparsedmodes) && (*unparsedmodes != ','))
 -                      {
 -                              ModeHandler *mh = ServerInstance->Modes->FindMode(*unparsedmodes, MODETYPE_CHANNEL);
 -                              if (!mh)
 -                              {
 -                                      ServerInstance->Logs->Log("m_spanningtree", SPARSE, "Unrecognised mode %c, dropping link", *unparsedmodes);
 -                                      return CMD_INVALID;
 -                              }
 +      Membership* memb = chan->ForceJoin(who, NULL, sourceserver->IsBursting());
 +      if (!memb)
 +      {
 +              // User was already on the channel, forward because of the modes they potentially got
 +              memb = chan->GetUser(who);
 +              if (memb)
 +                      fwdfjoin.add(memb, item.begin(), modeendit);
 +              return;
 +      }
  
 -                              modes += *unparsedmodes;
 -                              usr++;
 -                              unparsedmodes++;
 -                      }
 +      // Assign the id to the new Membership
 +      Membership::Id membid = 0;
 +      const std::string::size_type colon = item.rfind(':');
 +      if (colon != std::string::npos)
 +              membid = Membership::IdFromString(item.substr(colon+1));
 +      memb->id = membid;
  
 -                      /* Advance past the comma, to the nick */
 -                      usr++;
 +      // Add member to fwdfjoin with prefix modes
 +      fwdfjoin.add(memb, item.begin(), modeendit);
 +}
  
 -                      /* Check the user actually exists */
 -                      who = ServerInstance->FindUUID(usr);
 -                      if (who)
 -                      {
 -                              /* Check that the user's 'direction' is correct */
 -                              TreeServer* route_back_again = Utils->BestRouteTo(who->server);
 -                              if ((!route_back_again) || (route_back_again->GetSocket() != src_socket))
 -                                      continue;
 +void CommandFJoin::RemoveStatus(Channel* c)
 +{
 +      Modes::ChangeList changelist;
  
 -                              /* Add any modes this user had to the mode stack */
 -                              for (std::string::iterator x = modes.begin(); x != modes.end(); ++x)
 -                                      modestack.Push(*x, who->nick);
 +      const ModeParser::ModeHandlerMap& mhs = ServerInstance->Modes->GetModes(MODETYPE_CHANNEL);
 +      for (ModeParser::ModeHandlerMap::const_iterator i = mhs.begin(); i != mhs.end(); ++i)
 +      {
 +              ModeHandler* mh = i->second;
  
 -                              Channel::JoinUser(who, channel.c_str(), true, "", src_server->bursting, TS);
 -                      }
 -                      else
 -                      {
 -                              ServerInstance->Logs->Log("m_spanningtree",SPARSE, "Ignored nonexistant user %s in fjoin to %s (probably quit?)", usr, channel.c_str());
 -                              continue;
 -                      }
 -              }
 +              /* Passing a pointer to a modestacker here causes the mode to be put onto the mode stack,
 +               * rather than applied immediately. Module unloads require this to be done immediately,
 +               * for this function we require tidyness instead. Fixes bug #493
 +               */
 +              mh->RemoveMode(c, changelist);
        }
  
 -      /* Flush mode stacker if we lost the FJOIN or had equal TS */
 -      if (apply_other_sides_modes)
 -      {
 -              parameterlist stackresult;
 -              stackresult.push_back(channel);
 +      ServerInstance->Modes->Process(ServerInstance->FakeClient, c, NULL, changelist, ModeParser::MODE_LOCALONLY);
 +}
  
 -              while (modestack.GetStackedLine(stackresult))
 -              {
 -                      ServerInstance->SendMode(stackresult, srcuser);
 -                      stackresult.erase(stackresult.begin() + 1, stackresult.end());
 -              }
 +void CommandFJoin::LowerTS(Channel* chan, time_t TS, const std::string& newname)
 +{
 +      if (Utils->AnnounceTSChange)
 +              chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name.c_str(), newname.c_str(), (unsigned long) chan->age, (unsigned long) TS);
 +
 +      // While the name is equal in case-insensitive compare, it might differ in case; use the remote version
 +      chan->name = newname;
 +      chan->age = TS;
 +
 +      // Remove all pending invites
 +      chan->ClearInvites();
 +
 +      // Clear all modes
 +      CommandFJoin::RemoveStatus(chan);
 +
 +      // Unset all extensions
 +      chan->FreeAllExtItems();
 +
 +      // Clear the topic, if it isn't empty then send a topic change message to local users
 +      if (!chan->topic.empty())
 +      {
 +              chan->topic.clear();
 +              chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "TOPIC %s :", chan->name.c_str());
        }
 -      return CMD_SUCCESS;
 +      chan->setby.clear();
 +      chan->topicset = 0;
  }
  
 -void CommandFJoin::RemoveStatus(User* srcuser, parameterlist &params)
 +CommandFJoin::Builder::Builder(Channel* chan, TreeServer* source)
 +      : CmdBuilder(source->GetID(), "FJOIN")
  {
 -      if (params.size() < 1)
 -              return;
 +      push(chan->name).push_int(chan->age).push_raw(" +");
 +      pos = str().size();
 +      push_raw(chan->ChanModes(true)).push_raw(" :");
 +}
  
 -      Channel* c = ServerInstance->FindChan(params[0]);
 +void CommandFJoin::Builder::add(Membership* memb, std::string::const_iterator mbegin, std::string::const_iterator mend)
 +{
 +      push_raw(mbegin, mend).push_raw(',').push_raw(memb->user->uuid);
 +      push_raw(':').push_raw_int(memb->id);
 +      push_raw(' ');
 +}
  
 -      if (c)
 -      {
 -              irc::modestacker stack(false);
 -              parameterlist stackresult;
 -              stackresult.push_back(c->name);
 +bool CommandFJoin::Builder::has_room(std::string::size_type nummodes) const
 +{
 +      return ((str().size() + nummodes + UIDGenerator::UUID_LENGTH + 2 + membid_max_digits + 1) <= maxline);
 +}
  
 -              for (char modeletter = 'A'; modeletter <= 'z'; ++modeletter)
 -              {
 -                      ModeHandler* mh = ServerInstance->Modes->FindMode(modeletter, MODETYPE_CHANNEL);
 -
 -                      /* Passing a pointer to a modestacker here causes the mode to be put onto the mode stack,
 -                       * rather than applied immediately. Module unloads require this to be done immediately,
 -                       * for this function we require tidyness instead. Fixes bug #493
 -                       */
 -                      if (mh)
 -                              mh->RemoveMode(c, &stack);
 -              }
 +void CommandFJoin::Builder::clear()
 +{
 +      content.erase(pos);
 +      push_raw(" :");
 +}
  
 -              while (stack.GetStackedLine(stackresult))
 -              {
 -                      ServerInstance->SendMode(stackresult, srcuser);
 -                      stackresult.erase(stackresult.begin() + 1, stackresult.end());
 -              }
 -      }
 +const std::string& CommandFJoin::Builder::finalize()
 +{
 +      if (*content.rbegin() == ' ')
 +              content.erase(content.size()-1);
 +      return str();
  }
  
 +void FwdFJoinBuilder::add(Membership* memb, std::string::const_iterator mbegin, std::string::const_iterator mend)
 +{
 +      // Pseudoserver compatibility:
 +      // Some pseudoservers do not handle lines longer than 512 so we split long FJOINs into multiple messages.
 +      // The forwarded FJOIN can end up being longer than the original one if we have more modes set and won, for example.
 +
 +      // Check if the member fits into the current message. If not, send it and prepare a new one.
 +      if (!has_room(std::distance(mbegin, mend)))
 +      {
 +              finalize();
 +              Forward(sourceserver);
 +              clear();
 +      }
 +      // Add the member and their modes exactly as they sent them
 +      CommandFJoin::Builder::add(memb, mbegin, mend);
 +}
index d0c6401cd4fc6c0667f03fc69622e226fb1f0e89,493b05ebf78f6dda096e3c8b3ab04f86200dc16e..afd86c0ce530976254090618ae2afe6f81094eec
  
  
  #include "inspircd.h"
 -#include "socket.h"
  #include "xline.h"
  #include "main.h"
 -#include "../spanningtree.h"
 +#include "modules/spanningtree.h"
  
  #include "utils.h"
  #include "treeserver.h"
  
 -/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h */
 -
  /** We use this constructor only to create the 'root' item, Utils->TreeRoot, which
   * represents our own server. Therefore, it has no route, no parent, and
   * no socket associated with it. Its version string is our own local version.
   */
 -TreeServer::TreeServer(SpanningTreeUtilities* Util, std::string Name, std::string Desc, const std::string &id)
 -      : ServerName(Name.c_str()), ServerDesc(Desc), Utils(Util), ServerUser(ServerInstance->FakeClient)
 +TreeServer::TreeServer()
 +      : Server(ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc)
 +      , Parent(NULL), Route(NULL)
 +      , VersionString(ServerInstance->GetVersionString())
 +      , fullversion(ServerInstance->GetVersionString(true))
 +      , Socket(NULL), sid(ServerInstance->Config->GetSID()), behind_bursting(0), isdead(false)
 +      , pingtimer(this)
 +      , ServerUser(ServerInstance->FakeClient)
-       , age(ServerInstance->Time()), UserCount(ServerInstance->Users.GetLocalUsers().size())
++      , age(ServerInstance->Time()), UserCount(ServerInstance->Users.LocalUserCount())
 +      , OperCount(0), rtt(0), StartBurst(0), Hidden(false)
  {
 -      age = ServerInstance->Time();
 -      bursting = false;
 -      Parent = NULL;
 -      VersionString.clear();
 -      ServerUserCount = ServerOperCount = 0;
 -      VersionString = ServerInstance->GetVersionString();
 -      Route = NULL;
 -      Socket = NULL; /* Fix by brain */
 -      StartBurst = rtt = 0;
 -      Warned = Hidden = false;
        AddHashEntry();
 -      SetID(id);
  }
  
  /** When we create a new server, we call this constructor to initialize it.
   * This constructor initializes the server's Route and Parent, and sets up
   * its ping counters so that it will be pinged one minute from now.
   */
 -TreeServer::TreeServer(SpanningTreeUtilities* Util, std::string Name, std::string Desc, const std::string &id, TreeServer* Above, TreeSocket* Sock, bool Hide)
 -      : Parent(Above), ServerName(Name.c_str()), ServerDesc(Desc), Socket(Sock), Utils(Util), ServerUser(new FakeUser(id, Name)), Hidden(Hide)
 +TreeServer::TreeServer(const std::string& Name, const std::string& Desc, const std::string& id, TreeServer* Above, TreeSocket* Sock, bool Hide)
 +      : Server(Name, Desc)
 +      , Parent(Above), Socket(Sock), sid(id), behind_bursting(Parent->behind_bursting), isdead(false)
 +      , pingtimer(this)
 +      , ServerUser(new FakeUser(id, this))
 +      , age(ServerInstance->Time()), UserCount(0), OperCount(0), rtt(0), StartBurst(0), Hidden(Hide)
  {
 -      age = ServerInstance->Time();
 -      bursting = true;
 -      VersionString.clear();
 -      ServerUserCount = ServerOperCount = 0;
 -      SetNextPingTime(ServerInstance->Time() + Utils->PingFreq);
 -      SetPingFlag();
 -      Warned = false;
 -      rtt = 0;
 +      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "New server %s behind_bursting %u", GetName().c_str(), behind_bursting);
 +      CheckULine();
  
 -      long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
 -      this->StartBurst = ts;
 -      ServerInstance->Logs->Log("m_spanningtree",DEBUG, "Started bursting at time %lu", ts);
 +      ServerInstance->Timers.AddTimer(&pingtimer);
  
        /* find the 'route' for this server (e.g. the one directly connected
         * to the local server, which we can use to reach it)
         */
  
        this->AddHashEntry();
 -
 -      SetID(id);
 +      Parent->Children.push_back(this);
  }
  
 -const std::string& TreeServer::GetID()
 +void TreeServer::BeginBurst(unsigned long startms)
  {
 -      return sid;
 +      behind_bursting++;
 +
 +      unsigned long now = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
 +      // If the start time is in the future (clocks are not synced) then use current time
 +      if ((!startms) || (startms > now))
 +              startms = now;
 +      this->StartBurst = startms;
 +      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Server %s started bursting at time %lu behind_bursting %u", sid.c_str(), startms, behind_bursting);
  }
  
  void TreeServer::FinishBurstInternal()
  {
 -      this->bursting = false;
 -      SetNextPingTime(ServerInstance->Time() + Utils->PingFreq);
 -      SetPingFlag();
 -      for(unsigned int q=0; q < ChildCount(); q++)
 +      // Check is needed because 1202 protocol servers don't send the bursting state of a server, so servers
 +      // introduced during a netburst may later send ENDBURST which would normally decrease this counter
 +      if (behind_bursting > 0)
 +              behind_bursting--;
 +      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "FinishBurstInternal() %s behind_bursting %u", GetName().c_str(), behind_bursting);
 +
 +      for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
        {
 -              TreeServer* child = GetChild(q);
 +              TreeServer* child = *i;
                child->FinishBurstInternal();
        }
  }
  
  void TreeServer::FinishBurst()
  {
 -      FinishBurstInternal();
        ServerInstance->XLines->ApplyLines();
        long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
        unsigned long bursttime = ts - this->StartBurst;
        ServerInstance->SNO->WriteToSnoMask(Parent == Utils->TreeRoot ? 'l' : 'L', "Received end of netburst from \2%s\2 (burst time: %lu %s)",
 -              ServerName.c_str(), (bursttime > 10000 ? bursttime / 1000 : bursttime), (bursttime > 10000 ? "secs" : "msecs"));
 -      AddServerEvent(Utils->Creator, ServerName.c_str());
 -}
 +              GetName().c_str(), (bursttime > 10000 ? bursttime / 1000 : bursttime), (bursttime > 10000 ? "secs" : "msecs"));
 +      FOREACH_MOD_CUSTOM(Utils->Creator->GetEventProvider(), SpanningTreeEventListener, OnServerLink, (this));
  
 -void TreeServer::SetID(const std::string &id)
 -{
 -      ServerInstance->Logs->Log("m_spanningtree",DEBUG, "Setting SID to " + id);
 -      sid = id;
 -      Utils->sidlist[sid] = this;
 +      StartBurst = 0;
 +      FinishBurstInternal();
  }
  
 -int TreeServer::QuitUsers(const std::string &reason)
 +void TreeServer::SQuitChild(TreeServer* server, const std::string& reason)
  {
 -      const char* reason_s = reason.c_str();
 -      std::vector<User*> time_to_die;
 -      for (user_hash::iterator n = ServerInstance->Users->clientlist->begin(); n != ServerInstance->Users->clientlist->end(); n++)
 +      FOREACH_MOD_CUSTOM(Utils->Creator->GetEventProvider(), SpanningTreeEventListener, OnServerSplit, (server));
 +      stdalgo::erase(Children, server);
 +
 +      if (IsRoot())
        {
 -              if (n->second->server == ServerName)
 -              {
 -                      time_to_die.push_back(n->second);
 -              }
 +              // Server split from us, generate a SQUIT message and broadcast it
 +              ServerInstance->SNO->WriteGlobalSno('l', "Server \002" + server->GetName() + "\002 split: " + reason);
 +              CmdBuilder("SQUIT").push(server->GetID()).push_last(reason).Broadcast();
        }
 -      for (std::vector<User*>::iterator n = time_to_die.begin(); n != time_to_die.end(); n++)
 +      else
        {
 -              User* a = (User*)*n;
 -              if (!IS_LOCAL(a))
 -              {
 -                      if (this->Utils->quiet_bursts)
 -                              a->quietquit = true;
 -
 -                      if (ServerInstance->Config->HideSplits)
 -                              ServerInstance->Users->QuitUser(a, "*.net *.split", reason_s);
 -                      else
 -                              ServerInstance->Users->QuitUser(a, reason_s);
 -              }
 +              ServerInstance->SNO->WriteToSnoMask('L', "Server \002" + server->GetName() + "\002 split from server \002" + GetName() + "\002 with reason: " + reason);
        }
 -      return time_to_die.size();
 -}
  
 -/** This method is used to add the structure to the
 - * hash_map for linear searches. It is only called
 - * by the constructors.
 - */
 -void TreeServer::AddHashEntry()
 -{
 -      server_hash::iterator iter = Utils->serverlist.find(this->ServerName.c_str());
 -      if (iter == Utils->serverlist.end())
 -              Utils->serverlist[this->ServerName.c_str()] = this;
 -}
 -
 -/** This method removes the reference to this object
 - * from the hash_map which is used for linear searches.
 - * It is only called by the default destructor.
 - */
 -void TreeServer::DelHashEntry()
 -{
 -      server_hash::iterator iter = Utils->serverlist.find(this->ServerName.c_str());
 -      if (iter != Utils->serverlist.end())
 -              Utils->serverlist.erase(iter);
 -}
 -
 -/** These accessors etc should be pretty self-
 - * explanitory.
 - */
 -TreeServer* TreeServer::GetRoute()
 -{
 -      return Route;
 -}
 -
 -std::string TreeServer::GetName()
 -{
 -      return ServerName.c_str();
 -}
 +      unsigned int num_lost_servers = 0;
 +      server->SQuitInternal(num_lost_servers);
  
 -const std::string& TreeServer::GetDesc()
 -{
 -      return ServerDesc;
 -}
 +      const std::string quitreason = GetName() + " " + server->GetName();
 +      unsigned int num_lost_users = QuitUsers(quitreason);
  
 -const std::string& TreeServer::GetVersion()
 -{
 -      return VersionString;
 -}
 +      ServerInstance->SNO->WriteToSnoMask(IsRoot() ? 'l' : 'L', "Netsplit complete, lost \002%u\002 user%s on \002%u\002 server%s.",
 +              num_lost_users, num_lost_users != 1 ? "s" : "", num_lost_servers, num_lost_servers != 1 ? "s" : "");
  
 -void TreeServer::SetNextPingTime(time_t t)
 -{
 -      this->NextPing = t;
 -      LastPingWasGood = false;
 -}
 +      // No-op if the socket is already closed (i.e. it called us)
 +      if (server->IsLocal())
 +              server->GetSocket()->Close();
  
 -time_t TreeServer::NextPingTime()
 -{
 -      return NextPing;
 +      // Add the server to the cull list, the servers behind it are handled by cull() and the destructor
 +      ServerInstance->GlobalCulls.AddItem(server);
  }
  
 -bool TreeServer::AnsweredLastPing()
 +void TreeServer::SQuitInternal(unsigned int& num_lost_servers)
  {
 -      return LastPingWasGood;
 -}
 +      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Server %s lost in split", GetName().c_str());
  
 -void TreeServer::SetPingFlag()
 -{
 -      LastPingWasGood = true;
 -}
 -
 -unsigned int TreeServer::GetUserCount()
 -{
 -      return ServerUserCount;
 -}
 -
 -void TreeServer::SetUserCount(int diff)
 -{
 -      ServerUserCount += diff;
 -}
 -
 -void TreeServer::SetOperCount(int diff)
 -{
 -      ServerOperCount += diff;
 -}
 -
 -unsigned int TreeServer::GetOperCount()
 -{
 -      return ServerOperCount;
 -}
 -
 -TreeSocket* TreeServer::GetSocket()
 -{
 -      return Socket;
 -}
 -
 -TreeServer* TreeServer::GetParent()
 -{
 -      return Parent;
 -}
 +      for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
 +      {
 +              TreeServer* server = *i;
 +              server->SQuitInternal(num_lost_servers);
 +      }
  
 -void TreeServer::SetVersion(const std::string &Version)
 -{
 -      VersionString = Version;
 +      // Mark server as dead
 +      isdead = true;
 +      num_lost_servers++;
 +      RemoveHash();
  }
  
 -unsigned int TreeServer::ChildCount()
 +unsigned int TreeServer::QuitUsers(const std::string& reason)
  {
 -      return Children.size();
 -}
 +      std::string publicreason = ServerInstance->Config->HideSplits ? "*.net *.split" : reason;
  
 -TreeServer* TreeServer::GetChild(unsigned int n)
 -{
 -      if (n < Children.size())
 -      {
 -              /* Make sure they  cant request
 -               * an out-of-range object. After
 -               * all we know what these programmer
 -               * types are like *grin*.
 -               */
 -              return Children[n];
 -      }
 -      else
 +      const user_hash& users = ServerInstance->Users->GetUsers();
 +      unsigned int original_size = users.size();
 +      for (user_hash::const_iterator i = users.begin(); i != users.end(); )
        {
 -              return NULL;
 +              User* user = i->second;
 +              // Increment the iterator now because QuitUser() removes the user from the container
 +              ++i;
 +              TreeServer* server = TreeServer::Get(user);
 +              if (server->IsDead())
 +                      ServerInstance->Users->QuitUser(user, publicreason, &reason);
        }
 +      return original_size - users.size();
  }
  
 -void TreeServer::AddChild(TreeServer* Child)
 +void TreeServer::CheckULine()
  {
 -      Children.push_back(Child);
 -}
 +      uline = silentuline = false;
  
 -bool TreeServer::DelChild(TreeServer* Child)
 -{
 -      std::vector<TreeServer*>::iterator it = std::find(Children.begin(), Children.end(), Child);
 -      if (it != Children.end())
 +      ConfigTagList tags = ServerInstance->Config->ConfTags("uline");
 +      for (ConfigIter i = tags.first; i != tags.second; ++i)
        {
 -              Children.erase(it);
 -              return true;
 +              ConfigTag* tag = i->second;
 +              std::string server = tag->getString("server");
 +              if (!strcasecmp(server.c_str(), GetName().c_str()))
 +              {
 +                      if (this->IsRoot())
 +                      {
 +                              ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Servers should not uline themselves (at " + tag->getTagLocation() + ")");
 +                              return;
 +                      }
 +
 +                      uline = true;
 +                      silentuline = tag->getBool("silent");
 +                      break;
 +              }
        }
 -      return false;
  }
  
 -/** Removes child nodes of this node, and of that node, etc etc.
 - * This is used during netsplits to automatically tidy up the
 - * server tree. It is slow, we don't use it for much else.
 +/** This method is used to add the structure to the
 + * hash_map for linear searches. It is only called
 + * by the constructors.
   */
 -bool TreeServer::Tidy()
 +void TreeServer::AddHashEntry()
  {
 -      while (1)
 -      {
 -              std::vector<TreeServer*>::iterator a = Children.begin();
 -              if (a == Children.end())
 -                      return true;
 -              TreeServer* s = *a;
 -              s->Tidy();
 -              s->cull();
 -              Children.erase(a);
 -              delete s;
 -      }
 +      Utils->serverlist[GetName()] = this;
 +      Utils->sidlist[sid] = this;
  }
  
  CullResult TreeServer::cull()
  {
 -      if (ServerUser != ServerInstance->FakeClient)
 +      // Recursively cull all servers that are under us in the tree
 +      for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
 +      {
 +              TreeServer* server = *i;
 +              server->cull();
 +      }
 +
 +      if (!IsRoot())
                ServerUser->cull();
        return classbase::cull();
  }
  
  TreeServer::~TreeServer()
  {
 -      /* We'd better tidy up after ourselves, eh? */
 -      this->DelHashEntry();
 -      if (ServerUser != ServerInstance->FakeClient)
 +      // Recursively delete all servers that are under us in the tree first
 +      for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
 +              delete *i;
 +
 +      // Delete server user unless it's us
 +      if (!IsRoot())
                delete ServerUser;
 +}
 +
 +void TreeServer::RemoveHash()
 +{
 +      // XXX: Erase server from UserManager::uuidlist now, to allow sid reuse in the current main loop
 +      // iteration, before the cull list is applied
 +      ServerInstance->Users->uuidlist.erase(sid);
  
 -      server_hash::iterator iter = Utils->sidlist.find(GetID());
 -      if (iter != Utils->sidlist.end())
 -              Utils->sidlist.erase(iter);
 +      Utils->sidlist.erase(sid);
 +      Utils->serverlist.erase(GetName());
  }
index 803156446a2e50d86e544a204f16f1097cc6dfdf,b473277046c0af470fbd3afea566c89711090711..8196d37ba6299a01f509c9ed70279a2a99fcec9c
@@@ -20,7 -20,9 +20,8 @@@
   */
  
  
 -/* $ModDesc: Adds timed bans */
 -
  #include "inspircd.h"
++#include "listmode.h"
  
  /** Holds a timed ban
   */
@@@ -30,6 -32,7 +31,7 @@@ class TimedBa
        std::string channel;
        std::string mask;
        time_t expire;
+       Channel* chan;
  };
  
  typedef std::vector<TimedBan> timedbans;
@@@ -39,10 -42,21 +41,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(ERR_NOSUCHNICK, "%s :No such channel", parameters[0].c_str());
                        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, "%s :You do not have permission to set bans on this channel",
 +                              channel->name.c_str());
                        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;
 -              }
  
 -                      user->WriteServ("NOTICE %s :Ban already set", user->nick.c_str());
+               if (IsBanSet(channel, mask))
+               {
 -              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))
++                      user->WriteNotice("Ban already set");
+                       return CMD_FAILURE;
+               }
 +              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);
  
                // If halfop is loaded, send notice to halfops and above, otherwise send to ops and above
        }
  };
  
 +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;
 +
 +              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;
 +                      }
 +              }
 +      }
 +};
 +
+ class ChannelMatcher
+ {
+       Channel* const chan;
+  public:
+       ChannelMatcher(Channel* ch)
+               : chan(ch)
+       {
+       }
+       bool operator()(const TimedBan& tb) const
+       {
+               return (tb.chan == 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();)
                        Channel* cr = ServerInstance->FindChan(chan);
                        if (cr)
                        {
 -                              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 " + chan + " expired.";
                                cr->WriteAllExcept(ServerInstance->FakeClient, true, '@', empty, "NOTICE %s :%s", cr->name.c_str(), expiry.c_str());
                                ServerInstance->PI->SendChannelNotice(cr, '@', 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);
                        }
                }
        }
  
 -      virtual Version GetVersion()
+       void OnChannelDelete(Channel* chan)
+       {
+               // Remove all timed bans affecting the channel from internal bookkeeping
+               TimedBanList.erase(std::remove_if(TimedBanList.begin(), TimedBanList.end(), ChannelMatcher(chan)), TimedBanList.end());
+       }
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Adds timed bans", VF_COMMON | VF_VENDOR);
        }
  };
  
  MODULE_INIT(ModuleTimedBans)
 -
index 6456d6df555c2ff44e96add3cd4bbe4729449a59,40205da3118533479382f8f7f8dba2b5e182ffe1..3249f442bfb31e5662cfed2d8853eea7b2730f20
  #include "inspircd.h"
  #include "threadengines/threadengine_pthread.h"
  #include <pthread.h>
 -#include <signal.h>
  #include <fcntl.h>
  
 -ThreadEngine::ThreadEngine()
 -{
 -}
 -
  static void* entry_point(void* parameter)
  {
        /* Recommended by nenolod, signal safety on a per-thread basis */
  
  void ThreadEngine::Start(Thread* thread)
  {
 -      ThreadData* data = new ThreadData;
 -      thread->state = data;
 -
 -      if (pthread_create(&data->pthread_id, NULL, entry_point, thread) != 0)
 -      {
 -              thread->state = NULL;
 -              delete data;
 +      if (pthread_create(&thread->state.pthread_id, NULL, entry_point, thread) != 0)
                throw CoreException("Unable to create new thread: " + std::string(strerror(errno)));
 -      }
  }
  
 -ThreadEngine::~ThreadEngine()
 -{
 -}
 -
 -void ThreadData::FreeThread(Thread* thread)
 +void ThreadEngine::Stop(Thread* thread)
  {
        thread->SetExitFlag();
 -      pthread_join(pthread_id, NULL);
 +      pthread_join(thread->state.pthread_id, NULL);
  }
  
  #ifdef HAS_EVENTFD
@@@ -59,12 -75,13 +59,12 @@@ class ThreadSignalSocket : public Event
        ThreadSignalSocket(SocketThread* p, int newfd) : parent(p)
        {
                SetFd(newfd);
 -              ServerInstance->SE->AddFd(this, FD_WANT_FAST_READ | FD_WANT_NO_WRITE);
 +              SocketEngine::AddFd(this, FD_WANT_FAST_READ | FD_WANT_NO_WRITE);
        }
  
        ~ThreadSignalSocket()
        {
 -              ServerInstance->SE->DelFd(this);
 -              ServerInstance->SE->Close(GetFd());
 +              SocketEngine::Close(this);
        }
  
        void Notify()
                eventfd_write(fd, 1);
        }
  
 -      void HandleEvent(EventType et, int errornum)
 +      void OnEventHandlerRead() CXX11_OVERRIDE
        {
 -              if (et == EVENT_READ)
 -              {
 -                      eventfd_t dummy;
 -                      eventfd_read(fd, &dummy);
 -                      parent->OnNotify();
 -              }
 -              else
 -              {
 -                      ServerInstance->GlobalCulls.AddItem(this);
 -              }
 +              eventfd_t dummy;
 +              eventfd_read(fd, &dummy);
 +              parent->OnNotify();
 +      }
 +
 +      void OnEventHandlerWrite() CXX11_OVERRIDE
 +      {
 +              ServerInstance->GlobalCulls.AddItem(this);
 +      }
 +
 +      void OnEventHandlerError(int errcode) CXX11_OVERRIDE
 +      {
 +              ThreadSignalSocket::OnEventHandlerWrite();
        }
  };
  
@@@ -95,7 -109,7 +95,7 @@@ SocketThread::SocketThread(
        signal.sock = NULL;
        int fd = eventfd(0, EFD_NONBLOCK);
        if (fd < 0)
-               throw new CoreException("Could not create pipe " + std::string(strerror(errno)));
+               throw CoreException("Could not create pipe " + std::string(strerror(errno)));
        signal.sock = new ThreadSignalSocket(this, fd);
  }
  #else
@@@ -109,14 -123,15 +109,14 @@@ class ThreadSignalSocket : public Event
                parent(p), send_fd(sendfd)
        {
                SetFd(recvfd);
 -              ServerInstance->SE->NonBlocking(fd);
 -              ServerInstance->SE->AddFd(this, FD_WANT_FAST_READ | FD_WANT_NO_WRITE);
 +              SocketEngine::NonBlocking(fd);
 +              SocketEngine::AddFd(this, FD_WANT_FAST_READ | FD_WANT_NO_WRITE);
        }
  
        ~ThreadSignalSocket()
        {
                close(send_fd);
 -              ServerInstance->SE->DelFd(this);
 -              ServerInstance->SE->Close(GetFd());
 +              SocketEngine::Close(this);
        }
  
        void Notify()
                write(send_fd, &dummy, 1);
        }
  
 -      void HandleEvent(EventType et, int errornum)
 +      void OnEventHandlerRead() CXX11_OVERRIDE
 +      {
 +              char dummy[128];
 +              read(fd, dummy, 128);
 +              parent->OnNotify();
 +      }
 +
 +      void OnEventHandlerWrite() CXX11_OVERRIDE
 +      {
 +              ServerInstance->GlobalCulls.AddItem(this);
 +      }
 +
 +      void OnEventHandlerError(int errcode) CXX11_OVERRIDE
        {
 -              if (et == EVENT_READ)
 -              {
 -                      char dummy[128];
 -                      read(fd, dummy, 128);
 -                      parent->OnNotify();
 -              }
 -              else
 -              {
 -                      ServerInstance->GlobalCulls.AddItem(this);
 -              }
 +              ThreadSignalSocket::OnEventHandlerWrite();
        }
  };
  
@@@ -148,7 -160,7 +148,7 @@@ SocketThread::SocketThread(
        signal.sock = NULL;
        int fds[2];
        if (pipe(fds))
-               throw new CoreException("Could not create pipe " + std::string(strerror(errno)));
+               throw CoreException("Could not create pipe " + std::string(strerror(errno)));
        signal.sock = new ThreadSignalSocket(this, fds[0], fds[1]);
  }
  #endif
diff --combined src/usermanager.cpp
index 1966c9b4729f86193e8c8fa87694a3bf9395c936,76446c5b57fb07371adaa9b6730e6f335d7e0805..4ebc3b5834be57142002bb52b983e6a579279b81
  
  #include "inspircd.h"
  #include "xline.h"
 -#include "bancache.h"
 +#include "iohook.h"
 +
 +namespace
 +{
 +      class WriteCommonQuit : public User::ForEachNeighborHandler
 +      {
 +              std::string line;
 +              std::string operline;
 +
 +              void Execute(LocalUser* user) CXX11_OVERRIDE
 +              {
 +                      user->Write(user->IsOper() ? operline : line);
 +              }
 +
 +       public:
 +              WriteCommonQuit(User* user, const std::string& msg, const std::string& opermsg)
 +                      : line(":" + user->GetFullHost() + " QUIT :")
 +                      , operline(line)
 +              {
 +                      line += msg;
 +                      operline += opermsg;
 +                      user->ForEachNeighbor(*this, false);
 +              }
 +      };
 +}
  
  UserManager::UserManager()
 -      : unregistered_count(0), local_count(0)
 +      : unregistered_count(0)
  {
  }
  
 +UserManager::~UserManager()
 +{
 +      for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); ++i)
 +      {
 +              delete i->second;
 +      }
 +}
 +
  /* add a client connection to the sockets list */
  void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
  {
        }
        catch (...)
        {
 -              ServerInstance->Logs->Log("USERS", DEFAULT,"*** WTF *** Duplicated UUID! -- Crack smoking monkeys have been unleashed.");
 +              ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "*** WTF *** Duplicated UUID! -- Crack smoking monkeys have been unleashed.");
                ServerInstance->SNO->WriteToSnoMask('a', "WARNING *** Duplicate UUID allocated!");
                return;
        }
        UserIOHandler* eh = &New->eh;
  
 -      /* Give each of the modules an attempt to hook the user for I/O */
 -      FOREACH_MOD(I_OnHookIO, OnHookIO(eh, via));
 -
 -      if (eh->GetIOHook())
 -      {
 -              try
 -              {
 -                      eh->GetIOHook()->OnStreamSocketAccept(eh, client, server);
 -              }
 -              catch (CoreException& modexcept)
 -              {
 -                      ServerInstance->Logs->Log("SOCKET", DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
 -              }
 -      }
 +      // If this listener has an IO hook provider set then tell it about the connection
 +      if (via->iohookprov)
 +              via->iohookprov->OnAccept(eh, client, server);
  
 -      ServerInstance->Logs->Log("USERS", DEBUG,"New user fd: %d", socket);
 +      ServerInstance->Logs->Log("USERS", LOG_DEBUG, "New user fd: %d", socket);
  
        this->unregistered_count++;
  
        /* The users default nick is their UUID */
        New->nick = New->uuid;
 -      (*(this->clientlist))[New->nick] = New;
 +      this->clientlist[New->nick] = New;
  
        New->registered = REG_NONE;
-       New->signon = ServerInstance->Time() + ServerInstance->Config->dns_timeout;
+       New->signon = ServerInstance->Time();
        New->lastping = 1;
  
 -      ServerInstance->Users->AddLocalClone(New);
 -      ServerInstance->Users->AddGlobalClone(New);
 +      this->AddClone(New);
  
 -      New->localuseriter = this->local_users.insert(local_users.end(), New);
 -      local_count++;
 +      this->local_users.push_front(New);
  
 -      if ((this->local_users.size() > ServerInstance->Config->SoftLimit) || (this->local_users.size() >= (unsigned int)ServerInstance->SE->GetMaxFds()))
 +      if (this->local_users.size() > ServerInstance->Config->SoftLimit)
        {
                ServerInstance->SNO->WriteToSnoMask('a', "Warning: softlimit value has been reached: %d clients", ServerInstance->Config->SoftLimit);
                this->QuitUser(New,"No more connections allowed");
         * Check connect class settings and initialise settings into User.
         * This will be done again after DNS resolution. -- w00t
         */
 -      New->CheckClass();
 +      New->CheckClass(ServerInstance->Config->CCOnConnect);
        if (New->quitting)
                return;
  
         */
        New->exempt = (ServerInstance->XLines->MatchesLine("E",New) != NULL);
  
 -      if (BanCacheHit *b = ServerInstance->BanCache->GetHit(New->GetIPString()))
 +      BanCacheHit* const b = ServerInstance->BanCache.GetHit(New->GetIPString());
 +      if (b)
        {
                if (!b->Type.empty() && !New->exempt)
                {
                        /* user banned */
 -                      ServerInstance->Logs->Log("BANCACHE", DEBUG, std::string("BanCache: Positive hit for ") + New->GetIPString());
 -                      if (!ServerInstance->Config->MoronBanner.empty())
 -                              New->WriteServ("NOTICE %s :*** %s", New->nick.c_str(), ServerInstance->Config->MoronBanner.c_str());
 +                      ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Positive hit for " + New->GetIPString());
 +                      if (!ServerInstance->Config->XLineMessage.empty())
 +                              New->WriteNumeric(ERR_YOUREBANNEDCREEP, ":" + ServerInstance->Config->XLineMessage);
                        this->QuitUser(New, b->Reason);
                        return;
                }
                else
                {
 -                      ServerInstance->Logs->Log("BANCACHE", DEBUG, std::string("BanCache: Negative hit for ") + New->GetIPString());
 +                      ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Negative hit for " + New->GetIPString());
                }
        }
        else
                }
        }
  
 -      if (!ServerInstance->SE->AddFd(eh, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE))
 +      if (!SocketEngine::AddFd(eh, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE))
        {
 -              ServerInstance->Logs->Log("USERS", DEBUG,"Internal error on new connection");
 +              ServerInstance->Logs->Log("USERS", LOG_DEBUG, "Internal error on new connection");
                this->QuitUser(New, "Internal error handling connection");
        }
  
 -      /* NOTE: even if dns lookups are *off*, we still need to display this.
 -       * BOPM and other stuff requires it.
 -       */
 -      New->WriteServ("NOTICE Auth :*** Looking up your hostname...");
        if (ServerInstance->Config->RawLog)
 -              New->WriteServ("NOTICE Auth :*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
 +              New->WriteNotice("*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
  
 -      FOREACH_MOD(I_OnSetUserIP,OnSetUserIP(New));
 +      FOREACH_MOD(OnSetUserIP, (New));
        if (New->quitting)
                return;
  
 -      FOREACH_MOD(I_OnUserInit,OnUserInit(New));
 -
 -      if (ServerInstance->Config->NoUserDns)
 -      {
 -              New->WriteServ("NOTICE %s :*** Skipping host resolution (disabled by server administrator)", New->nick.c_str());
 -              New->dns_done = true;
 -      }
 -      else
 -      {
 -              New->StartDNSLookup();
 -      }
 +      FOREACH_MOD(OnUserInit, (New));
  }
  
 -void UserManager::QuitUser(User *user, const std::string &quitreason, const char* operreason)
 +void UserManager::QuitUser(User* user, const std::string& quitreason, const std::string* operreason)
  {
        if (user->quitting)
        {
 -              ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: Tried to quit quitting user: " + user->nick);
 +              ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Tried to quit quitting user: " + user->nick);
                return;
        }
  
        if (IS_SERVER(user))
        {
 -              ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: Tried to quit server user: " + user->nick);
 +              ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Tried to quit server user: " + user->nick);
                return;
        }
  
        user->quitting = true;
  
 -      ServerInstance->Logs->Log("USERS", DEBUG, "QuitUser: %s=%s '%s'", user->uuid.c_str(), user->nick.c_str(), quitreason.c_str());
 -      user->Write("ERROR :Closing link: (%s@%s) [%s]", user->ident.c_str(), user->host.c_str(), *operreason ? operreason : quitreason.c_str());
 +      ServerInstance->Logs->Log("USERS", LOG_DEBUG, "QuitUser: %s=%s '%s'", user->uuid.c_str(), user->nick.c_str(), quitreason.c_str());
 +      user->Write("ERROR :Closing link: (%s@%s) [%s]", user->ident.c_str(), user->host.c_str(), operreason ? operreason->c_str() : quitreason.c_str());
  
        std::string reason;
 -      std::string oper_reason;
        reason.assign(quitreason, 0, ServerInstance->Config->Limits.MaxQuit);
 -      if (operreason && *operreason)
 -              oper_reason.assign(operreason, 0, ServerInstance->Config->Limits.MaxQuit);
 -      else
 -              oper_reason = quitreason;
 +      if (!operreason)
 +              operreason = &reason;
  
        ServerInstance->GlobalCulls.AddItem(user);
  
        if (user->registered == REG_ALL)
        {
 -              FOREACH_MOD(I_OnUserQuit,OnUserQuit(user, reason, oper_reason));
 -              user->WriteCommonQuit(reason, oper_reason);
 +              FOREACH_MOD(OnUserQuit, (user, reason, *operreason));
 +              WriteCommonQuit(user, reason, *operreason);
        }
 -
 -      if (user->registered != REG_ALL)
 -              if (ServerInstance->Users->unregistered_count)
 -                      ServerInstance->Users->unregistered_count--;
 +      else
 +              unregistered_count--;
  
        if (IS_LOCAL(user))
        {
                LocalUser* lu = IS_LOCAL(user);
 -              FOREACH_MOD(I_OnUserDisconnect,OnUserDisconnect(lu));
 +              FOREACH_MOD(OnUserDisconnect, (lu));
                lu->eh.Close();
 -      }
  
 -      /*
 -       * this must come before the ServerInstance->SNO->WriteToSnoMaskso that it doesnt try to fill their buffer with anything
 -       * if they were an oper with +s +qQ.
 -       */
 -      if (user->registered == REG_ALL)
 -      {
 -              if (IS_LOCAL(user))
 -              {
 -                      if (!user->quietquit)
 -                      {
 -                              ServerInstance->SNO->WriteToSnoMask('q',"Client exiting: %s (%s) [%s]",
 -                                      user->GetFullRealHost().c_str(), user->GetIPString(), oper_reason.c_str());
 -                      }
 -              }
 -              else
 -              {
 -                      if ((!ServerInstance->SilentULine(user->server)) && (!user->quietquit))
 -                      {
 -                              ServerInstance->SNO->WriteToSnoMask('Q',"Client exiting on server %s: %s (%s) [%s]",
 -                                      user->server.c_str(), user->GetFullRealHost().c_str(), user->GetIPString(), oper_reason.c_str());
 -                      }
 -              }
 -              user->AddToWhoWas();
 +              if (lu->registered == REG_ALL)
 +                      ServerInstance->SNO->WriteToSnoMask('q',"Client exiting: %s (%s) [%s]", user->GetFullRealHost().c_str(), user->GetIPString().c_str(), operreason->c_str());
 +              local_users.erase(lu);
        }
  
 -      user_hash::iterator iter = this->clientlist->find(user->nick);
 -
 -      if (iter != this->clientlist->end())
 -              this->clientlist->erase(iter);
 -      else
 -              ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: Nick not found in clientlist, cannot remove: " + user->nick);
 +      if (!clientlist.erase(user->nick))
 +              ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Nick not found in clientlist, cannot remove: " + user->nick);
  
 -      ServerInstance->Users->uuidlist->erase(user->uuid);
 +      uuidlist.erase(user->uuid);
 +      user->PurgeEmptyChannels();
  }
  
 -
 -void UserManager::AddLocalClone(User *user)
 -{
 -      local_clones[user->GetCIDRMask()]++;
 -}
 -
 -void UserManager::AddGlobalClone(User *user)
 +void UserManager::AddClone(User* user)
  {
 -      global_clones[user->GetCIDRMask()]++;
 +      CloneCounts& counts = clonemap[user->GetCIDRMask()];
 +      counts.global++;
 +      if (IS_LOCAL(user))
 +              counts.local++;
  }
  
  void UserManager::RemoveCloneCounts(User *user)
  {
 -      if (IS_LOCAL(user))
 +      CloneMap::iterator it = clonemap.find(user->GetCIDRMask());
 +      if (it != clonemap.end())
        {
 -              clonemap::iterator x = local_clones.find(user->GetCIDRMask());
 -              if (x != local_clones.end())
 +              CloneCounts& counts = it->second;
 +              counts.global--;
 +              if (counts.global == 0)
                {
 -                      x->second--;
 -                      if (!x->second)
 -                      {
 -                              local_clones.erase(x);
 -                      }
 +                      // No more users from this IP, remove entry from the map
 +                      clonemap.erase(it);
 +                      return;
                }
 -      }
  
 -      clonemap::iterator y = global_clones.find(user->GetCIDRMask());
 -      if (y != global_clones.end())
 -      {
 -              y->second--;
 -              if (!y->second)
 -              {
 -                      global_clones.erase(y);
 -              }
 +              if (IS_LOCAL(user))
 +                      counts.local--;
        }
  }
  
 -      local_clones.clear();
 -      global_clones.clear();
+ void UserManager::RehashCloneCounts()
+ {
 -      const user_hash& hash = *ServerInstance->Users->clientlist;
++      clonemap.clear();
 -
 -              if (IS_LOCAL(u))
 -                      AddLocalClone(u);
 -              AddGlobalClone(u);
++      const user_hash& hash = ServerInstance->Users.GetUsers();
+       for (user_hash::const_iterator i = hash.begin(); i != hash.end(); ++i)
+       {
+               User* u = i->second;
 -unsigned long UserManager::GlobalCloneCount(User *user)
++              AddClone(u);
+       }
+ }
 +const UserManager::CloneCounts& UserManager::GetCloneCounts(User* user) const
  {
 -      clonemap::iterator x = global_clones.find(user->GetCIDRMask());
 -      if (x != global_clones.end())
 -              return x->second;
 +      CloneMap::const_iterator it = clonemap.find(user->GetCIDRMask());
 +      if (it != clonemap.end())
 +              return it->second;
        else
 -              return 0;
 -}
 -
 -unsigned long UserManager::LocalCloneCount(User *user)
 -{
 -      clonemap::iterator x = local_clones.find(user->GetCIDRMask());
 -      if (x != local_clones.end())
 -              return x->second;
 -      else
 -              return 0;
 -}
 -
 -/* this function counts all users connected, wether they are registered or NOT. */
 -unsigned int UserManager::UserCount()
 -{
 -      /*
 -       * XXX: Todo:
 -       *  As part of this restructuring, move clientlist/etc fields into usermanager.
 -       *      -- w00t
 -       */
 -      return this->clientlist->size();
 -}
 -
 -/* this counts only registered users, so that the percentages in /MAP don't mess up */
 -unsigned int UserManager::RegisteredUserCount()
 -{
 -      return this->clientlist->size() - this->UnregisteredUserCount();
 -}
 -
 -/* return how many users are opered */
 -unsigned int UserManager::OperCount()
 -{
 -      return this->all_opers.size();
 -}
 -
 -/* return how many users are unregistered */
 -unsigned int UserManager::UnregisteredUserCount()
 -{
 -      return this->unregistered_count;
 -}
 -
 -/* return how many local registered users there are */
 -unsigned int UserManager::LocalUserCount()
 -{
 -      /* Doesnt count unregistered clients */
 -      return (this->local_count - this->UnregisteredUserCount());
 +              return zeroclonecounts;
  }
  
  void UserManager::ServerNoticeAll(const char* text, ...)
  {
 -      if (!text)
 -              return;
 +      std::string message;
 +      VAFORMAT(message, text, text);
 +      message = "NOTICE $" + ServerInstance->Config->ServerName + " :" + message;
  
 -      char textbuffer[MAXBUF];
 -      char formatbuffer[MAXBUF];
 -      va_list argsPtr;
 -      va_start (argsPtr, text);
 -      vsnprintf(textbuffer, MAXBUF, text, argsPtr);
 -      va_end(argsPtr);
 -
 -      snprintf(formatbuffer,MAXBUF,"NOTICE $%s :%s", ServerInstance->Config->ServerName.c_str(), textbuffer);
 -
 -      for (LocalUserList::const_iterator i = local_users.begin(); i != local_users.end(); i++)
 +      for (LocalList::const_iterator i = local_users.begin(); i != local_users.end(); ++i)
        {
                User* t = *i;
 -              t->WriteServ(std::string(formatbuffer));
 +              t->WriteServ(message);
        }
  }
  
 -void UserManager::ServerPrivmsgAll(const char* text, ...)
 +void UserManager::GarbageCollect()
  {
 -      if (!text)
 -              return;
 -
 -      char textbuffer[MAXBUF];
 -      char formatbuffer[MAXBUF];
 -      va_list argsPtr;
 -      va_start (argsPtr, text);
 -      vsnprintf(textbuffer, MAXBUF, text, argsPtr);
 -      va_end(argsPtr);
 -
 -      snprintf(formatbuffer,MAXBUF,"PRIVMSG $%s :%s", ServerInstance->Config->ServerName.c_str(), textbuffer);
 -
 -      for (LocalUserList::const_iterator i = local_users.begin(); i != local_users.end(); i++)
 +      // Reset the already_sent IDs so we don't wrap it around and drop a message
 +      LocalUser::already_sent_id = 0;
 +      for (LocalList::const_iterator i = local_users.begin(); i != local_users.end(); ++i)
        {
 -              User* t = *i;
 -              t->WriteServ(std::string(formatbuffer));
 +              (**i).already_sent = 0;
 +              (**i).RemoveExpiredInvites();
        }
  }
  
 +/* this returns true when all modules are satisfied that the user should be allowed onto the irc server
 + * (until this returns true, a user will block in the waiting state, waiting to connect up to the
 + * registration timeout maximum seconds)
 + */
 +bool UserManager::AllModulesReportReady(LocalUser* user)
 +{
 +      ModResult res;
 +      FIRST_MOD_RESULT(OnCheckReady, res, (user));
 +      return (res == MOD_RES_PASSTHRU);
 +}
  
 -/* return how many users have a given mode e.g. 'a' */
 -int UserManager::ModeCount(const char mode)
 +/**
 + * This function is called once a second from the mainloop.
 + * It is intended to do background checking on all the user structs, e.g.
 + * stuff like ping checks, registration timeouts, etc.
 + */
 +void UserManager::DoBackgroundUserStuff()
  {
 -      int c = 0;
 -      for(user_hash::iterator i = clientlist->begin(); i != clientlist->end(); ++i)
 +      /*
 +       * loop over all local users..
 +       */
 +      for (LocalList::iterator i = local_users.begin(); i != local_users.end(); ++i)
        {
 -              User* u = i->second;
 -              if (u->modes[mode-65])
 -                      c++;
 +              LocalUser* curr = *i;
 +
 +              if (curr->CommandFloodPenalty || curr->eh.getSendQSize())
 +              {
 +                      unsigned int rate = curr->MyClass->GetCommandRate();
 +                      if (curr->CommandFloodPenalty > rate)
 +                              curr->CommandFloodPenalty -= rate;
 +                      else
 +                              curr->CommandFloodPenalty = 0;
 +                      curr->eh.OnDataReady();
 +              }
 +
 +              switch (curr->registered)
 +              {
 +                      case REG_ALL:
 +                              if (ServerInstance->Time() >= curr->nping)
 +                              {
 +                                      // This user didn't answer the last ping, remove them
 +                                      if (!curr->lastping)
 +                                      {
 +                                              time_t time = ServerInstance->Time() - (curr->nping - curr->MyClass->GetPingTime());
 +                                              const std::string message = "Ping timeout: " + ConvToStr(time) + (time != 1 ? " seconds" : " second");
 +                                              this->QuitUser(curr, message);
 +                                              continue;
 +                                      }
 +
 +                                      curr->Write("PING :" + ServerInstance->Config->ServerName);
 +                                      curr->lastping = 0;
 +                                      curr->nping = ServerInstance->Time() + curr->MyClass->GetPingTime();
 +                              }
 +                              break;
 +                      case REG_NICKUSER:
 +                              if (AllModulesReportReady(curr))
 +                              {
 +                                      /* User has sent NICK/USER, modules are okay, DNS finished. */
 +                                      curr->FullConnect();
 +                                      continue;
 +                              }
++
++                              // If the user has been quit in OnCheckReady then we shouldn't
++                              // quit them again for having a registration timeout.
++                              if (curr->quitting)
++                                      continue;
 +                              break;
 +              }
 +
-               if (curr->registered != REG_ALL && (ServerInstance->Time() > (curr->age + curr->MyClass->GetRegTimeout())))
++              if (curr->registered != REG_ALL && curr->MyClass && (ServerInstance->Time() > (curr->signon + curr->MyClass->GetRegTimeout())))
 +              {
 +                      /*
 +                       * registration timeout -- didnt send USER/NICK/HOST
 +                       * in the time specified in their connection class.
 +                       */
 +                      this->QuitUser(curr, "Registration timeout");
 +                      continue;
 +              }
        }
 -      return c;
  }
diff --combined win/inspircd.rc.cmake
index 975be5e2c7e9556b780f89aed38198f578f7f730,ba52ad5d2dec3a719ec7d408fd704ee8705005eb..06012b3f58ec0c88845da08e71f35a6389a5e35a
@@@ -1,8 -1,8 +1,8 @@@
  101           ICON            "inspircd.ico"\r
  \r
  1 VERSIONINFO\r
 -  FILEVERSION    @MAJOR_VERSION@,@MINOR_VERSION@,@PATCH_VERSION@\r
 -  PRODUCTVERSION @MAJOR_VERSION@,@MINOR_VERSION@,@PATCH_VERSION@\r
 +  FILEVERSION    @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@\r
 +  PRODUCTVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@\r
    FILEFLAGSMASK  0x3fL\r
  #ifdef _DEBUG\r
    FILEFLAGS 0x1L\r
@@@ -17,14 -17,14 +17,14 @@@ BEGI
      BEGIN\r
          BLOCK "040904b0"\r
          BEGIN\r
 -            VALUE "Comments", "InspIRCd @MAJOR_VERSION@.@MINOR_VERSION@ IRC Daemon"\r
 +            VALUE "Comments", "InspIRCd @VERSION_MAJOR@.@VERSION_MINOR@ IRC Daemon"\r
              VALUE "CompanyName", "InspIRCd Development Team"\r
              VALUE "FileDescription", "InspIRCd"\r
              VALUE "FileVersion", "@FULL_VERSION@"\r
              VALUE "InternalName", "InspIRCd"\r
-             VALUE "LegalCopyright", "Copyright (c) 2014 InspIRCd Development Team"\r
+             VALUE "LegalCopyright", "Copyright (c) 2015 InspIRCd Development Team"\r
              VALUE "OriginalFilename", "inspircd.exe"\r
 -            VALUE "ProductName", "InspIRCd - The Inspire IRC Daemon"\r
 +            VALUE "ProductName", "InspIRCd - Internet Relay Chat Daemon"\r
              VALUE "ProductVersion", "@FULL_VERSION@"\r
          END\r
      END\r