]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
Merge insp20
authorattilamolnar <attilamolnar@hush.com>
Fri, 30 Aug 2013 11:01:10 +0000 (13:01 +0200)
committerattilamolnar <attilamolnar@hush.com>
Fri, 30 Aug 2013 11:01:10 +0000 (13:01 +0200)
29 files changed:
1  2 
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/modules/unrealircd.conf.example
docs/conf/opers.conf.example
include/base.h
include/socketengine.h
src/commands/cmd_whowas.cpp
src/helperfuncs.cpp
src/inspircd.cpp
src/inspsocket.cpp
src/modules/extra/m_ssl_gnutls.cpp
src/modules/extra/m_ssl_openssl.cpp
src/modules/m_dnsbl.cpp
src/modules/m_ircv3.cpp
src/modules/m_permchannels.cpp
src/modules/m_spanningtree/fjoin.cpp
src/modules/m_spanningtree/main.cpp
src/modules/m_spanningtree/utils.h
src/modules/m_userip.cpp
src/socketengine.cpp
src/socketengines/socketengine_epoll.cpp
src/socketengines/socketengine_kqueue.cpp
src/socketengines/socketengine_poll.cpp
src/socketengines/socketengine_ports.cpp
src/socketengines/socketengine_select.cpp
win/inspircd_win32wrapper.cpp

index 0cabfccd0f718d9ca05792f2ffbc317c9d87b1e5,1b33004c4471e19d35cb40d00fc40c5fffdfe62d..bddb5846aa96d3fdc13ffb185f548a33032695ca
@@@ -97,11 -97,6 +97,11 @@@ Removes a user from a channel you speci
  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, the only difference is that that
@@@ -769,7 -764,7 +769,7 @@@ Closes all unregistered connections to 
   c            Blocks private messages and notices from users who do
                not share a common channel with you (requires
                commonchans module).
-  d            Deaf mode. User will not recieve any messages or notices
+  d            Deaf mode. User will not receive any messages or notices
                from channels they are in (requires deaf module).
   g            In combination with /allow, provides for server side
                ignore (requires callerid module).
  
   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
index b4c1e7d67b0e5a7d7000b9a79f49bda2c0c92987,2cccc5a2c72f1bec862134457dad3d7b11867fdc..2c5102fcce0a5b19ed8a9a87cd06f3ee83f88012
@@@ -77,7 -77,7 +77,7 @@@ LOCKSERV       UNLOCKSERV   JUMPSERVER"
   c            Blocks private messages and notices from users who do
                not share a common channel with you (requires
                commonchans module).
-  d            Deaf mode. User will not recieve any messages or notices
+  d            Deaf mode. User will not receive any messages or notices
                from channels they are in (requires deaf module).
   g            In combination with /allow, provides for server side
                ignore (requires callerid module).
  
   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
index 036a3ebfce4f05a765c465936c319e680894cea1,9512e17c448c92f4ece28ec03d7b0ab656f7fd69..8a498577a0846bac3603debcedfe03a0c0ab59cd
  #                                                                      #
  ########################################################################
  
 +#-#-#-#-#-#-#-#-#-#  CONFIGURATION FORMAT  #-#-#-#-#-#-#-#-#-#-#-#-#-#-
 +#                                                                     #
 +# In order to maintain compatibility with older configuration files,  #
 +# you can change the configuration parser to parse as it did in       #
 +# previous releases. When using the "compat" format, you need to use  #
 +# C++ escape sequences (e.g. \n) instead of XML ones (e.g. &nl;) and  #
 +# can not use <define> to create macros.                              #
 +#<config format="compat">
 +
  #-#-#-#-#-#-#-#-#-#  INCLUDE CONFIGURATION  #-#-#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # This optional tag allows you to include another config file         #
  #                                                                     #
  # Variables may be redefined and may reference other variables.       #
  # Value expansion happens at the time the tag is read.                #
 -#                                                                     #
 -# Using variable definitions REQUIRES that the config format be       #
 -# changed to "xml" from the default "compat" that uses escape         #
 -# sequences such as "\"" and "\n", and does not support <define>      #
 -<config format="xml">
  <define name="bindip" value="1.2.2.3">
  <define name="localips" value="&bindip;/24">
  
  #   |_| \_\___|\__,_|\__,_|   |_| |_| |_|_|___/ |____/|_|\__(_)       #
  #                                                                     #
  #  If you want to link servers to InspIRCd you must load the          #
- #  m_spanningtree.so module! Please see the modules list below for    #
+ #  m_spanningtree.so module! Please see the modules list for          #
  #  information on how to load this module! If you do not load this    #
- #  module, server ports will NOT be bound!                            #
- #                                                                     #
- # PLEASE NOTE: If you have build InspIRCd with IPv6 support, you MUST #
- # specify a bind address if you want the IRCd to bind to a IPv4 IP.   #
+ #  module, server ports will NOT work!                                #
  
  <bind
        # address: IP address to bind to if the box that you are hosting
        # for ssl to work. If you do not want this bind section 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">
  #                                                                     #
  
  <power
-        # hash: what hash these passwords are hashed with. requires the module
-        # for selected hash (m_md5.so, m_sha256.so or m_ripemd160.so) be
-        # loaded and the password hashing module (m_password_hash.so)
-        # loaded. Options here are: "md5", "sha256" and "ripemd160".
+        # hash: what hash these passwords are hashed with.
+        # Requires the module for selected hash (m_md5.so, m_sha256.so
+        # or m_ripemd160.so) be loaded and the password hashing module
+        # (m_password_hash.so) loaded.
+        # Options here are: "md5", "sha256" and "ripemd160", or one of
+        # these prefixed with "hmac-", e.g.: "hmac-sha256".
         # Optional, but recommended. Create hashed password with:
         # /mkpasswd <hash> <password>
         #hash="sha256"
           # maxconnwarn: Enable warnings when localmax or globalmax is hit (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"
           # 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  -#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  #   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" rules="examples/rules.txt.example">
  
  # Example of an executable file include. Note this will be read on rehash,
  # not when the command is run.
           # 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.
           # defaultmodes: What modes are set on a empty channel when a user
           # joins it and it is unregistered. This is similar to Asuka's
           # autochanmodes.
 -         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 haha@abuse.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 #-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  
  <performance
-              # netbuffersize: Size of the buffer used to recieve data from clients.
+              # netbuffersize: Size of the buffer used to receive data from clients.
               # The ircd may only read this amount of text in 1 go at any time.
               netbuffersize="10240"
  
-              # maxwho: Maximum number of results to show in a /who query.
-              maxwho="4096"
               # somaxconn: The maximum number of connections that may be waiting
               # 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 specifed 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:
            # (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:
          # 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
  # 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 76e9c09cb2740b1b21ef0d81ad68f4d0c7041ff9,fc0fc3b5ee673c6e774d179bbc28a8da2d50b0b3..382455bcd1672ea0c9c67f1ed226486b2b195ff5
        # failover (see above).
        timeout="300"
  
-       # ssl: If defined, this states extra modules that will be used when
+       # ssl: If defined, this states the SSL module that will be used when
        # making an outbound connection to the server. Options are: "openssl"
-       # and "gnutls" for encryption (they are compatible with each other).
-       # You must use the same (or a compatible) transport on both sides of the link.
+       # and "gnutls" (they are compatible with each other).
        #
        # You will need to load the m_ssl_openssl.so module for openssl,
        # m_ssl_gnutls.so for gnutls. The server port that you connect to
  # Simple autoconnect block. This enables automatic connection of a server
  # Recommended setup is to have leaves connect to the hub, and have no
  # automatic connections started by the hub.
 -<autoconnect period="300" server="hub.penguin.org">
 +<autoconnect period="10m" server="hub.penguin.org">
  
  # Failover autoconnect block. If you have multiple hubs, or want your network
  # to automatically link even if the hub is down, you can specify multiple
- # space seperated servers to autoconnect; they will be tried in a round
+ # space separated servers to autoconnect; they will be tried in a round
  # robin fashion until one succeeds. Period defines the time for restarting
  # a single loop.
  <autoconnect period="120"
index ca1340f040f4b7915f64075d4e3d84bb01e721e9,54a667f344f9aef6c39b4a39c774f84016763d3a..d329ab5220dc87247867f440e8493406c07e7b51
  #   Allow opers (channels/auspex) to see see all joins/parts/kicks in the channel
  #
  # Exemptchanops can be used to adjust the level at which users become visible or
- # the level at which they can see the full membe list of the channel.
+ # the level at which they can see the full member list of the channel.
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Autoop module: Adds basic channel access controls via the +w listmode.
  #           capsmap="ABCDEFGHIJKLMNOPQRSTUVWXYZ! ">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Block color module: Blocking color-coded messages with cmode +c
+ # Block color module: Blocking color-coded messages with chan mode +c
  #<module name="m_blockcolor.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # CallerID module: Adds usermode +g which activates hybrid-style 
- # callerid (== block all private messages unless you /accept first)
+ # callerid: block all private messages unless you /accept first
  #<module name="m_callerid.so">
  # 
  #-#-#-#-#-#-#-#-#-#-#- CALLERID  CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
  # 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
  # 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
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Channel Names module: Allows disabling channels which have certain
- # characters in the channel name such as bold, colorcodes, etc which
+ # characters in the channel name such as bold, colorcodes, etc. which
  # can be quite annoying and allow users to on occasion have a channel
  # that looks like the name of another channel on the network.
  #<module name="m_channames.so">
  # in a channel matching a mask 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 a 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: gives /check
  # Check is useful for looking up information on channels,
  # 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"
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Clones module: Adds an oper command /CLONES for detecting cloned
- # users. Warning: This module may be resource intensive when its
- # command is issued, use with care.
+ # users. Warning: This command may be resource intensive when it is
+ # issued, use with care.
  # This module is oper-only.
  # To use, CLONES must be in one of your oper class blocks.
  #<module name="m_clones.so">
  
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Channel cycle module. Server side /hop, with +ilk etc bypass.
+ # Channel cycle module. Server side /hop, with +ilk etc. bypass.
  #<module name="m_cycle.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #   quitmsg="Throttled" bootwait="10">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Custom prefixes: allows for channel prefixes to be added.
+ # 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
  #<customprefix name="admin" letter="a" prefix="&amp;" rank="40000" ranktoset="50000">
  #<customprefix name="halfop" letter="h" prefix="%" rank="20000" ranktoset="30000">
  #<customprefix name="halfvoice" letter="V" prefix="-" rank="1" ranktoset="20000">
+ #
+ # Do /reloadmodule m_customprefix.so after changing the settings of this module.
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Custom title module: Adds the /TITLE command which allows for trusted
  # Glob masks are accepted here also.
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Devoice Module: Let users devoice themselves.
+ # Devoice Module: Let users devoice themselves using /devoice #chan.
  #<module name="m_devoice.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # specfiy 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">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Gecosban: Implements extended ban r:, which stops anyone matching
  # must be in one of your oper class blocks.
  #<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
  #<module name="m_helpop.so">
  # specify below the path to the helpop.conf file, or if you like to   #
  # make a mess, define your helpop tags in this conf.                  #
  #                                                                     #
 -#<include file="conf/examples/inspircd.helpop-full.example">
 +#<include file="examples/inspircd.helpop-full.example">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # HIDECHANS module: Allows opers to hide their channels list from non-
+ # HIDECHANS module: Allows users to hide their channels list from non-
  # opers by setting user mode +I on themselves.
  #<module name="m_hidechans.so">
  #
  #<hostchange mask="a@b.com" action="set" value="blah.blah.blah">
  #<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">
  #<module name="m_ircv3.so">
  # The following block can be used to control which extensions are
  # enabled.
- #<ircv3 accoutnotify="on" awaynotify="on" extendedjoin="on">
+ #<ircv3 accountnotify="on" awaynotify="on" extendedjoin="on">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Join flood module: Adds support for join flood protection (+j)
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # 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 +K channel mode
  # are specified in a <class> tag that the oper is part of. This is so #
  # you can control who has access to this possible dangerous command.  #
  # If your server is locked and you get disconnected, do a REHASH from #
- # shell to open up again.
+ # shell to open up again.                                             #
  #
  # This module is oper-only.
  #
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # 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   #
- # the security tag, instead.                                          #
+ # the <security> tag, instead.                                        #
  #<module name="m_maphide.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #<module name="m_nokicks.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # No nicks module: Adds the +N channel mode, as well as the +b N:
- # extended bantype. +N stops all users from changing their nick,
- # the +b N: extban stops anyone from matching a +b N:nick!user@host
- # mask from changing their nick.
+ # No nicks module: Adds the +N channel mode, as well as the N extban.
+ # +N stops all users from changing their nick, the N extban stops
+ # anyone from matching a +b N:nick!user@host mask from changing their
+ # nick.
  #<module name="m_nonicks.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #<module name="m_nonotice.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Network buisness join module
+ # Network business join module
  # Allows an oper to join a channel using /OJOIN, giving them +Y on the
  # channel which makes them immune to kick/deop/etc.
  #<module name="m_ojoin.so">
  # If you are using the m_operjoin.so module, specify options here:    #
  #                                                                     #
  # channel     -      The channel name to join, can also be a comma    #
- #                    seperated list eg. "#channel1,#channel2".        #
+ #                    separated list eg. "#channel1,#channel2".        #
  #                                                                     #
  # override    -      Lets the oper join walking thru any modes that   #
  #                    might be set, even bans. Use "yes" or "no".      #
  #<operprefix prefix="!">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Oper MOTD module: Provides support for seperate message of the day
+ # Oper MOTD module: Provides support for separate message of the day
  # on oper-up
  # This module is oper-only.
  #<module name="m_opermotd.so">
  #                 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
  # Oper levels module: Gives each oper a level and prevents
  # actions being taken against higher level opers
  # Specify the level as the 'level' parameter of the <type> tag
- # This module is oper-only.
+ # This module is oper-only.
  #<module name="m_operlevels.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
                # You can also use $user for the user ident string.
                forwardmsg="NOTICE $nick :*** Forwarding PASS to $nickrequired"
  
-               # cmd: Command for the nick to run when it recieves a connect
+               # cmd: Command for the nick to run when it receives a connect
                # password. 
                cmd="PRIVMSG $nickrequired :IDENTIFY $pass">
  
  # whenever +P is set, unset, or the topic/modes on a +P channel is changed.
  # If you want to do this, set the filename below, and uncomment the include.
  #
- #<permchanneldb filename="data/permchannels.conf">
+ # 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">
  #
  # You may also create channels on startup by using the <permchannels> block.
  # Optional - If you specify to use the m_randquote.so module, then    #
  # specify below the path to the randquotes.conf file.                 #
  #                                                                     #
- #<randquote file="randquotes.conf">
+ #<randquote file="quotes.txt">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Redirect module: Adds channel redirection (mode +L)                 #
  # 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
- # system (ie: any Linux, BSD, but not Windows). You must have at least
- # 1 provider loaded to use m_filter or m_rline.
+ # system (i.e.: any Linux, BSD, but not Windows). You must have at
+ # least 1 provider loaded to use m_filter or m_rline.
  # On POSIX-compliant systems, regex syntax can be found by using the
  # command: 'man 7 regex'.
  #<module name="m_regex_posix.so">
  # alternative to /KICK
  #<module name="m_remove.so">
  
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# 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
  # This module is oper-only.
  # and is similar in operation to the way asuka and ircu handle services.
  #
  # At the same time, this offers +r for users and channels to mark them
- # as identified seperately from the idea of a master account, which
+ # as identified separately from the idea of a master account, which
  # can be useful for services which are heavily nick-as-account centric.
  #
  # This replaces m_services from 1.1 and earlier.
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SETNAME module: Adds the /SETNAME command
- # This module is oper-only.
- # To use, SETNAME must be in one of your oper class blocks.
  #<module name="m_setname.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # it directly affects the mode object itself.
  #<showwhois opersonly="yes"
  #
- # You may also set whether or not users should recieve whois notices, should
+ # You may also set whether or not users should receive whois notices, should
  # they be /whois'd by an oper.
  # showfromopers="yes">
  
  #<module name="m_sslmodes.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # GnuTLS ssl module: Adds support for client-server SSL using GnuTLS,
- # if enabled. You must copy the source for this module from the directory
- # src/modules/extra, or answer 'yes' in ./configure when asked if you
- # want to enable this, or it will not load.
+ # GnuTLS SSL module: Adds support for SSL connections using GnuTLS,
+ # if enabled. You must answer 'yes' in ./configure when asked or
+ # manually symlink the source for this module from the directory
+ # src/modules/extra, if you want to enable this, or it will not load.
  #<module name="m_ssl_gnutls.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-  GNUTLS CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # m_ssl_gnutls.so is too complex it describe here, see the wiki:      #
  # http://wiki.inspircd.org/Modules/ssl_gnutls                         #
- #                                                                     #
- # NOTE: If you want to use this module to encrypt and sign your       #
- # server to server traffic, you MUST load it before m_spanningtree in #
- # your configuration file!                                            #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SSL Info module: Allows users to retrieve information about other
  #<module name="m_sslinfo.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # OpenSSL ssl module: Adds support for client-server SSL using OpenSSL,
- # if enabled. You must copy the source for this module from the directory
- # src/modules/extra, or answer 'yes' in ./configure when asked if you
- # want to enable this, or it will not load.
+ # OpenSSL SSL module: Adds support for SSL connections using OpenSSL,
+ # if enabled. You must answer 'yes' in ./configure when asked or symlink
+ # the source for this module from the directory src/modules/extra, if
+ # you want to enable this, or it will not load.
  #<module name="m_ssl_openssl.so">
  #
  #-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # m_ssl_openssl.so is too complex it describe here, see the wiki:     #
  # http://wiki.inspircd.org/Modules/ssl_openssl                        #
- #                                                                     #
- # NOTE: If you want to use this module to encrypt and sign your       #
- # server to server traffic, you MUST load it before m_spanningtree in #
- # your configuration file!                                            #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Strip color module: Adds the channel mode +S
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Test line module: Adds the /TLINE command, used to test how many
- # users a /GLINE or /ZLINE etc would match.
+ # users a /GLINE or /ZLINE etc. would match.
  # This module is oper-only.
  # To use, TLINE must be in one of your oper class blocks.
  #<module name="m_tline.so">
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # UHNAMES support module: Adds support for the IRCX style UHNAMES
  # extension, which displays ident and hostname in the names list for
- # each user, saving clients from doing a WHO on the channel. Note that
- # this module is not widely supported yet. If a client does not support
- # UHNAMES it will not enable it, this will not break incompatible
- # clients.
+ # each user, saving clients from doing a WHO on the channel.
+ # If a client does not support UHNAMES it will not enable it, this will
+ # not break incompatible clients.
  #<module name="m_uhnames.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Userip module: Adds the /USERIP command
- # This module is oper-only.
- # To use, USERIP must be in one of your oper class blocks.
+ # Allows users to query their own IP, also allows opers to query the IP
+ # of anyone else.
  #<module name="m_userip.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
index 2d27b43a0b33e940f778bce732f25d8d34aa72d2,65896808f70ad3a78b0e643263cb14548a1256f5..afd54359c75492726f33c9f5f5bac09d02bff443
  <module name="m_chanfilter.so">
  <chanfilter hidemask="yes">
  
 -<module name="m_chanprotect.so">
 -
 -<chanprotect
 -      noservices="no"
 -      qprefix="~"
 -      aprefix="&amp;"
 -      deprotectself="yes"
 -      deprotectothers="yes">
 -
  <module name="m_check.so">
  <module name="m_chghost.so">
  <hostname charmap="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_/0123456789">
  # If you are using the m_operjoin.so module, specify options here:    #
  #                                                                     #
  # channel     -      The channel name to join, can also be a comma    #
- #                    seperated list eg. "#channel1,#channel2".        #
+ #                    separated list eg. "#channel1,#channel2".        #
  #                                                                     #
  # override    -      Lets the oper join walking thru any modes that   #
  #                    might be set, even bans. Use "yes" or "no".      #
  <module name="m_operlog.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Oper MOTD module: Provides support for seperate message of the day
+ # Oper MOTD module: Provides support for separate message of the day
  # on oper-up
  # This module is oper-only.
  #<module name="m_opermotd.so">
  <module name="m_sslmodes.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # GnuTLS ssl module: Adds support for client-server SSL using GnuTLS,
- # if enabled. You must copy the source for this module from the directory
- # src/modules/extra, or answer 'yes' in ./configure when asked if you
- # want to enable this, or it will not load.
+ # GnuTLS SSL module: Adds support for SSL connections using GnuTLS,
+ # if enabled. You must answer 'yes' in ./configure when asked or symlink
+ # the source for this module from the directory src/modules/extra, if
+ # you want to enable this, or it will not load. 
  #<module name="m_ssl_gnutls.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-  GNUTLS CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # m_ssl_gnutls.so is too complex it describe here, see the wiki:      #
  # http://wiki.inspircd.org/Modules/ssl_gnutls                         #
- #                                                                     #
- # NOTE: If you want to use this module to encrypt and sign your       #
- # server to server traffic, you MUST load it before m_spanningtree in #
- # your configuration file!                                            #
  
  <module name="m_sslinfo.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # OpenSSL ssl module: Adds support for client-server SSL using OpenSSL,
- # if enabled. You must copy the source for this module from the directory
- # src/modules/extra, or answer 'yes' in ./configure when asked if you
- # want to enable this, or it will not load.
+ # OpenSSL SSL module: Adds support for SSL connections using OpenSSL,
+ # if enabled. You must answer 'yes' in ./configure when asked or symlink
+ # the source for this module from the directory src/modules/extra, if
+ # you want to enable this, or it will not load.
  #<module name="m_ssl_openssl.so">
  #
  #-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # m_ssl_openssl.so is too complex it describe here, see the wiki:     #
  # http://wiki.inspircd.org/Modules/ssl_openssl                        #
- #                                                                     #
- # NOTE: If you want to use this module to encrypt and sign your       #
- # server to server traffic, you MUST load it before m_spanningtree in #
- # your configuration file!                                            #
  
  <module name="m_stripcolor.so">
  <module name="m_svshold.so">
index c62de0e55fa1550a7ef15646f1f0dfe78a0e9221,6cd00a409dd9cdf8e59a9cad995f13182bcc296c..e7b65b67567b62992f6fbcfbf8425ed76502eb3f
       #   - 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 $*)
 +     #   - users/samode-usermodes: allows opers with this priv to change the user modes of any other user using /SAMODE
       #   - channels/high-join-limit: allows opers with this priv to join <channels:opers> total channels instead of <channels:users> total channels.
       # PERMISSIONS:
       #   - 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 recieve data without worrying about being disconnected for exceeding limits (*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 potantially dangerous, as they grant users with them the ability to hammer your server's CPU/RAM as much as they want, essentially.
+      # *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"
  
       # usermodes: Oper-only usermodes that opers with this class can use.
        # Remember: This is case sensitive
        name="Brain"
  
-       # hash: what hash this password is hashed with. requires the module
-       # for selected hash (m_md5.so, m_sha256.so or m_ripemd160.so) be
-       # loaded and the password hashing module (m_password_hash.so)
-       # loaded. Options here are: "md5", "sha256" and "ripemd160".
+       # hash: what hash this password is hashed with.
+       # Requires the module for selected hash (m_md5.so, m_sha256.so
+       # or m_ripemd160.so) be loaded and the password hashing module
+       # (m_password_hash.so) loaded.
+       # Options here are: "md5", "sha256" and "ripemd160", or one of
+       # these prefixed with "hmac-", e.g.: "hmac-sha256".
        # Create hashed password with: /mkpasswd <hash> <password>
        hash="sha256"
  
diff --combined include/base.h
index 117ffab762c2317d09154138353652553caf8b5a,0a4456f3a7fd398c2aea015b38dc88c76a5d5ded..c6d361576f8772a663c18e9d079f2708806ee1ac
@@@ -20,7 -20,8 +20,7 @@@
   */
  
  
 -#ifndef BASE_H
 -#define BASE_H
 +#pragma once
  
  #include <map>
  #include <deque>
@@@ -121,7 -122,7 +121,7 @@@ class CoreExport usecountbas
  };
  
  template <typename T>
- class CoreExport reference
+ class reference
  {
        T* value;
   public:
@@@ -254,3 -255,5 +254,3 @@@ class CoreExport ServiceProvider : publ
        virtual ~ServiceProvider();
  };
  
 -
 -#endif
diff --combined include/socketengine.h
index 8e4c3dfc9e3dbf5dca98ae2a86b276b63c15502e,37b7d637350b833e46d38f13d932f253008f36d6..d97c0ea9f9b9bd6c139abd09575e459e6a9d5ebc
   */
  
  
 -#ifndef SOCKETENGINE_H
 -#define SOCKETENGINE_H
 +#pragma once
  
  #include <vector>
  #include <string>
  #include <map>
 -#include "inspircd_config.h"
 +#include "config.h"
  #include "socket.h"
  #include "base.h"
  
@@@ -127,7 -128,7 +127,7 @@@ enum EventMas
        /** Add a trial write. During the next DispatchEvents invocation, this
         * will call HandleEvent with EVENT_WRITE unless writes are known to be
         * blocking.
 -       * 
 +       *
         * This could be used to group several writes together into a single
         * send() syscall, or to ensure that writes are blocking when attempting
         * to use FD_WANT_FAST_WRITE.
        /** Assert that writes are known to block. This cancels FD_ADD_TRIAL_WRITE.
         * Reset by SE before running EVENT_WRITE
         */
 -      FD_WRITE_WILL_BLOCK = 0x8000, 
 +      FD_WRITE_WILL_BLOCK = 0x8000,
  
        /** Mask for trial read/trial write */
        FD_TRIAL_NOTE_MASK = 0x5000
@@@ -417,7 -418,7 +417,7 @@@ public
         * @param buf The buffer in which the data that is sent is stored.
         * @param len The size of the buffer.
         * @param flags A flag value that controls the sending of the data.
 -       * @param to The remote IP address and port.    
 +       * @param to The remote IP address and port.
         * @param tolen The size of the to parameter.
         * @return This method should return exactly the same values as the system call it emulates.
         */
         * Checks EAGAIN and WSAEWOULDBLOCK
         */
        static bool IgnoreError();
+       /** Return the last socket related error. strrerror(errno) on *nix
+        */
+       static std::string LastError();
+       /** Returns the error for the given error num, strerror(errnum) on *nix
+        */
+       static std::string GetError(int errnum);
  };
  
  inline bool SocketEngine::IgnoreError()
  }
  
  SocketEngine* CreateSocketEngine();
 -
 -#endif
 -
index b406781b1932f17997814e859f5daf9a55bc823e,3a6444b45b655f5346138054878f729dc1aad413..75d1ee8ff449416766f0ebdf164c267e05c79df9
  #include "inspircd.h"
  #include "commands/cmd_whowas.h"
  
 -WhoWasMaintainTimer * timer;
 -
 -CommandWhowas::CommandWhowas( Module* parent) : Command(parent, "WHOWAS", 1)
 +CommandWhowas::CommandWhowas( Module* parent)
 +      : Command(parent, "WHOWAS", 1)
 +      , GroupSize(0), MaxGroups(0), MaxKeep(0)
  {
        syntax = "<nick>{,<nick>}";
        Penalty = 2;
 -      timer = new WhoWasMaintainTimer(3600);
 -      ServerInstance->Timers->AddTimer(timer);
  }
  
  CmdResult CommandWhowas::Handle (const std::vector<std::string>& parameters, User* user)
  {
        /* if whowas disabled in config */
 -      if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0)
 +      if (this->GroupSize == 0 || this->MaxGroups == 0)
        {
                user->WriteNumeric(421, "%s %s :This command has been disabled.",user->nick.c_str(),name.c_str());
                return CMD_FAILURE;
        if (i == whowas.end())
        {
                user->WriteNumeric(406, "%s %s :There was no such nickname",user->nick.c_str(),parameters[0].c_str());
 -              user->WriteNumeric(369, "%s %s :End of WHOWAS",user->nick.c_str(),parameters[0].c_str());
 -              return CMD_FAILURE;
        }
        else
        {
                whowas_set* grp = i->second;
 -              if (grp->size())
 +              if (!grp->empty())
                {
                        for (whowas_set::iterator ux = grp->begin(); ux != grp->end(); ux++)
                        {
                                WhoWasGroup* u = *ux;
-                               time_t rawtime = u->signon;
-                               tm *timeinfo;
-                               char b[25];
-                               timeinfo = localtime(&rawtime);
-                               strncpy(b,asctime(timeinfo),24);
-                               b[24] = 0;
  
                                user->WriteNumeric(314, "%s %s %s %s * :%s",user->nick.c_str(),parameters[0].c_str(),
                                        u->ident.c_str(),u->dhost.c_str(),u->gecos.c_str());
                                        user->WriteNumeric(379, "%s %s :was connecting from *@%s",
                                                user->nick.c_str(), parameters[0].c_str(), u->host.c_str());
  
 -                              if (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
 -                                      user->WriteNumeric(312, "%s %s %s :%s",user->nick.c_str(),parameters[0].c_str(), ServerInstance->Config->HideWhoisServer.c_str(), signon.c_str());
 -                              else
 -                                      user->WriteNumeric(312, "%s %s %s :%s",user->nick.c_str(),parameters[0].c_str(), u->server.c_str(), signon.c_str());
+                               std::string signon = ServerInstance->TimeString(u->signon);
-                               user->WriteNumeric(312, "%s %s %s :%s",user->nick.c_str(), parameters[0].c_str(), (hide_server ? ServerInstance->Config->HideWhoisServer.c_str() : u->server.c_str()), b);
 +                              bool hide_server = (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"));
++                              user->WriteNumeric(312, "%s %s %s :%s",user->nick.c_str(), parameters[0].c_str(), (hide_server ? ServerInstance->Config->HideWhoisServer.c_str() : u->server.c_str()), signon.c_str());
                        }
                }
                else
                {
                        user->WriteNumeric(406, "%s %s :There was no such nickname",user->nick.c_str(),parameters[0].c_str());
 -                      user->WriteNumeric(369, "%s %s :End of WHOWAS",user->nick.c_str(),parameters[0].c_str());
 -                      return CMD_FAILURE;
                }
        }
  
@@@ -88,11 -89,15 +81,11 @@@ std::string CommandWhowas::GetStats(
  {
        int whowas_size = 0;
        int whowas_bytes = 0;
 -      whowas_users_fifo::iterator iter;
 -      for (iter = whowas_fifo.begin(); iter != whowas_fifo.end(); iter++)
 +      for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
        {
 -              whowas_set* n = (whowas_set*)whowas.find(iter->second)->second;
 -              if (n->size())
 -              {
 -                      whowas_size += n->size();
 -                      whowas_bytes += (sizeof(whowas_set) + ( sizeof(WhoWasGroup) * n->size() ) );
 -              }
 +              whowas_set* n = i->second;
 +              whowas_size += n->size();
 +              whowas_bytes += (sizeof(whowas_set) + ( sizeof(WhoWasGroup) * n->size() ) );
        }
        return "Whowas entries: " +ConvToStr(whowas_size)+" ("+ConvToStr(whowas_bytes)+" bytes)";
  }
  void CommandWhowas::AddToWhoWas(User* user)
  {
        /* if whowas disabled */
 -      if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0)
 +      if (this->GroupSize == 0 || this->MaxGroups == 0)
        {
                return;
        }
  
 -      whowas_users::iterator iter = whowas.find(irc::string(user->nick.c_str()));
 +      // Insert nick if it doesn't exist
 +      // 'first' will point to the newly inserted element or to the existing element with an equivalent key
 +      std::pair<whowas_users::iterator, bool> ret = whowas.insert(std::make_pair(irc::string(user->nick.c_str()), static_cast<whowas_set*>(NULL)));
  
 -      if (iter == whowas.end())
 +      if (ret.second) // If inserted
        {
 +              // This nick is new, create a list for it and add the first record to it
                whowas_set* n = new whowas_set;
 -              WhoWasGroup *a = new WhoWasGroup(user);
 -              n->push_back(a);
 -              whowas[user->nick.c_str()] = n;
 -              whowas_fifo.push_back(std::make_pair(ServerInstance->Time(),user->nick.c_str()));
 +              n->push_back(new WhoWasGroup(user));
 +              ret.first->second = n;
  
 -              if ((int)(whowas.size()) > ServerInstance->Config->WhoWasMaxGroups)
 +              // Add this nick to the fifo too
 +              whowas_fifo.push_back(std::make_pair(ServerInstance->Time(), ret.first->first));
 +
 +              if (whowas.size() > this->MaxGroups)
                {
 -                      whowas_users::iterator iter2 = whowas.find(whowas_fifo[0].second);
 -                      if (iter2 != whowas.end())
 +                      // Too many nicks, remove the nick which was inserted the longest time ago from both the map and the fifo
 +                      whowas_users::iterator it = whowas.find(whowas_fifo.front().second);
 +                      if (it != whowas.end())
                        {
 -                              whowas_set* n2 = (whowas_set*)iter2->second;
 -
 -                              if (n2->size())
 -                              {
 -                                      while (n2->begin() != n2->end())
 -                                      {
 -                                              WhoWasGroup *a2 = *(n2->begin());
 -                                              delete a2;
 -                                              n2->pop_front();
 -                                      }
 -                              }
 -
 -                              delete n2;
 -                              whowas.erase(iter2);
 +                              whowas_set* set = it->second;
 +                              for (whowas_set::iterator i = set->begin(); i != set->end(); ++i)
 +                                      delete *i;
 +
 +                              delete set;
 +                              whowas.erase(it);
                        }
                        whowas_fifo.pop_front();
                }
        }
        else
        {
 -              whowas_set* group = (whowas_set*)iter->second;
 -              WhoWasGroup *a = new WhoWasGroup(user);
 -              group->push_back(a);
 +              // We've met this nick before, add a new record to the list
 +              whowas_set* set = ret.first->second;
 +              set->push_back(new WhoWasGroup(user));
  
 -              if ((int)(group->size()) > ServerInstance->Config->WhoWasGroupSize)
 +              // If there are too many records for this nick, remove the oldest (front)
 +              if (set->size() > this->GroupSize)
                {
 -                      WhoWasGroup *a2 = (WhoWasGroup*)*(group->begin());
 -                      delete a2;
 -                      group->pop_front();
 +                      delete set->front();
 +                      set->pop_front();
                }
        }
  }
  
  /* on rehash, refactor maps according to new conf values */
 -void CommandWhowas::PruneWhoWas(time_t t)
 +void CommandWhowas::Prune()
  {
 -      /* config values */
 -      int groupsize = ServerInstance->Config->WhoWasGroupSize;
 -      int maxgroups = ServerInstance->Config->WhoWasMaxGroups;
 -      int maxkeep =   ServerInstance->Config->WhoWasMaxKeep;
 +      time_t min = ServerInstance->Time() - this->MaxKeep;
  
        /* first cut the list to new size (maxgroups) and also prune entries that are timed out. */
 -      whowas_users::iterator iter;
 -      int fifosize;
 -      while ((fifosize = (int)whowas_fifo.size()) > 0)
 +      while (!whowas_fifo.empty())
        {
 -              if (fifosize > maxgroups || whowas_fifo[0].first < t - maxkeep)
 +              if ((whowas_fifo.size() > this->MaxGroups) || (whowas_fifo.front().first < min))
                {
 -                      iter = whowas.find(whowas_fifo[0].second);
 +                      whowas_users::iterator iter = whowas.find(whowas_fifo.front().second);
  
                        /* hopefully redundant integrity check, but added while debugging r6216 */
                        if (iter == whowas.end())
                        {
                                /* this should never happen, if it does maps are corrupt */
 -                              ServerInstance->Logs->Log("WHOWAS",DEFAULT, "BUG: Whowas maps got corrupted! (1)");
 +                              ServerInstance->Logs->Log("WHOWAS", LOG_DEFAULT, "BUG: Whowas maps got corrupted! (1)");
                                return;
                        }
  
 -                      whowas_set* n = (whowas_set*)iter->second;
 -
 -                      if (n->size())
 -                      {
 -                              while (n->begin() != n->end())
 -                              {
 -                                      WhoWasGroup *a = *(n->begin());
 -                                      delete a;
 -                                      n->pop_front();
 -                              }
 -                      }
 +                      whowas_set* set = iter->second;
 +                      for (whowas_set::iterator i = set->begin(); i != set->end(); ++i)
 +                              delete *i;
  
 -                      delete n;
 +                      delete set;
                        whowas.erase(iter);
                        whowas_fifo.pop_front();
                }
        }
  
        /* Then cut the whowas sets to new size (groupsize) */
 -      fifosize = (int)whowas_fifo.size();
 -      for (int i = 0; i < fifosize; i++)
 +      for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
        {
 -              iter = whowas.find(whowas_fifo[0].second);
 -              /* hopefully redundant integrity check, but added while debugging r6216 */
 -              if (iter == whowas.end())
 +              whowas_set* n = i->second;
 +              while (n->size() > this->GroupSize)
                {
 -                      /* this should never happen, if it does maps are corrupt */
 -                      ServerInstance->Logs->Log("WHOWAS",DEFAULT, "BUG: Whowas maps got corrupted! (2)");
 -                      return;
 -              }
 -              whowas_set* n = (whowas_set*)iter->second;
 -              if (n->size())
 -              {
 -                      int nickcount = n->size();
 -                      while (n->begin() != n->end() && nickcount > groupsize)
 -                      {
 -                              WhoWasGroup *a = *(n->begin());
 -                              delete a;
 -                              n->pop_front();
 -                              nickcount--;
 -                      }
 +                      delete n->front();
 +                      n->pop_front();
                }
        }
  }
  
  /* call maintain once an hour to remove expired nicks */
 -void CommandWhowas::MaintainWhoWas(time_t t)
 +void CommandWhowas::Maintain()
  {
 -      for (whowas_users::iterator iter = whowas.begin(); iter != whowas.end(); iter++)
 +      time_t min = ServerInstance->Time() - this->MaxKeep;
 +      for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
        {
 -              whowas_set* n = (whowas_set*)iter->second;
 -              if (n->size())
 +              whowas_set* set = i->second;
 +              while (!set->empty() && set->front()->signon < min)
                {
 -                      while ((n->begin() != n->end()) && ((*n->begin())->signon < t - ServerInstance->Config->WhoWasMaxKeep))
 -                      {
 -                              WhoWasGroup *a = *(n->begin());
 -                              delete a;
 -                              n->erase(n->begin());
 -                      }
 +                      delete set->front();
 +                      set->pop_front();
                }
        }
  }
  
  CommandWhowas::~CommandWhowas()
  {
 -      if (timer)
 +      for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
        {
 -              ServerInstance->Timers->DelTimer(timer);
 -      }
 +              whowas_set* set = i->second;
 +              for (whowas_set::iterator j = set->begin(); j != set->end(); ++j)
 +                      delete *j;
  
 -      whowas_users::iterator iter;
 -      int fifosize;
 -      while ((fifosize = (int)whowas_fifo.size()) > 0)
 -      {
 -              iter = whowas.find(whowas_fifo[0].second);
 -
 -              /* hopefully redundant integrity check, but added while debugging r6216 */
 -              if (iter == whowas.end())
 -              {
 -                      /* this should never happen, if it does maps are corrupt */
 -                      ServerInstance->Logs->Log("WHOWAS",DEFAULT, "BUG: Whowas maps got corrupted! (3)");
 -                      return;
 -              }
 -
 -              whowas_set* n = (whowas_set*)iter->second;
 -
 -              if (n->size())
 -              {
 -                      while (n->begin() != n->end())
 -                      {
 -                              WhoWasGroup *a = *(n->begin());
 -                              delete a;
 -                              n->pop_front();
 -                      }
 -              }
 -
 -              delete n;
 -              whowas.erase(iter);
 -              whowas_fifo.pop_front();
 +              delete set;
        }
  }
  
@@@ -226,10 -292,23 +219,10 @@@ WhoWasGroup::WhoWasGroup(User* user) : 
  {
  }
  
 -WhoWasGroup::~WhoWasGroup()
 -{
 -}
 -
 -/* every hour, run this function which removes all entries older than Config->WhoWasMaxKeep */
 -void WhoWasMaintainTimer::Tick(time_t)
 -{
 -      Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
 -      if (whowas)
 -      {
 -              WhowasRequest(whowas, whowas, WhowasRequest::WHOWAS_MAINTAIN).Send();
 -      }
 -}
 -
  class ModuleWhoWas : public Module
  {
        CommandWhowas cmd;
 +
   public:
        ModuleWhoWas() : cmd(this)
        {
        void init()
        {
                ServerInstance->Modules->AddService(cmd);
 +              OnRehash(NULL);
        }
  
 -      void OnRequest(Request& request)
 +      void OnGarbageCollect()
        {
 -              WhowasRequest& req = static_cast<WhowasRequest&>(request);
 -              switch (req.type)
 -              {
 -                      case WhowasRequest::WHOWAS_ADD:
 -                              cmd.AddToWhoWas(req.user);
 -                              break;
 -                      case WhowasRequest::WHOWAS_STATS:
 -                              req.value = cmd.GetStats();
 -                              break;
 -                      case WhowasRequest::WHOWAS_PRUNE:
 -                              cmd.PruneWhoWas(ServerInstance->Time());
 -                              break;
 -                      case WhowasRequest::WHOWAS_MAINTAIN:
 -                              cmd.MaintainWhoWas(ServerInstance->Time());
 -                              break;
 -              }
 +              // Remove all entries older than MaxKeep
 +              cmd.Maintain();
 +      }
 +
 +      void OnUserQuit(User* user, const std::string& message, const std::string& oper_message)
 +      {
 +              cmd.AddToWhoWas(user);
 +      }
 +
 +      ModResult OnStats(char symbol, User* user, string_list &results)
 +      {
 +              if (symbol == 'z')
 +                      results.push_back(ServerInstance->Config->ServerName+" 249 "+user->nick+" :"+cmd.GetStats());
 +
 +              return MOD_RES_PASSTHRU;
 +      }
 +
 +      void OnRehash(User* user)
 +      {
 +              ConfigTag* tag = ServerInstance->Config->ConfValue("whowas");
 +              unsigned int NewGroupSize = tag->getInt("groupsize", 10, 0, 10000);
 +              unsigned int NewMaxGroups = tag->getInt("maxgroups", 10240, 0, 1000000);
 +              unsigned int NewMaxKeep = tag->getDuration("maxkeep", 3600, 3600);
 +
 +              if ((NewGroupSize == cmd.GroupSize) && (NewMaxGroups == cmd.MaxGroups) && (NewMaxKeep == cmd.MaxKeep))
 +                      return;
 +
 +              cmd.GroupSize = NewGroupSize;
 +              cmd.MaxGroups = NewMaxGroups;
 +              cmd.MaxKeep = NewMaxKeep;
 +              cmd.Prune();
        }
  
        Version GetVersion()
        {
 -              return Version("WHOWAS Command", VF_VENDOR);
 +              return Version("WHOWAS", VF_VENDOR);
        }
  };
  
diff --combined src/helperfuncs.cpp
index 2626da6bbd6132518866946809e73fd181503909,439320c1e00d85334c29d3da845357b246d11cfa..cfbd53c98fbe597359cfb2851129650584ecbec5
@@@ -22,6 -22,8 +22,6 @@@
   */
  
  
 -/* $Core */
 -
  #ifdef _WIN32
  #define _CRT_RAND_S
  #include <stdlib.h>
@@@ -36,7 -38,7 +36,7 @@@ std::string InspIRCd::GetServerDescript
  {
        std::string description;
  
 -      FOREACH_MOD(I_OnGetServerDescription,OnGetServerDescription(servername,description));
 +      FOREACH_MOD(OnGetServerDescription, (servername,description));
  
        if (!description.empty())
        {
@@@ -64,6 -66,19 +64,6 @@@ User* InspIRCd::FindNick(const std::str
        return iter->second;
  }
  
 -User* InspIRCd::FindNick(const char* nick)
 -{
 -      if (isdigit(*nick))
 -              return FindUUID(nick);
 -
 -      user_hash::iterator iter = this->Users->clientlist->find(nick);
 -
 -      if (iter == this->Users->clientlist->end())
 -              return NULL;
 -
 -      return iter->second;
 -}
 -
  User* InspIRCd::FindNickOnly(const std::string &nick)
  {
        user_hash::iterator iter = this->Users->clientlist->find(nick);
        return iter->second;
  }
  
 -User* InspIRCd::FindNickOnly(const char* nick)
 -{
 -      user_hash::iterator iter = this->Users->clientlist->find(nick);
 -
 -      if (iter == this->Users->clientlist->end())
 -              return NULL;
 -
 -      return iter->second;
 -}
 -
  User *InspIRCd::FindUUID(const std::string &uid)
  {
        user_hash::iterator finduuid = this->Users->uuidlist->find(uid);
  
        return finduuid->second;
  }
 -
 -User *InspIRCd::FindUUID(const char *uid)
 -{
 -      return FindUUID(std::string(uid));
 -}
 -
  /* find a channel record by channel name and return a pointer to it */
 -Channel* InspIRCd::FindChan(const char* chan)
 -{
 -      chan_hash::iterator iter = chanlist->find(chan);
 -
 -      if (iter == chanlist->end())
 -              /* Couldn't find it */
 -              return NULL;
 -
 -      return iter->second;
 -}
  
  Channel* InspIRCd::FindChan(const std::string &chan)
  {
@@@ -104,8 -145,8 +104,8 @@@ void InspIRCd::SendError(const std::str
                User* u = *i;
                if (u->registered == REG_ALL)
                {
 -                      u->WriteServ("NOTICE %s :%s",u->nick.c_str(),s.c_str());
 -              }
 +                      u->WriteNotice(s);
 +              }
                else
                {
                        /* Unregistered connections receive ERROR, not a NOTICE */
        }
  }
  
 -/* return channel count */
 -long InspIRCd::ChannelCount()
 -{
 -      return chanlist->size();
 -}
 -
  bool InspIRCd::IsValidMask(const std::string &mask)
  {
        const char* dest = mask.c_str();
@@@ -234,35 -281,47 +234,35 @@@ void InspIRCd::ProcessColors(file_cache
  }
  
  /* true for valid channel name, false else */
 -bool IsChannelHandler::Call(const char *chname, size_t max)
 +bool IsChannelHandler::Call(const std::string& chname)
  {
 -      const char *c = chname + 1;
 +      if (chname.empty() || chname.length() > ServerInstance->Config->Limits.ChanMax)
 +              return false;
  
 -      /* check for no name - don't check for !*chname, as if it is empty, it won't be '#'! */
 -      if (!chname || *chname != '#')
 -      {
 +      if (chname[0] != '#')
                return false;
 -      }
  
 -      while (*c)
 +      for (std::string::const_iterator i = chname.begin()+1; i != chname.end(); ++i)
        {
 -              switch (*c)
 +              switch (*i)
                {
                        case ' ':
                        case ',':
                        case 7:
                                return false;
                }
 -
 -              c++;
 -      }
 -
 -      size_t len = c - chname;
 -      /* too long a name - note funky pointer arithmetic here. */
 -      if (len > max)
 -      {
 -                      return false;
        }
  
        return true;
  }
  
  /* true for valid nickname, false else */
 -bool IsNickHandler::Call(const char* n, size_t max)
 +bool IsNickHandler::Call(const std::string& n)
  {
 -      if (!n || !*n)
 +      if (n.empty() || n.length() > ServerInstance->Config->Limits.NickMax)
                return false;
  
 -      unsigned int p = 0;
 -      for (const char* i = n; *i; i++, p++)
 +      for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
        {
                if ((*i >= 'A') && (*i <= '}'))
                {
                        continue;
                }
  
 -              if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i > n))
 +              if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i != n.begin()))
                {
                        /* "0"-"9", "-" can occur anywhere BUT the first char of a nickname */
                        continue;
                return false;
        }
  
 -      /* too long? or not */
 -      return (p <= max);
 +      return true;
  }
  
  /* return true for good ident, false else */
 -bool IsIdentHandler::Call(const char* n)
 +bool IsIdentHandler::Call(const std::string& n)
  {
 -      if (!n || !*n)
 +      if (n.empty())
                return false;
  
 -      for (const char* i = n; *i; i++)
 +      for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
        {
                if ((*i >= 'A') && (*i <= '}'))
                {
        return true;
  }
  
 -bool IsSIDHandler::Call(const std::string &str)
 +bool InspIRCd::IsSID(const std::string &str)
  {
        /* Returns true if the string given is exactly 3 characters long,
         * starts with a digit, and the other two characters are A-Z or digits
                         ((str[2] >= 'A' && str[2] <= 'Z') || isdigit(str[2])));
  }
  
 -/* open the proper logfile */
 -bool InspIRCd::OpenLog(char**, int)
 -{
 -      if (!Config->cmdline.writelog) return true; // Skip opening default log if -nolog
 -
 -      if (Config->cmdline.startup_log.empty())
 -              Config->cmdline.startup_log = LOG_PATH "/startup.log";
 -      FILE* startup = fopen(Config->cmdline.startup_log.c_str(), "a+");
 -
 -      if (!startup)
 -      {
 -              return false;
 -      }
 -
 -      FileWriter* fw = new FileWriter(startup);
 -      FileLogStream *f = new FileLogStream((Config->cmdline.forcedebug ? DEBUG : DEFAULT), fw);
 -
 -      this->Logs->AddLogType("*", f, true);
 -
 -      return true;
 -}
 -
  void InspIRCd::CheckRoot()
  {
  #ifndef _WIN32
        if (geteuid() == 0)
        {
                std::cout << "ERROR: You are running an irc server as root! DO NOT DO THIS!" << std::endl << std::endl;
 -              this->Logs->Log("STARTUP",DEFAULT,"Can't start as root");
 +              this->Logs->Log("STARTUP", LOG_DEFAULT, "Can't start as root");
                Exit(EXIT_STATUS_ROOT);
        }
  #endif
@@@ -342,15 -424,19 +342,15 @@@ void InspIRCd::SendWhoisLine(User* user
  
  void InspIRCd::SendWhoisLine(User* user, User* dest, int numeric, const char* format, ...)
  {
 -      char textbuffer[MAXBUF];
 -      va_list argsPtr;
 -      va_start (argsPtr, format);
 -      vsnprintf(textbuffer, MAXBUF, format, argsPtr);
 -      va_end(argsPtr);
 -
 -      this->SendWhoisLine(user, dest, numeric, std::string(textbuffer));
 +      std::string textbuffer;
 +      VAFORMAT(textbuffer, format, format)
 +      this->SendWhoisLine(user, dest, numeric, textbuffer);
  }
  
  /** Refactored by Brain, Jun 2009. Much faster with some clever O(1) array
   * lookups and pointer maths.
   */
 -long InspIRCd::Duration(const std::string &str)
 +unsigned long InspIRCd::Duration(const std::string &str)
  {
        unsigned char multiplier = 0;
        long total = 0;
        return total + subtotal;
  }
  
 +const char* InspIRCd::Format(va_list &vaList, const char* formatString)
 +{
 +      static std::vector<char> formatBuffer(1024);
 +
 +      while (true)
 +      {
 +              va_list dst;
 +              va_copy(dst, vaList);
 +
 +              int vsnret = vsnprintf(&formatBuffer[0], formatBuffer.size(), formatString, dst);
 +              if (vsnret > 0 && static_cast<unsigned>(vsnret) < formatBuffer.size())
 +              {
 +                      return &formatBuffer[0];
 +              }
 +
 +              formatBuffer.resize(formatBuffer.size() * 2);
 +      }
 +
 +      throw CoreException();
 +}
 +
 +const char* InspIRCd::Format(const char* formatString, ...)
 +{
 +      const char* ret;
 +      VAFORMAT(ret, formatString, formatString);
 +      return ret;
 +}
 +
  bool InspIRCd::ULine(const std::string& sserver)
  {
        if (sserver.empty())
@@@ -437,9 -495,48 +437,28 @@@ bool InspIRCd::SilentULine(const std::s
  
  std::string InspIRCd::TimeString(time_t curtime)
  {
-       return std::string(ctime(&curtime),24);
+ #ifdef _WIN32
+       if (curtime < 0)
+               curtime = 0;
+ #endif
+       struct tm* timeinfo = localtime(&curtime);
+       if (!timeinfo)
+       {
+               curtime = 0;
+               timeinfo = localtime(&curtime);
+       }
+       // If the calculated year exceeds four digits or is less than the year 1000,
+       // the behavior of asctime() is undefined
+       if (timeinfo->tm_year + 1900 > 9999)
+               timeinfo->tm_year = 9999 - 1900;
+       else if (timeinfo->tm_year + 1900 < 1000)
+               timeinfo->tm_year = 0;
+       return std::string(asctime(timeinfo),24);
  }
  
 -// You should only pass a single character to this.
 -void InspIRCd::AddExtBanChar(char c)
 -{
 -      std::string &tok = Config->data005;
 -      std::string::size_type ebpos = tok.find(" EXTBAN=,");
 -
 -      if (ebpos == std::string::npos)
 -      {
 -              tok.append(" EXTBAN=,");
 -              tok.push_back(c);
 -      }
 -      else
 -      {
 -              ebpos += 9;
 -              while (isalpha(tok[ebpos]) && tok[ebpos] < c)
 -                      ebpos++;
 -              tok.insert(ebpos, 1, c);
 -      }
 -}
 -
  std::string InspIRCd::GenRandomStr(int length, bool printable)
  {
        char* buf = new char[length];
diff --combined src/inspircd.cpp
index c94f0884222aaf7b7b9e764ef4dcedc50e614023,9516449a0858d27e804987ffba7a3106df3cd9f1..2171e2a9fecaaa40212f3b2ca05bbfae27eb446f
@@@ -26,7 -26,9 +26,7 @@@
   */
  
  
 -/* $Core */
  #include "inspircd.h"
 -#include "inspircd_version.h"
  #include <signal.h>
  
  #ifndef _WIN32
@@@ -61,6 -63,7 +61,6 @@@
  #include "testsuite.h"
  
  InspIRCd* ServerInstance = NULL;
 -int* mysig = NULL;
  
  /** Seperate from the other casemap tables so that code *can* still exclusively rely on RFC casemapping
   * if it must.
@@@ -76,17 -79,26 +76,17 @@@ unsigned const char *national_case_inse
   */
  const char* ExitCodes[] =
  {
 -              "No error", /* 0 */
 -              "DIE command", /* 1 */
 -              "execv() failed", /* 2 */
 -              "Internal error", /* 3 */
 -              "Config file error", /* 4 */
 -              "Logfile error", /* 5 */
 -              "POSIX fork failed", /* 6 */
 -              "Bad commandline parameters", /* 7 */
 -              "No ports could be bound", /* 8 */
 -              "Can't write PID file", /* 9 */
 -              "SocketEngine could not initialize", /* 10 */
 -              "Refusing to start up as root", /* 11 */
 -              "Found a <die> tag!", /* 12 */
 -              "Couldn't load module on startup", /* 13 */
 -              "Could not create windows forked process", /* 14 */
 -              "Received SIGTERM", /* 15 */
 -              "Bad command handler loaded", /* 16 */
 -              "RegisterServiceCtrlHandler failed", /* 17 */
 -              "UpdateSCMStatus failed", /* 18 */
 -              "CreateEvent failed" /* 19 */
 +              "No error",                                                             // 0
 +              "DIE command",                                                  // 1
 +              "Config file error",                                    // 2
 +              "Logfile error",                                                // 3
 +              "POSIX fork failed",                                    // 4
 +              "Bad commandline parameters",                   // 5
 +              "Can't write PID file",                                 // 6
 +              "SocketEngine could not initialize",    // 7
 +              "Refusing to start up as root",                 // 8
 +              "Couldn't load module on startup",              // 9
 +              "Received SIGTERM"                                              // 10
  };
  
  template<typename T> static void DeleteZero(T*&n)
  
  void InspIRCd::Cleanup()
  {
 +      // Close all listening sockets
        for (unsigned int i = 0; i < ports.size(); i++)
        {
 -              /* This calls the constructor and closes the listening socket */
                ports[i]->cull();
                delete ports[i];
        }
        /* Must be deleted before modes as it decrements modelines */
        if (FakeClient)
                FakeClient->cull();
 -      if (Res)
 -              Res->cull();
        DeleteZero(this->FakeClient);
        DeleteZero(this->Users);
        DeleteZero(this->Modes);
        DeleteZero(this->BanCache);
        DeleteZero(this->SNO);
        DeleteZero(this->Config);
 -      DeleteZero(this->Res);
        DeleteZero(this->chanlist);
        DeleteZero(this->PI);
        DeleteZero(this->Threads);
        DeleteZero(this->Timers);
        DeleteZero(this->SE);
 -      /* Close logging */
 -      this->Logs->CloseLogs();
 +      Logs->CloseLogs();
        DeleteZero(this->Logs);
  }
  
 -void InspIRCd::Restart(const std::string &reason)
 -{
 -      /* SendError flushes each client's queue,
 -       * regardless of writeability state
 -       */
 -      this->SendError(reason);
 -
 -      /* Figure out our filename (if theyve renamed it, we're boned) */
 -      std::string me;
 -
 -      char** argv = Config->cmdline.argv;
 -
 -#ifdef _WIN32
 -      char module[MAX_PATH];
 -      if (GetModuleFileNameA(NULL, module, MAX_PATH))
 -              me = module;
 -#else
 -      me = argv[0];
 -#endif
 -
 -      this->Cleanup();
 -
 -      if (execv(me.c_str(), argv) == -1)
 -      {
 -              /* Will raise a SIGABRT if not trapped */
 -              throw CoreException(std::string("Failed to execv()! error: ") + strerror(errno));
 -      }
 -}
 -
 -void InspIRCd::ResetMaxBans()
 -{
 -      for (chan_hash::const_iterator i = chanlist->begin(); i != chanlist->end(); i++)
 -              i->second->ResetMaxBans();
 -}
 -
 -/** Because hash_map doesn't free its buckets when we delete items, we occasionally
 - * recreate the hash to free them up.
 - * We do this by copying the entries from the old hash to a new hash, causing all
 - * empty buckets to be weeded out of the hash.
 - * Since this is quite expensive, it's not done very often.
 - */
 -void InspIRCd::RehashUsersAndChans()
 -{
 -      user_hash* old_users = Users->clientlist;
 -      Users->clientlist = new user_hash;
 -      for (user_hash::const_iterator n = old_users->begin(); n != old_users->end(); n++)
 -              Users->clientlist->insert(*n);
 -      delete old_users;
 -
 -      user_hash* old_uuid = Users->uuidlist;
 -      Users->uuidlist = new user_hash;
 -      for (user_hash::const_iterator n = old_uuid->begin(); n != old_uuid->end(); n++)
 -              Users->uuidlist->insert(*n);
 -      delete old_uuid;
 -
 -      chan_hash* old_chans = chanlist;
 -      chanlist = new chan_hash;
 -      for (chan_hash::const_iterator n = old_chans->begin(); n != old_chans->end(); n++)
 -              chanlist->insert(*n);
 -      delete old_chans;
 -
 -      // Reset the already_sent IDs so we don't wrap it around and drop a message
 -      LocalUser::already_sent_id = 0;
 -      for (LocalUserList::const_iterator i = Users->local_users.begin(); i != Users->local_users.end(); i++)
 -      {
 -              (**i).already_sent = 0;
 -              (**i).RemoveExpiredInvites();
 -      }
 -}
 -
  void InspIRCd::SetSignals()
  {
  #ifndef _WIN32
  
  void InspIRCd::QuickExit(int status)
  {
-       exit(0);
+       exit(status);
  }
  
  bool InspIRCd::DaemonSeed()
  #else
        signal(SIGTERM, InspIRCd::QuickExit);
  
 -      int childpid;
 -      if ((childpid = fork ()) < 0)
 +      int childpid = fork();
 +      if (childpid < 0)
                return false;
        else if (childpid > 0)
        {
        rlimit rl;
        if (getrlimit(RLIMIT_CORE, &rl) == -1)
        {
 -              this->Logs->Log("STARTUP",DEFAULT,"Failed to getrlimit()!");
 +              this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to getrlimit()!");
                return false;
        }
        rl.rlim_cur = rl.rlim_max;
  
        if (setrlimit(RLIMIT_CORE, &rl) == -1)
 -                      this->Logs->Log("STARTUP",DEFAULT,"setrlimit() failed, cannot increase coredump size.");
 +                      this->Logs->Log("STARTUP", LOG_DEFAULT, "setrlimit() failed, cannot increase coredump size.");
  
        return true;
  #endif
@@@ -207,7 -293,7 +207,7 @@@ void InspIRCd::WritePID(const std::stri
  #ifndef _WIN32
        std::string fname(filename);
        if (fname.empty())
 -              fname = DATA_PATH "/inspircd.pid";
 +              fname = ServerInstance->Config->Paths.PrependData("inspircd.pid");
        std::ofstream outfile(fname.c_str());
        if (outfile.is_open())
        {
        else
        {
                std::cout << "Failed to write PID-file '" << fname << "', exiting." << std::endl;
 -              this->Logs->Log("STARTUP",DEFAULT,"Failed to write PID-file '%s', exiting.",fname.c_str());
 +              this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to write PID-file '%s', exiting.",fname.c_str());
                Exit(EXIT_STATUS_PID);
        }
  #endif
@@@ -231,21 -317,26 +231,21 @@@ InspIRCd::InspIRCd(int argc, char** arg
          * THIS MUST MATCH THE ORDER OF DECLARATION OF THE FUNCTORS, e.g. the methods
          * themselves within the class.
          */
 -       NICKForced("NICKForced", NULL),
 -       OperQuit("OperQuit", NULL),
 +       OperQuit("operquit", NULL),
         GenRandom(&HandleGenRandom),
         IsChannel(&HandleIsChannel),
 -       IsSID(&HandleIsSID),
         Rehash(&HandleRehash),
         IsNick(&HandleIsNick),
         IsIdent(&HandleIsIdent),
 -       FloodQuitUser(&HandleFloodQuitUser),
         OnCheckExemption(&HandleOnCheckExemption)
  {
        ServerInstance = this;
  
 -      Extensions.Register(&NICKForced);
        Extensions.Register(&OperQuit);
  
        FailedPortList pl;
        int do_version = 0, do_nofork = 0, do_debug = 0,
            do_nolog = 0, do_root = 0, do_testsuite = 0;    /* flag variables */
 -      int c = 0;
  
        // Initialize so that if we exit before proper initialization they're not deleted
        this->Logs = 0;
        this->Parser = 0;
        this->XLines = 0;
        this->Modes = 0;
 -      this->Res = 0;
        this->ConfigThread = NULL;
        this->FakeClient = NULL;
  
        /* Default implementation does nothing */
        this->PI = new ProtocolInterface;
  
 -      this->s_signal = 0;
 -
        // Create base manager classes early, so nothing breaks
        this->Users = new UserManager;
  
 -      this->Users->clientlist = new user_hash();
 -      this->Users->uuidlist = new user_hash();
        this->chanlist = new chan_hash();
  
        this->Config = new ServerConfig;
        this->SNO = new SnomaskManager;
        this->BanCache = new BanCacheManager;
        this->Modules = new ModuleManager();
 +      dynamic_reference_base::reset_all();
        this->stats = new serverstats();
        this->Timers = new TimerManager;
        this->Parser = new CommandParser;
        struct option longopts[] =
        {
                { "nofork",     no_argument,            &do_nofork,     1       },
 -              { "logfile",    required_argument,      NULL,           'f'     },
                { "config",     required_argument,      NULL,           'c'     },
                { "debug",      no_argument,            &do_debug,      1       },
                { "nolog",      no_argument,            &do_nolog,      1       },
                { 0, 0, 0, 0 }
        };
  
 +      int c;
        int index;
 -      while ((c = getopt_long(argc, argv, ":c:f:", longopts, &index)) != -1)
 +      while ((c = getopt_long(argc, argv, ":c:", longopts, &index)) != -1)
        {
                switch (c)
                {
 -                      case 'f':
 -                              /* Log filename was set */
 -                              Config->cmdline.startup_log = optarg;
 -                      break;
                        case 'c':
                                /* Config filename was set */
                                ConfigFileName = optarg;
                        default:
                                /* Fall through to handle other weird values too */
                                std::cout << "Unknown parameter '" << argv[optind-1] << "'" << std::endl;
 -                              std::cout << "Usage: " << argv[0] << " [--nofork] [--nolog] [--debug] [--logfile <filename>] " << std::endl <<
 -                                      std::string(static_cast<int>(8+strlen(argv[0])), ' ') << "[--runasroot] [--version] [--config <config>] [--testsuite]" << std::endl;
 +                              std::cout << "Usage: " << argv[0] << " [--nofork] [--nolog] [--debug] [--config <config>]" << std::endl <<
 +                                      std::string(static_cast<int>(8+strlen(argv[0])), ' ') << "[--runasroot] [--version] [--testsuite]" << std::endl;
                                Exit(EXIT_STATUS_ARGV);
                        break;
                }
  
        if (do_version)
        {
 -              std::cout << std::endl << VERSION << " r" << REVISION << std::endl;
 +              std::cout << std::endl << VERSION << " " << REVISION << std::endl;
                Exit(EXIT_STATUS_NOERROR);
        }
  
        if (do_debug)
        {
                FileWriter* fw = new FileWriter(stdout);
 -              FileLogStream* fls = new FileLogStream(RAWIO, fw);
 +              FileLogStream* fls = new FileLogStream(LOG_RAWIO, fw);
                Logs->AddLogTypes("*", fls, true);
        }
 -      else if (!this->OpenLog(argv, argc))
 -      {
 -              std::cout << "ERROR: Could not open initial logfile " << Config->cmdline.startup_log << ": " << strerror(errno) << std::endl << std::endl;
 -              Exit(EXIT_STATUS_LOG);
 -      }
  
        if (!ServerConfig::FileExists(ConfigFileName.c_str()))
        {
  #endif
                {
                        std::cout << "ERROR: Cannot open config file: " << ConfigFileName << std::endl << "Exiting..." << std::endl;
 -                      this->Logs->Log("STARTUP",DEFAULT,"Unable to open config file %s", ConfigFileName.c_str());
 +                      this->Logs->Log("STARTUP", LOG_DEFAULT, "Unable to open config file %s", ConfigFileName.c_str());
                        Exit(EXIT_STATUS_CONFIG);
                }
        }
        std::cout << con_green << "(C) InspIRCd Development Team." << con_reset << std::endl << std::endl;
        std::cout << "Developers:" << std::endl;
        std::cout << con_green << "\tBrain, FrostyCoolSlug, w00t, Om, Special, peavey" << std::endl;
 -      std::cout << "\taquanight, psychon, dz, danieldg, jackmcbarn" << std::endl;\r
 +      std::cout << "\taquanight, psychon, dz, danieldg, jackmcbarn" << std::endl;
        std::cout << "\tAttila" << con_reset << std::endl << std::endl;
        std::cout << "Others:\t\t\t" << con_green << "See /INFO Output" << con_reset << std::endl;
  
                this->CheckRoot();
        else
        {
 -              std::cout << "* WARNING * WARNING * WARNING * WARNING * WARNING *" << std::endl\r
 -              << "YOU ARE RUNNING INSPIRCD AS ROOT. THIS IS UNSUPPORTED" << std::endl\r
 -              << "AND IF YOU ARE HACKED, CRACKED, SPINDLED OR MUTILATED" << std::endl\r
 -              << "OR ANYTHING ELSE UNEXPECTED HAPPENS TO YOU OR YOUR" << std::endl\r
 -              << "SERVER, THEN IT IS YOUR OWN FAULT. IF YOU DID NOT MEAN" << std::endl\r
 -              << "TO START INSPIRCD AS ROOT, HIT CTRL+C NOW AND RESTART" << std::endl\r
 -              << "THE PROGRAM AS A NORMAL USER. YOU HAVE BEEN WARNED!" << std::endl << std::endl\r
 -              << "InspIRCd starting in 20 seconds, ctrl+c to abort..." << std::endl;\r
 +              std::cout << "* WARNING * WARNING * WARNING * WARNING * WARNING *" << std::endl
 +              << "YOU ARE RUNNING INSPIRCD AS ROOT. THIS IS UNSUPPORTED" << std::endl
 +              << "AND IF YOU ARE HACKED, CRACKED, SPINDLED OR MUTILATED" << std::endl
 +              << "OR ANYTHING ELSE UNEXPECTED HAPPENS TO YOU OR YOUR" << std::endl
 +              << "SERVER, THEN IT IS YOUR OWN FAULT. IF YOU DID NOT MEAN" << std::endl
 +              << "TO START INSPIRCD AS ROOT, HIT CTRL+C NOW AND RESTART" << std::endl
 +              << "THE PROGRAM AS A NORMAL USER. YOU HAVE BEEN WARNED!" << std::endl << std::endl
 +              << "InspIRCd starting in 20 seconds, ctrl+c to abort..." << std::endl;
                sleep(20);
        }
  #endif
                if (!this->DaemonSeed())
                {
                        std::cout << "ERROR: could not go into daemon mode. Shutting down." << std::endl;
 -                      Logs->Log("STARTUP", DEFAULT, "ERROR: could not go into daemon mode. Shutting down.");
 +                      Logs->Log("STARTUP", LOG_DEFAULT, "ERROR: could not go into daemon mode. Shutting down.");
                        Exit(EXIT_STATUS_FORK);
                }
        }
  
        SE->RecoverFromFork();
  
 -      /* During startup we don't actually initialize this
 -       * in the thread engine.
 +      /* During startup we read the configuration now, not in
 +       * a seperate thread
         */
        this->Config->Read();
        this->Config->Apply(NULL, "");
        Logs->OpenFileLogs();
 +      ModeParser::InitBuiltinModes();
  
 -      this->Res = new DNS();
 -
 -      /*
 -       * Initialise SID/UID.
 -       * For an explanation as to exactly how this works, and why it works this way, see GetUID().
 -       *   -- w00t
 -       */
 +      // If we don't have a SID, generate one based on the server name and the server description
        if (Config->sid.empty())
 -      {
 -              // Generate one
 -              unsigned int sid = 0;
 -              char sidstr[4];
 -
 -              for (const char* x = Config->ServerName.c_str(); *x; ++x)
 -                      sid = 5 * sid + *x;
 -              for (const char* y = Config->ServerDesc.c_str(); *y; ++y)
 -                      sid = 5 * sid + *y;
 -              sprintf(sidstr, "%03d", sid % 1000);
 +              Config->sid = UIDGenerator::GenerateSID(Config->ServerName, Config->ServerDesc);
  
 -              Config->sid = sidstr;
 -      }
 +      // Initialize the UID generator with our sid
 +      this->UIDGen.init(Config->sid);
  
 -      /* set up fake client again this time with the correct uid */
 +      // Create the server user for this server
        this->FakeClient = new FakeUser(Config->sid, Config->ServerName);
  
 -      // Get XLine to do it's thing.
 -      this->XLines->CheckELines();
 +      // This is needed as all new XLines are marked pending until ApplyLines() is called
        this->XLines->ApplyLines();
  
        int bounditems = BindPorts(pl);
  
        this->Modules->LoadAll();
  
 -      /* Just in case no modules were loaded - fix for bug #101 */
 -      this->BuildISupport();
 +      // Build ISupport as ModuleManager::LoadAll() does not do it
 +      this->ISupport.Build();
        Config->ApplyDisabledCommands(Config->DisabledCommands);
  
        if (!pl.empty())
                if (kill(getppid(), SIGTERM) == -1)
                {
                        std::cout << "Error killing parent process: " << strerror(errno) << std::endl;
 -                      Logs->Log("STARTUP", DEFAULT, "Error killing parent process: %s",strerror(errno));
 +                      Logs->Log("STARTUP", LOG_DEFAULT, "Error killing parent process: %s",strerror(errno));
                }
        }
  
                fclose(stdout);
  
                if (dup2(fd, STDIN_FILENO) < 0)
 -                      Logs->Log("STARTUP", DEFAULT, "Failed to dup /dev/null to stdin.");
 +                      Logs->Log("STARTUP", LOG_DEFAULT, "Failed to dup /dev/null to stdin.");
                if (dup2(fd, STDOUT_FILENO) < 0)
 -                      Logs->Log("STARTUP", DEFAULT, "Failed to dup /dev/null to stdout.");
 +                      Logs->Log("STARTUP", LOG_DEFAULT, "Failed to dup /dev/null to stdout.");
                if (dup2(fd, STDERR_FILENO) < 0)
 -                      Logs->Log("STARTUP", DEFAULT, "Failed to dup /dev/null to stderr.");
 +                      Logs->Log("STARTUP", LOG_DEFAULT, "Failed to dup /dev/null to stderr.");
                close(fd);
        }
        else
        {
 -              Logs->Log("STARTUP", DEFAULT,"Keeping pseudo-tty open as we are running in the foreground.");
 +              Logs->Log("STARTUP", LOG_DEFAULT, "Keeping pseudo-tty open as we are running in the foreground.");
        }
  #else
        /* Set win32 service as running, if we are running as a service */
        QueryPerformanceFrequency(&stats->QPFrequency);
  #endif
  
 -      Logs->Log("STARTUP", DEFAULT, "Startup complete as '%s'[%s], %d max open sockets", Config->ServerName.c_str(),Config->GetSID().c_str(), SE->GetMaxFds());
 +      Logs->Log("STARTUP", LOG_DEFAULT, "Startup complete as '%s'[%s], %d max open sockets", Config->ServerName.c_str(),Config->GetSID().c_str(), SE->GetMaxFds());
  
  #ifndef _WIN32
        std::string SetUser = Config->ConfValue("security")->getString("runasuser");
  
                if (ret == -1)
                {
 -                      this->Logs->Log("SETGROUPS", DEFAULT, "setgroups() failed (wtf?): %s", strerror(errno));
 +                      this->Logs->Log("STARTUP", LOG_DEFAULT, "setgroups() failed (wtf?): %s", strerror(errno));
                        this->QuickExit(0);
                }
  
  
                if (!g)
                {
 -                      this->Logs->Log("SETGUID", DEFAULT, "getgrnam() failed (bad user?): %s", strerror(errno));
 +                      this->Logs->Log("STARTUP", LOG_DEFAULT, "getgrnam() failed (bad user?): %s", strerror(errno));
                        this->QuickExit(0);
                }
  
  
                if (ret == -1)
                {
 -                      this->Logs->Log("SETGUID", DEFAULT, "setgid() failed (bad user?): %s", strerror(errno));
 +                      this->Logs->Log("STARTUP", LOG_DEFAULT, "setgid() failed (bad user?): %s", strerror(errno));
                        this->QuickExit(0);
                }
        }
  
                if (!u)
                {
 -                      this->Logs->Log("SETGUID", DEFAULT, "getpwnam() failed (bad user?): %s", strerror(errno));
 +                      this->Logs->Log("STARTUP", LOG_DEFAULT, "getpwnam() failed (bad user?): %s", strerror(errno));
                        this->QuickExit(0);
                }
  
  
                if (ret == -1)
                {
 -                      this->Logs->Log("SETGUID", DEFAULT, "setuid() failed (bad user?): %s", strerror(errno));
 +                      this->Logs->Log("STARTUP", LOG_DEFAULT, "setuid() failed (bad user?): %s", strerror(errno));
                        this->QuickExit(0);
                }
        }
@@@ -628,14 -747,14 +628,14 @@@ void InspIRCd::UpdateTime(
  #endif
  }
  
 -int InspIRCd::Run()
 +void InspIRCd::Run()
  {
        /* See if we're supposed to be running the test suite rather than entering the mainloop */
        if (Config->cmdline.TestSuite)
        {
                TestSuite* ts = new TestSuite;
                delete ts;
 -              Exit(0);
 +              return;
        }
  
        UpdateTime();
                if (this->ConfigThread && this->ConfigThread->IsDone())
                {
                        /* Rehash has completed */
 -                      this->Logs->Log("CONFIG",DEBUG,"Detected ConfigThread exiting, tidying up...");
 +                      this->Logs->Log("CONFIG", LOG_DEBUG, "Detected ConfigThread exiting, tidying up...");
  
                        this->ConfigThread->Finish();
  
                        {
                                SNO->WriteToSnoMask('d', "\002EH?!\002 -- Time is jumping FORWARDS! Clock skipped %lu secs.", (unsigned long)TIME.tv_sec - OLDTIME);
                        }
 -\r
 +
                        OLDTIME = TIME.tv_sec;
  
                        if ((TIME.tv_sec % 3600) == 0)
                        {
 -                              this->RehashUsersAndChans();
 -                              FOREACH_MOD(I_OnGarbageCollect, OnGarbageCollect());
 +                              Users->GarbageCollect();
 +                              FOREACH_MOD(OnGarbageCollect, ());
                        }
  
                        Timers->TickTimers(TIME.tv_sec);
 -                      this->DoBackgroundUserStuff();
 +                      Users->DoBackgroundUserStuff();
  
                        if ((TIME.tv_sec % 5) == 0)
                        {
 -                              FOREACH_MOD(I_OnBackgroundTimer,OnBackgroundTimer(TIME.tv_sec));
 +                              FOREACH_MOD(OnBackgroundTimer, (TIME.tv_sec));
                                SNO->FlushSnotices();
                        }
                }
                GlobalCulls.Apply();
                AtomicActions.Run();
  
 -              if (this->s_signal)
 +              if (s_signal)
                {
                        this->SignalHandler(s_signal);
 -                      this->s_signal = 0;
 +                      s_signal = 0;
                }
        }
 -
 -      return 0;
  }
  
 -/**********************************************************************************/
 -
 -/**
 - * An ircd in five lines! bwahahaha. ahahahahaha. ahahah *cough*.
 - */
 -
 -/* 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 InspIRCd::AllModulesReportReady(LocalUser* user)
 -{
 -      ModResult res;
 -      FIRST_MOD_RESULT(OnCheckReady, res, (user));
 -      return (res == MOD_RES_PASSTHRU);
 -}
 +sig_atomic_t InspIRCd::s_signal = 0;
  
  void InspIRCd::SetSignal(int signal)
  {
 -      *mysig = signal;
 +      s_signal = signal;
  }
  
  /* On posix systems, the flow of the program starts right here, with
  ENTRYPOINT
  {
        new InspIRCd(argc, argv);
 -      mysig = &ServerInstance->s_signal;
        ServerInstance->Run();
        delete ServerInstance;
        return 0;
diff --combined src/inspsocket.cpp
index ec528571bba2f15028daafadc5362d13a8511949,356904f741b35e8efbcfe5bac61cdaf98721f31e..d7a25785d1d6506f1ca132211cfe8b91a60d83ab
@@@ -26,7 -26,6 +26,7 @@@
  #include "socket.h"
  #include "inspstring.h"
  #include "socketengine.h"
 +#include "iohook.h"
  
  #ifndef DISABLE_WRITEV
  #include <sys/uio.h>
@@@ -57,7 -56,7 +57,7 @@@ void BufferedSocket::DoConnect(const st
        if (err != I_ERR_NONE)
        {
                state = I_ERROR;
-               SetError(strerror(errno));
+               SetError(SocketEngine::LastError());
                OnError(err);
        }
  }
@@@ -67,7 -66,7 +67,7 @@@ BufferedSocketError BufferedSocket::Beg
        irc::sockets::sockaddrs addr, bind;
        if (!irc::sockets::aptosa(ipaddr, aport, addr))
        {
 -              ServerInstance->Logs->Log("SOCKET", DEBUG, "BUG: Hostname passed to BufferedSocket, rather than an IP address!");
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: Hostname passed to BufferedSocket, rather than an IP address!");
                return I_ERR_CONNECT;
        }
  
@@@ -99,7 -98,7 +99,7 @@@ BufferedSocketError BufferedSocket::Beg
  
        ServerInstance->SE->NonBlocking(fd);
  
 -      if (ServerInstance->SE->Connect(this, &dest.sa, sa_size(dest)) == -1)
 +      if (ServerInstance->SE->Connect(this, &dest.sa, dest.sa_size()) == -1)
        {
                if (errno != EINPROGRESS)
                        return I_ERR_CONNECT;
        this->Timeout = new SocketTimeout(this->GetFd(), this, timeout, ServerInstance->Time());
        ServerInstance->Timers->AddTimer(this->Timeout);
  
 -      ServerInstance->Logs->Log("SOCKET", DEBUG,"BufferedSocket::DoConnect success");
 +      ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BufferedSocket::DoConnect success");
        return I_ERR_NONE;
  }
  
@@@ -123,18 -122,18 +123,18 @@@ void StreamSocket::Close(
        {
                // final chance, dump as much of the sendq as we can
                DoWrite();
 -              if (IOHook)
 +              if (GetIOHook())
                {
                        try
                        {
 -                              IOHook->OnStreamSocketClose(this);
 +                              GetIOHook()->OnStreamSocketClose(this);
                        }
                        catch (CoreException& modexcept)
                        {
 -                              ServerInstance->Logs->Log("SOCKET", DEFAULT,"%s threw an exception: %s",
 +                              ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "%s threw an exception: %s",
                                        modexcept.GetSource(), modexcept.GetReason());
                        }
 -                      IOHook = NULL;
 +                      DelIOHook();
                }
                ServerInstance->SE->Shutdown(this, 2);
                ServerInstance->SE->DelFd(this);
@@@ -162,16 -161,16 +162,16 @@@ bool StreamSocket::GetNextLine(std::str
  
  void StreamSocket::DoRead()
  {
 -      if (IOHook)
 +      if (GetIOHook())
        {
                int rv = -1;
                try
                {
 -                      rv = IOHook->OnStreamSocketRead(this, recvq);
 +                      rv = GetIOHook()->OnStreamSocketRead(this, recvq);
                }
                catch (CoreException& modexcept)
                {
 -                      ServerInstance->Logs->Log("SOCKET", DEFAULT, "%s threw an exception: %s",
 +                      ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "%s threw an exception: %s",
                                modexcept.GetSource(), modexcept.GetReason());
                        return;
                }
                }
                else
                {
-                       error = strerror(errno);
+                       error = SocketEngine::LastError();
                        ServerInstance->SE->ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
                }
        }
@@@ -226,12 -225,12 +226,12 @@@ void StreamSocket::DoWrite(
                return;
        if (!error.empty() || fd < 0 || fd == INT_MAX)
        {
 -              ServerInstance->Logs->Log("SOCKET", DEBUG, "DoWrite on errored or closed socket");
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DoWrite on errored or closed socket");
                return;
        }
  
  #ifndef DISABLE_WRITEV
 -      if (IOHook)
 +      if (GetIOHook())
  #endif
        {
                int rv = -1;
                                        // The length limit of 1024 is to prevent merging strings
                                        // more than once when writes begin to block.
                                        std::string tmp;
-                                       tmp.reserve(sendq_len);
-                                       for(unsigned int i=0; i < sendq.size(); i++)
-                                               tmp.append(sendq[i]);
-                                       sendq.clear();
-                                       sendq.push_back(tmp);
+                                       tmp.reserve(1280);
+                                       while (!sendq.empty() && tmp.length() < 1024)
+                                       {
+                                               tmp.append(sendq.front());
+                                               sendq.pop_front();
+                                       }
+                                       sendq.push_front(tmp);
                                }
                                std::string& front = sendq.front();
                                int itemlen = front.length();
 -                              if (IOHook)
 +                              if (GetIOHook())
                                {
 -                                      rv = IOHook->OnStreamSocketWrite(this, front);
 +                                      rv = GetIOHook()->OnStreamSocketWrite(this, front);
                                        if (rv > 0)
                                        {
                                                // consumed the entire string, and is ready for more
                                                if (errno == EINTR || SocketEngine::IgnoreError())
                                                        ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK);
                                                else
-                                                       SetError(strerror(errno));
+                                                       SetError(SocketEngine::LastError());
                                                return;
                                        }
                                        else if (rv < itemlen)
                }
                catch (CoreException& modexcept)
                {
 -                      ServerInstance->Logs->Log("SOCKET", DEBUG,"%s threw an exception: %s",
 +                      ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "%s threw an exception: %s",
                                modexcept.GetSource(), modexcept.GetReason());
                }
        }
                        }
                        else
                        {
-                               error = strerror(errno);
+                               error = SocketEngine::LastError();
                        }
                }
                if (!error.empty())
@@@ -420,7 -421,7 +422,7 @@@ void StreamSocket::WriteData(const std:
  {
        if (fd < 0)
        {
 -              ServerInstance->Logs->Log("SOCKET", DEBUG, "Attempt to write data to dead socket: %s",
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to write data to dead socket: %s",
                        data.c_str());
                return;
        }
        ServerInstance->SE->ChangeEventMask(this, FD_ADD_TRIAL_WRITE);
  }
  
 -void SocketTimeout::Tick(time_t)
 +bool SocketTimeout::Tick(time_t)
  {
 -      ServerInstance->Logs->Log("SOCKET", DEBUG,"SocketTimeout::Tick");
 +      ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SocketTimeout::Tick");
  
        if (ServerInstance->SE->GetRef(this->sfd) != this->sock)
 -              return;
 +              return false;
  
        if (this->sock->state == I_CONNECTING)
        {
        }
  
        this->sock->Timeout = NULL;
 +      return false;
  }
  
  void BufferedSocket::OnConnected() { }
@@@ -478,8 -478,8 +480,8 @@@ BufferedSocket::~BufferedSocket(
        this->Close();
        if (Timeout)
        {
 -              ServerInstance->Timers->DelTimer(Timeout);
 -              Timeout = NULL;
 +              // The timer is removed from the TimerManager in Timer::~Timer()
 +              delete Timeout;
        }
  }
  
@@@ -496,7 -496,7 +498,7 @@@ void StreamSocket::HandleEvent(EventTyp
                                if (errornum == 0)
                                        SetError("Connection closed");
                                else
-                                       SetError(strerror(errornum));
+                                       SetError(SocketEngine::GetError(errornum));
                                switch (errornum)
                                {
                                        case ETIMEDOUT:
        }
        catch (CoreException& ex)
        {
 -              ServerInstance->Logs->Log("SOCKET", DEFAULT, "Caught exception in socket processing on FD %d - '%s'",
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Caught exception in socket processing on FD %d - '%s'",
                        fd, ex.GetReason());
                SetError(ex.GetReason());
        }
        if (!error.empty())
        {
 -              ServerInstance->Logs->Log("SOCKET", DEBUG, "Error on FD %d - '%s'", fd, error.c_str());
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Error on FD %d - '%s'", fd, error.c_str());
                OnError(errcode);
        }
  }
index dab377397c66fcb2af5a7d3bf631c0085aabcccb,1f1297ef90468bc8d38d841bb71593841b647a29..53fc38ec0ba5ea2035e5fa81ac56486488b95ab7
  #include <gcrypt.h>
  #include <gnutls/gnutls.h>
  #include <gnutls/x509.h>
 -#include "ssl.h"
 -#include "m_cap.h"
 +#include "modules/ssl.h"
 +#include "modules/cap.h"
 +
 +#if ((GNUTLS_VERSION_MAJOR > 2) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR > 9) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR == 9 && GNUTLS_VERSION_PATCH >= 8))
 +#define GNUTLS_HAS_MAC_GET_ID
 +#include <gnutls/crypto.h>
 +#endif
  
  #ifdef _WIN32
  # pragma comment(lib, "libgnutls.lib")
  # pragma comment(lib, "gdi32.lib")
  #endif
  
 -/* $ModDesc: Provides SSL support for clients */
 -/* $CompileFlags: pkgconfincludes("gnutls","/gnutls/gnutls.h","") exec("libgcrypt-config --cflags") */
 +/* $CompileFlags: pkgconfincludes("gnutls","/gnutls/gnutls.h","") exec("libgcrypt-config --cflags") -Wno-pedantic */
  /* $LinkerFlags: rpath("pkg-config --libs gnutls") pkgconflibs("gnutls","/libgnutls.so","-lgnutls") exec("libgcrypt-config --libs") */
 -/* $NoPedantic */
  
+ #ifndef GNUTLS_VERSION_MAJOR
+ #define GNUTLS_VERSION_MAJOR LIBGNUTLS_VERSION_MAJOR
+ #define GNUTLS_VERSION_MINOR LIBGNUTLS_VERSION_MINOR
+ #define GNUTLS_VERSION_PATCH LIBGNUTLS_VERSION_PATCH
+ #endif
  // These don't exist in older GnuTLS versions
- #if ((GNUTLS_VERSION_MAJOR > 2) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR > 1) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR == 1 && GNUTLS_VERSION_MICRO >= 7))
+ #if ((GNUTLS_VERSION_MAJOR > 2) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR > 1) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR == 1 && GNUTLS_VERSION_PATCH >= 7))
  #define GNUTLS_NEW_PRIO_API
  #endif
  
@@@ -103,186 -106,77 +109,188 @@@ public
        issl_session() : socket(NULL), sess(NULL) {}
  };
  
 -class CommandStartTLS : public SplitCommand
 +class GnuTLSIOHook : public SSLIOHook
  {
 - public:
 -      bool enabled;
 -      CommandStartTLS (Module* mod) : SplitCommand(mod, "STARTTLS")
 + private:
 +      void InitSession(StreamSocket* user, bool me_server)
        {
 -              enabled = true;
 -              works_before_reg = true;
 +              issl_session* session = &sessions[user->GetFd()];
 +
 +              gnutls_init(&session->sess, me_server ? GNUTLS_SERVER : GNUTLS_CLIENT);
 +              session->socket = user;
 +
 +              #ifdef GNUTLS_NEW_PRIO_API
 +              gnutls_priority_set(session->sess, priority);
++              #else
++              gnutls_set_default_priority(session->sess);
 +              #endif
 +              gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, x509_cred);
 +              gnutls_dh_set_prime_bits(session->sess, dh_bits);
 +              gnutls_transport_set_ptr(session->sess, reinterpret_cast<gnutls_transport_ptr_t>(session));
 +              gnutls_transport_set_push_function(session->sess, gnutls_push_wrapper);
 +              gnutls_transport_set_pull_function(session->sess, gnutls_pull_wrapper);
 +
 +              if (me_server)
 +                      gnutls_certificate_server_set_request(session->sess, GNUTLS_CERT_REQUEST); // Request client certificate if any.
 +
 +              Handshake(session, user);
        }
  
 -      CmdResult HandleLocal(const std::vector<std::string> &parameters, LocalUser *user)
 +      void CloseSession(issl_session* session)
        {
 -              if (!enabled)
 +              if (session->sess)
                {
 -                      user->WriteNumeric(691, "%s :STARTTLS is not enabled", user->nick.c_str());
 -                      return CMD_FAILURE;
 +                      gnutls_bye(session->sess, GNUTLS_SHUT_WR);
 +                      gnutls_deinit(session->sess);
                }
 +              session->socket = NULL;
 +              session->sess = NULL;
 +              session->cert = NULL;
 +              session->status = ISSL_NONE;
 +      }
  
 -              if (user->registered == REG_ALL)
 -              {
 -                      user->WriteNumeric(691, "%s :STARTTLS is not permitted after client registration is complete", user->nick.c_str());
 -              }
 -              else
 +      bool Handshake(issl_session* session, StreamSocket* user)
 +      {
 +              int ret = gnutls_handshake(session->sess);
 +
 +              if (ret < 0)
                {
 -                      if (!user->eh.GetIOHook())
 +                      if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
                        {
 -                              user->WriteNumeric(670, "%s :STARTTLS successful, go ahead with TLS handshake", user->nick.c_str());
 -                              /* We need to flush the write buffer prior to adding the IOHook,
 -                               * otherwise we'll be sending this line inside the SSL session - which
 -                               * won't start its handshake until the client gets this line. Currently,
 -                               * we assume the write will not block here; this is usually safe, as
 -                               * STARTTLS is sent very early on in the registration phase, where the
 -                               * user hasn't built up much sendq. Handling a blocked write here would
 -                               * be very annoying.
 -                               */
 -                              user->eh.DoWrite();
 -                              user->eh.AddIOHook(creator);
 -                              creator->OnStreamSocketAccept(&user->eh, NULL, NULL);
 +                              // Handshake needs resuming later, read() or write() would have blocked.
 +
 +                              if(gnutls_record_get_direction(session->sess) == 0)
 +                              {
 +                                      // gnutls_handshake() wants to read() again.
 +                                      session->status = ISSL_HANDSHAKING_READ;
 +                                      ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
 +                              }
 +                              else
 +                              {
 +                                      // gnutls_handshake() wants to write() again.
 +                                      session->status = ISSL_HANDSHAKING_WRITE;
 +                                      ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
 +                              }
                        }
                        else
 -                              user->WriteNumeric(691, "%s :STARTTLS failure", user->nick.c_str());
 +                      {
 +                              user->SetError("Handshake Failed - " + std::string(gnutls_strerror(ret)));
 +                              CloseSession(session);
 +                              session->status = ISSL_CLOSING;
 +                      }
 +
 +                      return false;
                }
 +              else
 +              {
 +                      // Change the seesion state
 +                      session->status = ISSL_HANDSHAKEN;
  
 -              return CMD_FAILURE;
 +                      VerifyCertificate(session,user);
 +
 +                      // Finish writing, if any left
 +                      ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
 +
 +                      return true;
 +              }
        }
 -};
  
 -class ModuleSSLGnuTLS : public Module
 -{
 -      issl_session* sessions;
 +      void VerifyCertificate(issl_session* session, StreamSocket* user)
 +      {
 +              if (!session->sess || !user)
 +                      return;
  
 -      gnutls_certificate_credentials_t x509_cred;
 -      gnutls_dh_params_t dh_params;
 -      gnutls_digest_algorithm_t hash;
 -      #ifdef GNUTLS_NEW_PRIO_API
 -      gnutls_priority_t priority;
 -      #endif
 +              unsigned int status;
 +              const gnutls_datum_t* cert_list;
 +              int ret;
 +              unsigned int cert_list_size;
 +              gnutls_x509_crt_t cert;
 +              char str[512];
 +              unsigned char digest[512];
 +              size_t digest_size = sizeof(digest);
 +              size_t name_size = sizeof(str);
 +              ssl_cert* certinfo = new ssl_cert;
 +              session->cert = certinfo;
  
 -      std::string sslports;
 -      int dh_bits;
 +              /* This verification function uses the trusted CAs in the credentials
 +               * structure. So you must have installed one or more CA certificates.
 +               */
 +              ret = gnutls_certificate_verify_peers2(session->sess, &status);
  
 -      bool cred_alloc;
 -      bool dh_alloc;
 +              if (ret < 0)
 +              {
 +                      certinfo->error = std::string(gnutls_strerror(ret));
 +                      return;
 +              }
  
 -      RandGen randhandler;
 -      CommandStartTLS starttls;
 +              certinfo->invalid = (status & GNUTLS_CERT_INVALID);
 +              certinfo->unknownsigner = (status & GNUTLS_CERT_SIGNER_NOT_FOUND);
 +              certinfo->revoked = (status & GNUTLS_CERT_REVOKED);
 +              certinfo->trusted = !(status & GNUTLS_CERT_SIGNER_NOT_CA);
  
 -      GenericCap capHandler;
 -      ServiceProvider iohook;
 +              /* Up to here the process is the same for X.509 certificates and
 +               * OpenPGP keys. From now on X.509 certificates are assumed. This can
 +               * be easily extended to work with openpgp keys as well.
 +               */
 +              if (gnutls_certificate_type_get(session->sess) != GNUTLS_CRT_X509)
 +              {
 +                      certinfo->error = "No X509 keys sent";
 +                      return;
 +              }
 +
 +              ret = gnutls_x509_crt_init(&cert);
 +              if (ret < 0)
 +              {
 +                      certinfo->error = gnutls_strerror(ret);
 +                      return;
 +              }
 +
 +              cert_list_size = 0;
 +              cert_list = gnutls_certificate_get_peers(session->sess, &cert_list_size);
 +              if (cert_list == NULL)
 +              {
 +                      certinfo->error = "No certificate was found";
 +                      goto info_done_dealloc;
 +              }
 +
 +              /* This is not a real world example, since we only check the first
 +               * certificate in the given chain.
 +               */
 +
 +              ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
 +              if (ret < 0)
 +              {
 +                      certinfo->error = gnutls_strerror(ret);
 +                      goto info_done_dealloc;
 +              }
 +
 +              gnutls_x509_crt_get_dn(cert, str, &name_size);
 +              certinfo->dn = str;
 +
 +              gnutls_x509_crt_get_issuer_dn(cert, str, &name_size);
 +              certinfo->issuer = str;
 +
 +              if ((ret = gnutls_x509_crt_get_fingerprint(cert, hash, digest, &digest_size)) < 0)
 +              {
 +                      certinfo->error = gnutls_strerror(ret);
 +              }
 +              else
 +              {
 +                      certinfo->fingerprint = BinToHex(digest, digest_size);
 +              }
 +
 +              /* Beware here we do not check for errors.
 +               */
 +              if ((gnutls_x509_crt_get_expiration_time(cert) < ServerInstance->Time()) || (gnutls_x509_crt_get_activation_time(cert) > ServerInstance->Time()))
 +              {
 +                      certinfo->error = "Not activated, or expired certificate";
 +              }
 +
 +info_done_dealloc:
 +              gnutls_x509_crt_deinit(cert);
 +      }
  
 -      inline static const char* UnknownIfNULL(const char* str)
 +      static const char* UnknownIfNULL(const char* str)
        {
                return str ? str : "UNKNOWN";
        }
        }
  
   public:
 +      issl_session* sessions;
 +      gnutls_certificate_credentials_t x509_cred;
  
 -      ModuleSSLGnuTLS()
 -              : starttls(this), capHandler(this, "tls"), iohook(this, "ssl/gnutls", SERVICE_IOHOOK)
 -      {
 -              gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
 +      gnutls_digest_algorithm_t hash;
 +      #ifdef GNUTLS_NEW_PRIO_API
 +      gnutls_priority_t priority;
 +      #endif
 +      int dh_bits;
  
 +      GnuTLSIOHook(Module* parent)
 +              : SSLIOHook(parent, "ssl/gnutls")
 +      {
                sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
 +      }
  
 -              gnutls_global_init(); // This must be called once in the program
 -              gnutls_x509_privkey_init(&x509_key);
 -
 -              #ifdef GNUTLS_NEW_PRIO_API
 -              // Init this here so it's always initialized, avoids an extra boolean
 -              gnutls_priority_init(&priority, "NORMAL", NULL);
 -              #endif
 -
 -              cred_alloc = false;
 -              dh_alloc = false;
 +      ~GnuTLSIOHook()
 +      {
 +              delete[] sessions;
        }
  
 -      void init()
 +      void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
        {
 -              // Needs the flag as it ignores a plain /rehash
 -              OnModuleRehash(NULL,"ssl");
 +              issl_session* session = &sessions[user->GetFd()];
  
 -              ServerInstance->GenRandom = &randhandler;
 +              /* For STARTTLS: Don't try and init a session on a socket that already has a session */
 +              if (session->sess)
 +                      return;
  
 -              // Void return, guess we assume success
 -              gnutls_certificate_set_dh_params(x509_cred, dh_params);
 -              Implementation eventlist[] = { I_On005Numeric, I_OnRehash, I_OnModuleRehash, I_OnUserConnect,
 -                      I_OnEvent, I_OnHookIO };
 -              ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
 +              InitSession(user, true);
 +      }
  
 -              ServerInstance->Modules->AddService(iohook);
 -              ServerInstance->Modules->AddService(starttls);
 +      void OnStreamSocketConnect(StreamSocket* user) CXX11_OVERRIDE
 +      {
 +              InitSession(user, false);
        }
  
 -      void OnRehash(User* user)
 +      void OnStreamSocketClose(StreamSocket* user) CXX11_OVERRIDE
        {
 -              sslports.clear();
 +              CloseSession(&sessions[user->GetFd()]);
 +      }
  
 -              ConfigTag* Conf = ServerInstance->Config->ConfValue("gnutls");
 -              starttls.enabled = Conf->getBool("starttls", true);
 +      int OnStreamSocketRead(StreamSocket* user, std::string& recvq) CXX11_OVERRIDE
 +      {
 +              issl_session* session = &sessions[user->GetFd()];
  
 -              if (Conf->getBool("showports", true))
 +              if (!session->sess)
                {
 -                      sslports = Conf->getString("advertisedports");
 -                      if (!sslports.empty())
 -                              return;
 -
 -                      for (size_t i = 0; i < ServerInstance->ports.size(); i++)
 -                      {
 -                              ListenSocket* port = ServerInstance->ports[i];
 -                              if (port->bind_tag->getString("ssl") != "gnutls")
 -                                      continue;
 +                      CloseSession(session);
 +                      user->SetError("No SSL session");
 +                      return -1;
 +              }
  
 -                              const std::string& portid = port->bind_desc;
 -                              ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT, "m_ssl_gnutls.so: Enabling SSL for port %s", portid.c_str());
 +              if (session->status == ISSL_HANDSHAKING_READ || session->status == ISSL_HANDSHAKING_WRITE)
 +              {
 +                      // The handshake isn't finished, try to finish it.
 +
 +                      if(!Handshake(session, user))
 +                      {
 +                              if (session->status != ISSL_CLOSING)
 +                                      return 0;
 +                              return -1;
 +                      }
 +              }
 +
 +              // If we resumed the handshake then session->status will be ISSL_HANDSHAKEN.
 +
 +              if (session->status == ISSL_HANDSHAKEN)
 +              {
 +                      char* buffer = ServerInstance->GetReadBuffer();
 +                      size_t bufsiz = ServerInstance->Config->NetBufferSize;
 +                      int ret = gnutls_record_recv(session->sess, buffer, bufsiz);
 +                      if (ret > 0)
 +                      {
 +                              recvq.append(buffer, ret);
 +                              return 1;
 +                      }
 +                      else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
 +                      {
 +                              return 0;
 +                      }
 +                      else if (ret == 0)
 +                      {
 +                              user->SetError("Connection closed");
 +                              CloseSession(session);
 +                              return -1;
 +                      }
 +                      else
 +                      {
 +                              user->SetError(gnutls_strerror(ret));
 +                              CloseSession(session);
 +                              return -1;
 +                      }
 +              }
 +              else if (session->status == ISSL_CLOSING)
 +                      return -1;
 +
 +              return 0;
 +      }
 +
 +      int OnStreamSocketWrite(StreamSocket* user, std::string& sendq) CXX11_OVERRIDE
 +      {
 +              issl_session* session = &sessions[user->GetFd()];
 +
 +              if (!session->sess)
 +              {
 +                      CloseSession(session);
 +                      user->SetError("No SSL session");
 +                      return -1;
 +              }
 +
 +              if (session->status == ISSL_HANDSHAKING_WRITE || session->status == ISSL_HANDSHAKING_READ)
 +              {
 +                      // The handshake isn't finished, try to finish it.
 +                      Handshake(session, user);
 +                      if (session->status != ISSL_CLOSING)
 +                              return 0;
 +                      return -1;
 +              }
 +
 +              int ret = 0;
 +
 +              if (session->status == ISSL_HANDSHAKEN)
 +              {
 +                      ret = gnutls_record_send(session->sess, sendq.data(), sendq.length());
 +
 +                      if (ret == (int)sendq.length())
 +                      {
 +                              ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_WRITE);
 +                              return 1;
 +                      }
 +                      else if (ret > 0)
 +                      {
 +                              sendq = sendq.substr(ret);
 +                              ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
 +                              return 0;
 +                      }
 +                      else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED || ret == 0)
 +                      {
 +                              ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
 +                              return 0;
 +                      }
 +                      else // (ret < 0)
 +                      {
 +                              user->SetError(gnutls_strerror(ret));
 +                              CloseSession(session);
 +                              return -1;
 +                      }
 +              }
 +
 +              return 0;
 +      }
 +
 +      ssl_cert* GetCertificate(StreamSocket* sock) CXX11_OVERRIDE
 +      {
 +              int fd = sock->GetFd();
 +              issl_session* session = &sessions[fd];
 +              return session->cert;
 +      }
 +
 +      void TellCiphersAndFingerprint(LocalUser* user)
 +      {
 +              const gnutls_session_t& sess = sessions[user->eh.GetFd()].sess;
 +              if (sess)
 +              {
 +                      std::string text = "*** You are connected using SSL cipher '";
 +
 +                      text += UnknownIfNULL(gnutls_kx_get_name(gnutls_kx_get(sess)));
 +                      text.append("-").append(UnknownIfNULL(gnutls_cipher_get_name(gnutls_cipher_get(sess)))).append("-");
 +                      text.append(UnknownIfNULL(gnutls_mac_get_name(gnutls_mac_get(sess)))).append("'");
 +
 +                      ssl_cert* cert = sessions[user->eh.GetFd()].cert;
 +                      if (!cert->fingerprint.empty())
 +                              text += " and your SSL fingerprint is " + cert->fingerprint;
 +
 +                      user->WriteNotice(text);
 +              }
 +      }
 +};
 +
 +class CommandStartTLS : public SplitCommand
 +{
 +      IOHook& hook;
 +
 + public:
 +      bool enabled;
 +      CommandStartTLS(Module* mod, IOHook& Hook)
 +              : SplitCommand(mod, "STARTTLS")
 +              , hook(Hook)
 +      {
 +              enabled = true;
 +              works_before_reg = true;
 +      }
 +
 +      CmdResult HandleLocal(const std::vector<std::string> &parameters, LocalUser *user)
 +      {
 +              if (!enabled)
 +              {
 +                      user->WriteNumeric(691, "%s :STARTTLS is not enabled", user->nick.c_str());
 +                      return CMD_FAILURE;
 +              }
 +
 +              if (user->registered == REG_ALL)
 +              {
 +                      user->WriteNumeric(691, "%s :STARTTLS is not permitted after client registration is complete", user->nick.c_str());
 +              }
 +              else
 +              {
 +                      if (!user->eh.GetIOHook())
 +                      {
 +                              user->WriteNumeric(670, "%s :STARTTLS successful, go ahead with TLS handshake", user->nick.c_str());
 +                              /* We need to flush the write buffer prior to adding the IOHook,
 +                               * otherwise we'll be sending this line inside the SSL session - which
 +                               * won't start its handshake until the client gets this line. Currently,
 +                               * we assume the write will not block here; this is usually safe, as
 +                               * STARTTLS is sent very early on in the registration phase, where the
 +                               * user hasn't built up much sendq. Handling a blocked write here would
 +                               * be very annoying.
 +                               */
 +                              user->eh.DoWrite();
 +                              user->eh.AddIOHook(&hook);
 +                              hook.OnStreamSocketAccept(&user->eh, NULL, NULL);
 +                      }
 +                      else
 +                              user->WriteNumeric(691, "%s :STARTTLS failure", user->nick.c_str());
 +              }
 +
 +              return CMD_FAILURE;
 +      }
 +};
 +
 +class ModuleSSLGnuTLS : public Module
 +{
 +      GnuTLSIOHook iohook;
 +
 +      gnutls_dh_params_t dh_params;
 +
 +      std::string sslports;
 +
 +      bool cred_alloc;
 +      bool dh_alloc;
 +
 +      RandGen randhandler;
 +      CommandStartTLS starttls;
 +
 +      GenericCap capHandler;
 +
 + public:
 +      ModuleSSLGnuTLS()
 +              : iohook(this), starttls(this, iohook), capHandler(this, "tls")
 +      {
 +              gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
 +
 +              gnutls_global_init(); // This must be called once in the program
 +              gnutls_x509_privkey_init(&x509_key);
 +
 +              #ifdef GNUTLS_NEW_PRIO_API
 +              // Init this here so it's always initialized, avoids an extra boolean
 +              gnutls_priority_init(&iohook.priority, "NORMAL", NULL);
 +              #endif
 +
 +              cred_alloc = false;
 +              dh_alloc = false;
 +      }
 +
 +      void init() CXX11_OVERRIDE
 +      {
 +              // Needs the flag as it ignores a plain /rehash
 +              OnModuleRehash(NULL,"ssl");
 +
 +              ServerInstance->GenRandom = &randhandler;
 +
 +              // Void return, guess we assume success
 +              gnutls_certificate_set_dh_params(iohook.x509_cred, dh_params);
 +
 +              ServerInstance->Modules->AddService(iohook);
 +              ServerInstance->Modules->AddService(starttls);
 +      }
 +
 +      void OnRehash(User* user) CXX11_OVERRIDE
 +      {
 +              sslports.clear();
 +
 +              ConfigTag* Conf = ServerInstance->Config->ConfValue("gnutls");
 +              starttls.enabled = Conf->getBool("starttls", true);
 +
 +              if (Conf->getBool("showports", true))
 +              {
 +                      sslports = Conf->getString("advertisedports");
 +                      if (!sslports.empty())
 +                              return;
 +
 +                      for (size_t i = 0; i < ServerInstance->ports.size(); i++)
 +                      {
 +                              ListenSocket* port = ServerInstance->ports[i];
 +                              if (port->bind_tag->getString("ssl") != "gnutls")
 +                                      continue;
 +
 +                              const std::string& portid = port->bind_desc;
 +                              ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Enabling SSL for port %s", portid.c_str());
  
                                if (port->bind_tag->getString("type", "clients") == "clients" && port->bind_addr != "127.0.0.1")
                                {
                }
        }
  
 -      void OnModuleRehash(User* user, const std::string &param)
 +      void OnModuleRehash(User* user, const std::string &param) CXX11_OVERRIDE
        {
                if(param != "ssl")
                        return;
  
                ConfigTag* Conf = ServerInstance->Config->ConfValue("gnutls");
  
 -              cafile = Conf->getString("cafile", CONFIG_PATH "/ca.pem");
 -              crlfile = Conf->getString("crlfile", CONFIG_PATH "/crl.pem");
 -              certfile = Conf->getString("certfile", CONFIG_PATH "/cert.pem");
 -              keyfile = Conf->getString("keyfile", CONFIG_PATH "/key.pem");
 -              dh_bits = Conf->getInt("dhbits");
 +              cafile = ServerInstance->Config->Paths.PrependConfig(Conf->getString("cafile", "ca.pem"));
 +              crlfile = ServerInstance->Config->Paths.PrependConfig(Conf->getString("crlfile", "crl.pem"));
 +              certfile = ServerInstance->Config->Paths.PrependConfig(Conf->getString("certfile", "cert.pem"));
 +              keyfile = ServerInstance->Config->Paths.PrependConfig(Conf->getString("keyfile", "key.pem"));
 +              int dh_bits = Conf->getInt("dhbits");
                std::string hashname = Conf->getString("hash", "md5");
  
                // The GnuTLS manual states that the gnutls_set_default_priority()
                if((dh_bits != 768) && (dh_bits != 1024) && (dh_bits != 2048) && (dh_bits != 3072) && (dh_bits != 4096))
                        dh_bits = 1024;
  
 +              iohook.dh_bits = dh_bits;
 +
 +              // As older versions of gnutls can't do this, let's disable it where needed.
 +#ifdef GNUTLS_HAS_MAC_GET_ID
 +              // As gnutls_digest_algorithm_t and gnutls_mac_algorithm_t are mapped 1:1, we can do this
 +              // There is no gnutls_dig_get_id() at the moment, but it may come later
 +              iohook.hash = (gnutls_digest_algorithm_t)gnutls_mac_get_id(hashname.c_str());
 +              if (iohook.hash == GNUTLS_DIG_UNKNOWN)
 +                      throw ModuleException("Unknown hash type " + hashname);
 +
 +              // Check if the user is walking around with their head in the ass,
 +              // giving us something that is a valid MAC but not digest
 +              gnutls_hash_hd_t is_digest;
 +              if (gnutls_hash_init(&is_digest, iohook.hash) < 0)
 +                      throw ModuleException("Unknown hash type " + hashname);
 +              gnutls_hash_deinit(is_digest, NULL);
 +#else
                if (hashname == "md5")
 -                      hash = GNUTLS_DIG_MD5;
 +                      iohook.hash = GNUTLS_DIG_MD5;
                else if (hashname == "sha1")
 -                      hash = GNUTLS_DIG_SHA1;
 +                      iohook.hash = GNUTLS_DIG_SHA1;
                else
                        throw ModuleException("Unknown hash type " + hashname);
 -
 +#endif
  
                int ret;
  
                if (cred_alloc)
                {
                        // Deallocate the old credentials
 -                      gnutls_certificate_free_credentials(x509_cred);
 +                      gnutls_certificate_free_credentials(iohook.x509_cred);
  
                        for(unsigned int i=0; i < x509_certs.size(); i++)
                                gnutls_x509_crt_deinit(x509_certs[i]);
                        x509_certs.clear();
                }
  
 -              ret = gnutls_certificate_allocate_credentials(&x509_cred);
 +              ret = gnutls_certificate_allocate_credentials(&iohook.x509_cred);
                cred_alloc = (ret >= 0);
                if (!cred_alloc)
 -                      ServerInstance->Logs->Log("m_ssl_gnutls",DEBUG, "m_ssl_gnutls.so: Failed to allocate certificate credentials: %s", gnutls_strerror(ret));
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Failed to allocate certificate credentials: %s", gnutls_strerror(ret));
  
 -              if((ret =gnutls_certificate_set_x509_trust_file(x509_cred, cafile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
 -                      ServerInstance->Logs->Log("m_ssl_gnutls",DEBUG, "m_ssl_gnutls.so: Failed to set X.509 trust file '%s': %s", cafile.c_str(), gnutls_strerror(ret));
 +              if((ret =gnutls_certificate_set_x509_trust_file(iohook.x509_cred, cafile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Failed to set X.509 trust file '%s': %s", cafile.c_str(), gnutls_strerror(ret));
  
 -              if((ret = gnutls_certificate_set_x509_crl_file (x509_cred, crlfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
 -                      ServerInstance->Logs->Log("m_ssl_gnutls",DEBUG, "m_ssl_gnutls.so: Failed to set X.509 CRL file '%s': %s", crlfile.c_str(), gnutls_strerror(ret));
 +              if((ret = gnutls_certificate_set_x509_crl_file (iohook.x509_cred, crlfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Failed to set X.509 CRL file '%s': %s", crlfile.c_str(), gnutls_strerror(ret));
  
                FileReader reader;
  
 -              reader.LoadFile(certfile);
 -              std::string cert_string = reader.Contents();
 +              reader.Load(certfile);
 +              std::string cert_string = reader.GetString();
                gnutls_datum_t cert_datum = { (unsigned char*)cert_string.data(), static_cast<unsigned int>(cert_string.length()) };
  
 -              reader.LoadFile(keyfile);
 -              std::string key_string = reader.Contents();
 +              reader.Load(keyfile);
 +              std::string key_string = reader.GetString();
                gnutls_datum_t key_datum = { (unsigned char*)key_string.data(), static_cast<unsigned int>(key_string.length()) };
  
                // If this fails, no SSL port will work. At all. So, do the smart thing - throw a ModuleException
                if((ret = gnutls_x509_privkey_import(x509_key, &key_datum, GNUTLS_X509_FMT_PEM)) < 0)
                        throw ModuleException("Unable to load GnuTLS server private key (" + keyfile + "): " + std::string(gnutls_strerror(ret)));
  
 -              if((ret = gnutls_certificate_set_x509_key(x509_cred, &x509_certs[0], certcount, x509_key)) < 0)
 +              if((ret = gnutls_certificate_set_x509_key(iohook.x509_cred, &x509_certs[0], certcount, x509_key)) < 0)
                        throw ModuleException("Unable to set GnuTLS cert/key pair: " + std::string(gnutls_strerror(ret)));
  
                #ifdef GNUTLS_NEW_PRIO_API
                // It's safe to call this every time as we cannot have this uninitialized, see constructor and below.
 -              gnutls_priority_deinit(priority);
 +              gnutls_priority_deinit(iohook.priority);
  
                // Try to set the priorities for ciphers, kex methods etc. to the user supplied string
                // If the user did not supply anything then the string is already set to "NORMAL"
                const char* priocstr = priorities.c_str();
                const char* prioerror;
  
 -              if ((ret = gnutls_priority_init(&priority, priocstr, &prioerror)) < 0)
 +              if ((ret = gnutls_priority_init(&iohook.priority, priocstr, &prioerror)) < 0)
                {
                        // gnutls did not understand the user supplied string, log and fall back to the default priorities
 -                      ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to set priorities to \"%s\": %s Syntax error at position %u, falling back to default (NORMAL)", priorities.c_str(), gnutls_strerror(ret), (unsigned int) (prioerror - priocstr));
 -                      gnutls_priority_init(&priority, "NORMAL", NULL);
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Failed to set priorities to \"%s\": %s Syntax error at position %u, falling back to default (NORMAL)", priorities.c_str(), gnutls_strerror(ret), (unsigned int) (prioerror - priocstr));
 +                      gnutls_priority_init(&iohook.priority, "NORMAL", NULL);
                }
  
                #else
                if (priorities != "NORMAL")
 -                      ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: You've set <gnutls:priority> to a value other than the default, but this is only supported with GnuTLS v2.1.7 or newer. Your GnuTLS version is older than that so the option will have no effect.");
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "You've set <gnutls:priority> to a value other than the default, but this is only supported with GnuTLS v2.1.7 or newer. Your GnuTLS version is older than that so the option will have no effect.");
                #endif
  
                #if(GNUTLS_VERSION_MAJOR < 2 || ( GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR < 12 ) )
 -              gnutls_certificate_client_set_retrieve_function (x509_cred, cert_callback);
 +              gnutls_certificate_client_set_retrieve_function (iohook.x509_cred, cert_callback);
                #else
 -              gnutls_certificate_set_retrieve_function (x509_cred, cert_callback);
 +              gnutls_certificate_set_retrieve_function (iohook.x509_cred, cert_callback);
                #endif
                ret = gnutls_dh_params_init(&dh_params);
                dh_alloc = (ret >= 0);
                if (!dh_alloc)
                {
 -                      ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to initialise DH parameters: %s", gnutls_strerror(ret));
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Failed to initialise DH parameters: %s", gnutls_strerror(ret));
                        return;
                }
  
                if (!dhfile.empty())
                {
                        // Try to load DH params from file
 -                      reader.LoadFile(dhfile);
 -                      std::string dhstring = reader.Contents();
 +                      reader.Load(dhfile);
 +                      std::string dhstring = reader.GetString();
                        gnutls_datum_t dh_datum = { (unsigned char*)dhstring.data(), static_cast<unsigned int>(dhstring.length()) };
  
                        if ((ret = gnutls_dh_params_import_pkcs3(dh_params, &dh_datum, GNUTLS_X509_FMT_PEM)) < 0)
                        {
                                // File unreadable or GnuTLS was unhappy with the contents, generate the DH primes now
 -                              ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT, "m_ssl_gnutls.so: Generating DH parameters because I failed to load them from file '%s': %s", dhfile.c_str(), gnutls_strerror(ret));
 +                              ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Generating DH parameters because I failed to load them from file '%s': %s", dhfile.c_str(), gnutls_strerror(ret));
                                GenerateDHParams();
                        }
                }
  
                int ret;
  
 -              if((ret = gnutls_dh_params_generate2(dh_params, dh_bits)) < 0)
 -                      ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to generate DH parameters (%d bits): %s", dh_bits, gnutls_strerror(ret));
 +              if((ret = gnutls_dh_params_generate2(dh_params, iohook.dh_bits)) < 0)
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Failed to generate DH parameters (%d bits): %s", iohook.dh_bits, gnutls_strerror(ret));
        }
  
        ~ModuleSSLGnuTLS()
  
                gnutls_x509_privkey_deinit(x509_key);
                #ifdef GNUTLS_NEW_PRIO_API
 -              gnutls_priority_deinit(priority);
 +              gnutls_priority_deinit(iohook.priority);
                #endif
  
                if (dh_alloc)
                        gnutls_dh_params_deinit(dh_params);
                if (cred_alloc)
 -                      gnutls_certificate_free_credentials(x509_cred);
 +                      gnutls_certificate_free_credentials(iohook.x509_cred);
  
                gnutls_global_deinit();
 -              delete[] sessions;
                ServerInstance->GenRandom = &ServerInstance->HandleGenRandom;
        }
  
 -      void OnCleanup(int target_type, void* item)
 +      void OnCleanup(int target_type, void* item) CXX11_OVERRIDE
        {
                if(target_type == TYPE_USER)
                {
                        LocalUser* user = IS_LOCAL(static_cast<User*>(item));
  
 -                      if (user && user->eh.GetIOHook() == this)
 +                      if (user && user->eh.GetIOHook() == &iohook)
                        {
                                // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
                                // Potentially there could be multiple SSL modules loaded at once on different ports.
                }
        }
  
 -      Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides SSL support for clients", VF_VENDOR);
        }
  
 -
 -      void On005Numeric(std::string &output)
 +      void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
                if (!sslports.empty())
 -                      output.append(" SSL=" + sslports);
 +                      tokens["SSL"] = sslports;
                if (starttls.enabled)
 -                      output.append(" STARTTLS");
 +                      tokens["STARTTLS"];
        }
  
 -      void OnHookIO(StreamSocket* user, ListenSocket* lsb)
 +      void OnHookIO(StreamSocket* user, ListenSocket* lsb) CXX11_OVERRIDE
        {
                if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "gnutls")
                {
                        /* Hook the user with our module */
 -                      user->AddIOHook(this);
 +                      user->AddIOHook(&iohook);
                }
        }
  
 -      void OnRequest(Request& request)
 +      void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
        {
 -              if (strcmp("GET_SSL_CERT", request.id) == 0)
 -              {
 -                      SocketCertificateRequest& req = static_cast<SocketCertificateRequest&>(request);
 -                      int fd = req.sock->GetFd();
 -                      issl_session* session = &sessions[fd];
 -
 -                      req.cert = session->cert;
 -              }
 -      }
 -
 -      void InitSession(StreamSocket* user, bool me_server)
 -      {
 -              issl_session* session = &sessions[user->GetFd()];
 -
 -              gnutls_init(&session->sess, me_server ? GNUTLS_SERVER : GNUTLS_CLIENT);
 -              session->socket = user;
 -
 -              #ifdef GNUTLS_NEW_PRIO_API
 -              gnutls_priority_set(session->sess, priority);
 -              #else
 -              gnutls_set_default_priority(session->sess);
 -              #endif
 -              gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, x509_cred);
 -              gnutls_dh_set_prime_bits(session->sess, dh_bits);
 -              gnutls_transport_set_ptr(session->sess, reinterpret_cast<gnutls_transport_ptr_t>(session));
 -              gnutls_transport_set_push_function(session->sess, gnutls_push_wrapper);
 -              gnutls_transport_set_pull_function(session->sess, gnutls_pull_wrapper);
 -
 -              if (me_server)
 -                      gnutls_certificate_server_set_request(session->sess, GNUTLS_CERT_REQUEST); // Request client certificate if any.
 -
 -              Handshake(session, user);
 -      }
 -
 -      void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
 -      {
 -              issl_session* session = &sessions[user->GetFd()];
 -
 -              /* For STARTTLS: Don't try and init a session on a socket that already has a session */
 -              if (session->sess)
 -                      return;
 -
 -              InitSession(user, true);
 -      }
 -
 -      void OnStreamSocketConnect(StreamSocket* user)
 -      {
 -              InitSession(user, false);
 -      }
 -
 -      void OnStreamSocketClose(StreamSocket* user)
 -      {
 -              CloseSession(&sessions[user->GetFd()]);
 -      }
 -
 -      int OnStreamSocketRead(StreamSocket* user, std::string& recvq)
 -      {
 -              issl_session* session = &sessions[user->GetFd()];
 -
 -              if (!session->sess)
 -              {
 -                      CloseSession(session);
 -                      user->SetError("No SSL session");
 -                      return -1;
 -              }
 -
 -              if (session->status == ISSL_HANDSHAKING_READ || session->status == ISSL_HANDSHAKING_WRITE)
 -              {
 -                      // The handshake isn't finished, try to finish it.
 -
 -                      if(!Handshake(session, user))
 -                      {
 -                              if (session->status != ISSL_CLOSING)
 -                                      return 0;
 -                              return -1;
 -                      }
 -              }
 -
 -              // If we resumed the handshake then session->status will be ISSL_HANDSHAKEN.
 -
 -              if (session->status == ISSL_HANDSHAKEN)
 -              {
 -                      char* buffer = ServerInstance->GetReadBuffer();
 -                      size_t bufsiz = ServerInstance->Config->NetBufferSize;
 -                      int ret = gnutls_record_recv(session->sess, buffer, bufsiz);
 -                      if (ret > 0)
 -                      {
 -                              recvq.append(buffer, ret);
 -                              return 1;
 -                      }
 -                      else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
 -                      {
 -                              return 0;
 -                      }
 -                      else if (ret == 0)
 -                      {
 -                              user->SetError("Connection closed");
 -                              CloseSession(session);
 -                              return -1;
 -                      }
 -                      else
 -                      {
 -                              user->SetError(gnutls_strerror(ret));
 -                              CloseSession(session);
 -                              return -1;
 -                      }
 -              }
 -              else if (session->status == ISSL_CLOSING)
 -                      return -1;
 -
 -              return 0;
 -      }
 -
 -      int OnStreamSocketWrite(StreamSocket* user, std::string& sendq)
 -      {
 -              issl_session* session = &sessions[user->GetFd()];
 -
 -              if (!session->sess)
 -              {
 -                      CloseSession(session);
 -                      user->SetError("No SSL session");
 -                      return -1;
 -              }
 -
 -              if (session->status == ISSL_HANDSHAKING_WRITE || session->status == ISSL_HANDSHAKING_READ)
 -              {
 -                      // The handshake isn't finished, try to finish it.
 -                      Handshake(session, user);
 -                      if (session->status != ISSL_CLOSING)
 -                              return 0;
 -                      return -1;
 -              }
 -
 -              int ret = 0;
 -
 -              if (session->status == ISSL_HANDSHAKEN)
 -              {
 -                      ret = gnutls_record_send(session->sess, sendq.data(), sendq.length());
 -
 -                      if (ret == (int)sendq.length())
 -                      {
 -                              ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_WRITE);
 -                              return 1;
 -                      }
 -                      else if (ret > 0)
 -                      {
 -                              sendq = sendq.substr(ret);
 -                              ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
 -                              return 0;
 -                      }
 -                      else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED || ret == 0)
 -                      {
 -                              ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
 -                              return 0;
 -                      }
 -                      else // (ret < 0)
 -                      {
 -                              user->SetError(gnutls_strerror(ret));
 -                              CloseSession(session);
 -                              return -1;
 -                      }
 -              }
 -
 -              return 0;
 -      }
 -
 -      bool Handshake(issl_session* session, StreamSocket* user)
 -      {
 -              int ret = gnutls_handshake(session->sess);
 -
 -              if (ret < 0)
 -              {
 -                      if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
 -                      {
 -                              // Handshake needs resuming later, read() or write() would have blocked.
 -
 -                              if(gnutls_record_get_direction(session->sess) == 0)
 -                              {
 -                                      // gnutls_handshake() wants to read() again.
 -                                      session->status = ISSL_HANDSHAKING_READ;
 -                                      ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
 -                              }
 -                              else
 -                              {
 -                                      // gnutls_handshake() wants to write() again.
 -                                      session->status = ISSL_HANDSHAKING_WRITE;
 -                                      ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
 -                              }
 -                      }
 -                      else
 -                      {
 -                              user->SetError("Handshake Failed - " + std::string(gnutls_strerror(ret)));
 -                              CloseSession(session);
 -                              session->status = ISSL_CLOSING;
 -                      }
 -
 -                      return false;
 -              }
 -              else
 -              {
 -                      // Change the seesion state
 -                      session->status = ISSL_HANDSHAKEN;
 -
 -                      VerifyCertificate(session,user);
 -
 -                      // Finish writing, if any left
 -                      ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
 -
 -                      return true;
 -              }
 -      }
 -
 -      void OnUserConnect(LocalUser* user)
 -      {
 -              if (user->eh.GetIOHook() == this)
 -              {
 -                      if (sessions[user->eh.GetFd()].sess)
 -                      {
 -                              const gnutls_session_t& sess = sessions[user->eh.GetFd()].sess;
 -                              std::string cipher = UnknownIfNULL(gnutls_kx_get_name(gnutls_kx_get(sess)));
 -                              cipher.append("-").append(UnknownIfNULL(gnutls_cipher_get_name(gnutls_cipher_get(sess)))).append("-");
 -                              cipher.append(UnknownIfNULL(gnutls_mac_get_name(gnutls_mac_get(sess))));
 -
 -                              ssl_cert* cert = sessions[user->eh.GetFd()].cert;
 -                              if (cert->fingerprint.empty())
 -                                      user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick.c_str(), cipher.c_str());
 -                              else
 -                                      user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\""
 -                                              " and your SSL fingerprint is %s", user->nick.c_str(), cipher.c_str(), cert->fingerprint.c_str());
 -                      }
 -              }
 -      }
 -
 -      void CloseSession(issl_session* session)
 -      {
 -              if (session->sess)
 -              {
 -                      gnutls_bye(session->sess, GNUTLS_SHUT_WR);
 -                      gnutls_deinit(session->sess);
 -              }
 -              session->socket = NULL;
 -              session->sess = NULL;
 -              session->cert = NULL;
 -              session->status = ISSL_NONE;
 -      }
 -
 -      void VerifyCertificate(issl_session* session, StreamSocket* user)
 -      {
 -              if (!session->sess || !user)
 -                      return;
 -
 -              unsigned int status;
 -              const gnutls_datum_t* cert_list;
 -              int ret;
 -              unsigned int cert_list_size;
 -              gnutls_x509_crt_t cert;
 -              char name[MAXBUF];
 -              unsigned char digest[MAXBUF];
 -              size_t digest_size = sizeof(digest);
 -              size_t name_size = sizeof(name);
 -              ssl_cert* certinfo = new ssl_cert;
 -              session->cert = certinfo;
 -
 -              /* This verification function uses the trusted CAs in the credentials
 -               * structure. So you must have installed one or more CA certificates.
 -               */
 -              ret = gnutls_certificate_verify_peers2(session->sess, &status);
 -
 -              if (ret < 0)
 -              {
 -                      certinfo->error = std::string(gnutls_strerror(ret));
 -                      return;
 -              }
 -
 -              certinfo->invalid = (status & GNUTLS_CERT_INVALID);
 -              certinfo->unknownsigner = (status & GNUTLS_CERT_SIGNER_NOT_FOUND);
 -              certinfo->revoked = (status & GNUTLS_CERT_REVOKED);
 -              certinfo->trusted = !(status & GNUTLS_CERT_SIGNER_NOT_CA);
 -
 -              /* Up to here the process is the same for X.509 certificates and
 -               * OpenPGP keys. From now on X.509 certificates are assumed. This can
 -               * be easily extended to work with openpgp keys as well.
 -               */
 -              if (gnutls_certificate_type_get(session->sess) != GNUTLS_CRT_X509)
 -              {
 -                      certinfo->error = "No X509 keys sent";
 -                      return;
 -              }
 -
 -              ret = gnutls_x509_crt_init(&cert);
 -              if (ret < 0)
 -              {
 -                      certinfo->error = gnutls_strerror(ret);
 -                      return;
 -              }
 -
 -              cert_list_size = 0;
 -              cert_list = gnutls_certificate_get_peers(session->sess, &cert_list_size);
 -              if (cert_list == NULL)
 -              {
 -                      certinfo->error = "No certificate was found";
 -                      goto info_done_dealloc;
 -              }
 -
 -              /* This is not a real world example, since we only check the first
 -               * certificate in the given chain.
 -               */
 -
 -              ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
 -              if (ret < 0)
 -              {
 -                      certinfo->error = gnutls_strerror(ret);
 -                      goto info_done_dealloc;
 -              }
 -
 -              gnutls_x509_crt_get_dn(cert, name, &name_size);
 -              certinfo->dn = name;
 -
 -              gnutls_x509_crt_get_issuer_dn(cert, name, &name_size);
 -              certinfo->issuer = name;
 -
 -              if ((ret = gnutls_x509_crt_get_fingerprint(cert, hash, digest, &digest_size)) < 0)
 -              {
 -                      certinfo->error = gnutls_strerror(ret);
 -              }
 -              else
 -              {
 -                      certinfo->fingerprint = irc::hex(digest, digest_size);
 -              }
 -
 -              /* Beware here we do not check for errors.
 -               */
 -              if ((gnutls_x509_crt_get_expiration_time(cert) < ServerInstance->Time()) || (gnutls_x509_crt_get_activation_time(cert) > ServerInstance->Time()))
 -              {
 -                      certinfo->error = "Not activated, or expired certificate";
 -              }
 -
 -info_done_dealloc:
 -              gnutls_x509_crt_deinit(cert);
 +              if (user->eh.GetIOHook() == &iohook)
 +                      iohook.TellCiphersAndFingerprint(user);
        }
  
 -      void OnEvent(Event& ev)
 +      void OnEvent(Event& ev) CXX11_OVERRIDE
        {
                if (starttls.enabled)
                        capHandler.HandleEvent(ev);
index 4cb6ee07b368d9a124b88c4a021bd33b7aa3d42a,7b7de023cdfd4f983051d48ebedef05175323ddb..29c3568ef117c93c7289bbefb5aad6298c39e6bc
  # define __AVAILABILITYMACROS__
  # define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER
  #endif
 - 
 +
  #include "inspircd.h"
 +#include "iohook.h"
  #include <openssl/ssl.h>
  #include <openssl/err.h>
 -#include "ssl.h"
 +#include "modules/ssl.h"
  
  #ifdef _WIN32
  # pragma comment(lib, "libcrypto.lib")
  # define MAX_DESCRIPTORS 10000
  #endif
  
 -/* $ModDesc: Provides SSL support for clients */
 -
 -/* $LinkerFlags: if("USE_FREEBSD_BASE_SSL") -lssl -lcrypto */
 -/* $CompileFlags: if(!"USE_FREEBSD_BASE_SSL") pkgconfversion("openssl","0.9.7") pkgconfincludes("openssl","/openssl/ssl.h","") */
 -/* $LinkerFlags: if(!"USE_FREEBSD_BASE_SSL") rpath("pkg-config --libs openssl") pkgconflibs("openssl","/libssl.so","-lssl -lcrypto -ldl") */
 -
 -/* $NoPedantic */
 -
 +/* $CompileFlags: pkgconfversion("openssl","0.9.7") pkgconfincludes("openssl","/openssl/ssl.h","") -Wno-pedantic */
 +/* $LinkerFlags: rpath("pkg-config --libs openssl") pkgconflibs("openssl","/libssl.so","-lssl -lcrypto") */
  
  enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_OPEN };
  
@@@ -95,144 -100,234 +95,144 @@@ static int OnVerify(int preverify_ok, X
        return 1;
  }
  
 -class ModuleSSLOpenSSL : public Module
 +class OpenSSLIOHook : public SSLIOHook
  {
 -      issl_session* sessions;
 -
 -      SSL_CTX* ctx;
 -      SSL_CTX* clictx;
 -
 -      std::string sslports;
 -      bool use_sha;
 -
 -      ServiceProvider iohook;
 - public:
 -
 -      ModuleSSLOpenSSL() : iohook(this, "ssl/openssl", SERVICE_IOHOOK)
 + private:
 +      bool Handshake(StreamSocket* user, issl_session* session)
        {
 -              sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
 +              int ret;
  
 -              /* Global SSL library initialization*/
 -              SSL_library_init();
 -              SSL_load_error_strings();
 +              if (session->outbound)
 +                      ret = SSL_connect(session->sess);
 +              else
 +                      ret = SSL_accept(session->sess);
  
 -              /* Build our SSL contexts:
 -               * NOTE: OpenSSL makes us have two contexts, one for servers and one for clients. ICK.
 -               */
 -              ctx = SSL_CTX_new( SSLv23_server_method() );
 -              clictx = SSL_CTX_new( SSLv23_client_method() );
 +              if (ret < 0)
 +              {
 +                      int err = SSL_get_error(session->sess, ret);
  
 -              SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
 -              SSL_CTX_set_mode(clictx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
 +                      if (err == SSL_ERROR_WANT_READ)
 +                      {
 +                              ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
 +                              session->status = ISSL_HANDSHAKING;
 +                              return true;
 +                      }
 +                      else if (err == SSL_ERROR_WANT_WRITE)
 +                      {
 +                              ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
 +                              session->status = ISSL_HANDSHAKING;
 +                              return true;
 +                      }
 +                      else
 +                      {
 +                              CloseSession(session);
 +                      }
  
 -              SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
 -              SSL_CTX_set_verify(clictx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
 -      }
 +                      return false;
 +              }
 +              else if (ret > 0)
 +              {
 +                      // Handshake complete.
 +                      VerifyCertificate(session, user);
  
 -      void init()
 -      {
 -              // Needs the flag as it ignores a plain /rehash
 -              OnModuleRehash(NULL,"ssl");
 -              Implementation eventlist[] = { I_On005Numeric, I_OnRehash, I_OnModuleRehash, I_OnHookIO, I_OnUserConnect };
 -              ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
 -              ServerInstance->Modules->AddService(iohook);
 -      }
 +                      session->status = ISSL_OPEN;
  
 -      void OnHookIO(StreamSocket* user, ListenSocket* lsb)
 -      {
 -              if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "openssl")
 +                      ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
 +
 +                      return true;
 +              }
 +              else if (ret == 0)
                {
 -                      /* Hook the user with our module */
 -                      user->AddIOHook(this);
 +                      CloseSession(session);
 +                      return true;
                }
 +
 +              return true;
        }
  
 -      void OnRehash(User* user)
 +      void CloseSession(issl_session* session)
        {
 -              sslports.clear();
 -
 -              ConfigTag* Conf = ServerInstance->Config->ConfValue("openssl");
 -
 -              if (Conf->getBool("showports", true))
 +              if (session->sess)
                {
 -                      sslports = Conf->getString("advertisedports");
 -                      if (!sslports.empty())
 -                              return;
 -
 -                      for (size_t i = 0; i < ServerInstance->ports.size(); i++)
 -                      {
 -                              ListenSocket* port = ServerInstance->ports[i];
 -                              if (port->bind_tag->getString("ssl") != "openssl")
 -                                      continue;
 -
 -                              const std::string& portid = port->bind_desc;
 -                              ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Enabling SSL for port %s", portid.c_str());
 -
 -                              if (port->bind_tag->getString("type", "clients") == "clients" && port->bind_addr != "127.0.0.1")
 -                              {
 -                                      /*
 -                                       * Found an SSL port for clients that is not bound to 127.0.0.1 and handled by us, display
 -                                       * the IP:port in ISUPPORT.
 -                                       *
 -                                       * We used to advertise all ports seperated by a ';' char that matched the above criteria,
 -                                       * but this resulted in too long ISUPPORT lines if there were lots of ports to be displayed.
 -                                       * To solve this by default we now only display the first IP:port found and let the user
 -                                       * configure the exact value for the 005 token, if necessary.
 -                                       */
 -                                      sslports = portid;
 -                                      break;
 -                              }
 -                      }
 +                      SSL_shutdown(session->sess);
 +                      SSL_free(session->sess);
                }
 +
 +              session->sess = NULL;
 +              session->status = ISSL_NONE;
 +              errno = EIO;
        }
  
 -      void OnModuleRehash(User* user, const std::string &param)
 +      void VerifyCertificate(issl_session* session, StreamSocket* user)
        {
 -              if (param != "ssl")
 +              if (!session->sess || !user)
                        return;
  
 -              std::string keyfile;
 -              std::string certfile;
 -              std::string cafile;
 -              std::string dhfile;
 -              OnRehash(user);
 -
 -              ConfigTag* conf = ServerInstance->Config->ConfValue("openssl");
 -
 -              cafile   = conf->getString("cafile", CONFIG_PATH "/ca.pem");
 -              certfile = conf->getString("certfile", CONFIG_PATH "/cert.pem");
 -              keyfile  = conf->getString("keyfile", CONFIG_PATH "/key.pem");
 -              dhfile   = conf->getString("dhfile", CONFIG_PATH "/dhparams.pem");
 -              std::string hash = conf->getString("hash", "md5");
 -              if (hash != "sha1" && hash != "md5")
 -                      throw ModuleException("Unknown hash type " + hash);
 -              use_sha = (hash == "sha1");
 +              X509* cert;
 +              ssl_cert* certinfo = new ssl_cert;
 +              session->cert = certinfo;
 +              unsigned int n;
 +              unsigned char md[EVP_MAX_MD_SIZE];
  
 -              std::string ciphers = conf->getString("ciphers", "");
 +              cert = SSL_get_peer_certificate((SSL*)session->sess);
  
 -              if (!ciphers.empty())
 +              if (!cert)
                {
 -                      if ((!SSL_CTX_set_cipher_list(ctx, ciphers.c_str())) || (!SSL_CTX_set_cipher_list(clictx, ciphers.c_str())))
 -                      {
 -                              ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't set cipher list to %s.", ciphers.c_str());
 -                              ERR_print_errors_cb(error_callback, this);
 -                      }
 +                      certinfo->error = "Could not get peer certificate: "+std::string(get_error());
 +                      return;
                }
  
 -              /* Load our keys and certificates
 -               * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
 -               */
 -              if ((!SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str())) || (!SSL_CTX_use_certificate_chain_file(clictx, certfile.c_str())))
 -              {
 -                      ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read certificate file %s. %s", certfile.c_str(), strerror(errno));
 -                      ERR_print_errors_cb(error_callback, this);
 -              }
 +              certinfo->invalid = (SSL_get_verify_result(session->sess) != X509_V_OK);
  
-               if (SelfSigned)
 -              if (((!SSL_CTX_use_PrivateKey_file(ctx, keyfile.c_str(), SSL_FILETYPE_PEM))) || (!SSL_CTX_use_PrivateKey_file(clictx, keyfile.c_str(), SSL_FILETYPE_PEM)))
++              if (!SelfSigned)
                {
 -                      ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read key file %s. %s", keyfile.c_str(), strerror(errno));
 -                      ERR_print_errors_cb(error_callback, this);
 +                      certinfo->unknownsigner = false;
 +                      certinfo->trusted = true;
                }
 -
 -              /* Load the CAs we trust*/
 -              if (((!SSL_CTX_load_verify_locations(ctx, cafile.c_str(), 0))) || (!SSL_CTX_load_verify_locations(clictx, cafile.c_str(), 0)))
 +              else
                {
 -                      ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read CA list from %s. This is only a problem if you want to verify client certificates, otherwise it's safe to ignore this message. Error: %s", cafile.c_str(), strerror(errno));
 -                      ERR_print_errors_cb(error_callback, this);
 +                      certinfo->unknownsigner = true;
 +                      certinfo->trusted = false;
                }
  
 -              FILE* dhpfile = fopen(dhfile.c_str(), "r");
 -              DH* ret;
 +              certinfo->dn = X509_NAME_oneline(X509_get_subject_name(cert),0,0);
 +              certinfo->issuer = X509_NAME_oneline(X509_get_issuer_name(cert),0,0);
  
 -              if (dhpfile == NULL)
 +              if (!X509_digest(cert, digest, md, &n))
                {
 -                      ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so Couldn't open DH file %s: %s", dhfile.c_str(), strerror(errno));
 -                      throw ModuleException("Couldn't open DH file " + dhfile + ": " + strerror(errno));
 +                      certinfo->error = "Out of memory generating fingerprint";
                }
                else
                {
 -                      ret = PEM_read_DHparams(dhpfile, NULL, NULL, NULL);
 -                      if ((SSL_CTX_set_tmp_dh(ctx, ret) < 0) || (SSL_CTX_set_tmp_dh(clictx, ret) < 0))
 -                      {
 -                              ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Couldn't set DH parameters %s. SSL errors follow:", dhfile.c_str());
 -                              ERR_print_errors_cb(error_callback, this);
 -                      }
 +                      certinfo->fingerprint = BinToHex(md, n);
                }
  
 -              fclose(dhpfile);
 -      }
 -
 -      void On005Numeric(std::string &output)
 -      {
 -              if (!sslports.empty())
 -                      output.append(" SSL=" + sslports);
 -      }
 -
 -      ~ModuleSSLOpenSSL()
 -      {
 -              SSL_CTX_free(ctx);
 -              SSL_CTX_free(clictx);
 -              delete[] sessions;
 -      }
 -
 -      void OnUserConnect(LocalUser* user)
 -      {
 -              if (user->eh.GetIOHook() == this)
 +              if ((ASN1_UTCTIME_cmp_time_t(X509_get_notAfter(cert), ServerInstance->Time()) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_get_notBefore(cert), ServerInstance->Time()) == 0))
                {
 -                      if (sessions[user->eh.GetFd()].sess)
 -                      {
 -                              if (!sessions[user->eh.GetFd()].cert->fingerprint.empty())
 -                                      user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\""
 -                                              " and your SSL fingerprint is %s", user->nick.c_str(), SSL_get_cipher(sessions[user->eh.GetFd()].sess), sessions[user->eh.GetFd()].cert->fingerprint.c_str());
 -                              else
 -                                      user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick.c_str(), SSL_get_cipher(sessions[user->eh.GetFd()].sess));
 -                      }
 +                      certinfo->error = "Not activated, or expired certificate";
                }
 -      }
 -
 -      void OnCleanup(int target_type, void* item)
 -      {
 -              if (target_type == TYPE_USER)
 -              {
 -                      LocalUser* user = IS_LOCAL((User*)item);
  
 -                      if (user && user->eh.GetIOHook() == this)
 -                      {
 -                              // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
 -                              // Potentially there could be multiple SSL modules loaded at once on different ports.
 -                              ServerInstance->Users->QuitUser(user, "SSL module unloading");
 -                      }
 -              }
 +              X509_free(cert);
        }
  
 -      Version GetVersion()
 + public:
 +      issl_session* sessions;
 +      SSL_CTX* ctx;
 +      SSL_CTX* clictx;
 +      const EVP_MD *digest;
 +
 +      OpenSSLIOHook(Module* mod)
 +              : SSLIOHook(mod, "ssl/openssl")
        {
 -              return Version("Provides SSL support for clients", VF_VENDOR);
 +              sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
        }
  
 -      void OnRequest(Request& request)
 +      ~OpenSSLIOHook()
        {
 -              if (strcmp("GET_SSL_CERT", request.id) == 0)
 -              {
 -                      SocketCertificateRequest& req = static_cast<SocketCertificateRequest&>(request);
 -                      int fd = req.sock->GetFd();
 -                      issl_session* session = &sessions[fd];
 -
 -                      req.cert = session->cert;
 -              }
 +              delete[] sessions;
        }
  
 -      void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
 +      void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
        {
                int fd = user->GetFd();
  
  
                if (SSL_set_fd(session->sess, fd) == 0)
                {
 -                      ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Can't set fd with SSL_set_fd: %d", fd);
                        return;
                }
  
                Handshake(user, session);
        }
  
 -      void OnStreamSocketConnect(StreamSocket* user)
 +      void OnStreamSocketConnect(StreamSocket* user) CXX11_OVERRIDE
        {
                int fd = user->GetFd();
                /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
  
                if (SSL_set_fd(session->sess, fd) == 0)
                {
 -                      ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Can't set fd with SSL_set_fd: %d", fd);
                        return;
                }
  
                Handshake(user, session);
        }
  
 -      void OnStreamSocketClose(StreamSocket* user)
 +      void OnStreamSocketClose(StreamSocket* user) CXX11_OVERRIDE
        {
                int fd = user->GetFd();
                /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
                CloseSession(&sessions[fd]);
        }
  
 -      int OnStreamSocketRead(StreamSocket* user, std::string& recvq)
 +      int OnStreamSocketRead(StreamSocket* user, std::string& recvq) CXX11_OVERRIDE
        {
                int fd = user->GetFd();
                /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
                return 0;
        }
  
 -      int OnStreamSocketWrite(StreamSocket* user, std::string& buffer)
 +      int OnStreamSocketWrite(StreamSocket* user, std::string& buffer) CXX11_OVERRIDE
        {
                int fd = user->GetFd();
  
                return 0;
        }
  
 -      bool Handshake(StreamSocket* user, issl_session* session)
 +      ssl_cert* GetCertificate(StreamSocket* sock) CXX11_OVERRIDE
        {
 -              int ret;
 -
 -              if (session->outbound)
 -                      ret = SSL_connect(session->sess);
 -              else
 -                      ret = SSL_accept(session->sess);
 +              int fd = sock->GetFd();
 +              issl_session* session = &sessions[fd];
 +              return session->cert;
 +      }
  
 -              if (ret < 0)
 +      void TellCiphersAndFingerprint(LocalUser* user)
 +      {
 +              issl_session& s = sessions[user->eh.GetFd()];
 +              if (s.sess)
                {
 -                      int err = SSL_get_error(session->sess, ret);
 -
 -                      if (err == SSL_ERROR_WANT_READ)
 -                      {
 -                              ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
 -                              session->status = ISSL_HANDSHAKING;
 -                              return true;
 -                      }
 -                      else if (err == SSL_ERROR_WANT_WRITE)
 -                      {
 -                              ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
 -                              session->status = ISSL_HANDSHAKING;
 -                              return true;
 -                      }
 -                      else
 -                      {
 -                              CloseSession(session);
 -                      }
 +                      std::string text = "*** You are connected using SSL cipher '" + std::string(SSL_get_cipher(s.sess)) + "'";
 +                      const std::string& fingerprint = s.cert->fingerprint;
 +                      if (!fingerprint.empty())
 +                              text += " and your SSL fingerprint is " + fingerprint;
  
 -                      return false;
 +                      user->WriteNotice(text);
                }
 -              else if (ret > 0)
 -              {
 -                      // Handshake complete.
 -                      VerifyCertificate(session, user);
 +      }
 +};
  
 -                      session->status = ISSL_OPEN;
 +class ModuleSSLOpenSSL : public Module
 +{
 +      std::string sslports;
 +      OpenSSLIOHook iohook;
  
 -                      ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
 + public:
 +      ModuleSSLOpenSSL() : iohook(this)
 +      {
 +              /* Global SSL library initialization*/
 +              SSL_library_init();
 +              SSL_load_error_strings();
  
 -                      return true;
 -              }
 -              else if (ret == 0)
 -              {
 -                      CloseSession(session);
 -                      return true;
 -              }
 +              /* Build our SSL contexts:
 +               * NOTE: OpenSSL makes us have two contexts, one for servers and one for clients. ICK.
 +               */
 +              iohook.ctx = SSL_CTX_new( SSLv23_server_method() );
 +              iohook.clictx = SSL_CTX_new( SSLv23_client_method() );
  
 -              return true;
 +              SSL_CTX_set_mode(iohook.ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
 +              SSL_CTX_set_mode(iohook.clictx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
 +
 +              SSL_CTX_set_verify(iohook.ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
 +              SSL_CTX_set_verify(iohook.clictx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
        }
  
 -      void CloseSession(issl_session* session)
 +      ~ModuleSSLOpenSSL()
        {
 -              if (session->sess)
 +              SSL_CTX_free(iohook.ctx);
 +              SSL_CTX_free(iohook.clictx);
 +      }
 +
 +      void init() CXX11_OVERRIDE
 +      {
 +              // Needs the flag as it ignores a plain /rehash
 +              OnModuleRehash(NULL,"ssl");
 +              ServerInstance->Modules->AddService(iohook);
 +      }
 +
 +      void OnHookIO(StreamSocket* user, ListenSocket* lsb) CXX11_OVERRIDE
 +      {
 +              if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "openssl")
                {
 -                      SSL_shutdown(session->sess);
 -                      SSL_free(session->sess);
 +                      /* Hook the user with our module */
 +                      user->AddIOHook(&iohook);
                }
 +      }
  
 -              session->sess = NULL;
 -              session->status = ISSL_NONE;
 -              errno = EIO;
 +      void OnRehash(User* user) CXX11_OVERRIDE
 +      {
 +              sslports.clear();
 +
 +              ConfigTag* Conf = ServerInstance->Config->ConfValue("openssl");
 +
 +              if (Conf->getBool("showports", true))
 +              {
 +                      sslports = Conf->getString("advertisedports");
 +                      if (!sslports.empty())
 +                              return;
 +
 +                      for (size_t i = 0; i < ServerInstance->ports.size(); i++)
 +                      {
 +                              ListenSocket* port = ServerInstance->ports[i];
 +                              if (port->bind_tag->getString("ssl") != "openssl")
 +                                      continue;
 +
 +                              const std::string& portid = port->bind_desc;
 +                              ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Enabling SSL for port %s", portid.c_str());
 +
 +                              if (port->bind_tag->getString("type", "clients") == "clients" && port->bind_addr != "127.0.0.1")
 +                              {
 +                                      /*
 +                                       * Found an SSL port for clients that is not bound to 127.0.0.1 and handled by us, display
 +                                       * the IP:port in ISUPPORT.
 +                                       *
 +                                       * We used to advertise all ports seperated by a ';' char that matched the above criteria,
 +                                       * but this resulted in too long ISUPPORT lines if there were lots of ports to be displayed.
 +                                       * To solve this by default we now only display the first IP:port found and let the user
 +                                       * configure the exact value for the 005 token, if necessary.
 +                                       */
 +                                      sslports = portid;
 +                                      break;
 +                              }
 +                      }
 +              }
        }
  
 -      void VerifyCertificate(issl_session* session, StreamSocket* user)
 +      void OnModuleRehash(User* user, const std::string &param) CXX11_OVERRIDE
        {
 -              if (!session->sess || !user)
 +              if (param != "ssl")
                        return;
  
 -              X509* cert;
 -              ssl_cert* certinfo = new ssl_cert;
 -              session->cert = certinfo;
 -              unsigned int n;
 -              unsigned char md[EVP_MAX_MD_SIZE];
 -              const EVP_MD *digest = use_sha ? EVP_sha1() : EVP_md5();
 +              std::string keyfile;
 +              std::string certfile;
 +              std::string cafile;
 +              std::string dhfile;
 +              OnRehash(user);
  
 -              cert = SSL_get_peer_certificate((SSL*)session->sess);
 +              ConfigTag* conf = ServerInstance->Config->ConfValue("openssl");
  
 -              if (!cert)
 +              cafile   = ServerInstance->Config->Paths.PrependConfig(conf->getString("cafile", "ca.pem"));
 +              certfile = ServerInstance->Config->Paths.PrependConfig(conf->getString("certfile", "cert.pem"));
 +              keyfile  = ServerInstance->Config->Paths.PrependConfig(conf->getString("keyfile", "key.pem"));
 +              dhfile   = ServerInstance->Config->Paths.PrependConfig(conf->getString("dhfile", "dhparams.pem"));
 +              std::string hash = conf->getString("hash", "md5");
 +
 +              iohook.digest = EVP_get_digestbyname(hash.c_str());
 +              if (iohook.digest == NULL)
 +                      throw ModuleException("Unknown hash type " + hash);
 +
 +              std::string ciphers = conf->getString("ciphers", "");
 +
 +              SSL_CTX* ctx = iohook.ctx;
 +              SSL_CTX* clictx = iohook.clictx;
 +
 +              if (!ciphers.empty())
                {
 -                      certinfo->error = "Could not get peer certificate: "+std::string(get_error());
 -                      return;
 +                      if ((!SSL_CTX_set_cipher_list(ctx, ciphers.c_str())) || (!SSL_CTX_set_cipher_list(clictx, ciphers.c_str())))
 +                      {
 +                              ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Can't set cipher list to %s.", ciphers.c_str());
 +                              ERR_print_errors_cb(error_callback, this);
 +                      }
                }
  
 -              certinfo->invalid = (SSL_get_verify_result(session->sess) != X509_V_OK);
 +              /* Load our keys and certificates
 +               * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
 +               */
 +              if ((!SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str())) || (!SSL_CTX_use_certificate_chain_file(clictx, certfile.c_str())))
 +              {
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Can't read certificate file %s. %s", certfile.c_str(), strerror(errno));
 +                      ERR_print_errors_cb(error_callback, this);
 +              }
  
 -              if (!SelfSigned)
 +              if (((!SSL_CTX_use_PrivateKey_file(ctx, keyfile.c_str(), SSL_FILETYPE_PEM))) || (!SSL_CTX_use_PrivateKey_file(clictx, keyfile.c_str(), SSL_FILETYPE_PEM)))
                {
 -                      certinfo->unknownsigner = false;
 -                      certinfo->trusted = true;
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Can't read key file %s. %s", keyfile.c_str(), strerror(errno));
 +                      ERR_print_errors_cb(error_callback, this);
                }
 -              else
 +
 +              /* Load the CAs we trust*/
 +              if (((!SSL_CTX_load_verify_locations(ctx, cafile.c_str(), 0))) || (!SSL_CTX_load_verify_locations(clictx, cafile.c_str(), 0)))
                {
 -                      certinfo->unknownsigner = true;
 -                      certinfo->trusted = false;
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Can't read CA list from %s. This is only a problem if you want to verify client certificates, otherwise it's safe to ignore this message. Error: %s", cafile.c_str(), strerror(errno));
 +                      ERR_print_errors_cb(error_callback, this);
                }
  
 -              certinfo->dn = X509_NAME_oneline(X509_get_subject_name(cert),0,0);
 -              certinfo->issuer = X509_NAME_oneline(X509_get_issuer_name(cert),0,0);
 +              FILE* dhpfile = fopen(dhfile.c_str(), "r");
 +              DH* ret;
  
 -              if (!X509_digest(cert, digest, md, &n))
 +              if (dhpfile == NULL)
                {
 -                      certinfo->error = "Out of memory generating fingerprint";
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Couldn't open DH file %s: %s", dhfile.c_str(), strerror(errno));
 +                      throw ModuleException("Couldn't open DH file " + dhfile + ": " + strerror(errno));
                }
                else
                {
 -                      certinfo->fingerprint = irc::hex(md, n);
 +                      ret = PEM_read_DHparams(dhpfile, NULL, NULL, NULL);
 +                      if ((SSL_CTX_set_tmp_dh(ctx, ret) < 0) || (SSL_CTX_set_tmp_dh(clictx, ret) < 0))
 +                      {
 +                              ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Couldn't set DH parameters %s. SSL errors follow:", dhfile.c_str());
 +                              ERR_print_errors_cb(error_callback, this);
 +                      }
                }
  
 -              if ((ASN1_UTCTIME_cmp_time_t(X509_get_notAfter(cert), ServerInstance->Time()) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_get_notBefore(cert), ServerInstance->Time()) == 0))
 +              fclose(dhpfile);
 +      }
 +
 +      void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
 +      {
 +              if (!sslports.empty())
 +                      tokens["SSL"] = sslports;
 +      }
 +
 +      void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
 +      {
 +              if (user->eh.GetIOHook() == &iohook)
 +                      iohook.TellCiphersAndFingerprint(user);
 +      }
 +
 +      void OnCleanup(int target_type, void* item) CXX11_OVERRIDE
 +      {
 +              if (target_type == TYPE_USER)
                {
 -                      certinfo->error = "Not activated, or expired certificate";
 +                      LocalUser* user = IS_LOCAL((User*)item);
 +
 +                      if (user && user->eh.GetIOHook() == &iohook)
 +                      {
 +                              // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
 +                              // Potentially there could be multiple SSL modules loaded at once on different ports.
 +                              ServerInstance->Users->QuitUser(user, "SSL module unloading");
 +                      }
                }
 +      }
  
 -              X509_free(cert);
 +      Version GetVersion() CXX11_OVERRIDE
 +      {
 +              return Version("Provides SSL support for clients", VF_VENDOR);
        }
  };
  
  static int error_callback(const char *str, size_t len, void *u)
  {
 -      ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "SSL error: " + std::string(str, len - 1));
 +      ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "SSL error: " + std::string(str, len - 1));
  
        //
        // XXX: Remove this line, it causes valgrind warnings...
diff --combined src/modules/m_dnsbl.cpp
index becc7a6e85d59762b01886b1d0c544d8386d1d4a,3a9360380e43decfaa1bf556db0dfb0914ed959a..3c9d64d49a598ad5c7822deacdd25985465bdc48
  
  #include "inspircd.h"
  #include "xline.h"
 -
 -/* $ModDesc: Provides handling of DNS blacklists */
 +#include "modules/dns.h"
  
  /* Class holding data for a single entry */
- class DNSBLConfEntry
+ class DNSBLConfEntry : public refcountbase
  {
        public:
                enum EnumBanaction { I_UNKNOWN, I_KILL, I_ZLINE, I_KLINE, I_GLINE, I_MARK };
                unsigned char records[256];
                unsigned long stats_hits, stats_misses;
                DNSBLConfEntry(): type(A_BITMASK),duration(86400),bitmask(0),stats_hits(0), stats_misses(0) {}
 -              ~DNSBLConfEntry() { }
  };
  
  
  /** Resolver for CGI:IRC hostnames encoded in ident/GECOS
   */
 -class DNSBLResolver : public Resolver
 +class DNSBLResolver : public DNS::Request
  {
        std::string theiruid;
        LocalStringExt& nameExt;
        LocalIntExt& countExt;
-       DNSBLConfEntry *ConfEntry;
+       reference<DNSBLConfEntry> ConfEntry;
  
   public:
  
-       DNSBLResolver(DNS::Manager *mgr, Module *me, LocalStringExt& match, LocalIntExt& ctr, const std::string &hostname, LocalUser* u, DNSBLConfEntry *conf)
 -      DNSBLResolver(Module *me, LocalStringExt& match, LocalIntExt& ctr, const std::string &hostname, LocalUser* u, reference<DNSBLConfEntry> conf, bool &cached)
 -              : Resolver(hostname, DNS_QUERY_A, cached, me), theiruid(u->uuid), nameExt(match), countExt(ctr), ConfEntry(conf)
++      DNSBLResolver(DNS::Manager *mgr, Module *me, LocalStringExt& match, LocalIntExt& ctr, const std::string &hostname, LocalUser* u, reference<DNSBLConfEntry> conf)
 +              : DNS::Request(mgr, me, hostname, DNS::QUERY_A, true), theiruid(u->uuid), nameExt(match), countExt(ctr), ConfEntry(conf)
        {
        }
  
        /* Note: This may be called multiple times for multiple A record results */
 -      virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
 +      void OnLookupComplete(const DNS::Query *r) CXX11_OVERRIDE
        {
                /* Check the user still exists */
                LocalUser* them = (LocalUser*)ServerInstance->FindUUID(theiruid);
 -              if (them)
 +              if (!them)
 +                      return;
 +
 +              const DNS::ResourceRecord &ans_record = r->answers[0];
 +
 +              int i = countExt.get(them);
 +              if (i)
 +                      countExt.set(them, i - 1);
 +
 +              // Now we calculate the bitmask: 256*(256*(256*a+b)+c)+d
 +
 +              unsigned int bitmask = 0, record = 0;
 +              bool match = false;
 +              in_addr resultip;
 +
 +              inet_aton(ans_record.rdata.c_str(), &resultip);
 +
 +              switch (ConfEntry->type)
 +              {
 +                      case DNSBLConfEntry::A_BITMASK:
 +                              bitmask = resultip.s_addr >> 24; /* Last octet (network byte order) */
 +                              bitmask &= ConfEntry->bitmask;
 +                              match = (bitmask != 0);
 +                      break;
 +                      case DNSBLConfEntry::A_RECORD:
 +                              record = resultip.s_addr >> 24; /* Last octet */
 +                              match = (ConfEntry->records[record] == 1);
 +                      break;
 +              }
 +
 +              if (match)
                {
 -                      int i = countExt.get(them);
 -                      if (i)
 -                              countExt.set(them, i - 1);
 -                      // Now we calculate the bitmask: 256*(256*(256*a+b)+c)+d
 -                      if(result.length())
 +                      std::string reason = ConfEntry->reason;
 +                      std::string::size_type x = reason.find("%ip%");
 +                      while (x != std::string::npos)
                        {
 -                              unsigned int bitmask = 0, record = 0;
 -                              bool match = false;
 -                              in_addr resultip;
 +                              reason.erase(x, 4);
 +                              reason.insert(x, them->GetIPString());
 +                              x = reason.find("%ip%");
 +                      }
  
 -                              inet_aton(result.c_str(), &resultip);
 +                      ConfEntry->stats_hits++;
  
 -                              switch (ConfEntry->type)
 +                      switch (ConfEntry->banaction)
 +                      {
 +                              case DNSBLConfEntry::I_KILL:
                                {
 -                                      case DNSBLConfEntry::A_BITMASK:
 -                                              bitmask = resultip.s_addr >> 24; /* Last octet (network byte order) */
 -                                              bitmask &= ConfEntry->bitmask;
 -                                              match = (bitmask != 0);
 -                                      break;
 -                                      case DNSBLConfEntry::A_RECORD:
 -                                              record = resultip.s_addr >> 24; /* Last octet */
 -                                              match = (ConfEntry->records[record] == 1);
 +                                      ServerInstance->Users->QuitUser(them, "Killed (" + reason + ")");
                                        break;
                                }
 -
 -                              if (match)
 +                              case DNSBLConfEntry::I_MARK:
                                {
 -                                      std::string reason = ConfEntry->reason;
 -                                      std::string::size_type x = reason.find("%ip%");
 -                                      while (x != std::string::npos)
 +                                      if (!ConfEntry->ident.empty())
                                        {
 -                                              reason.erase(x, 4);
 -                                              reason.insert(x, them->GetIPString());
 -                                              x = reason.find("%ip%");
 +                                              them->WriteServ("304 " + them->nick + " :Your ident has been set to " + ConfEntry->ident + " because you matched " + reason);
 +                                              them->ChangeIdent(ConfEntry->ident);
                                        }
  
 -                                      ConfEntry->stats_hits++;
 -
 -                                      switch (ConfEntry->banaction)
 +                                      if (!ConfEntry->host.empty())
                                        {
 -                                              case DNSBLConfEntry::I_KILL:
 -                                              {
 -                                                      ServerInstance->Users->QuitUser(them, "Killed (" + reason + ")");
 -                                                      break;
 -                                              }
 -                                              case DNSBLConfEntry::I_MARK:
 -                                              {
 -                                                      if (!ConfEntry->ident.empty())
 -                                                      {
 -                                                              them->WriteServ("304 " + them->nick + " :Your ident has been set to " + ConfEntry->ident + " because you matched " + reason);
 -                                                              them->ChangeIdent(ConfEntry->ident.c_str());
 -                                                      }
 -
 -                                                      if (!ConfEntry->host.empty())
 -                                                      {
 -                                                              them->WriteServ("304 " + them->nick + " :Your host has been set to " + ConfEntry->host + " because you matched " + reason);
 -                                                              them->ChangeDisplayedHost(ConfEntry->host.c_str());
 -                                                      }
 -
 -                                                      nameExt.set(them, ConfEntry->name);
 -                                                      break;
 -                                              }
 -                                              case DNSBLConfEntry::I_KLINE:
 -                                              {
 -                                                      KLine* kl = new KLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
 -                                                                      "*", them->GetIPString());
 -                                                      if (ServerInstance->XLines->AddLine(kl,NULL))
 -                                                      {
 -                                                              std::string timestr = ServerInstance->TimeString(kl->expiry);
 -                                                              ServerInstance->SNO->WriteGlobalSno('x',"K:line added due to DNSBL match on *@%s to expire on %s: %s",
 -                                                                      them->GetIPString(), timestr.c_str(), reason.c_str());
 -                                                              ServerInstance->XLines->ApplyLines();
 -                                                      }
 -                                                      else
 -                                                              delete kl;
 -                                                      break;
 -                                              }
 -                                              case DNSBLConfEntry::I_GLINE:
 -                                              {
 -                                                      GLine* gl = new GLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
 -                                                                      "*", them->GetIPString());
 -                                                      if (ServerInstance->XLines->AddLine(gl,NULL))
 -                                                      {
 -                                                              std::string timestr = ServerInstance->TimeString(gl->expiry);
 -                                                              ServerInstance->SNO->WriteGlobalSno('x',"G:line added due to DNSBL match on *@%s to expire on %s: %s",
 -                                                                      them->GetIPString(), timestr.c_str(), reason.c_str());
 -                                                              ServerInstance->XLines->ApplyLines();
 -                                                      }
 -                                                      else
 -                                                              delete gl;
 -                                                      break;
 -                                              }
 -                                              case DNSBLConfEntry::I_ZLINE:
 -                                              {
 -                                                      ZLine* zl = new ZLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
 -                                                                      them->GetIPString());
 -                                                      if (ServerInstance->XLines->AddLine(zl,NULL))
 -                                                      {
 -                                                              std::string timestr = ServerInstance->TimeString(zl->expiry);
 -                                                              ServerInstance->SNO->WriteGlobalSno('x',"Z:line added due to DNSBL match on *@%s to expire on %s: %s",
 -                                                                      them->GetIPString(), timestr.c_str(), reason.c_str());
 -                                                              ServerInstance->XLines->ApplyLines();
 -                                                      }
 -                                                      else
 -                                                              delete zl;
 -                                                      break;
 -                                              }
 -                                              case DNSBLConfEntry::I_UNKNOWN:
 -                                              {
 -                                                      break;
 -                                              }
 -                                              break;
 +                                              them->WriteServ("304 " + them->nick + " :Your host has been set to " + ConfEntry->host + " because you matched " + reason);
 +                                              them->ChangeDisplayedHost(ConfEntry->host);
                                        }
  
 -                                      ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s%s detected as being on a DNS blacklist (%s) with result %d", them->nick.empty() ? "<unknown>" : "", them->GetFullRealHost().c_str(), ConfEntry->domain.c_str(), (ConfEntry->type==DNSBLConfEntry::A_BITMASK) ? bitmask : record);
 +                                      nameExt.set(them, ConfEntry->name);
 +                                      break;
 +                              }
 +                              case DNSBLConfEntry::I_KLINE:
 +                              {
 +                                      KLine* kl = new KLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
 +                                                      "*", them->GetIPString());
 +                                      if (ServerInstance->XLines->AddLine(kl,NULL))
 +                                      {
 +                                              std::string timestr = ServerInstance->TimeString(kl->expiry);
 +                                              ServerInstance->SNO->WriteGlobalSno('x',"K:line added due to DNSBL match on *@%s to expire on %s: %s",
 +                                                      them->GetIPString().c_str(), timestr.c_str(), reason.c_str());
 +                                              ServerInstance->XLines->ApplyLines();
 +                                      }
 +                                      else
 +                                              delete kl;
 +                                      break;
 +                              }
 +                              case DNSBLConfEntry::I_GLINE:
 +                              {
 +                                      GLine* gl = new GLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
 +                                                      "*", them->GetIPString());
 +                                      if (ServerInstance->XLines->AddLine(gl,NULL))
 +                                      {
 +                                              std::string timestr = ServerInstance->TimeString(gl->expiry);
 +                                              ServerInstance->SNO->WriteGlobalSno('x',"G:line added due to DNSBL match on *@%s to expire on %s: %s",
 +                                                      them->GetIPString().c_str(), timestr.c_str(), reason.c_str());
 +                                              ServerInstance->XLines->ApplyLines();
 +                                      }
 +                                      else
 +                                              delete gl;
 +                                      break;
                                }
 -                              else
 -                                      ConfEntry->stats_misses++;
 +                              case DNSBLConfEntry::I_ZLINE:
 +                              {
 +                                      ZLine* zl = new ZLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
 +                                                      them->GetIPString());
 +                                      if (ServerInstance->XLines->AddLine(zl,NULL))
 +                                      {
 +                                              std::string timestr = ServerInstance->TimeString(zl->expiry);
 +                                              ServerInstance->SNO->WriteGlobalSno('x',"Z:line added due to DNSBL match on *@%s to expire on %s: %s",
 +                                                      them->GetIPString().c_str(), timestr.c_str(), reason.c_str());
 +                                              ServerInstance->XLines->ApplyLines();
 +                                      }
 +                                      else
 +                                              delete zl;
 +                                      break;
 +                              }
 +                              case DNSBLConfEntry::I_UNKNOWN:
 +                              default:
 +                                      break;
                        }
 -                      else
 -                              ConfEntry->stats_misses++;
 +
 +                      ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s%s detected as being on a DNS blacklist (%s) with result %d", them->nick.empty() ? "<unknown>" : "", them->GetFullRealHost().c_str(), ConfEntry->domain.c_str(), (ConfEntry->type==DNSBLConfEntry::A_BITMASK) ? bitmask : record);
                }
 +              else
 +                      ConfEntry->stats_misses++;
        }
  
 -      virtual void OnError(ResolverError e, const std::string &errormessage)
 +      void OnError(const DNS::Query *q) CXX11_OVERRIDE
        {
                LocalUser* them = (LocalUser*)ServerInstance->FindUUID(theiruid);
 -              if (them)
 -              {
 -                      int i = countExt.get(them);
 -                      if (i)
 -                              countExt.set(them, i - 1);
 -              }
 -      }
 +              if (!them)
 +                      return;
  
 -      virtual ~DNSBLResolver()
 -      {
 +              int i = countExt.get(them);
 +              if (i)
 +                      countExt.set(them, i - 1);
 +
 +              if (q->error == DNS::ERROR_NO_RECORDS || q->error == DNS::ERROR_DOMAIN_NOT_FOUND)
 +                      ConfEntry->stats_misses++;
        }
  };
  
  class ModuleDNSBL : public Module
  {
-       std::vector<DNSBLConfEntry *> DNSBLConfEntries;
+       std::vector<reference<DNSBLConfEntry> > DNSBLConfEntries;
 +      dynamic_reference<DNS::Manager> DNS;
        LocalStringExt nameExt;
        LocalIntExt countExt;
  
                return DNSBLConfEntry::I_UNKNOWN;
        }
   public:
 -      ModuleDNSBL() : nameExt("dnsbl_match", this), countExt("dnsbl_pending", this) { }
 +      ModuleDNSBL() : DNS(this, "DNS"), nameExt("dnsbl_match", this), countExt("dnsbl_pending", this) { }
  
 -      void init()
 +      void init() CXX11_OVERRIDE
        {
                ReadConf();
                ServerInstance->Modules->AddService(nameExt);
                ServerInstance->Modules->AddService(countExt);
 -              Implementation eventlist[] = { I_OnRehash, I_OnSetUserIP, I_OnStats, I_OnSetConnectClass, I_OnCheckReady };
 -              ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
  
-       ~ModuleDNSBL()
-       {
-               ClearEntries();
-       }
 -      Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides handling of DNS blacklists", VF_VENDOR);
        }
  
-       /** Clear entries and free the mem it was using
-        */
-       void ClearEntries()
-       {
-               for (std::vector<DNSBLConfEntry *>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++)
-                       delete *i;
-               DNSBLConfEntries.clear();
-       }
        /** Fill our conf vector with data
         */
        void ReadConf()
        {
-               ClearEntries();
+               DNSBLConfEntries.clear();
  
                ConfigTagList dnsbls = ServerInstance->Config->ConfTags("dnsbl");
                for(ConfigIter i = dnsbls.first; i != dnsbls.second; ++i)
                {
                        ConfigTag* tag = i->second;
-                       DNSBLConfEntry *e = new DNSBLConfEntry();
+                       reference<DNSBLConfEntry> e = new DNSBLConfEntry();
  
                        e->name = tag->getString("name");
                        e->ident = tag->getString("ident");
                        }
  
                        e->banaction = str2banaction(tag->getString("action"));
 -                      e->duration = ServerInstance->Duration(tag->getString("duration", "60"));
 +                      e->duration = tag->getDuration("duration", 60, 1);
  
                        /* Use portparser for record replies */
  
                                std::string location = tag->getTagLocation();
                                ServerInstance->SNO->WriteGlobalSno('a', "DNSBL(%s): Invalid banaction", location.c_str());
                        }
 -                      else if (e->duration <= 0)
 -                      {
 -                              std::string location = tag->getTagLocation();
 -                              ServerInstance->SNO->WriteGlobalSno('a', "DNSBL(%s): Invalid duration", location.c_str());
 -                      }
                        else
                        {
                                if (e->reason.empty())
  
                                /* add it, all is ok */
                                DNSBLConfEntries.push_back(e);
-                               continue;
                        }
-                       /* delete and drop it, error somewhere */
-                       delete e;
                }
        }
  
 -      void OnRehash(User* user)
 +      void OnRehash(User* user) CXX11_OVERRIDE
        {
                ReadConf();
        }
  
 -      void OnSetUserIP(LocalUser* user)
 +      void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE
        {
 -              if ((user->exempt) || (user->client_sa.sa.sa_family != AF_INET))
 +              if ((user->exempt) || (user->client_sa.sa.sa_family != AF_INET) || !DNS)
                        return;
  
                if (user->MyClass)
                                return;
                }
                else
 -                      ServerInstance->Logs->Log("m_dnsbl", DEBUG, "User has no connect class in OnSetUserIP");
 -
 -              unsigned char a, b, c, d;
 -              char reversedipbuf[128];
 -              std::string reversedip;
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User has no connect class in OnSetUserIP");
  
 -              d = (unsigned char) (user->client_sa.in4.sin_addr.s_addr >> 24) & 0xFF;
 -              c = (unsigned char) (user->client_sa.in4.sin_addr.s_addr >> 16) & 0xFF;
 -              b = (unsigned char) (user->client_sa.in4.sin_addr.s_addr >> 8) & 0xFF;
 -              a = (unsigned char) user->client_sa.in4.sin_addr.s_addr & 0xFF;
 +              unsigned int a, b, c, d;
 +              d = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 24) & 0xFF;
 +              c = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 16) & 0xFF;
 +              b = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 8) & 0xFF;
 +              a = (unsigned int) user->client_sa.in4.sin_addr.s_addr & 0xFF;
  
 -              snprintf(reversedipbuf, 128, "%d.%d.%d.%d", d, c, b, a);
 -              reversedip = std::string(reversedipbuf);
 +              const std::string reversedip = ConvToStr(d) + "." + ConvToStr(c) + "." + ConvToStr(b) + "." + ConvToStr(a);
  
                countExt.set(user, DNSBLConfEntries.size());
  
                // For each DNSBL, we will run through this lookup
 -              unsigned int i = 0;
 -              while (i < DNSBLConfEntries.size())
 +              for (unsigned i = 0; i < DNSBLConfEntries.size(); ++i)
                {
                        // Fill hostname with a dnsbl style host (d.c.b.a.domain.tld)
                        std::string hostname = reversedip + "." + DNSBLConfEntries[i]->domain;
  
                        /* now we'd need to fire off lookups for `hostname'. */
 -                      bool cached;
 -                      DNSBLResolver *r = new DNSBLResolver(this, nameExt, countExt, hostname, user, DNSBLConfEntries[i], cached);
 -                      ServerInstance->AddResolver(r, cached);
 +                      DNSBLResolver *r = new DNSBLResolver(*this->DNS, this, nameExt, countExt, hostname, user, DNSBLConfEntries[i]);
 +                      try
 +                      {
 +                              this->DNS->Process(r);
 +                      }
 +                      catch (DNS::Exception &ex)
 +                      {
 +                              delete r;
 +                              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, std::string(ex.GetReason()));
 +                      }
 +
                        if (user->quitting)
                                break;
 -                      i++;
                }
        }
  
 -      ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass)
 +      ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
        {
                std::string dnsbl;
                if (!myclass->config->readString("dnsbl", dnsbl))
                        return MOD_RES_PASSTHRU;
                return MOD_RES_DENY;
        }
 -      
 -      ModResult OnCheckReady(LocalUser *user)
 +
 +      ModResult OnCheckReady(LocalUser *user) CXX11_OVERRIDE
        {
                if (countExt.get(user))
                        return MOD_RES_DENY;
                return MOD_RES_PASSTHRU;
        }
  
 -      ModResult OnStats(char symbol, User* user, string_list &results)
 +      ModResult OnStats(char symbol, User* user, string_list &results) CXX11_OVERRIDE
        {
                if (symbol != 'd')
                        return MOD_RES_PASSTHRU;
  
                unsigned long total_hits = 0, total_misses = 0;
  
-               for (std::vector<DNSBLConfEntry*>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++)
+               for (std::vector<reference<DNSBLConfEntry> >::const_iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); ++i)
                {
                        total_hits += (*i)->stats_hits;
                        total_misses += (*i)->stats_misses;
diff --combined src/modules/m_ircv3.cpp
index 59a69f6691ede180a2a43afa50e1ec8056618714,da42d823d2678a63712a4a05ba3de3416a882cd8..f46ae97b4de39a516692fc0490d92a5fc92106f7
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
 -/* $ModDesc: Provides support for extended-join, away-notify and account-notify CAP capabilities */
 -
  #include "inspircd.h"
 -#include "account.h"
 -#include "m_cap.h"
 +#include "modules/account.h"
 +#include "modules/cap.h"
  
  class ModuleIRCv3 : public Module
  {
@@@ -36,7 -38,7 +36,7 @@@
                UserChanList chans(user->chans);
  
                std::map<User*, bool> exceptions;
 -              FOREACH_MOD(I_OnBuildNeighborList, OnBuildNeighborList(user, chans, exceptions));
 +              FOREACH_MOD(OnBuildNeighborList, (user, chans, exceptions));
  
                // Send it to all local users who were explicitly marked as neighbours by modules and have the required ext
                for (std::map<User*, bool>::const_iterator i = exceptions.begin(); i != exceptions.end(); ++i)
        {
        }
  
 -      void init()
 +      void init() CXX11_OVERRIDE
        {
                OnRehash(NULL);
 -              Implementation eventlist[] = { I_OnUserJoin, I_OnPostJoin, I_OnSetAway, I_OnEvent, I_OnRehash };
 -              ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
        }
  
 -      void OnRehash(User* user)
 +      void OnRehash(User* user) CXX11_OVERRIDE
        {
                ConfigTag* conf = ServerInstance->Config->ConfValue("ircv3");
-               accountnotify = conf->getBool("accoutnotify", true);
 -              accountnotify = conf->getBool("accountnotify", conf->getBool("accoutnotify", true));
++              accountnotify = conf->getBool("accountnotify", true);
                awaynotify = conf->getBool("awaynotify", true);
                extendedjoin = conf->getBool("extendedjoin", true);
        }
  
 -      void OnEvent(Event& ev)
 +      void OnEvent(Event& ev) CXX11_OVERRIDE
        {
                if (awaynotify)
                        cap_awaynotify.HandleEvent(ev);
                }
        }
  
 -      void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
 +      void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE
        {
                // Remember who is not going to see the JOIN because of other modules
 -              if ((awaynotify) && (IS_AWAY(memb->user)))
 +              if ((awaynotify) && (memb->user->IsAway()))
                        last_excepts = excepts;
  
                if (!extendedjoin)
                }
        }
  
 -      ModResult OnSetAway(User* user, const std::string &awaymsg)
 +      ModResult OnSetAway(User* user, const std::string &awaymsg) CXX11_OVERRIDE
        {
                if (awaynotify)
                {
                return MOD_RES_PASSTHRU;
        }
  
 -      void OnPostJoin(Membership *memb)
 +      void OnPostJoin(Membership *memb) CXX11_OVERRIDE
        {
 -              if ((!awaynotify) || (!IS_AWAY(memb->user)))
 +              if ((!awaynotify) || (!memb->user->IsAway()))
                        return;
  
                std::string line = ":" + memb->user->GetFullHost() + " AWAY :" + memb->user->awaymsg;
                ServerInstance->Modules->SetPriority(this, I_OnUserJoin, PRIORITY_LAST);
        }
  
 -      Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for extended-join, away-notify and account-notify CAP capabilities", VF_VENDOR);
        }
index 2a3dff6ee764fd58e50e0d68f7547f5ecf760c7b,0a7dc8ed96b9b2d551fa03cac6b528da4531b50a..a19a184e08bb91d4cdfd05c15581ce99e7815e91
  
  
  #include "inspircd.h"
++#include "listmode.h"
 +#include <fstream>
  
 -/* $ModDesc: Provides support for channel mode +P to provide permanent channels */
  
 -// Not in a class due to circular dependancy hell.
 -static std::string permchannelsconf;
 -static bool WriteDatabase(Module* mod, bool save_listmodes)
+ struct ListModeData
+ {
+       std::string modes;
+       std::string params;
+ };
 +/** Handles the +P channel mode
 + */
 +class PermChannel : public ModeHandler
  {
 -      FILE *f;
 -
 -      if (permchannelsconf.empty())
 + public:
 +      PermChannel(Module* Creator)
 +              : ModeHandler(Creator, "permanent", 'P', PARAM_NONE, MODETYPE_CHANNEL)
        {
 -              // Fake success.
 -              return true;
 +              oper = true;
        }
  
 -      std::string tempname = permchannelsconf + ".tmp";
 +      ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding)
 +      {
 +              if (adding == channel->IsModeSet(this))
 +                      return MODEACTION_DENY;
 +
 +              channel->SetMode(this, adding);
 +              if (!adding)
 +                      channel->CheckDestroy();
 +
 +              return MODEACTION_ALLOW;
 +      }
 +};
  
- static bool WriteDatabase(PermChannel& permchanmode)
 +// Not in a class due to circular dependancy hell.
 +static std::string permchannelsconf;
++static bool WriteDatabase(PermChannel& permchanmode, Module* mod, bool save_listmodes)
 +{
++      ChanModeReference ban(mod, "ban");
        /*
         * We need to perform an atomic write so as not to fuck things up.
 -       * So, let's write to a temporary file, flush and sync the FD, then rename the file..
 -       *              -- w00t
 +       * So, let's write to a temporary file, flush it, then rename the file..
 +       *     -- w00t
         */
 -      f = fopen(tempname.c_str(), "w");
 -      if (!f)
 +      
 +      // If the user has not specified a configuration file then we don't write one.
 +      if (permchannelsconf.empty())
 +              return true;
 +
 +      std::string permchannelsnewconf = permchannelsconf + ".tmp";
 +      std::ofstream stream(permchannelsnewconf.c_str());
 +      if (!stream.is_open())
        {
 -              ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: Cannot create database! %s (%d)", strerror(errno), errno);
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot create database! %s (%d)", strerror(errno), errno);
                ServerInstance->SNO->WriteToSnoMask('a', "database: cannot create new db: %s (%d)", strerror(errno), errno);
                return false;
        }
 +      
 +      stream << "# This file is automatically generated by m_permchannels. Any changes will be overwritten." << std::endl
 +              << "<config format=\"xml\">" << std::endl;
  
 -      fputs("# Permchannels DB\n# This file is autogenerated; any changes will be overwritten!\n<config format=\"compat\">\n", f);
 -      // Now, let's write.
 -      std::string line;
        for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++)
        {
                Channel* chan = i->second;
 -              if (!chan->IsModeSet('P'))
 +              if (!chan->IsModeSet(permchanmode))
                        continue;
  
 -                      lm.modes = std::string(chan->bans.size(), 'b');
 -                      for (BanList::const_iterator j = chan->bans.begin(); j != chan->bans.end(); ++j)
 -                      {
 -                              lm.params += j->data;
 -                              lm.params += ' ';
 -                      }
+               std::string chanmodes = chan->ChanModes(true);
+               if (save_listmodes)
+               {
+                       ListModeData lm;
+                       // Bans are managed by the core, so we have to process them separately
 -                      FOREACH_MOD(I_OnSyncChannel, OnSyncChannel(chan, mod, &lm));
++                      static_cast<ListModeBase*>(*ban)->DoSyncChannel(chan, mod, &lm);
+                       // All other listmodes are managed by modules, so we need to ask them (call their
+                       // OnSyncChannel() handler) to give our ProtoSendMode() a list of modes that are
+                       // set on the channel. The ListModeData struct is passed as an opaque pointer
+                       // that will be passed back to us by the module handling the mode.
 -              std::string chants = ConvToStr(chan->age);
 -              std::string topicts = ConvToStr(chan->topicset);
 -              const char* items[] =
 -              {
 -                      "<permchannels channel=",
 -                      chan->name.c_str(),
 -                      " ts=",
 -                      chants.c_str(),
 -                      " topic=",
 -                      chan->topic.c_str(),
 -                      " topicts=",
 -                      topicts.c_str(),
 -                      " topicsetby=",
 -                      chan->setby.c_str(),
 -                      " modes=",
 -                      chanmodes.c_str(),
 -                      ">\n"
 -              };
 -
 -              line.clear();
 -              int item = 0, ipos = 0;
 -              while (item < 13)
 -              {
 -                      char c = items[item][ipos++];
 -                      if (c == 0)
 -                      {
 -                              // end of this string; hop to next string, insert a quote
 -                              item++;
 -                              ipos = 0;
 -                              c = '"';
 -                      }
 -                      else if (c == '\\' || c == '"')
 -                      {
 -                              line += '\\';
 -                      }
 -                      line += c;
 -              }
 -
 -              // Erase last '"'
 -              line.erase(line.end()-1);
 -              fputs(line.c_str(), f);
++                      FOREACH_MOD(OnSyncChannel, (chan, mod, &lm));
+                       if (!lm.modes.empty())
+                       {
+                               // Remove the last space
+                               lm.params.erase(lm.params.end()-1);
+                               // If there is at least a space in chanmodes (that is, a non-listmode has a parameter)
+                               // insert the listmode mode letters before the space. Otherwise just append them.
+                               std::string::size_type p = chanmodes.find(' ');
+                               if (p == std::string::npos)
+                                       chanmodes += lm.modes;
+                               else
+                                       chanmodes.insert(p, lm.modes);
+                               // Append the listmode parameters (the masks themselves)
+                               chanmodes += ' ';
+                               chanmodes += lm.params;
+                       }
+               }
-                       << "\" modes=\"" << ServerConfig::Escape(chan->ChanModes(true))
 +              stream << "<permchannels channel=\"" << ServerConfig::Escape(chan->name)
++                      << "\" ts=\"" << chan->age
 +                      << "\" topic=\"" << ServerConfig::Escape(chan->topic)
++                      << "\" topicts=\"" << chan->topicset
++                      << "\" topicsetby=\"" << ServerConfig::Escape(chan->setby)
++                      << "\" modes=\"" << ServerConfig::Escape(chanmodes)
 +                      << "\">" << std::endl;
        }
  
 -      int write_error = 0;
 -      write_error = ferror(f);
 -      write_error |= fclose(f);
 -      if (write_error)
 +      if (stream.fail())
        {
 -              ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: Cannot write to new database! %s (%d)", strerror(errno), errno);
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot write to new database! %s (%d)", strerror(errno), errno);
                ServerInstance->SNO->WriteToSnoMask('a', "database: cannot write to new db: %s (%d)", strerror(errno), errno);
                return false;
        }
 +      stream.close();
  
  #ifdef _WIN32
        if (remove(permchannelsconf.c_str()))
        {
 -              ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: Cannot remove old database! %s (%d)", strerror(errno), errno);
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot remove old database! %s (%d)", strerror(errno), errno);
                ServerInstance->SNO->WriteToSnoMask('a', "database: cannot remove old database: %s (%d)", strerror(errno), errno);
                return false;
        }
  #endif
        // Use rename to move temporary to new db - this is guarenteed not to fuck up, even in case of a crash.
 -      if (rename(tempname.c_str(), permchannelsconf.c_str()) < 0)
 +      if (rename(permchannelsnewconf.c_str(), permchannelsconf.c_str()) < 0)
        {
 -              ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: Cannot move new to old database! %s (%d)", strerror(errno), errno);
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot move new to old database! %s (%d)", strerror(errno), errno);
                ServerInstance->SNO->WriteToSnoMask('a', "database: cannot replace old with new db: %s (%d)", strerror(errno), errno);
                return false;
        }
        return true;
  }
  
 -
 -
 -/** Handles the +P channel mode
 - */
 -class PermChannel : public ModeHandler
 -{
 - public:
 -      PermChannel(Module* Creator) : ModeHandler(Creator, "permanent", 'P', PARAM_NONE, MODETYPE_CHANNEL) { oper = true; }
 -
 -      ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
 -      {
 -              if (adding)
 -              {
 -                      if (!channel->IsModeSet('P'))
 -                      {
 -                              channel->SetMode('P',true);
 -                              return MODEACTION_ALLOW;
 -                      }
 -              }
 -              else
 -              {
 -                      if (channel->IsModeSet('P'))
 -                      {
 -                              channel->SetMode(this,false);
 -                              if (channel->GetUserCounter() == 0)
 -                              {
 -                                      channel->DelUser(ServerInstance->FakeClient);
 -                              }
 -                              return MODEACTION_ALLOW;
 -                      }
 -              }
 -
 -              return MODEACTION_DENY;
 -      }
 -};
 -
  class ModulePermanentChannels : public Module
  {
        PermChannel p;
        bool dirty;
+       bool save_listmodes;
  public:
  
        ModulePermanentChannels() : p(this), dirty(false)
        {
        }
  
 -      void init()
 +      void init() CXX11_OVERRIDE
        {
                ServerInstance->Modules->AddService(p);
 -              Implementation eventlist[] = { I_OnChannelPreDelete, I_OnPostTopicChange, I_OnRawMode, I_OnRehash, I_OnBackgroundTimer };
 -              ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
  
                OnRehash(NULL);
        }
                        {
                                chan_hash::iterator at = iter;
                                iter++;
 -                              FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(c));
 +                              FOREACH_MOD(OnChannelDelete, (c));
                                ServerInstance->chanlist->erase(at);
                                ServerInstance->GlobalCulls.AddItem(c);
                        }
                return Module::cull();
        }
  
 -      virtual void OnRehash(User *user)
 +      void OnRehash(User *user) CXX11_OVERRIDE
        {
-               permchannelsconf = ServerInstance->Config->ConfValue("permchanneldb")->getString("filename");
+               ConfigTag* tag = ServerInstance->Config->ConfValue("permchanneldb");
+               permchannelsconf = tag->getString("filename");
+               save_listmodes = tag->getBool("listmodes");
        }
  
        void LoadDatabase()
                {
                        ConfigTag* tag = i->second;
                        std::string channel = tag->getString("channel");
--                      std::string topic = tag->getString("topic");
                        std::string modes = tag->getString("modes");
  
-                       if (channel.empty())
+                       if ((channel.empty()) || (channel.length() > ServerInstance->Config->Limits.ChanMax))
                        {
-                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Malformed permchannels tag with empty channel name.");
 -                              ServerInstance->Logs->Log("m_permchannels", DEFAULT, "Ignoring permchannels tag with empty or too long channel name (\"" + channel + "\")");
++                              ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring permchannels tag with empty or too long channel name (\"" + channel + "\")");
                                continue;
                        }
  
  
                        if (!c)
                        {
-                               c = new Channel(channel, ServerInstance->Time());
-                               if (!topic.empty())
 -                              time_t TS = tag->getInt("ts");
 -                              c = new Channel(channel, ((TS > 0) ? TS : ServerInstance->Time()));
++                              time_t TS = tag->getInt("ts", ServerInstance->Time(), 1);
++                              c = new Channel(channel, TS);
 -                              c->SetTopic(NULL, topic, true);
 -                              c->setby = tag->getString("topicsetby");
 -                              if (c->setby.empty())
 -                                      c->setby = ServerInstance->Config->ServerName;
+                               unsigned int topicset = tag->getInt("topicts");
 -                              // SetTopic() sets the topic TS to now, if there was no topicts saved then don't overwrite that with a 0
 -                              if (topicset > 0)
++                              c->topic = tag->getString("topic");
++
++                              if ((topicset != 0) || (!c->topic.empty()))
 +                              {
-                                       c->SetTopic(ServerInstance->FakeClient, topic);
-                                       /*
-                                        * Due to the way protocol works in 1.2, we need to hack the topic TS in such a way that this
-                                        * topic will always win over others.
-                                        *
-                                        * This is scheduled for (proper) fixing in a later release, and can be removed at a later date.
-                                        */
-                                       c->topicset = 42;
++                                      if (topicset == 0)
++                                              topicset = ServerInstance->Time();
+                                       c->topicset = topicset;
++                                      c->setby = tag->getString("topicsetby");
++                                      if (c->setby.empty())
++                                              c->setby = ServerInstance->Config->ServerName;
 +                              }
-                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Added %s with topic %s", channel.c_str(), topic.c_str());
 -                              ServerInstance->Logs->Log("m_permchannels", DEBUG, "Added %s with topic %s", channel.c_str(), topic.c_str());
++                              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Added %s with topic %s", channel.c_str(), c->topic.c_str());
  
                                if (modes.empty())
                                        continue;
                }
        }
  
 -      virtual ModResult OnRawMode(User* user, Channel* chan, const char mode, const std::string &param, bool adding, int pcnt)
 +      ModResult OnRawMode(User* user, Channel* chan, const char mode, const std::string &param, bool adding, int pcnt) CXX11_OVERRIDE
        {
 -              if (chan && (chan->IsModeSet('P') || mode == 'P'))
 +              if (chan && (chan->IsModeSet(p) || mode == p.GetModeChar()))
                        dirty = true;
  
                return MOD_RES_PASSTHRU;
        }
  
 -      virtual void OnPostTopicChange(User*, Channel *c, const std::string&)
 +      void OnPostTopicChange(User*, Channel *c, const std::string&) CXX11_OVERRIDE
        {
 -              if (c->IsModeSet('P'))
 +              if (c->IsModeSet(p))
                        dirty = true;
        }
  
 -      void OnBackgroundTimer(time_t)
 +      void OnBackgroundTimer(time_t) CXX11_OVERRIDE
        {
                if (dirty)
-                       WriteDatabase(p);
 -                      WriteDatabase(this, save_listmodes);
++                      WriteDatabase(p, this, save_listmodes);
                dirty = false;
        }
  
                // Load only when there are no linked servers - we set the TS of the channels we
                // create to the current time, this can lead to desync because spanningtree has
                // no way of knowing what we do
 -              ProtoServerList serverlist;
 +              ProtocolInterface::ServerList serverlist;
                ServerInstance->PI->GetServerList(serverlist);
                if (serverlist.size() < 2)
                {
                        }
                        catch (CoreException& e)
                        {
 -                              ServerInstance->Logs->Log("m_permchannels", DEFAULT, "Error loading permchannels database: " + std::string(e.GetReason()));
 +                              ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error loading permchannels database: " + std::string(e.GetReason()));
                        }
                }
        }
  
 -      virtual Version GetVersion()
+       void ProtoSendMode(void* opaque, TargetTypeFlags type, void* target, const std::vector<std::string>& modes, const std::vector<TranslateType>& translate)
+       {
+               // We never pass an empty modelist but better be sure
+               if (modes.empty())
+                       return;
+               ListModeData* lm = static_cast<ListModeData*>(opaque);
+               // Append the mode letters without the trailing '+' (for example "IIII", "gg")
+               lm->modes.append(modes[0].begin()+1, modes[0].end());
+               // Append the parameters
+               for (std::vector<std::string>::const_iterator i = modes.begin()+1; i != modes.end(); ++i)
+               {
+                       lm->params += *i;
+                       lm->params += ' ';
+               }
+       }
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for channel mode +P to provide permanent channels",VF_VENDOR);
        }
  
 -      virtual ModResult OnChannelPreDelete(Channel *c)
 +      ModResult OnChannelPreDelete(Channel *c) CXX11_OVERRIDE
        {
 -              if (c->IsModeSet('P'))
 +              if (c->IsModeSet(p))
                        return MOD_RES_DENY;
  
                return MOD_RES_PASSTHRU;
index 93320757c7e8249c4fae49dac12e37b6ff87f0e3,ee18c8e8742dd02021ce2c978a447e8fb8ef1f51..0fb4468772bb83ea62bc2d58f3e66d59c4ab1ff4
  #include "treesocket.h"
  
  /** 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> :[[modes,]<uuid> [[modes,]<uuid> ... ]]
 +       * The last parameter is a list consisting of zero or more (modelist, uuid)
 +       * pairs (permanent channels may have zero users). The mode list for each
 +       * user 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.
 +       *
         */
  
 -      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 */
 -
 -      TreeSocket* src_socket = Utils->FindServer(srcuser->server)->GetRoute()->GetSocket();
 -
 +      time_t TS = ConvToInt(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->Logs->Log(MODNAME, LOG_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;
        }
  
 -      if (created)
 +      const std::string& channel = params[0];
 +      Channel* chan = ServerInstance->FindChan(channel);
 +      bool apply_other_sides_modes = true;
 +
 +      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)
 +                      /* If our TS is less than theirs, we dont accept their modes */
 +                      if (ourTS < TS)
                        {
 -                              chan = new Channel(channel, TS);
 +                              apply_other_sides_modes = false;
 +                      }
 +                      else if (ourTS > TS)
 +                      {
 +                              /* Our TS greater than theirs, clear all our modes from the channel, accept theirs. */
 +                              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();
 +
 +                              CommandFJoin::RemoveStatus(chan);
 +
 +                              // 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 */
        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;
+                       std::string modeparam;
+                       if ((paramit != lastparamit) && (mh->GetNumParams(true)))
+                       {
+                               modeparam = *paramit;
+                               ++paramit;
+                       }
+                       stack.Push(*i, modeparam);
+               }
                std::vector<std::string> modelist;
-               modelist.push_back(channel);
  
-               /* Remember, params[params.size() - 1] is userlist, and we don't want to apply *that* */
-               modelist.insert(modelist.end(), params.begin()+2, params.end()-1);
-               ServerInstance->Modes->Process(modelist, srcuser, ModeParser::MODE_LOCALONLY | ModeParser::MODE_MERGE);
+               // Mode parser needs to know what channel to act on.
+               modelist.push_back(params[0]);
+               while (stack.GetStackedLine(modelist))
+               {
 -                      ServerInstance->Modes->Process(modelist, srcuser, true);
++                      ServerInstance->Modes->Process(modelist, srcuser, ModeParser::MODE_LOCALONLY | ModeParser::MODE_MERGE);
+                       modelist.erase(modelist.begin() + 1, modelist.end());
+               }
 -
 -              ServerInstance->Modes->Process(modelist, srcuser, true);
        }
  
 -      /* Now, process every 'modes,nick' pair */
 +      irc::modestacker modestack(true);
 +      TreeSocket* src_socket = Utils->FindServer(srcuser->server)->GetSocket();
 +
 +      /* Now, process every 'modes,uuid' pair */
 +      irc::tokenstream users(*params.rbegin());
 +      std::string item;
 +      irc::modestacker* modestackptr = (apply_other_sides_modes ? &modestack : NULL);
        while (users.GetToken(item))
        {
 -              const char* usr = item.c_str();
 -              if (usr && *usr)
 -              {
 -                      const char* unparsedmodes = usr;
 -                      std::string modes;
 -
 -
 -                      /* 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;
 -                              }
 -
 -                              modes += *unparsedmodes;
 -                              usr++;
 -                              unparsedmodes++;
 -                      }
 +              if (!ProcessModeUUIDPair(item, src_socket, chan, modestackptr))
 +                      return CMD_INVALID;
 +      }
  
 -                      /* Advance past the comma, to the nick */
 -                      usr++;
 +      /* Flush mode stacker if we lost the FJOIN or had equal TS */
 +      if (apply_other_sides_modes)
 +              CommandFJoin::ApplyModeStack(srcuser, chan, modestack);
  
 -                      /* 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;
 +      return CMD_SUCCESS;
 +}
  
 -                              /* 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);
 +bool CommandFJoin::ProcessModeUUIDPair(const std::string& item, TreeSocket* src_socket, Channel* chan, irc::modestacker* modestack)
 +{
 +      std::string::size_type comma = item.find(',');
  
 -                              Channel::JoinUser(who, channel.c_str(), true, "", route_back_again->bursting, TS);
 -                      }
 -                      else
 -                      {
 -                              ServerInstance->Logs->Log("m_spanningtree",SPARSE, "Ignored nonexistant user %s in fjoin to %s (probably quit?)", usr, channel.c_str());
 -                              continue;
 -                      }
 -              }
 +      // Comma not required anymore if the user has no modes
 +      std::string uuid = ((comma == std::string::npos) ? item : item.substr(comma+1));
 +      User* who = ServerInstance->FindUUID(uuid);
 +      if (!who)
 +      {
 +              // Probably KILLed, ignore
 +              return true;
        }
  
 -      /* Flush mode stacker if we lost the FJOIN or had equal TS */
 -      if (apply_other_sides_modes)
 +      /* 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))
        {
 -              parameterlist stackresult;
 -              stackresult.push_back(channel);
 +              return true;
 +      }
  
 -              while (modestack.GetStackedLine(stackresult))
 +      /* Check if the user received at least one mode */
 +      if ((modestack) && (comma > 0) && (comma != std::string::npos))
 +      {
 +              /* Iterate through the modes and see if they are valid here, if so, apply */
 +              std::string::const_iterator commait = item.begin()+comma;
 +              for (std::string::const_iterator i = item.begin(); i != commait; ++i)
                {
 -                      ServerInstance->SendMode(stackresult, srcuser);
 -                      stackresult.erase(stackresult.begin() + 1, stackresult.end());
 +                      if (!ServerInstance->Modes->FindMode(*i, MODETYPE_CHANNEL))
 +                      {
 +                              ServerInstance->SNO->WriteToSnoMask('d', "Unrecognised mode '%c' for a user in FJOIN, dropping link", *i);
 +                              return false;
 +                      }
 +
 +                      /* Add any modes this user had to the mode stack */
 +                      modestack->Push(*i, who->nick);
                }
        }
 -      return CMD_SUCCESS;
 +
 +      chan->ForceJoin(who, NULL, route_back_again->bursting);
 +      return true;
  }
  
 -void CommandFJoin::RemoveStatus(User* srcuser, parameterlist &params)
 +void CommandFJoin::RemoveStatus(Channel* c)
  {
 -      if (params.size() < 1)
 -              return;
 -
 -      Channel* c = ServerInstance->FindChan(params[0]);
 +      irc::modestacker stack(false);
  
 -      if (c)
 +      for (char modeletter = 'A'; modeletter <= 'z'; ++modeletter)
        {
 -              irc::modestacker stack(false);
 -              parameterlist stackresult;
 -              stackresult.push_back(c->name);
 +              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);
 +      }
  
 -              for (char modeletter = 'A'; modeletter <= 'z'; ++modeletter)
 -              {
 -                      ModeHandler* mh = ServerInstance->Modes->FindMode(modeletter, MODETYPE_CHANNEL);
 +      ApplyModeStack(ServerInstance->FakeClient, c, stack);
 +}
  
 -                      /* 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::ApplyModeStack(User* srcuser, Channel* c, irc::modestacker& stack)
 +{
 +      parameterlist stackresult;
 +      stackresult.push_back(c->name);
  
 -              while (stack.GetStackedLine(stackresult))
 -              {
 -                      ServerInstance->SendMode(stackresult, srcuser);
 -                      stackresult.erase(stackresult.begin() + 1, stackresult.end());
 -              }
 +      while (stack.GetStackedLine(stackresult))
 +      {
 +              ServerInstance->Modes->Process(stackresult, srcuser, ModeParser::MODE_LOCALONLY);
 +              stackresult.erase(stackresult.begin() + 1, stackresult.end());
        }
  }
 -
index 98f9a304bb2e7f36f881813b545b11ceef7736cd,ce1792a02b6717a89a3939cafcb9b23fb57746f9..9ece3c03dcf4e1b90ff94b766f76a9eb8be07050
   */
  
  
 -/* $ModDesc: Provides a spanning tree server link protocol */
 -
  #include "inspircd.h"
  #include "socket.h"
  #include "xline.h"
 +#include "iohook.h"
  
 -#include "cachetimer.h"
  #include "resolvers.h"
  #include "main.h"
  #include "utils.h"
  #include "protocolinterface.h"
  
  ModuleSpanningTree::ModuleSpanningTree()
 +      : commands(NULL), DNS(this, "DNS")
  {
 -      Utils = new SpanningTreeUtilities(this);
 -      commands = new SpanningTreeCommands(this);
 -      RefreshTimer = NULL;
  }
  
  SpanningTreeCommands::SpanningTreeCommands(ModuleSpanningTree* module)
 -      : rconnect(module, module->Utils), rsquit(module, module->Utils),
 +      : rconnect(module), rsquit(module), map(module),
        svsjoin(module), svspart(module), svsnick(module), metadata(module),
 -      uid(module), opertype(module), fjoin(module), fmode(module), ftopic(module),
 -      fhost(module), fident(module), fname(module)
 +      uid(module), opertype(module), fjoin(module), ijoin(module), resync(module),
 +      fmode(module), ftopic(module), fhost(module), fident(module), fname(module),
 +      away(module), addline(module), delline(module), encap(module), idle(module),
 +      nick(module), ping(module), pong(module), push(module), save(module),
 +      server(module), squit(module), snonotice(module), version(module),
 +      burst(module), endburst(module)
  {
  }
  
  void ModuleSpanningTree::init()
  {
 +      ServerInstance->SNO->EnableSnomask('l', "LINK");
 +
 +      Utils = new SpanningTreeUtilities(this);
 +      Utils->TreeRoot = new TreeServer;
 +      commands = new SpanningTreeCommands(this);
        ServerInstance->Modules->AddService(commands->rconnect);
        ServerInstance->Modules->AddService(commands->rsquit);
 -      ServerInstance->Modules->AddService(commands->svsjoin);
 -      ServerInstance->Modules->AddService(commands->svspart);
 -      ServerInstance->Modules->AddService(commands->svsnick);
 -      ServerInstance->Modules->AddService(commands->metadata);
 -      ServerInstance->Modules->AddService(commands->uid);
 -      ServerInstance->Modules->AddService(commands->opertype);
 -      ServerInstance->Modules->AddService(commands->fjoin);
 -      ServerInstance->Modules->AddService(commands->fmode);
 -      ServerInstance->Modules->AddService(commands->ftopic);
 -      ServerInstance->Modules->AddService(commands->fhost);
 -      ServerInstance->Modules->AddService(commands->fident);
 -      ServerInstance->Modules->AddService(commands->fname);
 -      RefreshTimer = new CacheRefreshTimer(Utils);
 -      ServerInstance->Timers->AddTimer(RefreshTimer);
 -
 -      Implementation eventlist[] =
 -      {
 -              I_OnPreCommand, I_OnGetServerDescription, I_OnUserInvite, I_OnPostTopicChange,
 -              I_OnWallops, I_OnUserNotice, I_OnUserMessage, I_OnBackgroundTimer, I_OnUserJoin,
 -              I_OnChangeHost, I_OnChangeName, I_OnChangeIdent, I_OnUserPart, I_OnUnloadModule,
 -              I_OnUserQuit, I_OnUserPostNick, I_OnUserKick, I_OnRemoteKill, I_OnRehash, I_OnPreRehash,
 -              I_OnOper, I_OnAddLine, I_OnDelLine, I_OnMode, I_OnLoadModule, I_OnStats,
 -              I_OnSetAway, I_OnPostCommand, I_OnUserConnect, I_OnAcceptConnection
 -      };
 -      ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
 +      ServerInstance->Modules->AddService(commands->map);
  
        delete ServerInstance->PI;
 -      ServerInstance->PI = new SpanningTreeProtocolInterface(Utils);
 +      ServerInstance->PI = new SpanningTreeProtocolInterface;
        loopCall = false;
  
        // update our local user count
 -      Utils->TreeRoot->SetUserCount(ServerInstance->Users->local_users.size());
 +      Utils->TreeRoot->UserCount = ServerInstance->Users->local_users.size();
  }
  
  void ModuleSpanningTree::ShowLinks(TreeServer* Current, User* user, int hops)
        {
                Parent = Current->GetParent()->GetName();
        }
 -      for (unsigned int q = 0; q < Current->ChildCount(); q++)
 +
 +      const TreeServer::ChildServers& children = Current->GetChildren();
 +      for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i)
        {
 -              if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName()))))
 +              TreeServer* server = *i;
 +              if ((server->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(server->GetName()))))
                {
 -                      if (IS_OPER(user))
 +                      if (user->IsOper())
                        {
 -                               ShowLinks(Current->GetChild(q),user,hops+1);
 +                               ShowLinks(server, user, hops+1);
                        }
                }
                else
                {
 -                      ShowLinks(Current->GetChild(q),user,hops+1);
 +                      ShowLinks(server, user, hops+1);
                }
        }
        /* Don't display the line if its a uline, hide ulines is on, and the user isnt an oper */
 -      if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetName())) && (!IS_OPER(user)))
 +      if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetName())) && (!user->IsOper()))
                return;
        /* Or if the server is hidden and they're not an oper */
 -      else if ((Current->Hidden) && (!IS_OPER(user)))
 +      else if ((Current->Hidden) && (!user->IsOper()))
                return;
  
 -      std::string servername = Current->GetName();
 -      user->WriteNumeric(364, "%s %s %s :%d %s",      user->nick.c_str(), servername.c_str(),
 -                      (Utils->FlatLinks && (!IS_OPER(user))) ? ServerInstance->Config->ServerName.c_str() : Parent.c_str(),
 -                      (Utils->FlatLinks && (!IS_OPER(user))) ? 0 : hops,
 +      user->WriteNumeric(364, "%s %s %s :%d %s",      user->nick.c_str(), Current->GetName().c_str(),
 +                      (Utils->FlatLinks && (!user->IsOper())) ? ServerInstance->Config->ServerName.c_str() : Parent.c_str(),
 +                      (Utils->FlatLinks && (!user->IsOper())) ? 0 : hops,
                        Current->GetDesc().c_str());
  }
  
 -int ModuleSpanningTree::CountServs()
 -{
 -      return Utils->serverlist.size();
 -}
 -
  void ModuleSpanningTree::HandleLinks(const std::vector<std::string>& parameters, User* user)
  {
        ShowLinks(Utils->TreeRoot,user,0);
        user->WriteNumeric(365, "%s * :End of /LINKS list.",user->nick.c_str());
 -      return;
  }
  
  std::string ModuleSpanningTree::TimeToStr(time_t secs)
@@@ -141,53 -164,62 +141,53 @@@ restart
        {
                TreeServer *s = i->second;
  
 -              if (s->GetSocket() && s->GetSocket()->GetLinkState() == DYING)
 +              // Skip myself
 +              if (s->IsRoot())
 +                      continue;
 +
 +              if (s->GetSocket()->GetLinkState() == DYING)
                {
                        s->GetSocket()->Close();
                        goto restart;
                }
  
 -              // Fix for bug #792, do not ping servers that are not connected yet!
 -              // Remote servers have Socket == NULL and local connected servers have
 -              // Socket->LinkState == CONNECTED
 -              if (s->GetSocket() && s->GetSocket()->GetLinkState() != CONNECTED)
 +              // Do not ping servers that are not fully connected yet!
 +              // Servers which are connected to us have IsLocal() == true and if they're fully connected
 +              // then Socket->LinkState == CONNECTED. Servers that are linked to another server are always fully connected.
 +              if (s->IsLocal() && s->GetSocket()->GetLinkState() != CONNECTED)
                        continue;
  
                // Now do PING checks on all servers
 -              TreeServer *mts = Utils->BestRouteTo(s->GetID());
 -
 -              if (mts)
 +              // Only ping if this server needs one
 +              if (curtime >= s->NextPingTime())
                {
 -                      // Only ping if this server needs one
 -                      if (curtime >= s->NextPingTime())
 +                      // And if they answered the last
 +                      if (s->AnsweredLastPing())
                        {
 -                              // And if they answered the last
 -                              if (s->AnsweredLastPing())
 -                              {
 -                                      // They did, send a ping to them
 -                                      s->SetNextPingTime(curtime + Utils->PingFreq);
 -                                      TreeSocket *tsock = mts->GetSocket();
 -
 -                                      // ... if we can find a proper route to them
 -                                      if (tsock)
 -                                      {
 -                                              tsock->WriteLine(":" + ServerInstance->Config->GetSID() + " PING " +
 -                                                              ServerInstance->Config->GetSID() + " " + s->GetID());
 -                                              s->LastPingMsec = ts;
 -                                      }
 -                              }
 -                              else
 +                              // They did, send a ping to them
 +                              s->SetNextPingTime(curtime + Utils->PingFreq);
 +                              s->GetSocket()->WriteLine(":" + ServerInstance->Config->GetSID() + " PING " + s->GetID());
 +                              s->LastPingMsec = ts;
 +                      }
 +                      else
 +                      {
 +                              // They didn't answer the last ping, if they are locally connected, get rid of them.
 +                              if (s->IsLocal())
                                {
 -                                      // They didn't answer the last ping, if they are locally connected, get rid of them.
 -                                      TreeSocket *sock = s->GetSocket();
 -                                      if (sock)
 -                                      {
 -                                              sock->SendError("Ping timeout");
 -                                              sock->Close();
 -                                              goto restart;
 -                                      }
 +                                      TreeSocket* sock = s->GetSocket();
 +                                      sock->SendError("Ping timeout");
 +                                      sock->Close();
 +                                      goto restart;
                                }
                        }
 +              }
  
 -                      // If warn on ping enabled and not warned and the difference is sufficient and they didn't answer the last ping...
 -                      if ((Utils->PingWarnTime) && (!s->Warned) && (curtime >= s->NextPingTime() - (Utils->PingFreq - Utils->PingWarnTime)) && (!s->AnsweredLastPing()))
 -                      {
 -                              /* The server hasnt responded, send a warning to opers */
 -                              std::string servername = s->GetName();
 -                              ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", servername.c_str(), Utils->PingWarnTime);
 -                              s->Warned = true;
 -                      }
 +              // If warn on ping enabled and not warned and the difference is sufficient and they didn't answer the last ping...
 +              if ((Utils->PingWarnTime) && (!s->Warned) && (curtime >= s->NextPingTime() - (Utils->PingFreq - Utils->PingWarnTime)) && (!s->AnsweredLastPing()))
 +              {
 +                      /* The server hasnt responded, send a warning to opers */
 +                      ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", s->GetName().c_str(), Utils->PingWarnTime);
 +                      s->Warned = true;
                }
        }
  }
@@@ -239,7 -271,7 +239,7 @@@ void ModuleSpanningTree::ConnectServer(
                return;
        }
  
 -      QueryType start_type = DNS_QUERY_AAAA;
 +      DNS::QueryType start_type = DNS::QUERY_AAAA;
        if (strchr(x->IPAddr.c_str(),':'))
        {
                in6_addr n;
        if (ipvalid)
        {
                /* Gave a hook, but it wasnt one we know */
 -              TreeSocket* newsocket = new TreeSocket(Utils, x, y, x->IPAddr);
 +              TreeSocket* newsocket = new TreeSocket(x, y, x->IPAddr);
                if (newsocket->GetFd() > -1)
                {
                        /* Handled automatically on success */
                        ServerInstance->GlobalCulls.AddItem(newsocket);
                }
        }
 +      else if (!DNS)
 +      {
 +              ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: Hostname given and m_dns.so is not loaded, unable to resolve.", x->Name.c_str());
 +      }
        else
        {
 +              ServernameResolver* snr = new ServernameResolver(*DNS, x->IPAddr, x, start_type, y);
                try
                {
 -                      bool cached = false;
 -                      ServernameResolver* snr = new ServernameResolver(Utils, x->IPAddr, x, cached, start_type, y);
 -                      ServerInstance->AddResolver(snr, cached);
 +                      DNS->Process(snr);
                }
 -              catch (ModuleException& e)
 +              catch (DNS::Exception& e)
                {
 +                      delete snr;
                        ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason());
                        ConnectServer(y, false);
                }
@@@ -331,13 -359,12 +331,13 @@@ ModResult ModuleSpanningTree::HandleVer
        TreeServer* found = Utils->FindServerMask(parameters[0]);
        if (found)
        {
 -              std::string Version = found->GetVersion();
 -              user->WriteNumeric(351, "%s :%s",user->nick.c_str(),Version.c_str());
                if (found == Utils->TreeRoot)
                {
 -                      ServerInstance->Config->Send005(user);
 +                      // Pass to default VERSION handler.
 +                      return MOD_RES_PASSTHRU;
                }
 +              std::string Version = found->GetVersion();
 +              user->WriteNumeric(351, "%s :%s",user->nick.c_str(),Version.c_str());
        }
        else
        {
   */
  void ModuleSpanningTree::RemoteMessage(User* user, const char* format, ...)
  {
 -      char text[MAXBUF];
 -      va_list argsPtr;
 -
 -      va_start(argsPtr, format);
 -      vsnprintf(text, MAXBUF, format, argsPtr);
 -      va_end(argsPtr);
 +      std::string text;
 +      VAFORMAT(text, format, format);
  
        if (IS_LOCAL(user))
 -              user->WriteServ("NOTICE %s :%s", user->nick.c_str(), text);
 +              user->WriteNotice(text);
        else
                ServerInstance->PI->SendUserNotice(user, text);
  }
@@@ -381,7 -412,8 +381,7 @@@ ModResult ModuleSpanningTree::HandleCon
                        }
                        else
                        {
 -                              std::string servername = CheckDupe->GetParent()->GetName();
 -                              RemoteMessage(user, "*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002", x->Name.c_str(), servername.c_str());
 +                              RemoteMessage(user, "*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002", x->Name.c_str(), CheckDupe->GetParent()->GetName().c_str());
                                return MOD_RES_DENY;
                        }
                }
        return MOD_RES_DENY;
  }
  
 +void ModuleSpanningTree::On005Numeric(std::map<std::string, std::string>& tokens)
 +{
 +      tokens["MAP"];
 +}
 +
  void ModuleSpanningTree::OnGetServerDescription(const std::string &servername,std::string &description)
  {
        TreeServer* s = Utils->FindServer(servername);
@@@ -408,11 -435,11 +408,11 @@@ void ModuleSpanningTree::OnUserInvite(U
  {
        if (IS_LOCAL(source))
        {
 -              parameterlist params;
 +              CmdBuilder params(source, "INVITE");
                params.push_back(dest->uuid);
                params.push_back(channel->name);
                params.push_back(ConvToStr(expiry));
 -              Utils->DoOneToMany(source->uuid,"INVITE",params);
 +              params.Broadcast();
        }
  }
  
@@@ -422,37 -449,123 +422,37 @@@ void ModuleSpanningTree::OnPostTopicCha
        if (!IS_LOCAL(user))
                return;
  
 -      parameterlist params;
 -      params.push_back(chan->name);
 -      params.push_back(":"+topic);
 -      Utils->DoOneToMany(user->uuid,"TOPIC",params);
 +      CommandFTopic::Builder(user, chan).Broadcast();
  }
  
 -void ModuleSpanningTree::OnWallops(User* user, const std::string &text)
 +void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type, const std::string& text, char status, const CUList& exempt_list, MessageType msgtype)
  {
 -      if (IS_LOCAL(user))
 -      {
 -              parameterlist params;
 -              params.push_back(":"+text);
 -              Utils->DoOneToMany(user->uuid,"WALLOPS",params);
 -      }
 -}
 -
 -void ModuleSpanningTree::OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
 -{
 -      /* Server origin */
 -      if (user == NULL)
 -              return;
 -
 -      if (target_type == TYPE_USER)
 -      {
 -              User* d = (User*)dest;
 -              if (!IS_LOCAL(d) && IS_LOCAL(user))
 -              {
 -                      parameterlist params;
 -                      params.push_back(d->uuid);
 -                      params.push_back(":"+text);
 -                      Utils->DoOneToOne(user->uuid,"NOTICE",params,d->server);
 -              }
 -      }
 -      else if (target_type == TYPE_CHANNEL)
 -      {
 -              if (IS_LOCAL(user))
 -              {
 -                      Channel *c = (Channel*)dest;
 -                      if (c)
 -                      {
 -                              std::string cname = c->name;
 -                              if (status)
 -                                      cname = status + cname;
 -                              TreeServerList list;
 -                              Utils->GetListOfServersForChannel(c,list,status,exempt_list);
 -                              for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
 -                              {
 -                                      TreeSocket* Sock = i->second->GetSocket();
 -                                      if (Sock)
 -                                              Sock->WriteLine(":"+std::string(user->uuid)+" NOTICE "+cname+" :"+text);
 -                              }
 -                      }
 -              }
 -      }
 -      else if (target_type == TYPE_SERVER)
 -      {
 -              if (IS_LOCAL(user))
 -              {
 -                      char* target = (char*)dest;
 -                      parameterlist par;
 -                      par.push_back(target);
 -                      par.push_back(":"+text);
 -                      Utils->DoOneToMany(user->uuid,"NOTICE",par);
 -              }
 -      }
 -}
 -
 -void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
 -{
 -      /* Server origin */
 -      if (user == NULL)
 +      if (!IS_LOCAL(user))
                return;
  
 +      const char* message_type = (msgtype == MSG_PRIVMSG ? "PRIVMSG" : "NOTICE");
        if (target_type == TYPE_USER)
        {
 -              // route private messages which are targetted at clients only to the server
 -              // which needs to receive them
 -              User* d = (User*)dest;
 -              if (!IS_LOCAL(d) && (IS_LOCAL(user)))
 +              User* d = (User*) dest;
 +              if (!IS_LOCAL(d))
                {
 -                      parameterlist params;
 +                      CmdBuilder params(user, message_type);
                        params.push_back(d->uuid);
 -                      params.push_back(":"+text);
 -                      Utils->DoOneToOne(user->uuid,"PRIVMSG",params,d->server);
 +                      params.push_last(text);
 +                      params.Unicast(d);
                }
        }
        else if (target_type == TYPE_CHANNEL)
        {
 -              if (IS_LOCAL(user))
 -              {
 -                      Channel *c = (Channel*)dest;
 -                      if (c)
 -                      {
 -                              std::string cname = c->name;
 -                              if (status)
 -                                      cname = status + cname;
 -                              TreeServerList list;
 -                              Utils->GetListOfServersForChannel(c,list,status,exempt_list);
 -                              for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
 -                              {
 -                                      TreeSocket* Sock = i->second->GetSocket();
 -                                      if (Sock)
 -                                              Sock->WriteLine(":"+std::string(user->uuid)+" PRIVMSG "+cname+" :"+text);
 -                              }
 -                      }
 -              }
 +              Utils->SendChannelMessage(user->uuid, (Channel*)dest, text, status, exempt_list, message_type);
        }
        else if (target_type == TYPE_SERVER)
        {
 -              if (IS_LOCAL(user))
 -              {
 -                      char* target = (char*)dest;
 -                      parameterlist par;
 -                      par.push_back(target);
 -                      par.push_back(":"+text);
 -                      Utils->DoOneToMany(user->uuid,"PRIVMSG",par);
 -              }
 +              char* target = (char*) dest;
 +              CmdBuilder par(user, message_type);
 +              par.push_back(target);
 +              par.push_last(text);
 +              par.Broadcast();
        }
  }
  
@@@ -468,10 -581,25 +468,10 @@@ void ModuleSpanningTree::OnUserConnect(
        if (user->quitting)
                return;
  
 -      parameterlist params;
 -      params.push_back(user->uuid);
 -      params.push_back(ConvToStr(user->age));
 -      params.push_back(user->nick);
 -      params.push_back(user->host);
 -      params.push_back(user->dhost);
 -      params.push_back(user->ident);
 -      params.push_back(user->GetIPString());
 -      params.push_back(ConvToStr(user->signon));
 -      params.push_back("+"+std::string(user->FormatModes(true)));
 -      params.push_back(":"+user->fullname);
 -      Utils->DoOneToMany(ServerInstance->Config->GetSID(), "UID", params);
 +      CommandUID::Builder(user).Broadcast();
  
 -      if (IS_OPER(user))
 -      {
 -              params.clear();
 -              params.push_back(user->oper->name);
 -              Utils->DoOneToMany(user->uuid,"OPERTYPE",params);
 -      }
 +      if (user->IsOper())
 +              CommandOpertype::Builder(user).Broadcast();
  
        for(Extensible::ExtensibleStore::const_iterator i = user->GetExtList().begin(); i != user->GetExtList().end(); i++)
        {
                        ServerInstance->PI->SendMetaData(user, item->name, value);
        }
  
 -      Utils->TreeRoot->SetUserCount(1); // increment by 1
 +      Utils->TreeRoot->UserCount++;
  }
  
 -void ModuleSpanningTree::OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
 +void ModuleSpanningTree::OnUserJoin(Membership* memb, bool sync, bool created_by_local, CUList& excepts)
  {
        // Only do this for local users
 -      if (IS_LOCAL(memb->user))
 +      if (!IS_LOCAL(memb->user))
 +              return;
 +
 +      if (created_by_local)
        {
 -              parameterlist params;
 -              // set up their permissions and the channel TS with FJOIN.
 -              // All users are FJOINed now, because a module may specify
 -              // new joining permissions for the user.
 +              CmdBuilder params("FJOIN");
                params.push_back(memb->chan->name);
                params.push_back(ConvToStr(memb->chan->age));
 -              params.push_back(std::string("+") + memb->chan->ChanModes(true));
 -              params.push_back(memb->modes+","+memb->user->uuid);
 -              Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FJOIN",params);
 +              params.push_raw(" +").push_raw(memb->chan->ChanModes(true));
 +              params.push(memb->modes).push_raw(',').push_raw(memb->user->uuid);
 +              params.Broadcast();
 +      }
 +      else
 +      {
 +              CmdBuilder params(memb->user, "IJOIN");
 +              params.push_back(memb->chan->name);
 +              if (!memb->modes.empty())
 +              {
 +                      params.push_back(ConvToStr(memb->chan->age));
 +                      params.push_back(memb->modes);
 +              }
 +              params.Broadcast();
        }
  }
  
@@@ -517,7 -634,9 +517,7 @@@ void ModuleSpanningTree::OnChangeHost(U
        if (user->registered != REG_ALL || !IS_LOCAL(user))
                return;
  
 -      parameterlist params;
 -      params.push_back(newhost);
 -      Utils->DoOneToMany(user->uuid,"FHOST",params);
 +      CmdBuilder(user, "FHOST").push(newhost).Broadcast();
  }
  
  void ModuleSpanningTree::OnChangeName(User* user, const std::string &gecos)
        if (user->registered != REG_ALL || !IS_LOCAL(user))
                return;
  
 -      parameterlist params;
 -      params.push_back(gecos);
 -      Utils->DoOneToMany(user->uuid,"FNAME",params);
 +      CmdBuilder(user, "FNAME").push(gecos).Broadcast();
  }
  
  void ModuleSpanningTree::OnChangeIdent(User* user, const std::string &ident)
        if ((user->registered != REG_ALL) || (!IS_LOCAL(user)))
                return;
  
 -      parameterlist params;
 -      params.push_back(ident);
 -      Utils->DoOneToMany(user->uuid,"FIDENT",params);
 +      CmdBuilder(user, "FIDENT").push(ident).Broadcast();
  }
  
  void ModuleSpanningTree::OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts)
  {
        if (IS_LOCAL(memb->user))
        {
 -              parameterlist params;
 +              CmdBuilder params(memb->user, "PART");
                params.push_back(memb->chan->name);
                if (!partmessage.empty())
 -                      params.push_back(":"+partmessage);
 -              Utils->DoOneToMany(memb->user->uuid,"PART",params);
 +                      params.push_last(partmessage);
 +              params.Broadcast();
        }
  }
  
@@@ -552,17 -675,23 +552,17 @@@ void ModuleSpanningTree::OnUserQuit(Use
  {
        if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
        {
 -              parameterlist params;
 -
                if (oper_message != reason)
 -              {
 -                      params.push_back(":"+oper_message);
 -                      Utils->DoOneToMany(user->uuid,"OPERQUIT",params);
 -              }
 -              params.clear();
 -              params.push_back(":"+reason);
 -              Utils->DoOneToMany(user->uuid,"QUIT",params);
 +                      ServerInstance->PI->SendMetaData(user, "operquit", oper_message);
 +
 +              CmdBuilder(user, "QUIT").push_last(reason).Broadcast();
        }
  
        // Regardless, We need to modify the user Counts..
        TreeServer* SourceServer = Utils->FindServer(user->server);
        if (SourceServer)
        {
 -              SourceServer->SetUserCount(-1); // decrement by 1
 +              SourceServer->UserCount--;
        }
  }
  
@@@ -570,7 -699,7 +570,7 @@@ void ModuleSpanningTree::OnUserPostNick
  {
        if (IS_LOCAL(user))
        {
 -              parameterlist params;
 +              CmdBuilder params(user, "NICK");
                params.push_back(user->nick);
  
                /** IMPORTANT: We don't update the TS if the oldnick is just a case change of the newnick!
                        user->age = ServerInstance->Time();
  
                params.push_back(ConvToStr(user->age));
 -              Utils->DoOneToMany(user->uuid,"NICK",params);
 +              params.Broadcast();
        }
        else if (!loopCall && user->nick == user->uuid)
        {
 -              parameterlist params;
 +              CmdBuilder params("SAVE");
                params.push_back(user->uuid);
                params.push_back(ConvToStr(user->age));
 -              Utils->DoOneToMany(ServerInstance->Config->GetSID(),"SAVE",params);
 +              params.Broadcast();
        }
  }
  
  void ModuleSpanningTree::OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts)
  {
 -      parameterlist params;
 +      if ((!IS_LOCAL(source) || source != ServerInstance->FakeClient))
 +              return;
 +
 +      CmdBuilder params(source, "KICK");
        params.push_back(memb->chan->name);
        params.push_back(memb->user->uuid);
 -      params.push_back(":"+reason);
 -      if (IS_LOCAL(source))
 -      {
 -              Utils->DoOneToMany(source->uuid,"KICK",params);
 -      }
 -      else if (source == ServerInstance->FakeClient)
 -      {
 -              Utils->DoOneToMany(ServerInstance->Config->GetSID(),"KICK",params);
 -      }
 -}
 -
 -void ModuleSpanningTree::OnRemoteKill(User* source, User* dest, const std::string &reason, const std::string &operreason)
 -{
 -      if (!IS_LOCAL(source))
 -              return; // Only start routing if we're origin.
 -
 -      ServerInstance->OperQuit.set(dest, operreason);
 -      parameterlist params;
 -      params.push_back(":"+operreason);
 -      Utils->DoOneToMany(dest->uuid,"OPERQUIT",params);
 -      params.clear();
 -      params.push_back(dest->uuid);
 -      params.push_back(":"+reason);
 -      Utils->DoOneToMany(source->uuid,"KILL",params);
 +      params.push_last(reason);
 +      params.Broadcast();
  }
  
  void ModuleSpanningTree::OnPreRehash(User* user, const std::string &parameter)
        if (loopCall)
                return; // Don't generate a REHASH here if we're in the middle of processing a message that generated this one
  
 -      ServerInstance->Logs->Log("remoterehash", DEBUG, "called with param %s", parameter.c_str());
 +      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "OnPreRehash called with param %s", parameter.c_str());
  
        // Send out to other servers
        if (!parameter.empty() && parameter[0] != '-')
        {
 -              parameterlist params;
 +              CmdBuilder params((user ? user->uuid : ServerInstance->Config->GetSID()), "REHASH");
                params.push_back(parameter);
 -              Utils->DoOneToAllButSender(user ? user->uuid : ServerInstance->Config->GetSID(), "REHASH", params, user ? user->server : ServerInstance->Config->ServerName);
 +              params.Forward(user ? Utils->BestRouteTo(user->server) : NULL);
        }
  }
  
@@@ -654,21 -802,26 +654,28 @@@ void ModuleSpanningTree::OnLoadModule(M
  
  void ModuleSpanningTree::OnUnloadModule(Module* mod)
  {
 +      if (!Utils)
 +              return;
        ServerInstance->PI->SendMetaData(NULL, "modules", "-" + mod->ModuleSourceFile);
  
 -      unsigned int items = Utils->TreeRoot->ChildCount();
 -      for(unsigned int x = 0; x < items; x++)
 +      // Close all connections which use an IO hook provided by this module
 +      const TreeServer::ChildServers& list = Utils->TreeRoot->GetChildren();
 +      for (TreeServer::ChildServers::const_iterator i = list.begin(); i != list.end(); ++i)
        {
 -              TreeServer* srv = Utils->TreeRoot->GetChild(x);
 -              TreeSocket* sock = srv->GetSocket();
 -              if (sock && sock->GetIOHook() == mod)
 +              TreeSocket* sock = (*i)->GetSocket();
 +              if (sock && sock->GetIOHook() && sock->GetIOHook()->creator == mod)
                {
                        sock->SendError("SSL module unloaded");
                        sock->Close();
                }
        }
 -              if (sock->GetIOHook() == mod)
+       for (SpanningTreeUtilities::TimeoutList::const_iterator i = Utils->timeoutlist.begin(); i != Utils->timeoutlist.end(); ++i)
+       {
+               TreeSocket* sock = i->first;
++              if (sock->GetIOHook() && sock->GetIOHook()->creator == mod)
+                       sock->Close();
+       }
  }
  
  // note: the protocol does not allow direct umode +o except
@@@ -678,38 -831,96 +685,38 @@@ void ModuleSpanningTree::OnOper(User* u
  {
        if (user->registered != REG_ALL || !IS_LOCAL(user))
                return;
 -      parameterlist params;
 -      params.push_back(opertype);
 -      Utils->DoOneToMany(user->uuid,"OPERTYPE",params);
 +      CommandOpertype::Builder(user).Broadcast();
  }
  
  void ModuleSpanningTree::OnAddLine(User* user, XLine *x)
  {
 -      if (!x->IsBurstable() || loopCall)
 +      if (!x->IsBurstable() || loopCall || (user && !IS_LOCAL(user)))
                return;
  
 -      parameterlist params;
 -      params.push_back(x->type);
 -      params.push_back(x->Displayable());
 -      params.push_back(ServerInstance->Config->ServerName);
 -      params.push_back(ConvToStr(x->set_time));
 -      params.push_back(ConvToStr(x->duration));
 -      params.push_back(":" + x->reason);
 -
        if (!user)
 -      {
 -              /* Server-set lines */
 -              Utils->DoOneToMany(ServerInstance->Config->GetSID(), "ADDLINE", params);
 -      }
 -      else if (IS_LOCAL(user))
 -      {
 -              /* User-set lines */
 -              Utils->DoOneToMany(user->uuid, "ADDLINE", params);
 -      }
 +              user = ServerInstance->FakeClient;
 +
 +      CommandAddLine::Builder(x, user).Broadcast();
  }
  
  void ModuleSpanningTree::OnDelLine(User* user, XLine *x)
  {
 -      if (!x->IsBurstable() || loopCall)
 +      if (!x->IsBurstable() || loopCall || (user && !IS_LOCAL(user)))
                return;
  
 -      parameterlist params;
 -      params.push_back(x->type);
 -      params.push_back(x->Displayable());
 -
        if (!user)
 -      {
 -              /* Server-unset lines */
 -              Utils->DoOneToMany(ServerInstance->Config->GetSID(), "DELLINE", params);
 -      }
 -      else if (IS_LOCAL(user))
 -      {
 -              /* User-unset lines */
 -              Utils->DoOneToMany(user->uuid, "DELLINE", params);
 -      }
 -}
 +              user = ServerInstance->FakeClient;
  
 -void ModuleSpanningTree::OnMode(User* user, void* dest, int target_type, const parameterlist &text, const std::vector<TranslateType> &translate)
 -{
 -      if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
 -      {
 -              parameterlist params;
 -              std::string output_text;
 -
 -              ServerInstance->Parser->TranslateUIDs(translate, text, output_text);
 -
 -              if (target_type == TYPE_USER)
 -              {
 -                      User* u = (User*)dest;
 -                      params.push_back(u->uuid);
 -                      params.push_back(output_text);
 -                      Utils->DoOneToMany(user->uuid, "MODE", params);
 -              }
 -              else
 -              {
 -                      Channel* c = (Channel*)dest;
 -                      params.push_back(c->name);
 -                      params.push_back(ConvToStr(c->age));
 -                      params.push_back(output_text);
 -                      Utils->DoOneToMany(user->uuid, "FMODE", params);
 -              }
 -      }
 +      CmdBuilder params(user, "DELLINE");
 +      params.push_back(x->type);
 +      params.push_back(x->Displayable());
 +      params.Broadcast();
  }
  
  ModResult ModuleSpanningTree::OnSetAway(User* user, const std::string &awaymsg)
  {
        if (IS_LOCAL(user))
 -      {
 -              parameterlist params;
 -              if (!awaymsg.empty())
 -              {
 -                      params.push_back(ConvToStr(user->awaytime));
 -                      params.push_back(":" + awaymsg);
 -              }
 -              Utils->DoOneToMany(user->uuid, "AWAY", params);
 -      }
 +              CommandAway::Builder(user, awaymsg).Broadcast();
  
        return MOD_RES_PASSTHRU;
  }
  void ModuleSpanningTree::ProtoSendMode(void* opaque, TargetTypeFlags target_type, void* target, const parameterlist &modeline, const std::vector<TranslateType> &translate)
  {
        TreeSocket* s = (TreeSocket*)opaque;
 -      std::string output_text;
 -
 -      ServerInstance->Parser->TranslateUIDs(translate, modeline, output_text);
 +      std::string output_text = CommandParser::TranslateUIDs(translate, modeline);
  
        if (target)
        {
@@@ -742,15 -955,15 +749,15 @@@ void ModuleSpanningTree::ProtoSendMetaD
        if (u)
                s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA "+u->uuid+" "+extname+" :"+extdata);
        else if (c)
 -              s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA "+c->name+" "+extname+" :"+extdata);
 +              s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA "+c->name+" "+ConvToStr(c->age)+" "+extname+" :"+extdata);
        else if (!target)
                s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA * "+extname+" :"+extdata);
  }
  
  CullResult ModuleSpanningTree::cull()
  {
 -      Utils->cull();
 -      ServerInstance->Timers->DelTimer(RefreshTimer);
 +      if (Utils)
 +              Utils->cull();
        return this->Module::cull();
  }
  
index 9e6f41852f2d1c4ba631551d8c95c4859a14af85,a0543b6bd30ae0d3e12b8d23b9d7b9cd2eccfa8a..164bed1ca61b46f07b7b5684cebf741242eb1675
   */
  
  
 -#ifndef M_SPANNINGTREE_UTILS_H
 -#define M_SPANNINGTREE_UTILS_H
 +#pragma once
  
  #include "inspircd.h"
 +#include "cachetimer.h"
  
  /* Foward declarations */
  class TreeServer;
@@@ -32,24 -32,25 +32,25 @@@ class Link
  class Autoconnect;
  class ModuleSpanningTree;
  class SpanningTreeUtilities;
 +class CmdBuilder;
 +
 +extern SpanningTreeUtilities* Utils;
  
  /* This hash_map holds the hash equivalent of the server
   * tree, used for rapid linear lookups.
   */
 -#ifdef HASHMAP_DEPRECATED
 -      typedef nspace::hash_map<std::string, TreeServer*, nspace::insensitive, irc::StrHashComp> server_hash;
 -#else
 -      typedef nspace::hash_map<std::string, TreeServer*, nspace::hash<std::string>, irc::StrHashComp> server_hash;
 -#endif
 -
 -typedef std::map<TreeServer*,TreeServer*> TreeServerList;
 +typedef TR1NS::unordered_map<std::string, TreeServer*, irc::insensitive, irc::StrHashComp> server_hash;
  
  /** Contains helper functions and variables for this module,
   * and keeps them out of the global namespace
   */
  class SpanningTreeUtilities : public classbase
  {
 +      CacheRefreshTimer RefreshTimer;
 +
   public:
 +      typedef std::set<TreeSocket*> TreeSocketSet;
+       typedef std::map<TreeSocket*, std::pair<std::string, int> > TimeoutList;
  
        /** Creator module
         */
@@@ -91,7 -92,7 +92,7 @@@
        server_hash sidlist;
        /** List of all outgoing sockets and their timeouts
         */
-       std::map<TreeSocket*, std::pair<std::string, int> > timeoutlist;
+       TimeoutList timeoutlist;
        /** Holds the data from the <link> tags in the conf
         */
        std::vector<reference<Link> > LinkBlocks;
         */
        ~SpanningTreeUtilities();
  
 -      void RouteCommand(TreeServer*, const std::string&, const parameterlist&, User*);
 +      void RouteCommand(TreeServer* origin, CommandBase* cmd, const parameterlist& parameters, User* user);
  
        /** Send a message from this server to one other local or remote
         */
 -      bool DoOneToOne(const std::string &prefix, const std::string &command, const parameterlist &params, const std::string& target);
 +      bool DoOneToOne(const CmdBuilder& params, const std::string& target);
  
        /** Send a message from this server to all but one other, local or remote
         */
 -      bool DoOneToAllButSender(const std::string &prefix, const std::string &command, const parameterlist &params, const std::string& omit);
 +      void DoOneToAllButSender(const CmdBuilder& params, TreeServer* omit);
  
        /** Send a message from this server to all others
         */
 -      bool DoOneToMany(const std::string &prefix, const std::string &command, const parameterlist &params);
 +      void DoOneToMany(const CmdBuilder& params);
  
        /** Read the spanningtree module's tags from the config file
         */
        void ReadConfiguration();
  
 -      /** Add a server to the server list for GetListOfServersForChannel
 +      /** Handle nick collision
         */
 -      void AddThisServer(TreeServer* server, TreeServerList &list);
 +      int DoCollision(User* u, TreeServer* server, time_t remotets, const std::string& remoteident, const std::string& remoteip, const std::string& remoteuid);
  
        /** Compile a list of servers which contain members of channel c
         */
 -      void GetListOfServersForChannel(Channel* c, TreeServerList &list, char status, const CUList &exempt_list);
 +      void GetListOfServersForChannel(Channel* c, TreeSocketSet& list, char status, const CUList& exempt_list);
  
        /** Find a server by name
         */
        /** Refresh the IP cache used for allowing inbound connections
         */
        void RefreshIPCache();
 +
 +      /** Sends a PRIVMSG or a NOTICE to a channel obeying an exempt list and an optional prefix
 +       */
 +      void SendChannelMessage(const std::string& prefix, Channel* target, const std::string& text, char status, const CUList& exempt_list, const char* message_type, TreeSocket* omit = NULL);
  };
  
 -#endif
 +inline void SpanningTreeUtilities::DoOneToMany(const CmdBuilder& params)
 +{
 +      DoOneToAllButSender(params, NULL);
 +}
diff --combined src/modules/m_userip.cpp
index 97e4c92845aeeb4490084745d2795912ae7f1266,5ea84c04aa8b925c382b48a12db83d0a70ac300e..79e69ec5e85b96fbc01943610fdbba4499f134d9
@@@ -21,6 -21,8 +21,6 @@@
  
  #include "inspircd.h"
  
 -/* $ModDesc: Provides support for USERIP command */
 -
  /** Handle /USERIP
   */
  class CommandUserip : public Command
@@@ -36,7 -38,7 +36,7 @@@
                std::string retbuf = "340 " + user->nick + " :";
                int nicks = 0;
                bool checked_privs = false;
-               bool has_privs;
+               bool has_privs = false;
  
                for (int i = 0; i < (int)parameters.size(); i++)
                {
@@@ -59,8 -61,8 +59,8 @@@
                                                continue;
                                }
  
 -                              retbuf = retbuf + u->nick + (IS_OPER(u) ? "*" : "") + "=";
 -                              if (IS_AWAY(u))
 +                              retbuf = retbuf + u->nick + (u->IsOper() ? "*" : "") + "=";
 +                              if (u->IsAway())
                                        retbuf += "-";
                                else
                                        retbuf += "+";
@@@ -85,20 -87,28 +85,20 @@@ class ModuleUserIP : public Modul
        {
        }
  
 -      void init()
 +      void init() CXX11_OVERRIDE
        {
                ServerInstance->Modules->AddService(cmd);
 -              Implementation eventlist[] = { I_On005Numeric };
 -              ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
 -      }
 -
 -      virtual void On005Numeric(std::string &output)
 -      {
 -              output = output + " USERIP";
        }
  
 -      virtual ~ModuleUserIP()
 +      void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
 +              tokens["USERIP"];
        }
  
 -      virtual Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for USERIP command",VF_VENDOR);
        }
 -
  };
  
  MODULE_INIT(ModuleUserIP)
 -
diff --combined src/socketengine.cpp
index 80e9eaed97309ac1b4726e9364d35b7c6c99971f,8af598b069ed6184565810688b416f2823f88810..219d154f276df68e7e3beb386f2ca01ec2146dc7
@@@ -60,7 -60,7 +60,7 @@@ void SocketEngine::ChangeEventMask(Even
                new_m &= ~FD_WANT_READ_MASK;
        if (change & FD_WANT_WRITE_MASK)
                new_m &= ~FD_WANT_WRITE_MASK;
 -      
 +
        // if adding a trial read/write, insert it into the set
        if (change & FD_TRIAL_NOTE_MASK && !(old_m & FD_TRIAL_NOTE_MASK))
                trials.insert(eh->GetFd());
@@@ -255,3 -255,26 +255,26 @@@ void SocketEngine::GetStats(float &kbit
        kbitpersec_in = in_kbit / 1024;
        kbitpersec_out = out_kbit / 1024;
  }
+ std::string SocketEngine::LastError()
+ {
+ #ifndef _WIN32
+       return strerror(errno);
+ #else
+       char szErrorString[500];
+       DWORD dwErrorCode = WSAGetLastError();
+       if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)szErrorString, _countof(szErrorString), NULL) == 0)
+               sprintf_s(szErrorString, _countof(szErrorString), "Error code: %u", dwErrorCode);
+       return szErrorString;
+ #endif
+ }
+ std::string SocketEngine::GetError(int errnum)
+ {
+ #ifndef _WIN32
+       return strerror(errnum);
+ #else
+       WSASetLastError(errnum);
+       return LastError();
+ #endif
+ }
index 68f14cc3840e7a5878097514cd28c50ed60f728f,6913076a297f5019925494af4d0f9007194c2908..099f793a143c67ce75e14e940dc03d97220f1873
@@@ -61,9 -61,9 +61,9 @@@ EPollEngine::EPollEngine(
        }
        else
        {
 -              ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
                std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
-               ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
+               ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
        }
  
        // This is not a maximum, just a hint at the eventual number of sockets that may be polled.
  
        if (EngineHandle == -1)
        {
 -              ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Could not initialize socket engine: %s", strerror(errno));
 -              ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.");
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Could not initialize socket engine: %s", strerror(errno));
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.");
                std::cout << "ERROR: Could not initialize epoll socket engine: " << strerror(errno) << std::endl;
                std::cout << "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now." << std::endl;
-               ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
+               ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
        }
  
        ref = new EventHandler* [GetMaxFds()];
@@@ -119,13 -119,13 +119,13 @@@ bool EPollEngine::AddFd(EventHandler* e
        int fd = eh->GetFd();
        if ((fd < 0) || (fd > GetMaxFds() - 1))
        {
 -              ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
                return false;
        }
  
        if (ref[fd])
        {
 -              ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd);
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
                return false;
        }
  
        int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev);
        if (i < 0)
        {
 -              ServerInstance->Logs->Log("SOCKET",DEBUG,"Error adding fd: %d to socketengine: %s", fd, strerror(errno));
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Error adding fd: %d to socketengine: %s", fd, strerror(errno));
                return false;
        }
  
 -      ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
 +      ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
  
        ref[fd] = eh;
        SocketEngine::SetEventMask(eh, event_mask);
@@@ -168,7 -168,7 +168,7 @@@ void EPollEngine::DelFd(EventHandler* e
        int fd = eh->GetFd();
        if ((fd < 0) || (fd > GetMaxFds() - 1))
        {
 -              ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
                return;
        }
  
  
        if (i < 0)
        {
 -              ServerInstance->Logs->Log("SOCKET",DEBUG,"epoll_ctl can't remove socket: %s", strerror(errno));
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "epoll_ctl can't remove socket: %s", strerror(errno));
        }
  
        ref[fd] = NULL;
  
 -      ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
 +      ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
        CurrentSetSize--;
  }
  
@@@ -202,7 -202,7 +202,7 @@@ int EPollEngine::DispatchEvents(
                EventHandler* eh = ref[events[j].data.fd];
                if (!eh)
                {
 -                      ServerInstance->Logs->Log("SOCKET",DEBUG,"Got event on unknown fd: %d", events[j].data.fd);
 +                      ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Got event on unknown fd: %d", events[j].data.fd);
                        epoll_ctl(EngineHandle, EPOLL_CTL_DEL, events[j].data.fd, &events[j]);
                        continue;
                }
index a6c84133a10d52c5db6b97eb768db44556d9cd00,8694a0bdd84d4cc2e5249aba5fa4d3f3ed6f5732..99ac51499c012e231771a3192136286f56d2acb0
@@@ -72,9 -72,9 +72,9 @@@ KQueueEngine::KQueueEngine(
        sysctl(mib, 2, &MAX_DESCRIPTORS, &len, NULL, 0);
        if (MAX_DESCRIPTORS <= 0)
        {
 -              ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
                std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
-               ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
+               ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
        }
  
        this->RecoverFromFork();
@@@ -93,11 -93,11 +93,11 @@@ void KQueueEngine::RecoverFromFork(
        EngineHandle = kqueue();
        if (EngineHandle == -1)
        {
 -              ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
 -              ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: this is a fatal error, exiting now.");
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: this is a fatal error, exiting now.");
                std::cout << "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features." << std::endl;
                std::cout << "ERROR: this is a fatal error, exiting now." << std::endl;
-               ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
+               ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
        }
        CurrentSetSize = 0;
  }
@@@ -126,7 -126,7 +126,7 @@@ bool KQueueEngine::AddFd(EventHandler* 
        int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
        if (i == -1)
        {
 -              ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to add fd: %d %s",
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Failed to add fd: %d %s",
                                          fd, strerror(errno));
                return false;
        }
        OnSetEvent(eh, 0, event_mask);
        CurrentSetSize++;
  
 -      ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
 +      ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
        return true;
  }
  
@@@ -146,7 -146,7 +146,7 @@@ void KQueueEngine::DelFd(EventHandler* 
  
        if ((fd < 0) || (fd > GetMaxFds() - 1))
        {
 -              ServerInstance->Logs->Log("SOCKET",DEFAULT,"DelFd() on invalid fd: %d", fd);
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "DelFd() on invalid fd: %d", fd);
                return;
        }
  
  
        if (j < 0)
        {
 -              ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to remove fd: %d %s",
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Failed to remove fd: %d %s",
                                          fd, strerror(errno));
        }
  
        CurrentSetSize--;
        ref[fd] = NULL;
  
 -      ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
 +      ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
  }
  
  void KQueueEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
                EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD, 0, 0, NULL);
                int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
                if (i < 0) {
 -                      ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s",
 +                      ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Failed to mark for writing: %d %s",
                                                  eh->GetFd(), strerror(errno));
                }
        }
                EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
                int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
                if (i < 0) {
 -                      ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s",
 +                      ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Failed to mark for writing: %d %s",
                                                  eh->GetFd(), strerror(errno));
                }
        }
                EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, NULL);
                int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
                if (i < 0) {
 -                      ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s",
 +                      ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Failed to mark for writing: %d %s",
                                                  eh->GetFd(), strerror(errno));
                }
        }
index 37f4b6836a09038d3b3abd3199c187a4608c9716,ea7780686572405a54f5f92baab1a17df225bf63..493b22630e406d4dbbf362efaa742d614ed90d30
@@@ -20,6 -20,9 +20,6 @@@
   */
  
  
 -#include "inspircd.h"
 -#include "exitcodes.h"
 -
  #ifndef SOCKETENGINE_POLL
  #define SOCKETENGINE_POLL
  
  #include <vector>
  #include <string>
  #include <map>
 -#include "inspircd_config.h"
 +#include "exitcodes.h"
  #include "inspircd.h"
  #include "socketengine.h"
  
  #ifndef _WIN32
-       #ifndef __USE_XOPEN
-           #define __USE_XOPEN /* fuck every fucking OS ever made. needed by poll.h to work.*/
-       #endif
-       #include <poll.h>
-       #include <sys/poll.h>
+ # ifndef __USE_XOPEN
+ #  define __USE_XOPEN /* fuck every fucking OS ever made. needed by poll.h to work.*/
+ # endif
+ # include <poll.h>
+ # include <sys/poll.h>
+ # include <sys/resource.h>
  #else
-       /* *grumble* */
-       #define struct pollfd WSAPOLLFD
-       #define poll WSAPoll
+ # define struct pollfd WSAPOLLFD
+ # define poll WSAPoll
  #endif
  
  class InspIRCd;
@@@ -73,40 -76,20 +73,20 @@@ public
  
  #endif
  
- #ifdef BSD
-       #include <sys/sysctl.h>
- #else
-       #include <ulimit.h>
- #endif
  PollEngine::PollEngine()
  {
        CurrentSetSize = 0;
- #ifdef BSD
-       int mib[2];
-       size_t len;
-       mib[0] = CTL_KERN;
- #ifdef KERN_MAXFILESPERPROC
-       mib[1] = KERN_MAXFILESPERPROC;
- #else
-       mib[1] = KERN_MAXFILES;
- #endif
-       len = sizeof(MAX_DESCRIPTORS);
-       sysctl(mib, 2, &MAX_DESCRIPTORS, &len, NULL, 0);
- #else
-       int max = ulimit(4, 0);
-       if (max > 0)
+       struct rlimit limits;
+       if (!getrlimit(RLIMIT_NOFILE, &limits))
        {
-               MAX_DESCRIPTORS = max;
+               MAX_DESCRIPTORS = limits.rlim_cur;
        }
        else
        {
 -              ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno));
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno));
                std::cout << "ERROR: Can't determine maximum number of open sockets: " << strerror(errno) << std::endl;
-               ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
+               ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
        }
- #endif
  
        ref = new EventHandler* [GetMaxFds()];
        events = new struct pollfd[GetMaxFds()];
@@@ -137,13 -120,13 +117,13 @@@ bool PollEngine::AddFd(EventHandler* eh
        int fd = eh->GetFd();
        if ((fd < 0) || (fd > GetMaxFds() - 1))
        {
 -              ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
                return false;
        }
  
        if (fd_mappings.find(fd) != fd_mappings.end())
        {
 -              ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd);
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
                return false;
        }
  
        events[index].fd = fd;
        events[index].events = mask_to_poll(event_mask);
  
 -      ServerInstance->Logs->Log("SOCKET", DEBUG,"New file descriptor: %d (%d; index %d)", fd, events[fd].events, index);
 +      ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d (%d; index %d)", fd, events[fd].events, index);
        SocketEngine::SetEventMask(eh, event_mask);
        CurrentSetSize++;
        return true;
@@@ -173,7 -156,7 +153,7 @@@ void PollEngine::OnSetEvent(EventHandle
        std::map<int, unsigned int>::iterator it = fd_mappings.find(eh->GetFd());
        if (it == fd_mappings.end())
        {
 -              ServerInstance->Logs->Log("SOCKET",DEBUG,"SetEvents() on unknown fd: %d", eh->GetFd());
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SetEvents() on unknown fd: %d", eh->GetFd());
                return;
        }
  
@@@ -185,14 -168,14 +165,14 @@@ void PollEngine::DelFd(EventHandler* eh
        int fd = eh->GetFd();
        if ((fd < 0) || (fd > MAX_DESCRIPTORS))
        {
 -              ServerInstance->Logs->Log("SOCKET", DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
                return;
        }
  
        std::map<int, unsigned int>::iterator it = fd_mappings.find(fd);
        if (it == fd_mappings.end())
        {
 -              ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd() on unknown fd: %d", fd);
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd() on unknown fd: %d", fd);
                return;
        }
  
  
        CurrentSetSize--;
  
 -      ServerInstance->Logs->Log("SOCKET", DEBUG, "Remove file descriptor: %d (index: %d) "
 +      ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d (index: %d) "
                        "(Filled gap with: %d (index: %d))", fd, index, last_fd, last_index);
  }
  
@@@ -272,7 -255,7 +252,7 @@@ int PollEngine::DispatchEvents(
                                        // whoops, deleted out from under us
                                        continue;
                        }
 -                      
 +
                        if (events[index].revents & POLLOUT)
                        {
                                int mask = eh->GetEventMask();
index 8a2fb87f409ced96fd8ffa7b178f2f5901cb14e5,f7c547d4510d79367297db443d3868db868dc579..ba4e8f2d7330ec580c7975d5d8909d17a7f0f82c
@@@ -32,6 -32,7 +32,6 @@@
  #include <vector>
  #include <string>
  #include <map>
 -#include "inspircd_config.h"
  #include "inspircd.h"
  #include "socketengine.h"
  #include <port.h>
@@@ -74,19 -75,19 +74,19 @@@ PortsEngine::PortsEngine(
        }
        else
        {
 -              ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
 +              ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
                std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
-               ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
+               ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
        }
        EngineHandle = port_create();
  
        if (EngineHandle == -1)
        {
 -              ServerInstance->Logs->Log("SOCKET",SPARSE,"ERROR: Could not initialize socket engine: %s", strerror(errno));
 -              ServerInstance->Logs->Log("SOCKET",SPARSE,"ERROR: This is a fatal error, exiting now.");
 +              ServerInstance->Logs->Log("SOCKET", LOG_SPARSE, "ERROR: Could not initialize socket engine: %s", strerror(errno));
 +              ServerInstance->Logs->Log("SOCKET", LOG_SPARSE, "ERROR: This is a fatal error, exiting now.");
                std::cout << "ERROR: Could not initialize socket engine: " << strerror(errno) << std::endl;
                std::cout << "ERROR: This is a fatal error, exiting now." << std::endl;
-               ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
+               ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
        }
        CurrentSetSize = 0;
  
@@@ -125,7 -126,7 +125,7 @@@ bool PortsEngine::AddFd(EventHandler* e
        SocketEngine::SetEventMask(eh, event_mask);
        port_associate(EngineHandle, PORT_SOURCE_FD, fd, mask_to_events(event_mask), eh);
  
 -      ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
 +      ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
        CurrentSetSize++;
        return true;
  }
@@@ -147,7 -148,7 +147,7 @@@ void PortsEngine::DelFd(EventHandler* e
        CurrentSetSize--;
        ref[fd] = NULL;
  
 -      ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
 +      ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
  }
  
  int PortsEngine::DispatchEvents()
index de366266ff1133f40e3cd8d3cc320b93c626b324,c21b200c22977b7c2da657b1b4087de25634080e..f995b5472d66d1d4a83c985a6adafcb03c34ea7a
@@@ -18,6 -18,8 +18,6 @@@
   */
  
  
 -#include "inspircd_config.h"
 -
  #include "inspircd.h"
  #include "socketengine.h"
  
@@@ -84,7 -86,7 +84,7 @@@ bool SelectEngine::AddFd(EventHandler* 
  
        CurrentSetSize++;
  
 -      ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
 +      ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
        return true;
  }
  
@@@ -104,7 -106,7 +104,7 @@@ void SelectEngine::DelFd(EventHandler* 
        if (fd == MaxFD)
                --MaxFD;
  
 -      ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
 +      ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
  }
  
  void SelectEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
@@@ -177,7 -179,9 +177,9 @@@ int SelectEngine::DispatchEvents(
                        if (has_write)
                        {
                                WriteEvents++;
-                               SetEventMask(ev, ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
+                               int newmask = (ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
+                               this->OnSetEvent(ev, ev->GetEventMask(), newmask);
+                               SetEventMask(ev, newmask);
                                ev->HandleEvent(EVENT_WRITE);
                        }
                }
index 2836674bbdf9e70f1f38c1e98f856011db3899bd,048baf38be99a7ea2b9dcc8ed5dfd5745637b99e..eba18dca3019ee2d148bbeee7a4a1acaacecc464
@@@ -22,7 -22,6 +22,7 @@@
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
 +
  #include "inspircd_win32wrapper.h"
  #include "inspircd.h"
  #include "configreader.h"
@@@ -86,7 -85,7 +86,7 @@@ CoreExport int insp_inet_pton(int af, c
                }
                return 1;
        }
 -      
 +
        return 0;
  }
  
@@@ -164,7 -163,7 +164,7 @@@ int getopt_long(int ___argc, char *cons
  //                                    optind++;               // Trash this next argument, we won't be needing it.
                                        par = ___argv[optind-1];
                                }
 -                      }                       
 +                      }
  
                        // increment the argument for next time
  //                    optind++;
                        {
                                if (__longopts[i].val == -1 || par == 0)
                                        return 1;
 -                              
 +
                                return __longopts[i].val;
 -                      }                       
 +                      }
                        break;
                }
        }
@@@ -222,10 -221,3 +222,3 @@@ DWORD CWin32Exception::GetErrorCode(
  {
        return dwErrorCode;
  }
- #include "../src/modules/m_spanningtree/link.h"
- #include "modules/ssl.h"
- template class reference<Link>;
- template class reference<Autoconnect>;
- template class reference<ssl_cert>;
- template class reference<OperInfo>;