]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
Merge insp20
authorAttila Molnar <attilamolnar@hush.com>
Mon, 7 Apr 2014 11:40:51 +0000 (13:40 +0200)
committerAttila Molnar <attilamolnar@hush.com>
Mon, 7 Apr 2014 11:40:51 +0000 (13:40 +0200)
30 files changed:
1  2 
configure
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
make/utilities.pm
src/channels.cpp
src/cidr.cpp
src/command_parse.cpp
src/configreader.cpp
src/coremods/core_channel/cmd_kick.cpp
src/coremods/core_channel/cmd_names.cpp
src/coremods/core_ison.cpp
src/coremods/core_who.cpp
src/inspircd.cpp
src/modules/extra/m_ssl_openssl.cpp
src/modules/m_banredirect.cpp
src/modules/m_globalload.cpp
src/modules/m_httpd.cpp
src/modules/m_operprefix.cpp
src/modules/m_samode.cpp
src/modules/m_saquit.cpp
src/modules/m_spanningtree/idle.cpp
src/modules/m_spanningtree/treesocket1.cpp
src/modules/m_watch.cpp
src/socket.cpp
src/socketengines/socketengine_select.cpp

diff --combined configure
index 4db9064b986f6d1d3f3381d7065cbf859d58df0e,27f7e6f1ec4290a7d310f491040243f8ff5dd90e..0a91e65c5bc4e24bd75693f43fd0ad3274a804a8
+++ b/configure
@@@ -33,17 -33,35 +33,17 @@@ BEGIN 
  use strict;
  use warnings FATAL => qw(all);
  
 -use Data::Dumper;
 -BEGIN {
 -      $Data::Dumper::Sortkeys = 1;
 -      $Data::Dumper::Useqq = 1;
 -};
 -
  use File::Copy ();
 -use Socket;
 +use File::Spec::Functions qw(rel2abs);
  use Cwd;
  use Getopt::Long;
  
 -# Utility functions for our buildsystem
 -use make::utilities;
  use make::configure;
 -use make::gnutlscert;
 -use make::opensslcert;
 -
 -###############################################################################################
 -#
 -#                                 NON-EDITABLE VARIABLES
 -#
 -###############################################################################################
 -
 -our ($opt_use_gnutls, $opt_rebuild, $opt_use_openssl, $opt_nointeractive, $opt_ports,
 -    $opt_epoll, $opt_kqueue, $opt_noports, $opt_noepoll, $opt_nokqueue,
 -    $opt_noipv6, $opt_maxbuf, $opt_disable_debug, $opt_freebsd_port,
 -      $opt_system, $opt_uid);
 +use make::utilities;
  
 -our ($opt_cc, $opt_base_dir, $opt_config_dir, $opt_module_dir, $opt_binary_dir, $opt_data_dir, $opt_log_dir);
 +our ($opt_use_gnutls, $opt_use_openssl, $opt_nointeractive, $opt_socketengine,
 +     $opt_system, $opt_uid, $opt_base_dir, $opt_config_dir, $opt_module_dir, $opt_binary_dir,
 +     $opt_data_dir, $opt_log_dir);
  
  sub list_extras ();
  
@@@ -56,23 -74,36 +56,23 @@@ my @opt_disableextras
  
  GetOptions (
        'enable-gnutls' => \$opt_use_gnutls,
 -      'rebuild' => \$opt_rebuild,
        'system' => \$opt_system,
        'uid=s' => \$opt_uid,
        'enable-openssl' => \$opt_use_openssl,
        'disable-interactive' => \$opt_nointeractive,
 -      'enable-ports' => \$opt_ports,
 -      'enable-epoll' => \$opt_epoll,
 -      'enable-kqueue' => \$opt_kqueue,
 -      'disable-ports' => \$opt_noports,
 -      'disable-epoll' => \$opt_noepoll,
 -      'disable-kqueue' => \$opt_nokqueue,
 -      'disable-ipv6' => \$opt_noipv6,
 -      'with-cc=s' => \$opt_cc,
 -      'with-maxbuf=i' => \$opt_maxbuf,
 -      'enable-freebsd-ports-openssl' => \$opt_freebsd_port,
 +      'socketengine=s' => \$opt_socketengine,
        'prefix=s' => \$opt_base_dir,
        'config-dir=s' => \$opt_config_dir,
        'module-dir=s' => \$opt_module_dir,
        'binary-dir=s' => \$opt_binary_dir,
        'data-dir=s' => \$opt_data_dir,
        'log-dir=s' => \$opt_log_dir,
 -      'disable-debuginfo' => sub { $opt_disable_debug = 1 },
 -      'help'  => sub { showhelp(); },
 -      'update' => sub { update(); },
 -      'clean' => sub { clean(); },
 +      'help'  => \&cmd_help,
 +      'update' => \&cmd_update,
 +      'clean' => \&cmd_clean,
        'list-extras' => sub { list_extras; exit 0; }, # This, --enable-extras, and --disable-extras are for non-interactive managing.
        'enable-extras=s@' => \@opt_enableextras, # ^
        'disable-extras=s@' => \@opt_disableextras, # ^
 -      'generate-openssl-cert' => sub { make_openssl_cert(); exit(0); },
 -      'generate-gnutls-cert' => sub { make_gnutls_cert(); exit(0); }
  );
  
  if (scalar(@opt_enableextras) + scalar(@opt_disableextras) > 0) {
@@@ -94,20 -125,30 +94,20 @@@ our $interactive = !
        (defined $opt_data_dir) ||
        (defined $opt_log_dir) ||
        (defined $opt_nointeractive) ||
 -      (defined $opt_cc) ||
 -      (defined $opt_noipv6) ||
 -      (defined $opt_kqueue) ||
 -      (defined $opt_epoll) ||
 -      (defined $opt_ports) ||
 +      (defined $opt_socketengine) ||
        (defined $opt_use_openssl) ||
 -      (defined $opt_nokqueue) ||
 -      (defined $opt_noepoll) ||
 -      (defined $opt_noports) ||
 -      (defined $opt_maxbuf) ||
        (defined $opt_system) ||
        (defined $opt_uid) ||
 -      (defined $opt_use_gnutls) ||
 -      (defined $opt_freebsd_port)
 +      (defined $opt_use_gnutls)
  );
  
 -chomp(our $topdir = getcwd());
 -our $this = resolve_directory($topdir);                                               # PWD, Regardless.
 -our @modlist = ();                                                                    # Declare for Module List..
 -our %config = ();                                                                     # Initiate Configuration Hash..
 -our $cache_loaded = getcache();
 -$config{ME} = resolve_directory($topdir);                             # Present Working Directory
 +our $topdir = getcwd();
 +our %config = read_configure_cache();
 +
 +print "Checking for cache from previous configure... ";
 +print %config ? "found\n" : "not found\n";
  
 -$config{BASE_DIR} = $config{ME}."/run";
 +$config{BASE_DIR} = $topdir."/run";
  
  if (defined $opt_base_dir) {
        $config{BASE_DIR} = $opt_base_dir;
  }
  
  if (defined $opt_system) {
 -      $config{UID} = $opt_uid || 'ircd';
 -      $config{CONFIG_DIR}      = '/etc/inspircd';
 -      $config{MODULE_DIR}      = '/usr/lib/inspircd';
 -      $config{BINARY_DIR}      = '/usr/sbin/';
 -      $config{BUILD_DIR}       = resolve_directory($config{ME}."/build");         # Build Directory
 -      $config{DATA_DIR}        = '/var/inspircd';
 -      $config{LOG_DIR}         = '/var/log/inspircd';
 +      $config{UID} = defined $opt_uid ? $opt_uid : 'ircd';
 +      $config{CONFIG_DIR} = '/etc/inspircd';
 +      $config{MODULE_DIR} = '/usr/lib/inspircd';
 +      $config{BINARY_DIR} = '/usr/sbin/';
 +      $config{BUILD_DIR} = $topdir."/build";
 +      $config{DATA_DIR} = '/var/inspircd';
 +      $config{LOG_DIR} = '/var/log/inspircd';
  } else {
 -      $config{UID} = $opt_uid || $<;
 -      $config{CONFIG_DIR}      = resolve_directory($config{BASE_DIR}."/conf");        # Configuration Directory
 -      $config{MODULE_DIR}      = resolve_directory($config{BASE_DIR}."/modules");     # Modules Directory
 -      $config{BINARY_DIR}      = resolve_directory($config{BASE_DIR}."/bin");         # Binary Directory
 -      $config{BUILD_DIR}       = resolve_directory($config{ME}."/build");         # Build Directory
 -      $config{DATA_DIR}        = resolve_directory($config{BASE_DIR}."/data");        # Data directory
 -      $config{LOG_DIR}         = resolve_directory($config{BASE_DIR}."/logs");        # Log directory
 +      $config{UID} = defined $opt_uid ? $opt_uid : $<;
 +      $config{CONFIG_DIR} = rel2abs($config{BASE_DIR}."/conf");
 +      $config{MODULE_DIR} = rel2abs($config{BASE_DIR}."/modules");
 +      $config{BINARY_DIR} = rel2abs($config{BASE_DIR}."/bin");
 +      $config{BUILD_DIR} = rel2abs($topdir."/build");
 +      $config{DATA_DIR} = rel2abs($config{BASE_DIR}."/data");
 +      $config{LOG_DIR} = rel2abs($config{BASE_DIR}."/logs");
  }
  
  if (defined $opt_config_dir) {
@@@ -148,37 -189,89 +148,37 @@@ if (defined $opt_data_dir) 
  if (defined $opt_log_dir) {
        $config{LOG_DIR} = $opt_log_dir;
  }
 -chomp($config{HAS_GNUTLS}   = `pkg-config --modversion gnutls 2>/dev/null`); # GNUTLS Version.
 -
 -if (defined $opt_freebsd_port)
 -{
 -      chomp($config{HAS_OPENSSL} = `pkg-config --modversion openssl 2>/dev/null`);
 -      chomp($config{HAS_OPENSSL_PORT}  = `pkg-config --modversion openssl 2>/dev/null`);
 -      $config{USE_FREEBSD_BASE_SSL} = "n";
 -}
 -else
 -{
 -      if ($^O eq "freebsd")
 -      {
 -              # default: use base ssl
 -              chomp($config{HAS_OPENSSL} = `openssl version | cut -d ' ' -f 2`);                      # OpenSSL version, freebsd specific
 -              chomp($config{HAS_OPENSSL_PORT}  = `pkg-config --modversion openssl 2>/dev/null`);      # Port version, may be different
 -      }
 -      else
 -      {
 -              chomp($config{HAS_OPENSSL}  = `pkg-config --modversion openssl 2>/dev/null`);           # Openssl version, others
 -              $config{HAS_OPENSSL_PORT} = "";
 -      }
 -}
 +chomp($config{HAS_GNUTLS}   = `pkg-config --modversion gnutls 2>/dev/null`);
 +chomp($config{HAS_OPENSSL}  = `pkg-config --modversion openssl 2>/dev/null`);
  
  chomp(our $gnutls_ver = $config{HAS_GNUTLS});
  chomp(our $openssl_ver = $config{HAS_OPENSSL});
 -$config{USE_GNUTLS}       = "n";
 +$config{USE_GNUTLS}       = 0;
  if (defined $opt_use_gnutls)
  {
        $config{USE_GNUTLS} = "y";                                      # Use gnutls.
  }
 -$config{USE_OPENSSL}  = "n";                                          # Use openssl.
 +$config{USE_OPENSSL}  = 0;                                            # Use openssl.
  if (defined $opt_use_openssl)
  {
        $config{USE_OPENSSL} = "y";
  }
  
 -if (!defined $opt_disable_debug) {
 -      $config{OPTIMISATI}      = "-g1";                               # Optimisation Flag
 -} else {
 -      $config{OPTIMISATI}      = "-O2";
 -}
 +$config{STARTSCRIPT} = $^O eq 'darwin' ? 'org.inspircd.plist' : 'inspircd';
  
 -$config{HAS_STRLCPY}  = "false";                                      # strlcpy Check.
 -$config{HAS_STDINT}    = "false";                                     # stdint.h check
 -$config{USE_KQUEUE}    = "y";                                         # kqueue enabled
 -if (defined $opt_nokqueue) {
 -      $config{USE_KQUEUE} = "n";
 -}
 -$config{USE_POLL}     = "y";                                  # poll enabled
 -$config{USE_EPOLL}      = "y";                                        # epoll enabled
 -if (defined $opt_noepoll)
 -{
 -      $config{USE_EPOLL} = "n";
 +$config{CXX} = defined $ENV{CXX} && !system("$ENV{CXX} -v > /dev/null 2>&1") ? $ENV{CXX} : find_compiler();
 +if ($config{CXX} eq "") {
 +      print "A C++ compiler could not be detected on your system!\n";
 +      print "Set the CXX environment variable to the full path if this is incorrect.\n";
 +      exit 1; 
  }
 -$config{USE_PORTS}      = "y";                                        # epoll enabled
 -if (defined $opt_noports)
 -{
 -      $config{USE_PORTS} = "n";
 -}
 -$config{_SOMAXCONN} = SOMAXCONN;                                      # Max connections in accept queue
 -$config{OSNAME}                   = $^O;                                      # Operating System Name
 -$config{IS_DARWIN}      = "NO";                                       # Is OSX?
 -$config{STARTSCRIPT}    = "inspircd";                 # start script?
 -$config{DESTINATION}    = "BASE";                             # Is target path.
 -if ($config{OSNAME} =~ /darwin/i)
 -{
 -      $config{IS_DARWIN} = "YES";
 -      $config{STARTSCRIPT}      = "org.inspircd.plist";               # start script for OSX.
 -      $config{CC}                 = "xcrun clang++";                                  # C++ compiler for OSX.
 -}
 -else
 -{
 -      $config{CC}                 = "g++";                                            # C++ compiler
 -}
 -if (defined $opt_cc)
 -{
 -      $config{CC} = $opt_cc;
 +
 +our %cxx = get_compiler_info($config{CXX});
 +if ($cxx{UNSUPPORTED}) {
 +      print "Your C++ compiler is too old to build InspIRCd!\n";
 +      print "Reason: $cxx{REASON}\n";
 +      exit 1;
  }
 -our $exec = $config{CC} . " -dumpversion | cut -c 1";
 -chomp($config{GCCVER}         = `$exec`);                             # Major GCC Version
 -$exec = $config{CC} . " -dumpversion | cut -c 3";
 -chomp($config{GCCMINOR}               = `$exec`);
 -$config{MAXBUF}                       = "512";                                # Max buffer size
  
  if ($config{HAS_OPENSSL} =~ /^([-[:digit:].]+)(?:[a-z])?(?:\-[a-z][0-9])?/) {
        $config{HAS_OPENSSL} = $1;
        $config{HAS_OPENSSL} = "";
  }
  
 -if (($config{GCCVER} eq "") || ($config{GCCMINOR} eq "")) {
 -      if ($config{IS_DARWIN} eq "YES") {
 -              print $config{CC} . " was not found! You require clang++ (the LLVM C++ compiler, part of the OSX developer tools) to build InspIRCd!\n";
 -      } else {
 -              print $config{CC} . " was not found! You require g++ (the GNU C++ compiler, part of GCC) to build InspIRCd!\n";         
 -      }
 -      exit;
 -}
 -
 -# Get and Set some important vars..
 -getmodules();
 +$config{HAS_CLOCK_GETTIME} = run_test 'clock_gettime()', test_file($config{CXX}, 'clock_gettime.cpp', '-lrt');
 +$config{HAS_EVENTFD} = run_test 'eventfd()', test_file($config{CXX}, 'eventfd.cpp');
  
 -sub clean
 -{
 -      unlink(".config.cache");
 +if ($config{HAS_EPOLL} = run_test 'epoll', test_header($config{CXX}, 'sys/epoll.h')) {
 +      $config{SOCKETENGINE} ||= 'epoll';
  }
  
 -our ($has_epoll, $has_ports, $has_kqueue) = (0, 0, 0);
 -
 -sub update
 -{
 -      eval {
 -              chomp($topdir = getcwd());
 -              $this = resolve_directory($topdir);                                          # PWD, Regardless.
 -              getmodules();
 -              # Does the cache file exist?
 -              if (!getcache()) {
 -                      # No, No it doesn't.. *BASH*
 -                      print "You have not run ./configure before. Please do this before trying to run the update script.\n";
 -                      exit 0;
 -              } else {
 -                      # We've Loaded the cache file and all our variables..
 -                      print "Updating files...\n";
 -                      if (defined($opt_disable_debug) && $opt_disable_debug == 1)
 -                      {
 -                              print "Disabling debug information (-g).\n";
 -                              $config{OPTIMISATI} = "";
 -                      }
 -                      $has_epoll = $config{HAS_EPOLL};
 -                      $has_ports = $config{HAS_PORTS};
 -                      $has_kqueue = $config{HAS_KQUEUE};
 -                      writefiles(1);
 -                      makecache();
 -                      print "Complete.\n";
 -                      exit;
 -              }
 -      };
 -      if ($@)
 -      {
 -              print "Configure update failed: $@\n";
 -      }
 -      exit;
 +if ($config{HAS_KQUEUE} = run_test 'kqueue', test_file($config{CXX}, 'kqueue.cpp')) {
 +      $config{SOCKETENGINE} ||= 'kqueue';
  }
  
 -
 -sub test_compile {
 -      my $feature = shift;
 -      my $fail = 0;
 -      $fail ||= system "$config{CC} -o test_$feature make/check_$feature.cpp >/dev/null 2>&1";
 -      $fail ||= system "./test_$feature";
 -      unlink "test_$feature";
 -      return !$fail;
 +if ($config{HAS_PORTS} = run_test 'Solaris IOCP', test_header($config{CXX}, 'port.h')) {
 +      $config{SOCKETENGINE} ||= 'ports';
  }
  
 -print "Running non-interactive configure...\n" unless $interactive;
 -print "Checking for cache from previous configure... ";
 -print ($cache_loaded ? "found\n" : "not found\n");
 -$config{SYSTEM} = lc $^O;
 -print "Checking operating system version... $config{SYSTEM}\n";
 -
 -$exec = $config{CC} . " -dumpversion | cut -c 1";
 -chomp($config{GCCVER}         = `$exec`);                             # Major GCC Version
 -$exec = $config{CC} . " -dumpversion | cut -c 3";
 -chomp($config{GCCMINOR}               = `$exec`);
 -
 -printf "Checking if stdint.h exists... ";
 -$config{HAS_STDINT} = "true";
 -our $fail = 0;
 -open(STDINT, "</usr/include/stdint.h") or $config{HAS_STDINT} = "false";
 -if ($config{HAS_STDINT} eq "true") {
 -      close(STDINT);
 +if ($config{HAS_POLL} = run_test 'poll', test_header($config{CXX}, 'poll.h')) {
 +      $config{SOCKETENGINE} ||= 'poll';
  }
 -print "yes\n" if $config{HAS_STDINT} eq "true";
 -print "no\n" if $config{HAS_STDINT} eq "false";
 -
 -printf "Checking if strlcpy exists... ";
 -# Perform the strlcpy() test..
 -$config{HAS_STRLCPY} = "false";
 -$fail = 0;
 -open(STRLCPY, "</usr/include/string.h") or $fail = 1;
 -if (!$fail) {
 -      while (defined(my $line = <STRLCPY>)) {
 -              chomp($line);
 -              # try and find the delcaration of:
 -              # size_t strlcpy(...)
 -              if ($line =~ /size_t(\0x9|\s)+strlcpy/) {
 -                      $config{HAS_STRLCPY} = "true";
 -              }
 -      }
 -      close(STRLCPY);
 -}
 -print "yes\n" if $config{HAS_STRLCPY} eq "true";
 -print "no\n" if $config{HAS_STRLCPY} eq "false";
 -
 -printf "Checking if kqueue exists... ";
 -$has_kqueue = 0;
 -$fail = 0;
 -open(KQUEUE, "</usr/include/sys/event.h") or $fail = 1;
 -if (!$fail) {
 -      while (defined(my $line = <KQUEUE>)) {
 -              chomp($line);
 -              # try and find the delcaration of:
 -              # int kqueue(void);
 -              if ($line =~ /int(\0x9|\s)+kqueue/) {
 -                      $has_kqueue = 1;
 +
 +# Select is available on all platforms
 +$config{HAS_SELECT} = 1;
 +$config{SOCKETENGINE} ||= "select";
 +
 +if (defined $opt_socketengine) {
 +      my $cfgkey = "HAS_" . uc $opt_socketengine;
 +      if ($config{$cfgkey} && -f "src/socketengines/socketengine_$opt_socketengine.cpp") {
 +              $config{SOCKETENGINE} = $opt_socketengine;
 +      } else {
 +              print "Unable to use a socket engine which is not supported on this platform ($opt_socketengine)!\n";
 +              print "Available socket engines are:";
 +              foreach (<src/socketengines/socketengine_*.cpp>) {
 +                      s/src\/socketengines\/socketengine_(\w+)\.cpp/$1/;
 +                      print " $1" if $config{"HAS_" . uc $1};
                }
 +              print "\n";     
 +              exit 1;
        }
 -      close(KQUEUE);
  }
 -print "yes\n" if $has_kqueue == 1;
 -print "no\n" if $has_kqueue == 0;
 -
 -printf "Checking for epoll support... ";
 -$has_epoll = test_compile('epoll');
 -print $has_epoll ? "yes\n" : "no\n";
 -
 -printf "Checking for eventfd support... ";
 -$config{HAS_EVENTFD} = test_compile('eventfd') ? 'true' : 'false';
 -print $config{HAS_EVENTFD} eq 'true' ? "yes\n" : "no\n";
 -
 -printf "Checking if Solaris I/O completion ports are available... ";
 -$has_ports = 0;
 -our $system = `uname -s`;
 -chomp ($system);
 -$has_ports = 1 if ($system eq "SunOS");
 -
 -if ($has_ports) {
 -      my $kernel = `uname -r`;
 -      chomp($kernel);
 -      if (($kernel !~ /^5\.1./)) {
 -              $has_ports = 0;
 -      }
 -}
 -print "yes\n" if $has_ports == 1;
 -print "no\n" if $has_ports == 0;
 -
 -$config{HAS_EPOLL} = $has_epoll;
 -$config{HAS_KQUEUE} = $has_kqueue;
  
 -printf "Checking for libgnutls... ";
 +print "Checking for libgnutls... ";
  if (defined($config{HAS_GNUTLS}) && (($config{HAS_GNUTLS}) || ($config{HAS_GNUTLS} eq "y"))) {
        if (defined($gnutls_ver) && ($gnutls_ver ne "")) {
                print "yes\n";
        $config{HAS_GNUTLS} = "n";
  }
  
 -printf "Checking for openssl... ";
 +print "Checking for openssl... ";
  if (defined($config{HAS_OPENSSL}) && (($config{HAS_OPENSSL}) || ($config{HAS_OPENSSL} eq "y"))) {
        if (defined($openssl_ver) && ($openssl_ver ne "")) {
                print "yes\n";
        $config{HAS_OPENSSL} = "n";
  }
  
 -printf "Checking if you are running an ancient, unsupported OS... ";
 -if ($config{OSNAME} =~ /FreeBSD/i)
 -{
 -      my $version = `uname -r`;
 -      if ($version =~ /^4\./)
 -      {
 -              print "yes.\n";
 -              print "FreeBSD 4.x is no longer supported. By ANYONE.\n";
 -              print "To build, you will need to add the following to CXXFLAGS:\n";
 -              print "\t-L/usr/local/lib -lgnugetopt -DHAVE_DECL_GETOPT=1\n";
 -      }
 -      else
 -      {
 -              print "no ($version)\n";
 -      }
 -}
 -else
 -{
 -      print "no ($config{OSNAME})\n";
 -}
 -
 -################################################################################
 -#                       BEGIN INTERACTIVE PART                              #
 -################################################################################
 -
 -# Clear the Screen..
  if ($interactive)
  {
 -      print "\e[2J\e[0G\e[0d"; # J = Erase in Display, 2 = Entire Screen, (G, d) = Move cursor to (..,..)
 -      my $wholeos = $^O;
 +      # Clear the screen.
 +      system 'tput', 'clear';
 +
 +      my $revision = get_revision();
 +      chomp(my $version = `sh src/version.sh`);
  
 -      my $rev = getrevision();
        # Display Introduction Message..
        print <<"STOP" ;
- Welcome to the \e[1mInspIRCd\e[0m Configuration program! (\e[1minteractive mode\e[0m)
+ Welcome to the \e[1mInspIRCd\e[0m configuration program! (\e[1minteractive mode\e[0m)
  \e[1mPackage maintainers: Type ./configure --help for non-interactive help\e[0m
  
  *** If you are unsure of any of these values, leave it blank for    ***
@@@ -274,108 -501,143 +274,108 @@@ Press \e[1m<RETURN>\e[0m to accept the 
  a new value. Please note: You will \e[1mHAVE\e[0m to read the docs
  dir, otherwise you won't have a config file!
  
 -Your operating system is: \e[1;32m$config{OSNAME}\e[0m ($wholeos)
 -Your InspIRCd revision ID is \e[1;32mr$rev\e[0m
 +Your operating system is: \e[1;32m$^O\e[0m 
  STOP
 -      if ($rev eq "r0") {
 -              print " (Non-SVN build)";
 -      }
 -      print ".\n\n";
 -
 -      $config{CHANGE_COMPILER} = "n";
 -      print "I have detected the following compiler: \e[1;32m$config{CC}\e[0m (version \e[1;32m$config{GCCVER}.$config{GCCMINOR}\e[0m)\n";
 -
 -      while (($config{GCCVER} < 3) || ($config{GCCVER} eq "")) {
 -              print "\e[1;32mIMPORTANT!\e[0m A GCC 2.x compiler has been detected, and
 -should NOT be used. You should probably specify a newer compiler.\n\n";
 -              yesno('CHANGE_COMPILER',"Do you want to change the compiler?");
 -              if ($config{CHANGE_COMPILER} =~ /y/i) {
 -                      print "What command do you want to use to invoke your compiler?\n";
 -                      print "[\e[1;32m$config{CC}\e[0m] -> ";
 -                      chomp($config{CC} = <STDIN>);
 -                      if ($config{CC} eq "") {
 -                              $config{CC} = "g++";
 -                      }
 -                      chomp(my $foo = `$config{CC} -dumpversion | cut -c 1`);
 -                      if ($foo ne "") {
 -                              chomp($config{GCCVER}       = `$config{CC} -dumpversion | cut -c 1`); # we must redo these if we change compilers
 -                              chomp($config{GCCMINOR}     = `$config{CC} -dumpversion | cut -c 3`);
 -                              print "Queried compiler: \e[1;32m$config{CC}\e[0m (version \e[1;32m$config{GCCVER}.$config{GCCMINOR}\e[0m)\n";
 -                              if ($config{GCCVER} < 3) {
 -                                      print "\e[1;32mGCC 2.x WILL NOT WORK!\e[0m. Let's try that again, shall we?\n";
 -                              }
 -                      }
 -                      else {
 -                              print "\e[1;32mWARNING!\e[0m Could not execute the compiler you specified. You may want to try again.\n";
 -                      }
 -              }
 -      }
 +      print "Your InspIRCd version is: \e[1;32m";
 +      print $revision eq 'release' ? substr($version, 9) : substr($revision, 1);
 +      print "\e[0m\n\n";
 +      print "The following compiler has been detected: \e[1;32m$cxx{NAME} $cxx{VERSION}\e[0m ($config{CXX})\n\n";
 +
 +      # Check that the user actually wants this version.
 +      if (index($version, '+') != -1) {
 +              print <<"EOW" ;
 +\e[1;31mWARNING!\e[0m You are building a development version. This contains code which has
 +not been tested as heavily and may contain various faults which could seriously
 +affect the running of your server. It is recommended that you use a stable
 +version instead.
 +
 +You can obtain the latest stable version from https://github.com/inspircd/inspircd/releases
 +or by running `git checkout insp20` if you are installing from Git.
  
 -      print "\n";
 +EOW
 +      exit 1 unless prompt_bool(1, 'I understand this warning and want to continue anyway.', 0);
 +      }
  
        # Directory Settings..
        my $tmpbase = $config{BASE_DIR};
 -      dir_check("do you wish to install the InspIRCd base", "BASE_DIR");
 +      $config{BASE_DIR} = prompt_dir(1, 'What directory do you wish to install the InspIRCd base?', $config{BASE_DIR});
        if ($tmpbase ne $config{BASE_DIR}) {
 -              $config{CONFIG_DIR}      = resolve_directory($config{BASE_DIR}."/conf");           # Configuration Dir
 -              $config{MODULE_DIR}      = resolve_directory($config{BASE_DIR}."/modules");     # Modules Directory
 -              $config{DATA_DIR}        = resolve_directory($config{BASE_DIR}."/data");        # Data Directory
 -              $config{LOG_DIR}         = resolve_directory($config{BASE_DIR}."/logs");        # Log Directory
 -              $config{BINARY_DIR}      = resolve_directory($config{BASE_DIR}."/bin");     # Binary Directory
 +              $config{CONFIG_DIR} = rel2abs($config{BASE_DIR}."/conf");
 +              $config{MODULE_DIR} = rel2abs($config{BASE_DIR}."/modules");
 +              $config{DATA_DIR} = rel2abs($config{BASE_DIR}."/data");
 +              $config{LOG_DIR} = rel2abs($config{BASE_DIR}."/logs");
 +              $config{BINARY_DIR} = rel2abs($config{BASE_DIR}."/bin");
        }
  
 -      dir_check("are the configuration files", "CONFIG_DIR");
 -      dir_check("are the modules to be compiled to", "MODULE_DIR");
 -      dir_check("is the IRCd binary to be placed", "BINARY_DIR");
 -      dir_check("are variable data files to be located in", "DATA_DIR");
 -      dir_check("are the logs to be stored in", "LOG_DIR");
 -      dir_check("do you want the build to take place", "BUILD_DIR");
 -              
 +      $config{BINARY_DIR} = prompt_dir(1, 'In what directory should the InspIRCd binary be placed?', $config{BINARY_DIR});
 +      $config{CONFIG_DIR} = prompt_dir(1, 'In what directory are the configuration files to be stored?', $config{CONFIG_DIR});
 +      $config{DATA_DIR} = prompt_dir(1, 'In what directory are variable data files to be stored?', $config{DATA_DIR});
 +      $config{LOG_DIR} = prompt_dir(1, 'In what directory are log files to be stored?', $config{LOG_DIR});
 +      $config{MODULE_DIR} = prompt_dir(1, 'In what directory are the modules to be placed?', $config{MODULE_DIR});
 +      $config{BUILD_DIR} = prompt_dir(1, 'In what directory do you want the build to take place?', $config{BUILD_DIR});
 +
        my $chose_hiperf = 0;
 -      if ($has_kqueue) {
 -              yesno('USE_KQUEUE',"You are running a BSD operating system, and kqueue\nwas detected. Would you like to enable kqueue support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable kqueue?");
 -              print "\n";
 -              if ($config{USE_KQUEUE} eq "y") {
 +      if ($config{HAS_KQUEUE}) {
 +              $config{USE_KQUEUE} = prompt_bool(1, 'Your operating system has support for the high performance kqueue socket engine. Would you like to enable it?', 1);
 +              if ($config{USE_KQUEUE}) {
 +                      $config{SOCKETENGINE} = "kqueue";
                        $chose_hiperf = 1;
                }
        }
 -      if ($has_epoll) {
 -              yesno('USE_EPOLL',"You are running a Linux 2.6+ operating system, and epoll\nwas detected. Would you like to enable epoll support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable epoll?");
 -              print "\n";
 -              if ($config{USE_EPOLL} eq "y") {
 +      if ($config{HAS_EPOLL}) {
 +              $config{USE_EPOLL} = prompt_bool(1, 'Your operating system has support for the high performance epoll socket engine. Would you like to enable it?', 1);
 +              if ($config{USE_EPOLL}) {
 +                      $config{SOCKETENGINE} = "epoll";
                        $chose_hiperf = 1;
                }
        }
 -      if ($has_ports) {
 -              yesno('USE_PORTS',"You are running Solaris 10.\nWould you like to enable I/O completion ports support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable support for I/O completion ports?");
 -              print "\n";
 -              if ($config{USE_PORTS} eq "y") {
 +      if ($config{HAS_PORTS}) {
 +              $config{USE_PORTS} = prompt_bool(1, 'Your operating system has support for the high performance IOCP socket engine. Would you like to enable it?', 1);
 +              if ($config{USE_PORTS}) {
 +                      $config{SOCKETENGINE} = "ports";
                        $chose_hiperf = 1;
                }
        }
  
 -      if (!$chose_hiperf) {
 -              yesno('USE_POLL', "Would you like to use poll?\n This is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable poll?");
 -              if ($config{USE_POLL} ne "y")
 -              {
 -                      print "No high-performance socket engines are available, or you chose\n";
 -                      print "not to enable one. Defaulting to select() engine.\n\n";
 +      if (!$chose_hiperf && $config{HAS_POLL}) {
 +              $config{USE_POLL} = prompt_bool(1, 'Your operating system has support for the mid performance poll socket engine. Would you like to enable it?', 1);
 +              if ($config{USE_POLL}) {
 +                      $config{SOCKETENGINE} = "poll";
                }
        }
 -
 -      $config{USE_FREEBSD_BASE_SSL} = "n";
 -      $config{USE_FREEBSD_PORTS_SSL} = "n";
 -      if ($config{HAS_OPENSSL_PORT} ne "")
 +      unless ($chose_hiperf || $config{USE_POLL})
        {
 -              $config{USE_FREEBSD_PORTS_SSL} = "y";
 -              print "I have detected the OpenSSL FreeBSD port installed on your system,\n";
 -              print "version \e[1;32m".$config{HAS_OPENSSL_PORT}."\e[0m. Your base system OpenSSL is version \e[1;32m".$openssl_ver."\e[0m.\n\n";
 -              yesno('USE_FREEBSD_PORTS_SSL', "Do you want to use the FreeBSD ports version?");
 -              print "\n";
 -              $config{USE_FREEBSD_BASE_SSL} = "y" if ($config{USE_FREEBSD_PORTS_SSL} eq "n");
 -
 -              if ($config{USE_FREEBSD_BASE_SSL} eq "n")
 -              {
 -                      # update to port version
 -                      $openssl_ver = $config{HAS_OPENSSL_PORT};
 -              }
 -      }
 -      else
 -      {
 -              $config{USE_FREEBSD_BASE_SSL} = "y" if ($^O eq "freebsd");
 +              print "No high-performance socket engines are available, or you chose not to enable one. Defaulting to select() engine.\n\n";
 +              $config{SOCKETENGINE} = "select";
        }
  
 -      $config{USE_SSL} = "n";
 -      $config{MODUPDATE} = 'n';
 -
        if ($config{HAS_GNUTLS} eq "y" || $config{HAS_OPENSSL} eq "y")
        {
                print "Detected GnuTLS version: \e[1;32m" . $gnutls_ver . "\e[0m\n";
                print "Detected OpenSSL version: \e[1;32m" . $openssl_ver . "\e[0m\n\n";
  
 -              yesno('USE_SSL', "One or more SSL libraries detected. Would you like to enable SSL support?");
 -              if ($config{USE_SSL} eq "y")
 +              $config{USE_SSL} = prompt_bool(1, 'One or more SSL libraries detected. Would you like to enable SSL support?', 1);
 +              if ($config{USE_SSL})
                {
                        if ($config{HAS_GNUTLS} eq "y")
                        {
 -                              yesno('USE_GNUTLS',"Would you like to enable SSL with m_ssl_gnutls? (recommended)");
 -                              if ($config{USE_GNUTLS} eq "y")
 +                              $config{USE_GNUTLS} = prompt_bool(1, 'Would you like to enable SSL with m_ssl_gnutls (recommended)?', 1);
 +                              if ($config{USE_GNUTLS})
                                {
 -                                      print "\nUsing GnuTLS SSL module.\n";
 +                                      print "Using GnuTLS SSL module.\n\n";
 +                                      unlink 'src/modules/m_ssl_gnutls.cpp' if -f 'src/modules/m_ssl_gnutls.cpp';
 +                                      symlink "extra/m_ssl_gnutls.cpp", "src/modules/m_ssl_gnutls.cpp" or print STDERR "Symlink failed: $!\n";
                                }
                        }
  
                        if ($config{HAS_OPENSSL} eq "y")
                        {
 -                              yesno('USE_OPENSSL', "Would you like to enable SSL with m_ssl_openssl?");
 -                              if ($config{USE_OPENSSL} eq "y")
 +                              $config{USE_OPENSSL} = prompt_bool(1, 'Would you like to enable SSL with m_ssl_openssl (recommended)?', 1);
 +                              if ($config{USE_OPENSSL})
                                {
 -                                      print "\nUsing OpenSSL SSL module.\nYou will get better performance if you move to GnuTLS in the future.\n";
 +                                      print "Using OpenSSL SSL module.\n\n";
 +                                      unlink 'src/modules/m_ssl_openssl.cpp' if -f 'src/modules/m_ssl_openssl.cpp';
 +                                      symlink "extra/m_ssl_openssl.cpp", "src/modules/m_ssl_openssl.cpp" or print STDERR "Symlink failed: $!\n";
                                }
                        }
                }
                print "\nCould not detect OpenSSL or GnuTLS. Make sure pkg-config is installed and\n";
                print "is in your path.\n\n";
        }
 -
 -      yesno('MODUPDATE',"Would you like to check for updates to third-party modules?");
 -      print "\n";
 -      if ($config{MODUPDATE} eq "y") {
 -              print "Checking for upgrades to extra and third-party modules... ";
 -              system "./modulemanager upgrade";
 -      }
  }
  
  # We are on a POSIX system, we can enable POSIX extras without asking
  symlink "extra/m_regex_posix.cpp", "src/modules/m_regex_posix.cpp";
  
 -dumphash();
 -
 -if (($config{USE_GNUTLS} eq "y") && ($config{HAS_GNUTLS} ne "y"))
 +if (($config{USE_GNUTLS}) && ($config{HAS_GNUTLS} ne "y"))
  {
-       print "Sorry, but i couldn't detect gnutls. Make sure pkg-config is in your path.\n";
 -      print "Sorry, but I couldn't detect GnuTLS. Make sure gnutls-config is in your path.\n";
 -      exit(0);
++      print "Sorry, but I couldn't detect GnuTLS. Make sure pkg-config is in your path.\n";
 +      exit 1;
  }
 -if (($config{USE_OPENSSL} eq "y") && ($config{HAS_OPENSSL} ne "y"))
 +if (($config{USE_OPENSSL}) && ($config{HAS_OPENSSL} ne "y"))
  {
-       print "Sorry, but i couldn't detect openssl. Make sure pkg-config is in your path.\n";
 -      print "Sorry, but I couldn't detect OpenSSL. Make sure openssl is in your path.\n";
 -      exit(0);
++      print "Sorry, but I couldn't detect OpenSSL. Make sure pkg-config is in your path.\n";
 +      exit 1;
  }
 -our $failed = 0;
 -
 -$config{CERTGEN} ||= 'y';
 -yesno('CERTGEN',"Would you like to generate SSL certificates now?") if ($interactive && ($config{USE_GNUTLS} eq "y" || $config{USE_OPENSSL} eq "y"));
  
 -if ($config{USE_GNUTLS} eq "y") {
 -      unless (-r "src/modules/m_ssl_gnutls.cpp") {
 -              print "Symlinking src/modules/m_ssl_gnutls.cpp from extra/\n";
 -              symlink "extra/m_ssl_gnutls.cpp", "src/modules/m_ssl_gnutls.cpp" or print STDERR "Symlink failed: $!";
 -      }
 -      if ($interactive && $config{CERTGEN} eq 'y')
 -      {
 -              unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem") {
 -                      print "SSL certificates not found, generating.. \n\n
 -*************************************************************
 -* Generating the private key may take some time, once done, *
 -* answer the questions which follow. If you are unsure,     *
 -* just hit enter!                                           *
 -*************************************************************\n\n";
 -                      $failed = make_gnutls_cert();
 -                      if ($failed) {
 -                              print "\n\e[1;32mCertificate generation failed!\e[0m\n\n";
 -                      } else {
 +if ($config{USE_GNUTLS} || $config{USE_OPENSSL}) {
 +      if (my $val = prompt_bool($interactive, 'Would you like to generate SSL certificates now?', $interactive)) {
 +              unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem" && -r "$config{CONFIG_DIR}/dhparams.pem") {
 +                      unless (system './tools/genssl auto') {
                                print "\nCertificate generation complete, copying to config directory... ";
                                File::Copy::move("key.pem", "$config{CONFIG_DIR}/key.pem") or print STDERR "Could not copy key.pem!\n";
                                File::Copy::move("cert.pem", "$config{CONFIG_DIR}/cert.pem") or print STDERR "Could not copy cert.pem!\n";
 +                              File::Copy::move("dhparams.pem", "$config{CONFIG_DIR}/dhparams.pem") or print STDERR "Could not copy dhparams.pem!\n";
                                print "Done.\n\n";
                        }
 -              }
 -              else {
 -                      print "SSL certificates found, skipping.\n\n";
 -              }
 -      }
 -      else
 -      {
 -              print "Skipping SSL certificate generation\nin non-interactive mode.\n\n";
 -      }
 -}
 -
 -if ($config{USE_OPENSSL} eq "y") {
 -      unless (-r "src/modules/m_ssl_openssl.cpp") {
 -              print "Symlinking src/modules/m_ssl_openssl.cpp from extra/\n";
 -              symlink "extra/m_ssl_openssl.cpp", "src/modules/m_ssl_openssl.cpp" or print STDERR "Symlink failed: $!";
 -      }
 -      $failed = 0;
 -      if ($interactive && $config{CERTGEN} eq 'y')
 -      {
 -              unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem") {
 -                      print "SSL certificates not found, generating.. \n\n
 -*************************************************************
 -* Generating the certificates may take some time, go grab a *
 -* coffee or something.                                            *
 -*************************************************************\n\n";
 -                      make_openssl_cert();
 -                      print "\nCertificate generation complete, copying to config directory... ";
 -                      File::Copy::move("key.pem", "$config{CONFIG_DIR}/key.pem") or print STDERR "Could not copy key.pem!\n";
 -                      File::Copy::move("cert.pem", "$config{CONFIG_DIR}/cert.pem") or print STDERR "Could not copy cert.pem!\n";
 -                      File::Copy::move("dhparams.pem", "$config{CONFIG_DIR}/dhparams.pem") or print STDERR "Could not copy dhparams.pem!\n";
 -                      print "Done.\n\n";
                } else {
-                       print "SSL Certificates found, skipping.\n\n"
+                       print "SSL certificates found, skipping.\n\n"
                }
 +      } else {
 +              print "Skipping SSL certificate generation in non-interactive mode.\n\n";
        }
 -      else
 -      {
 -              print "Skipping SSL certificate generation\nin non-interactive mode.\n\n";
 -      }
 -}
 -if (($config{USE_GNUTLS} eq "n") && ($config{USE_OPENSSL} eq "n")) {
 -      print "Skipping SSL certificate generation as SSL support is not available.\n\n";
 +} else {
 +      print "Skipping SSL Certificate generation, SSL support is not available.\n\n";
  }
  
 -depcheck();
 -writefiles(1);
 -makecache();
 +print "Writing \e[1;32m.config.cache\e[0m ...\n";
 +write_configure_cache(%config);
 +writefiles();
 +dump_hash();
  
 -print "\n\n";
 +print "\n";
  print "To build your server with these settings, please run '\e[1;32mmake\e[0m' now.\n";
 -if (($config{USE_GNUTLS} eq "y") || ($config{USE_OPENSSL} eq "y")) {
 +if ($config{USE_GNUTLS} || $config{USE_OPENSSL}) {
        print "Please note: for \e[1;32mSSL support\e[0m you will need to load required\n";
        print "modules in your config. This configure script has added those modules to the\n";
-       print "build process. For more info please refer to:\n";
+       print "build process. For more info, please refer to:\n";
        print "\e[1;32mhttp://wiki.inspircd.org/Installation_From_Tarball\e[0m\n";
  }
 -print "*** \e[1;32mRemember to edit your configuration files!!!\e[0m ***\n\n\n";
 -if (($config{OSNAME} eq "OpenBSD") && ($config{CC} ne "eg++")) {
 -      print "\e[1;32mWARNING!\e[0m You are running OpenBSD but you are using the base gcc package\nrather than eg++. This compile will most likely fail, but I'm letting you\ngo ahead with it anyway, just in case I'm wrong :-)\n";
 -}
 -
 -if ($config{GCCVER} < "3") {
 -      print <<FOO2;
 -\e[1;32mWARNING!\e[0m You are attempting to compile InspIRCd on GCC 2.x!
 -GCC 2.x series compilers only had partial (read as broken) C++ support, and
 -your compile will most likely fail horribly! If you have any problems, do NOT
 -report them to the bugtracker or forums without first upgrading your compiler
 -to a newer 3.x or 4.x (or whatever is available currently) version.
 -FOO2
 -}
 -
 -################################################################################
 -#                           HELPER FUNCTIONS                          #
 -################################################################################
 -sub getcache {
 -      # Retrieves the .config.cache file, and loads values into the main config hash.
 -      open(CACHE, ".config.cache") or return 0;
 -      while (<CACHE>) {
 -              chomp;
 -              # Ignore Blank lines, and comments..
 -              next if /^\s*$/;
 -              next if /^\s*#/;
 -              my ($key, $value) = split("=", $_, 2);
 -              $value =~ /^\"(.*)\"$/;
 -              # Do something with data here!
 -              $config{$key} = $1;
 -      }
 -      close(CACHE);
 -      return 1;
 -}
 -
 -sub makecache {
 -      # Dump the contents of %config
 -      print "Writing \e[1;32mcache file\e[0m for future ./configures ...\n";
 -      open(FILEHANDLE, ">.config.cache");
 -      foreach my $key (keys %config) {
 -              print FILEHANDLE "$key=\"$config{$key}\"\n";
 -      }
 -      close(FILEHANDLE);
 -}
 -
 -sub dir_check {
 -      my ($desc, $hash_key) = @_;
 -      my $complete = 0;
 -      while (!$complete) {
 -              print "In what directory $desc?\n";
 -              print "[\e[1;32m$config{$hash_key}\e[0m] -> ";
 -              chomp(my $var = <STDIN>);
 -              if ($var eq "") {
 -                      $var = $config{$hash_key};
 -              }
 -              if ($var =~ /^\~\/(.+)$/) {
 -                      # Convert it to a full path..
 -                      $var = resolve_directory($ENV{HOME} . "/" . $1);
 -              }
 -              elsif ((($config{OSNAME} =~ /MINGW32/i) and ($var !~ /^[A-Z]{1}:\\.*/)) and (substr($var,0,1) ne "/"))
 -              {
 -                      # Assume relative Path was given.. fill in the rest.
 -                      $var = $this . "/$var";
 -              }
 -
 -              $var = resolve_directory($var);
 -              if (! -e $var) {
 -                      print "$var does not exist. Create it?\n[\e[1;32my\e[0m] ";
 -                      chomp(my $tmp = <STDIN>);
 -                      if (($tmp eq "") || ($tmp =~ /^y/i)) {
 -                              # Attempt to Create the Dir..
 -                              my $chk = eval {
 -                                      use File::Path ();
 -                                      File::Path::mkpath($var, 0, 0777);
 -                                      1;
 -                              };
 -                              unless (defined($chk) && -d $var) {
 -                                      print "Unable to create directory. ($var)\n\n";
 -                                      # Restart Loop..
 -                                      next;
 -                              }
 -                      } else {
 -                              # They said they don't want to create, and we can't install there.
 -                              print "\n\n";
 -                              next;
 -                      }
 -              } else {
 -                      if (!is_dir($var)) {
 -                              # Target exists, but is not a directory.
 -                              print "File $var exists, but is not a directory.\n\n";
 -                              next;
 -                      }
 -              }
 -              # Either Dir Exists, or was created fine.
 -              $config{$hash_key} = $var;
 -              $complete = 1;
 -              print "\n";
 -      }
 -}
 -
 -our $SHARED = "";
 -
 -my ($mliflags, $mfrules, $mobjs, $mfcount) = ("", "", "", 0);
 +print "*** \e[1;32mRemember to edit your configuration files!!!\e[0m ***\n\n";
  
  sub writefiles {
 -      my($writeheader) = @_;
 -      # First File.. inspircd_config.h
        chomp(my $incos = `uname -n -s -r`);
        chomp(my $version = `sh src/version.sh`);
 -      chomp(my $revision2 = getrevision());
 +      my $revision = get_revision();
        my $branch = "InspIRCd-0.0";
        if ($version =~ /^(InspIRCd-[0-9]+\.[0-9]+)\.[0-9]+/)
        {
                $branch = $1;
        }
 -      if ($writeheader == 1)
 -      {
 -              print "Writing \e[1;32minspircd_config.h\e[0m\n";
 -              open(FILEHANDLE, ">include/inspircd_config.h.tmp");
 -              print FILEHANDLE <<EOF;
 +      print "Writing \e[1;32mconfig.h\e[0m\n";
 +      open(FILEHANDLE, ">include/config.h.tmp");
 +      print FILEHANDLE <<EOF;
  /* Auto generated by configure, do not modify! */
 -#ifndef __CONFIGURATION_AUTO__
 -#define __CONFIGURATION_AUTO__
 +#pragma once
  
 -/* this is for windows support. */
 -#define CoreExport /**/
 -#define DllExport /**/
 +#define BRANCH "$branch"
 +#define VERSION "$version"
 +#define REVISION "$revision"
 +#define SYSTEM "$incos"
 +#define INSPIRCD_SOCKETENGINE_NAME "$config{SOCKETENGINE}"
  
  #define CONFIG_PATH "$config{CONFIG_DIR}"
  #define DATA_PATH "$config{DATA_DIR}"
  #define LOG_PATH "$config{LOG_DIR}"
  #define MOD_PATH "$config{MODULE_DIR}"
 -#define SOMAXCONN_S "$config{_SOMAXCONN}"
 -#define ENTRYPOINT int main(int argc, char** argv)
  
  EOF
 -print FILEHANDLE "#define MAXBUF " . ($config{MAXBUF}+2) . "\n";
  
 -              if ($config{OSNAME} =~ /SunOS/i) {
 -                      print FILEHANDLE "#define IS_SOLARIS\n";
 -              }
 -              if ($config{OSNAME} =~ /MINGW32/i) {
 -                      print FILEHANDLE "#define IS_MINGW\n";
 -              }
 -              if ($config{GCCVER} >= 3) {
 -                      print FILEHANDLE "#define GCC3\n";
 -              }
 -              if ($config{HAS_STRLCPY} eq "true") {
 -                      print FILEHANDLE "#define HAS_STRLCPY\n";
 -              }
 -              if ($config{HAS_STDINT} eq "true") {
 -                      print FILEHANDLE "#define HAS_STDINT\n";
 -              }
 -              if ($config{HAS_EVENTFD} eq 'true') {
 -                      print FILEHANDLE "#define HAS_EVENTFD\n";
 -              }
 -              if ($config{OSNAME} !~ /DARWIN/i) {
 -                      print FILEHANDLE "#define HAS_CLOCK_GETTIME\n";
 -              }
 -              my $use_hiperf = 0;
 -              if (($has_kqueue) && ($config{USE_KQUEUE} eq "y")) {
 -                      print FILEHANDLE "#define USE_KQUEUE\n";
 -                      $config{SOCKETENGINE} = "socketengine_kqueue";
 -                      $use_hiperf = 1;
 -              }
 -              if (($has_epoll) && ($config{USE_EPOLL} eq "y")) {
 -                      print FILEHANDLE "#define USE_EPOLL\n";
 -                      $config{SOCKETENGINE} = "socketengine_epoll";
 -                      $use_hiperf = 1;
 -              }
 -              if (($has_ports) && ($config{USE_PORTS} eq "y")) {
 -                      print FILEHANDLE "#define USE_PORTS\n";
 -                      $config{SOCKETENGINE} = "socketengine_ports";
 -                      $use_hiperf = 1;
 -              }
 -              # user didn't choose either epoll or select for their OS.
 -              # default them to USE_SELECT (ewwy puke puke)
 -              if (!$use_hiperf) {
 -                      print "no hi-perf, " . $config{USE_POLL};
 -                      if ($config{USE_POLL} eq "y")
 -                      {
 -                              print FILEHANDLE "#define USE_POLL\n";
 -                              $config{SOCKETENGINE} = "socketengine_poll";
 -                      }
 -                      else
 -                      {
 -                              print FILEHANDLE "#define USE_SELECT\n";
 -                              $config{SOCKETENGINE} = "socketengine_select";
 -                      }
 -              }
 -              print FILEHANDLE "\n#include \"threadengines/threadengine_pthread.h\"\n\n#endif\n";
 -              close(FILEHANDLE);
 -
 -              open(FILEHANDLE, ">include/inspircd_version.h.tmp");
 -              print FILEHANDLE <<EOF;
 -#define BRANCH "$branch"
 -#define VERSION "$version"
 -#define REVISION "$revision2"
 -#define SYSTEM "$incos"
 -EOF
 -              close FILEHANDLE;
 -
 -              for my $file (qw(include/inspircd_config.h include/inspircd_version.h)) {
 -                      my $diff = 0;
 -                      open my $fh1, $file or $diff = 1;
 -                      open my $fh2, $file.'.tmp' or die "Can't read $file.tmp that we just wrote: $!";
 -                      while (!$diff) {
 -                              my $line1 = <$fh1>;
 -                              my $line2 = <$fh2>;
 -                              if (defined($line1) != defined($line2)) {
 -                                      $diff = 1;
 -                              } elsif (!defined $line1) {
 -                                      last;
 -                              } else {
 -                                      $diff = ($line1 ne $line2);
 -                              }
 -                      }
 -                      if ($diff) {
 -                              unlink $file;
 -                              rename "$file.tmp", $file;
 -                      } else {
 -                              unlink "$file.tmp";
 -                      }
 -              }
 +      if ($config{HAS_EVENTFD}) {
 +              print FILEHANDLE "#define HAS_EVENTFD\n";
        }
 +      if ($config{HAS_CLOCK_GETTIME}) {
 +              print FILEHANDLE "#define HAS_CLOCK_GETTIME\n";
 +      }
 +
 +      print FILEHANDLE "\n#include \"threadengines/threadengine_pthread.h\"\n";
 +      close(FILEHANDLE);
  
 -      # Write all .in files.
 -      my $tmp = "";
 -      my $file = "";
 -      my $exe = "inspircd";
 +      unlink 'include/config.h';
 +      rename 'include/config.h.tmp', 'include/config.h';
  
        # Do this once here, and cache it in the .*.inc files,
        # rather than attempting to read src/version.sh from
        # compiled code -- we might not have the source to hand.
 -      # Fix for bug#177 by Brain.
 -
 -      chomp($version = `sh ./src/version.sh`);
 -      chomp(my $revision = getrevision());
 -      $version = "$version(r$revision)";
 -
 -      # We can actually parse any file starting with . and ending with .inc,
 -      # but right now we only parse .inspircd.inc to form './inspircd'
 -      prepare_dynamic_makefile();
  
        my @dotfiles = qw(main.mk inspircd);
 -      push @dotfiles, 'org.inspircd.plist' if $config{OSNAME} eq 'darwin';
 +      push @dotfiles, 'org.inspircd.plist' if $^O eq 'darwin';
  
        foreach my $file (@dotfiles) {
                open(FILEHANDLE, "make/template/$file") or die "Can't open make/template/$file: $!";
                $_ = join '', <FILEHANDLE>;
                close(FILEHANDLE);
  
 -              $config{BUILD_DIR} ||= resolve_directory($config{ME}."/build");
 +              $config{BUILD_DIR} ||= rel2abs($topdir."/build");
 +              $config{COMPILER} = lc $cxx{NAME};
 +              $config{SYSTEM} = lc $^O;
  
                for my $var (qw(
 -                      CC SYSTEM BASE_DIR CONFIG_DIR MODULE_DIR BINARY_DIR BUILD_DIR DATA_DIR UID
 -                      STARTSCRIPT DESTINATION SOCKETENGINE
 +                      CXX COMPILER SYSTEM BASE_DIR CONFIG_DIR MODULE_DIR BINARY_DIR BUILD_DIR DATA_DIR UID
 +                      STARTSCRIPT SOCKETENGINE
                )) {
                        s/\@$var\@/$config{$var}/g;
                }
 -              s/\@EXECUTABLE\@/$exe/ if defined $exe;
 +
                s/\@VERSION\@/$version/ if defined $version;
  
                if ($file eq 'main.mk') {
                        s/\@IFDEF (\S+)/ifdef $1/g;
                        s/\@IFNDEF (\S+)/ifndef $1/g;
                        s/\@IFEQ (\S+) (\S+)/ifeq ($1,$2)/g;
 +                      s/\@IFNEQ (\S+) (\S+)/ifneq ($1,$2)/g;
                        s/\@ELSIFEQ (\S+) (\S+)/else ifeq ($1,$2)/g;
                        s/\@ELSE/else/g;
                        s/\@ENDIF/endif/g;
                        s/\@IFDEF (\S+)/.if defined($1)/g;
                        s/\@IFNDEF (\S+)/.if !defined($1)/g;
                        s/\@IFEQ (\S+) (\S+)/.if $1 == $2/g;
 +                      s/\@IFNEQ (\S+) (\S+)/.if $1 != $2/g;
                        s/\@ELSIFEQ (\S+) (\S+)/.elif $1 == $2/g;
                        s/\@ELSE/.else/g;
                        s/\@ENDIF/.endif/g;
                }
        }
  
 -      chmod 0755, 'inspircd';
 -}
 -
 -sub depcheck
 -{
 -      getmodules();
 -      for my $mod (@modlist) {
 -              getcompilerflags("src/modules/m_$mod.cpp");
 -              getlinkerflags("src/modules/m_$mod.cpp");
 -      }
 -}
 -
 -sub prepare_dynamic_makefile
 -{
 -      my $i = 0;
 -
 -      if (!$has_epoll)
 -      {
 -              $config{USE_EPOLL} = 0;
 -      }
 -      if (!$has_kqueue)
 -      {
 -              $config{USE_KQUEUE} = 0;
 -      }
 -      if (!$has_ports)
 -      {
 -              $config{USE_PORTS} = 0;
 -      }
 +      chmod 0750, 'inspircd';
  }
  
  # Routine to list out the extra/ modules that have been enabled.
@@@ -610,7 -1151,7 +610,7 @@@ EXTRA:    for my $extra (@extras) 
        for my $extra (keys(%extras)) {
                next unless $extras{$extra} =~ m/enabled/; # only process enabled extras.
                my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
 -              my @deps = split / +/, getdependencies($abs_extra);
 +              my @deps = split /\s+/, get_property($abs_extra, 'ModDep');
                for my $dep (@deps) {
                        if (exists($extras{$dep})) {
                                my $ref = \$extras{$dep}; # Take reference.
@@@ -657,10 -1198,10 +657,10 @@@ sub enable_extras (@) 
                        next;
                }
                # Get dependencies, and add them to be processed.
 -              my @deps = split / +/, getdependencies($extrapath);
 +              my @deps = split /\s+/, get_property($extrapath, 'ModDep');
                for my $dep (@deps) {
                        next if scalar(grep { $_ eq $dep } (@extras)) > 0; # Skip if we're going to be enabling it anyway.
 -                      if (!-e "src/modules/$dep") {
 +                      if (!-e "src/modules/$dep" && !-e "include/$dep") {
                                if (-e "src/modules/extra/$dep") {
                                        print STDERR "Will also enable extra \e[32;1m$dep\e[0m (needed by \e[32;1m$extra\e[0m)\n";
                                        push @extras, $dep;
@@@ -693,7 -1234,7 +693,7 @@@ EXTRA:    for my $extra (@extras) 
                }
                # Check if anything needs this.
                for my $file (@files) {
 -                      my @deps = split / +/, getdependencies("src/modules/extra/$file");
 +                      my @deps = split /\s+/, get_property("src/modules/extra/$file", 'ModDep');
                        # File depends on this extra...
                        if (scalar(grep { $_ eq $extra } @deps) > 0) {
                                # And is both enabled and not about to be disabled.
index c7d672107b357537e9758bb2cbfd1db7359bff44,556d4c1a832d662ed692675b379688c5e2a3758e..7b819845ff3c960130ff118f92cb5066f5b3d9ec
@@@ -34,7 -34,7 +34,7 @@@ UNINVITE  AWAY     DCCALLOW  SILENCE   
  MKPASSWD  VHOST    TITLE     SETNAME
  
  WHOIS     WHOWAS   ISON      USERHOST  WATCH
 -LIST      NAMES    WHO       MOTD      RULES
 +LIST      NAMES    WHO       MOTD
  ADMIN     MAP      LINKS     LUSERS    TIME
  STATS     VERSION  INFO      MODULES   COMMANDS
  SSLINFO
@@@ -72,7 -72,7 +72,7 @@@ private message you when you have userm
  a nick to your accept list, ACCEPT -nick removes a nick from your
  accept list, and ACCEPT * displays your accept list.">
  
- <helpop key="cycle" value="/CYCLE [channel]
+ <helpop key="cycle" value="/CYCLE [channel] :[reason]
  
  Cycles a channel (leaving and rejoining), overrides restrictions that
  would stop a new user joining, such as user limits and channel keys.">
@@@ -97,21 -97,16 +97,21 @@@ 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
+ This behaves identically to /REMOVE, the only difference is that the
  [channel] and [nick] parameters are switched around to match /KICK's
  syntax. Also, /REMOVE is a builtin mIRC command which caused trouble
- for some users. This feature was added in the 1.1 branch.">
+ for some users.">
  
  <helpop key="devoice" value="/DEVOICE [channel]
  
- Devoices yourself from the specified channel.">
+ Devoices yourself on the specified channel.">
  
  <helpop key="silence" value="/SILENCE [+/-][hostmask] [p|c|i|n|t|a|x]
  
@@@ -129,7 -124,7 +129,7 @@@ Valid SILENCE Flag
   a        Block all of the above
   x        Exception
  
- Multiple letters may be specified. For an exception, you msut pair x
+ Multiple letters may be specified. For an exception, you must pair x
  with what you want excepted. For example, if you wanted to except
  everything from people with a host matching *.foo.net, you would do
  /SILENCE +*!*@*.foo.net xa
@@@ -162,7 -157,7 +162,7 @@@ Returns the server's version informatio
  
  <helpop key="ping" value="/PING [server]
  
- Ping a server. Target server will answer with a PONG.">
+ Ping a server. The server will answer with a PONG.">
  
  <helpop key="pong" value="/PONG [server]
  
@@@ -196,11 -191,11 +196,11 @@@ Leaves one or more channels you specify
  <helpop key="kick" value="/KICK [channel] [nick] {[reason]}
  
  Kicks a user from a channel you specify. You must be
At least a channel halfoperator to kick a user.">
at least a channel halfoperator to kick a user.">
  
  <helpop key="mode" value="/MODE [target] [+|-][modes]{[+|-][modes]} {mode parameters}
  
- Sets the mode for a channel or a nickname specified in [target]
+ Sets the mode for a channel or a nickname specified in [target].
  A user may only set modes upon themselves, and may not set the
  +o usermode, and a user may only change channel modes of
  channels where they are at least a halfoperator.
@@@ -234,7 -229,8 +234,8 @@@ The following flags after the mask hav
  
   a      Show all users who have an away message matching the given mask
   i      Show all users who have an ident (username) matching the given mask
-  p      Show all users who are connected on the given port number
+  p      Show all users who are connected on the given port number (IRC
+         operators only)
   r      Show all users whose realnames match the mask. When this
          flag is set it overrides the meaning of the search-pattern,
          which must contain a glob pattern intended to match GECOS
          this flag is set it overrides the meaning of the
          search-pattern, which must contain the mode sequence to
          search for, for example to find all users with +i and
-         without +s, issue the command WHO +i-s m.
+         without +s, issue the command WHO +i-s m (IRC operators only)
   t      Show users connected within this number of seconds
   M      Show all users who have metadata attached to them with
-         the given key name
+         the given key name (IRC operators only)
  
   f      Show only remote (far) users
   l      Show only local users
@@@ -264,6 -260,11 +265,6 @@@ Show the message of the day for [server
  contain important server rules and notices and should be read prior
  to using a server.">
  
 -<helpop key="rules" value="/RULES
 -
 -Show the rules file for the local server. This is similar in effect to
 -except that these are not sent automatically on connect.">
 -
  <helpop key="oper" value="/OPER [login] [password]
  
  Attempts to authenticate a user as an IRC operator.
@@@ -373,7 -374,7 +374,7 @@@ SAJOIN    SAPART    SAMODE      SATOPI
  
  KILL      SAQUIT    GLINE       ZLINE    QLINE
  KLINE     RLINE     ELINE       CBAN     SHUN
 -FILTER    OJOIN
 +FILTER    OJOIN     CLEARCHAN
  
  CONNECT   SQUIT     RCONNECT    RSQUIT
  
@@@ -383,9 -384,9 +384,9 @@@ RELOADMODULE   GLOADMODULE  GUNLOADMODU
  GRELOADMODULE  RELOAD       CLOSE
  LOCKSERV       UNLOCKSERV   JUMPSERVER">
  
- <helpop key="userip" value="/USERIP [nickname]
+ <helpop key="userip" value="/USERIP [nicknames]
  
- Returns the ip and nickname of a user.">
+ Returns the ip and nickname of the given users.">
  
  <helpop key="tline" value="/TLINE [host or ip mask]
  
@@@ -452,12 -453,13 +453,13 @@@ n    Block private and channel notice
  P    Block part messages
  q    Block quit messages
  o    Don't match against opers
+ c    Strip all color codes from the message before matching
  *    Represents all of the above flags
  -    Does nothing, a non-op for when you do not want to specify any
       flags
  
  The reason for the filter will be used as the reason for the action,
- unless the ation is 'none', and is sent to the user when their text is
+ unless the action is 'none', and is sent to the user when their text is
  blocked by 'block' and 'silent' actions.
  
  A gline duration may be specified in seconds, or in the format
@@@ -562,7 -564,7 +564,7 @@@ the result."
  
  <helpop key="opermotd" value="/OPERMOTD
  
Redisplays the Oper MOTD.">
Displays the Oper MOTD.">
  
  <helpop key="nicklock" value="/NICKLOCK [nick] [new nick]
  
@@@ -587,7 -589,7 +589,7 @@@ Changes the ident of the user to the ne
  
  <helpop key="shun" value="/SHUN [nick!user@host] {[duration] :[reason]}
  
- Sets or removes a shun (serverside ignore) on a host and ident mask.
+ Sets or removes a shun (server side ignore) on a host and ident mask.
  You must specify all three parameters to add a shun, and one parameter
  to remove a shun (just the nick!user@host section).
  
@@@ -603,7 -605,7 +605,7 @@@ command to function."
  
  <helpop key="restart" value="/RESTART [password]
  
- This command restarts down the local server. A single parameter is
+ This command restarts the local server. A single parameter is
  required, which must match the password in the configuration for the
  command to function.">
  
@@@ -615,21 -617,21 +617,21 @@@ Shows all currently available commands.
  
  This command will disconnect a user from IRC with the given reason.">
  
- <helpop key="rehash" value="/REHASH
+ <helpop key="rehash" value="/REHASH [mask]
  
  This command will cause the server configuration file to be reread and
- values reinitialized for all servers matchin the server mask, or the
+ values reinitialized for all servers matching the server mask, or the
  local server if one is not specified.">
  
  <helpop key="connect" value="/CONNECT [servermask]
  
- Add a connection to the server matching the given servermask. You must
+ Add a connection to the server matching the given server mask. You must
  have configured the server for linking in your configuration file
  before trying to link them.">
  
  <helpop key="squit" value="/SQUIT [servermask]
  
- Disconnects the server matching the given servermask from this server.">
+ Disconnects the server matching the given server mask from this server.">
  
  <helpop key="modules" value="/MODULES
  
@@@ -754,16 -756,6 +756,16 @@@ Reloads the specified core command."
  
  Closes all unregistered connections to the local server.">
  
 +<helpop key="clearchan" value="/CLEARCHAN <channel> [<KILL|KICK|G|Z>] [<reason>]
 +
 +Quits or kicks all non-opers from a channel, optionally G/Z-Lines them.
 +Useful for quickly nuking bot channels.
 +
 +The default method, KILL, simply disconnects the victims from the server,
 +while methods G and Z also add G/Z-Lines for all the targets.
 +
 +When used, the victims won't see each other getting kicked or quitting.">
 +
  ######################
  # User/Channel Modes #
  ######################
                commonchans module).
   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
+  g            In combination with /ACCEPT, provides for server side
                ignore (requires callerid module).
   h            Marks as 'available for help' in WHOIS (IRCop only,
                requires helpop module).
                hideoper module).
   I            Hides a user's entire channel list in WHOIS from
                non-IRCops (requires hidechans module).
-  Q            Makes an operator invisible, preventing users from
-               seeing their presence, including in channel user lists
-               (IRCop only, requires invisible module).
   R            Blocks private messages from unregistered users
                (requires services account module).
   S            Strips mIRC color/bold/underline codes out of private
  
   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
                      channel (requirs stripcolor module).
   T                  Blocks /NOTICEs to the channel from users who are
                      not at least halfop (requires nonotice module).
-  
   g [mask]           Blocks messages matching the given blob mask
                      (requires chanfilter module).
   X [mode]           Makes channel operators immune to the specified
@@@ -934,16 -920,16 +933,16 @@@ symbols may be only available to opers
  
  Valid symbols are:
  
- e  Show e-lines (local ban exemptions)
- g  Show g-lines (host bans)
- k  Show k-lines (local host bans)
- q  Show q-lines (nick mask bans)
- Z  Show z-lines (IP mask bans)
+ e  Show E-lines (local ban exemptions)
+ g  Show G-lines (host bans)
+ k  Show K-lines (local host bans)
+ q  Show Q-lines (nick mask bans)
  R  Show R-lines (regular expression bans)
+ Z  Show Z-lines (IP mask bans)
  
- H  Show shuns
  s  Show filters
  C  Show channel bans
+ H  Show shuns
  
  c  Show link blocks
  d  Show configured DNSBLs and related statistics
@@@ -980,8 -966,6 +979,6 @@@ Note that all /STATS use is broadcast t
          messages.
   f      Allows receipt of flooding notices.
   g      Allows receipt of globops (requires globops module).
-  G      Allows receipt of notices of use of oper-override (requires
-         override module)
   j      Allows receipt of channel creation notices (requires
          chancreate module).
   J      Allows receipt of remote channel creation notices (requires
   Q      Allows receipt of remote quit messages.
   t      Allows receipt of attempts to use /STATS (local and remote).
   v      Allows receipt of oper-override notices (requires override module).
-  x      Allows receipt of Xline notices (g/z/q/k/e/R/shuns).">
+  x      Allows receipt of local Xline notices (g/Z/q/k/e/R/shuns).
+  X      Allows receipt of remote Xline notices (g/Z/q/k/e/R/shuns).">
  
  ######################
  #      EXTBANS       #
index 8042970bb28960370c88e3e66c82a18cb02efff3,5554098633c08458393243a4576b917da68f17c3..7d505b26186ea6f28167fa7e6b1b802f30359caf
@@@ -37,7 -37,7 +37,7 @@@ UNINVITE  AWAY     DCCALLOW  SILENCE   
  MKPASSWD  VHOST    TITLE     SETNAME
  
  WHOIS     WHOWAS   ISON      USERHOST  WATCH
 -LIST      NAMES    WHO       MOTD      RULES
 +LIST      NAMES    WHO       MOTD
  ADMIN     MAP      LINKS     LUSERS    TIME
  STATS     VERSION  INFO      MODULES   COMMANDS
  SSLINFO
@@@ -61,7 -61,7 +61,7 @@@ SAJOIN    SAPART    SAMODE      SATOPI
  
  KILL      SAQUIT    GLINE       ZLINE    QLINE
  KLINE     RLINE     ELINE       CBAN     SHUN
 -FILTER
 +FILTER    CLEARCHAN
  
  CONNECT   SQUIT     RCONNECT    RSQUIT
  
@@@ -102,9 -102,6 +102,6 @@@ LOCKSERV       UNLOCKSERV   JUMPSERVER"
                hideoper module).
   I            Hides a user's entire channel list in WHOIS from
                non-IRCops (requires hidechans module).
-  Q            Makes an operator invisible, preventing users from
-               seeing their presence, including in channel user lists
-               (IRCop only, requires invisible module).
   R            Blocks private messages from unregistered users
                (requires services account module).
   S            Strips mIRC color/bold/underline codes out of private
  
   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
@@@ -230,8 -224,6 +227,6 @@@ help channel if you have any questions.
          messages.
   f      Allows receipt of flooding notices.
   g      Allows receipt of globops (requires globops module).
-  G      Allows receipt of notices of use of oper-override (requires
-         override module)
   j      Allows receipt of channel creation notices (requires
          chancreate module).
   J      Allows receipt of remote channel creation notices (requires
   Q      Allows receipt of remote quit messages.
   t      Allows receipt of attempts to use /STATS (local and remote).
   v      Allows receipt of oper-override notices (requires override module).
-  x      Allows receipt of Xline notices (g/z/q/k/e/R/shuns).">
+  x      Allows receipt of local Xline notices (g/Z/q/k/e/R/shuns).
+  X      Allows receipt of remote Xline notices (g/Z/q/k/e/R/shuns).">
  
  <helpop key="extbans" value="Extended Bans
  ----------
                module).
   U:n!u@h      Blocks unregistered users matching the given ban.
                (requires m_services_account)
+  z:certfp     Blocks users having the given certificate fingerprint
+               (requires m_sslmodes)
  
   Redirect     n!u@h#channel will redirect the banned user to #channel
                when they try to join (requires banredirect module).
index 49559ffee8eef1fac524b002e0cc935f4f9e39e0,2617d1e6c04467d9e0c92c94b3c678784ee18cf7..fd625aef1a45fa2873c9052beb999c2bb0cedf69
@@@ -18,7 -18,7 +18,7 @@@
  ##################################||####################################
  #                                                                      #
  #         This is an example of the config file for InspIRCd.          #
- #             Change the options to suit your network                  #
+ #             Change the options to suit your network.                 #
  #                                                                      #
  #                                                                      #
  #    ____                _   _____ _     _       ____  _ _   _         #
  #   something new or different to this version and you SHOULD READ IT. #
  #                                                                      #
  ########################################################################
- #                                                                      #
- #         Unalphabeticalise the modules list at your own risk          #
- #                                                                      #
- ########################################################################
  
 +#-#-#-#-#-#-#-#-#-#  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         #
@@@ -65,7 -52,7 +61,7 @@@
  #<include file="file.conf">                                           #
  #<include executable="/path/to/executable parameters">                #
  #                                                                     #
- # Executable Include Example:                                         #
+ # Executable include example:                                         #
  #<include executable="/usr/bin/wget -q -O - http://mynet.net/inspircd.conf">
  #                                                                     #
  
  #                                                                     #
  # Variables may be redefined and may reference other variables.       #
  # Value expansion happens at the time the tag is read.                #
 -#                                                                     #
 -# Using variable definitions REQUIRES that the config format be       #
 -# changed to "xml" from the default "compat" that uses escape         #
 -# sequences such as "\"" and "\n", and does not support <define>      #
 -<config format="xml">
  <define name="bindip" value="1.2.2.3">
  <define name="localips" value="&bindip;/24">
  
        # 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">
  
- # When linking servers, the openssl and gnutls implementations are completely
+ # When linking servers, the OpenSSL and GnuTLS implementations are completely
  # link-compatible and can be used alongside each other
  # on each end of the link without any significant issues.
  # Supported ssl types are: "openssl" and "gnutls".
- # You must load, m_ssl_openssl for openssl
- # or m_ssl_gnutls for gnutls.
+ # You must load, m_ssl_openssl for OpenSSL or m_ssl_gnutls for GnuTLS.
  
  <bind address="" port="7000,7001" type="servers">
  <bind address="1.2.3.4" port="7005" type="servers" ssl="openssl">
  #-#-#-#-#-#-#-#-#-#-  DIE/RESTART CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-
  #                                                                     #
  #   You can configure the passwords here which you wish to use for    #
- #   the die and restart commands. Only trusted IRCop's who will       #
+ #   the /DIE and /RESTART commands. Only trusted IRCop's who will     #
  #   need this ability should know the die and restart password.       #
  #                                                                     #
  
         # (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:
+        # Optional, but recommended. Create hashed passwords with:
         # /mkpasswd <hash> <password>
         #hash="sha256"
  
           # for selected hash (m_md5.so, m_sha256.so or m_ripemd160.so) be
           # loaded and the password hashing module (m_password_hash.so)
           # loaded. Options here are: "md5", "sha256" and "ripemd160".
-          # Optional, but recommended. Create hashed password with:
+          # Optional, but recommended. Create hashed passwords 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"
           # before they are dropped. Keep this value higher than the length of
           # your network's /LIST or /WHO output, or you will have lots of
           # disconnects from sendq overruns!
-          hardsendq="1048576"
+          # Setting this to "1M" is equivalent to "1048576", "8K" is 8192, etc.
+          hardsendq="1M"
  
           # softsendq: amount of data in a client's send queue before the server
           # begins delaying their commands in order to allow the sendq to drain
           softsendq="8192"
  
           # recvq: amount of data allowed in a client's queue before they are dropped.
-          recvq="8192"
+          # Entering "8K" is equivalent to "8192", see above.
+          recvq="8K"
  
           # threshold: This specifies the amount of command penalty a user is allowed to have
           # before being quit or fakelagged due to flood. Normal commands have a penalty of 1,
           # globalmax: Maximum global (network-wide) connections per IP.
           globalmax="3"
  
 +         # resolvehostnames: If disabled, no DNS lookups will be performed on connecting users
 +         # in this class. This can save a lot of resources on very busy servers.
 +         resolvehostnames="yes"
 +
           # useident: Defines if users in this class must respond to a ident query or not.
           useident="no"
  
  
  # This file has all the information about oper classes, types and o:lines.
  # You *MUST* edit it.
 -<include file="conf/examples/opers.conf.example">
 +<include file="examples/opers.conf.example">
  
  # This file has all the information about server links and ulined servers.
  # You *MUST* edit it if you intend to link servers.
 -<include file="conf/examples/links.conf.example">
 +<include file="examples/links.conf.example">
  
  #-#-#-#-#-#-#-#-#-#-  MISCELLANEOUS CONFIGURATION  -#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # Files block - contains files whose contents are used by the ircd
  #
  #   motd - displayed on connect and when a user executes /MOTD
 -#   rules - displayed when the user executes /RULES
  # Modules can also define their own files
 -<files motd="conf/examples/motd.txt.example" rules="conf/examples/rules.txt.example">
 +<files motd="examples/motd.txt.example">
  
  # Example of an executable file include. Note this will be read on rehash,
  # not when the command is run.
 -#<execfiles rules="wget -O - http://www.example.com/rules.txt">
 +#<execfiles motd="wget -O - http://www.example.com/motd.txt">
  
  #-#-#-#-#-#-#-#-#-#-#-# MAXIMUM CHANNELS -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
            # users: Maximum number of channels a user can be in at once.
            users="20"
  
-           # opers: Maximum number of channels a oper can be in at once.
+           # opers: Maximum number of channels an oper can be in at once.
            opers="60">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-# DNS SERVER -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  
  <options
-          # prefixquit: What (if anything) a users' quit message
+          # prefixquit: What (if anything) users' quit messages
           # should be prefixed with.
           prefixquit="Quit: "
  
-          # suffixquit: What (if anything) a users' quit message
+          # suffixquit: What (if anything) users' quit messages
           # should be suffixed with.
           suffixquit=""
  
-          # prefixpart: What (if anything) a users' part message
+          # prefixpart: What (if anything) users' part messages
           # should be prefixed with.
           prefixpart="&quot;"
           # NOTE: Use "\"" instead of "&quot;" if not using <config format="xml">
  
-          # suffixpart: What (if anything) users' part message
+          # suffixpart: What (if anything) users' part message
           # should be suffixed with.
           suffixpart="&quot;"
  
           # 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.
           # it will just message the user normally.
           ircumsgprefix="no"
  
-          # announcets: If set to yes, when the TimeStamp on a channel changes, all users
-          # in channel will be sent a NOTICE about it.
+          # announcets: If set to yes, when the timestamp on a channel changes, all users
+          # in the channel will be sent a NOTICE about it.
           announcets="yes"
  
           # allowmismatch: Setting this option to yes will allow servers to link even
-          # if they don't have the same VF_OPTCOMMON modules loaded. Setting this to
+          # if they don't have the same "optionally common" modules loaded. Setting this to
           # yes may introduce some desyncs and weirdness.
           allowmismatch="no"
  
           # falling back to IPv4 otherwise.
           defaultbind="auto"
  
-          # hostintopic: If enabled, channels will show the host of the topicsetter
-          # in the topic. If set to no, it will only show the nick of the topicsetter.
+          # hostintopic: If enabled, channels will show the host of the topic setter
+          # in the topic. If set to no, it will only show the nick of the topic setter.
           hostintopic="yes"
  
           # pingwarning: If a server does not respond to a ping within x seconds,
           serverpingfreq="60"
  
           # 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.
+          # joins it and it is unregistered.
 -         defaultmodes="nt"
 +         defaultmodes="not"
  
 -         # moronbanner: This is the text that is sent to a user when they are
 +         # xlinemessage: This is the text that is sent to a user when they are
           # banned from the server.
 -         moronbanner="You're banned! Email abuse@example.com with the ERROR line below for help."
 +         xlinemessage="You're banned! Email irc@example.com with the ERROR line below for help."
  
           # exemptchanops: exemptions for channel access restrictions based on prefix.
           exemptchanops="nonick:v flood:o"
  
           # invitebypassmodes: This allows /invite to bypass other channel modes.
-          # (Such as +k, +j, +l, etc)
+          # (Such as +k, +j, +l, etc.)
           invitebypassmodes="yes"
  
           # nosnoticestack: This prevents snotices from 'stacking' and giving you
           # the message saying '(last message repeated X times)'. Defaults to no.
 -         nosnoticestack="no"
 -
 -         # welcomenotice: When turned on, this sends a NOTICE to connecting users
 -         # with the text Welcome to <networkname>! after successful registration.
 -         # Defaults to yes.
 -         welcomenotice="yes">
 +         nosnoticestack="no">
  
  
  #-#-#-#-#-#-#-#-#-#-#-# PERFORMANCE CONFIGURATION #-#-#-#-#-#-#-#-#-#-#
               # in the accept queue. This is *NOT* the total maximum number of
               # connections per server. Some systems may only allow this to be up
               # to 5, while others (such as linux and *BSD) default to 128.
 +             # Setting this above the limit imposed by your OS can have undesired
 +             # effects.
               somaxconn="128"
  
 -             # limitsomaxconn: By default, somaxconn (see above) is limited to a
 -             # safe maximum value in the 2.0 branch for compatibility reasons.
 -             # This setting can be used to disable this limit, forcing InspIRCd
 -             # to use the value specified above.
 -             limitsomaxconn="true"
 -
               # softlimit: This optional feature allows a defined softlimit for
               # connections. If defined, it sets a soft max connections value.
               softlimit="12800"
  
 +             # clonesonconnect: If this is set to false, we won't check for clones
 +             # on initial connection, but only after the DNS check is done.
 +             # This can be useful where your main class is more restrictive
 +             # than some other class a user can be assigned after DNS lookup is complete.
 +             # Turning this option off will make the server spend more time on users we may
 +             # potentially not want. Normally this should be neglible, though.
 +             # Default value is true
 +             clonesonconnect="true"
 +
               # quietbursts: When syncing or splitting from a network, a server
               # can generate a lot of connect and quit messages to opers with
               # +C and +Q snomasks. Setting this to yes squelches those messages,
               # which makes it easier for opers, but degrades the functionality of
               # bots like BOPM during netsplits.
 -             quietbursts="yes"
 -
 -             # nouserdns: If enabled, no DNS lookups will be performed on
 -             # connecting users. This can save a lot of resources on very busy servers.
 -             nouserdns="no">
 +             quietbursts="yes">
  
  #-#-#-#-#-#-#-#-#-#-#-# SECURITY CONFIGURATION  #-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  
  <security
 +          # allowcoreunload: If this value is set to yes, Opers will be able to
 +          # unload core modules (e.g. cmd_privmsg.so).
 +          allowcoreunload="no"
  
            # announceinvites: This option controls which members of the channel
            # receive an announcement when someone is INVITEd. Available values:
            # (Commands like /notice, /privmsg, /kick, etc)
            maxtargets="20"
  
 -          # customversion: Displays a custom string when a user /version's
 -          # the ircd. This may be set for security reasons or vanity reasons.
 +          # customversion: A custom message to be displayed in the comments field
 +          # of the VERSION command response. This does not hide the InspIRCd version.
            customversion=""
  
            # operspywhois: show opers (users/auspex) the +s channels a user is in. Values:
          # maxident: Maximum length of a ident/username.
          maxident="11"
  
 +        # maxhost: Maximum length of a hostname.
 +        maxhost="64"
 +
          # maxquit: Maximum length of a quit message.
          maxquit="255"
  
          # maxaway: Maximum length of an away message.
          maxaway="200">
  
 +#-#-#-#-#-#-#-#-#-#-#-#-# PATHS CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
 +#                                                                     #
 +# This configuration tag defines the location that InspIRCd stores    #
 +# various types of files such as configuration files, log files and   #
 +# modules. You will probably not need to change these from the values #
 +# set when InspIRCd was built unless you are using a binary package   #
 +# where you do not have the ability to set build time configuration.  #
 +#<path configdir="conf" datadir="data" logdir="logs" moduledir="modules">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Logging
  # to do what they want.
  #
  # An example log tag would be:
 -#  <log method="file" type="OPER" level="default" target="logs/opers.log">
 +#  <log method="file" type="OPER" level="default" target="opers.log">
  # which would log all information on /oper (failed and successful) to
  # a file called opers.log.
  #
  # The following log tag is highly default and uncustomised. It is recommended you
  # sort out your own log tags. This is just here so you get some output.
  
 -<log method="file" type="* -USERINPUT -USEROUTPUT" level="default" target="logs/ircd.log">
 +<log method="file" type="* -USERINPUT -USEROUTPUT" level="default" target="ircd.log">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-  WHOWAS OPTIONS   -#-#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # 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 5e36689f022205379c5039b5c9b4c1058e16364b,e4ebea556d3b09a38956bfb95d653860aff5f713..dbb29f1ff298cfe67c3c984fe4f110eb6adfea1c
@@@ -29,7 -29,7 +29,7 @@@
  
        # allowmask: Range of IP addresses to allow for this link.
        # Can be a CIDR (see example).
 -      allowmask="203.0.113.0/24"
 +      allowmask="203.0.113.0/24 127.0.0.0/8 2001:db8::/32"
  
        # timeout: If defined, this option defines how long the server
        # will wait to consider the connect attempt failed and try the
@@@ -40,8 -40,8 +40,8 @@@
        # making an outbound connection to the server. Options are: "openssl"
        # 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
+       # You will need to load the m_ssl_openssl.so module for OpenSSL,
+       # m_ssl_gnutls.so for GnuTLS. The server port that you connect to
        # must be capable of accepting this type of connection.
        ssl="gnutls"
  
@@@ -54,7 -54,7 +54,7 @@@
        # bind: Local IP address to bind to.
        bind="1.2.3.4"
  
-       # statshidden: defines if IP is shown to opers when
+       # statshidden: Defines if IP is shown to opers when
        # /stats c is invoked.
        statshidden="no"
  
@@@ -95,7 -95,7 +95,7 @@@
  # Simple autoconnect block. This enables automatic connection of a server
  # Recommended setup is to have leaves connect to the hub, and have no
  # automatic connections started by the hub.
 -<autoconnect period="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
index cfd55d84e6b65e50c6df7d4fbb95cb51411c3e3c,b4a5afab6f70af891ee945348064d0a1c84aaaa6..b17fd73fb135c2b0a0e1473d1c6ffd7bd8a2ece5
@@@ -26,7 -26,7 +26,7 @@@
  #                                                                     #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # MD5 Module - Allows other modules to generate MD5 hashes, usually for
+ # MD5 module: Allows other modules to generate MD5 hashes, usually for
  # cryptographic uses and security.
  #
  # IMPORTANT:
@@@ -36,7 -36,7 +36,7 @@@
  #<module name="m_md5.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # SHA256 Module - Allows other modules to generate SHA256 hashes,
+ # SHA256 module: Allows other modules to generate SHA256 hashes,
  # usually for cryptographic uses and security.
  #
  # IMPORTANT:
  #
  #<module name="m_sha256.so">
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # RIPEMD160 Module - Allows other modules to generate RIPEMD160 hashes,
+ # RIPEMD160 module: Allows other modules to generate RIPEMD160 hashes,
  # usually for cryptographic uses and security.
- # 
+ #
  # IMPORTANT:
  # Other modules may rely on this module being loaded to function.
  #<module name="m_ripemd160.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Abbreviation module: Provides the ability to abbreviate commands a-la 
+ # Abbreviation module: Provides the ability to abbreviate commands a-la
  # BBC BASIC keywords.
  #<module name="m_abbreviation.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Alias module: Allows you to define server-side command aliases
+ # Alias module: Allows you to define server-side command aliases.
  #<module name="m_alias.so">
  #
- # Fantasy settings:
- #
+ # Set the 'prefix' for in-channel aliases (fantasy commands) to the
+ # specified character. If not set, the default is "!".
+ # If 'allowbots' is disabled, +B clients will not be able to use
+ # fantasy commands. If not set, the default is no.
  #<fantasy prefix="!" allowbots="no">
  #
- # prefix:
- #  Set the prefix for in-channel aliases (fantasy commands) to the
- #  specified character. If not set, the default is "!".
- # allowbots:
- #  If this is set to no, +B clients will not be able to use fantasy
- #  commands. If not set, the default is no.
- #
  #-#-#-#-#-#-#-#-#-#-#-  ALIAS DEFINITIONS  -#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # If you have the m_alias.so module loaded, you may also define       #
@@@ -81,8 -76,8 +76,8 @@@
  # commands to services, however they are not limited to just this use.#
  # An alias tag requires the following values to be defined in it:     #
  #                                                                     #
- # text        -      The text to detect as the actual command line,   #
- #                    Cant contain spaces, but case insensitive.       #
+ # text        -      The text to detect as the actual command line.   #
+ #                    Can't contain spaces, but case insensitive.      #
  #                    You may have multiple aliases with the same      #
  #                    command name (text="" value), however the first  #
  #                    found will be executed if its format value is    #
  #                    to successfully trigger. If they are not, then   #
  #                    the user receives a 'no such nick' 401 numeric.  #
  #                                                                     #
- # uline       -      Defining this value with 'yes', 'true' or '1'    #
- #                    will ensure that the user given in 'requires'    #
- #                    must also be on a u-lined server, as well as     #
- #                    actually being on the network. If the user is    #
- #                    online, but not on a u-lined server, then an     #
- #                    oper-alert is sent out as this is possibly signs #
- #                    of a user trying to impersonate a service.       #
+ # uline       -      Setting this to true will ensure that the user   #
+ #                    given in 'requires' is also on a u-lined server, #
+ #                    as well as actually being on the network. If the #
+ #                    user is online, but not on a u-lined server,     #
+ #                    then an oper alert is sent out as this is        #
+ #                    possibly a sign of a user trying to impersonate  #
+ #                    a service.                                       #
  #                                                                     #
- # operonly    -      Defining this value, with a value of 'yes', '1'  #
- #                    or true will make the alias oper only. If a non- #
- #                    oper attempts to use the alias, it will appear   #
- #                    to not exist.                                    #
+ # operonly    -      If true, this will make the alias oper only.     #
+ #                    If a non-oper attempts to use the alias, it will #
+ #                    appear to not exist.                             #
  #                                                                     #
  #<alias text="NICKSERV" replace="PRIVMSG NickServ :$2-" requires="NickServ" uline="yes">
  #<alias text="CHANSERV" replace="PRIVMSG ChanServ :$2-" requires="ChanServ" uline="yes">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Allowinvite module: Gives channel mode +A to allow all users to use
- # /invite, and extban A to deny invite from specific masks
+ # /INVITE, and extban A to deny invite from specific masks.
  #<module name="m_allowinvite.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Alltime module: Shows time on all connected servers at once.
- # This module is oper-only and provides /alltime.
+ # This module is oper-only and provides /ALLTIME.
  # To use, ALLTIME must be in one of your oper class blocks.
  #<module name="m_alltime.so">
  
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Autoop module: Adds basic channel access controls via the +w listmode.
- # For example, +w o:R:Brain will op anyone identified to the account "Brain"
- # on join.
+ # For example +w o:*!Attila@127.0.0.1 will op anyone matching that mask
+ # on join. This can be combined with extbans, for example +w o:R:Brain
+ # will op anyone identified to the account "Brain".
+ # Another useful combination is with SSL client certificate
+ # fingerprints: +w h:z:72db600734bb9546c1bdd02377bc21d2a9690d48 will
+ # give halfop to the user(s) having the given certificate.
  #<module name="m_autoop.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Ban except module: Adds support for channel ban exceptions (+e)
+ # Ban except module: Adds support for channel ban exceptions (+e).
  #<module name="m_banexception.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #<module name="m_banredirect.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Block amsg module: Attempt to block all usage of /amsg and /ame
+ # Block amsg module: Attempt to block all usage of /amsg and /ame.
  #<module name="m_blockamsg.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-  BLOCKAMSG CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
  #<blockamsg delay="3" action="killopers">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Block CAPS module: Blocking all-CAPS messages with cmode +B
+ # Block CAPS module: Blocking all-CAPS messages with channel mode +B.
  #<module name="m_blockcaps.so">
- #                                                                     #
+ #
  #-#-#-#-#-#-#-#-#-#-#-  BLOCKCAPS CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # percent        - How many percent of text must be caps before text  #
  #           capsmap="ABCDEFGHIJKLMNOPQRSTUVWXYZ! ">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Block color module: Blocking color-coded messages with chan mode +c
+ # Block color module: Blocking color-coded messages with chan mode +c.
  #<module name="m_blockcolor.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Botmode module: Adds the user mode +B
+ # Botmode module: Adds the user mode +B. If set on a user, it will
+ # show that the user is a bot in /WHOIS.
  #<module name="m_botmode.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # CallerID module: Adds usermode +g which activates hybrid-style 
+ # CallerID module: Adds usermode +g which activates hybrid-style
  # callerid: block all private messages unless you /accept first
  #<module name="m_callerid.so">
- # 
+ #
  #-#-#-#-#-#-#-#-#-#-#- CALLERID  CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
- # maxaccepts     - Maximum number of entires a user can add to his    #
+ # maxaccepts     - Maximum number of entries a user can add to his    #
  #                  /accept list. Default is 16 entries.               #
- # operoverride   - Can opers (note: ALL opers) ignore callerid mode?  #
+ # operoverride   - Can opers (note: ALL opers) override callerid?     #
  #                  Default is no.                                     #
  # tracknick      - Preserve /accept entries when a user changes nick? #
  #                  If no (the default), the user is removed from      #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # CAP module: Provides the CAP negotiation mechanism seen in
- # ratbox-derived ircds
+ # ratbox-derived ircds.
  #<module name="m_cap.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #<module name="m_cban.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Censor module: Adds channel and user mode +G
+ # Censor module: Adds channel and user mode +G.
  #<module name="m_censor.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-  CENSOR  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
  # (http://cgiirc.sourceforge.net).
 +# Adds snomask +w for monitoring CGI:IRC connections.
  #<module name="m_cgiirc.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-# CGIIRC  CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
  # will be connecting to your network, and an optional cgiirc tag.
  # For more information see: http://wiki.inspircd.org/Modules/cgiirc
  #
- # Set to yes if you want to notice opers when CGI clients connect
+ # Set to yes if you want to notice opers when CGI:IRC clients connect.
  # <cgiirc opernotice="no">
  #
  # The type field indicates where the module should get the real
  #
  # When you connect CGI:IRC clients, there are two connect classes which
  # apply to these clients. When the client initially connects, the connect
- # class which matches the cgi:irc site's host is checked. Therefore you
+ # class which matches the CGI:IRC site's host is checked. Therefore you
  # must raise the maximum local/global clients for this ip as high as you
  # want to allow cgi clients. After the client has connected and is
  # determined to be a cgi:irc client, the class which matches the client's
  # real IP is then checked. You may set this class to a lower value, so that
  # the real IP of the client can still be restricted to, for example, 3
  # sessions maximum.
- #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Channel create module: Adds snomask +j, which will notify opers of
  #<module name="m_chancreate.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Channel filter module: Allows channel-op defined message
- # filtering using simple string matches (channel mode +g)
+ # Channel filter module: Allows channel-op defined message filtering
+ # using simple string matches (channel mode +g).
  #<module name="m_chanfilter.so">
  #
  # If hidemask is set to yes, the user will not be shown the mask when
  #<chanfilter hidemask="yes">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Channel History module: Displays the last 'X' lines of chat to a user
+ # Channel history module: Displays the last 'X' lines of chat to a user
  # joining a channel with +H 'X:T' set; 'T' is the maximum time to keep
  # lines in the history buffer. Designed so that the new user knows what
  # the current topic of conversation is when joining the channel.
  # 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 logging module: Used to send snotice output to channels, to
  # allow staff to centrally monitor and discuss network activity.
  #
  # The "channel" field is where you want the messages to go, "snomasks"
  #<chanlog snomasks="AOcC" channel="#opers">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Channel Names module: Allows disabling channels which have certain
+ # Channel names module: Allows disabling channels which have certain
  # characters in the channel name such as bold, colorcodes, etc. which
  # can be quite annoying and allow users to on occasion have a channel
  # that looks like the name of another channel on the network.
        allowrange="">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Channelban: Implements extended ban j:, which stops anyone in already
- # in a channel matching a mask like +b j:#channel*mask from joining.
+ # Channelban: Implements extended ban j:, which stops anyone already
+ # in a channel matching a ban like +b j:#channel*mask from joining.
  #<module name="m_channelban.so">
  
 -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 -# Chanprotect module: Gives +q and +a channel modes.
 -#<module name="m_chanprotect.so">
 -
 -<chanprotect
 -      # noservices: With this set to yes, when a user joins an empty channel,
 -      # the server will set +q on them. If set to no, it will only set +o
 -      # on them until they register the channel.
 -      noservices="no"
 -
 -      # qprefix: Prefix (symbol) to use for +q users.
 -      qprefix="~"
 -
 -      # aprefix: Prefix (symbol) to use for +a users.
 -      aprefix="&amp;"
 -
 -      # deprotectself: If this value is set (true, yes or 1), it will allow
 -      # +a and +q users to remove the +a and +q from themselves, otherwise,
 -      # the status will have to be removed by services.
 -      deprotectself="yes"
 -
 -      # deprotectothers: If this value is set to yes, true, or 1, then any
 -      # user with +q or +a may remove the +q or +a from other users.
 -      deprotectothers="yes">
 -
 -
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Check module: gives /check
- # Check is useful for looking up information on channels,
- # users, IP addresses and hosts.
+ # Check module: Adds the /CHECK command.
+ # Check is useful for looking up information on channels, users,
+ # IP addresses and hosts.
  # This module is oper-only.
  # To use, CHECK must be in one of your oper class blocks.
  #<module name="m_check.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # CHGHOST module: Adds the /CHGHOST command
+ # CHGHOST module: Adds the /CHGHOST command.
  # This module is oper-only.
  # To use, CHGHOST must be in one of your oper class blocks.
  # NOTE: Services will not be able to set vhosts on users if this module
  # specify your own custom list of chars with the <hostname> tag:     #
  #                                                                    #
  # charmap        - A list of chars accepted as valid by the /CHGHOST #
- #                  and /SETHOST commands. Also note that the list is # 
+ #                  and /SETHOST commands. Also note that the list is #
  #                  case-sensitive.                                   #
  #<hostname charmap="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_/0123456789">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # CHGIDENT module: Adds the /CHGIDENT command
+ # CHGIDENT module: Adds the /CHGIDENT command.
  # This module is oper-only.
  # To use, CHGIDENT must be in one of your oper class blocks.
  #<module name="m_chgident.so">
  # To use, CHGNAME must be in one of your oper class blocks.
  #<module name="m_chgname.so">
  
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# Clear chan module: Allows opers to masskick, masskill or mass-G/ZLine
 +# all users on a channel using /CLEARCHAN.
 +#<module name="m_clearchan.so">
 +
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Cloaking module: Adds usermode +x and cloaking support.
  # Relies on the module m_md5.so being loaded.
  # cloak prefix as shown below. The cloak key must be shared across    #
  # the network for correct cloaking.                                   #
  #                                                                     #
 -# There are four methods of cloaking:                                 #
 +# There are two methods of cloaking:                                  #
  #                                                                     #
  #   half           Cloak only the "unique" portion of a host; show    #
  #                  the last 2 parts of the domain, /16 subnet of IPv4 #
  #                  or /48 subnet of the IPv6 address.                 #
  #                                                                     #
  #   full           Cloak the users completely, using three slices for #
- #                  common CIDR bans (IPv4: /16, /24; IPv6: /48, /64)  #
+ #                  common CIDR bans (IPv4: /16, /24; IPv6: /48, /64). #
  #                                                                     #
 -# These methods use a single key that can be any length of text.      #
 +# The methods use a single key that can be any length of text.        #
  # An optional prefix may be specified to mark cloaked hosts.          #
 -#                                                                     #
 -# The following methods are maintained for backwards compatibility;   #
 -# they are slightly less secure, and always hide unresolved IPs.      #
 -#                                                                     #
 -#   compat-host    InspIRCd 1.2-compatible host-based cloaking.       #
 -#   compat-ip      InspIRCd 1.2-compatible ip-always cloaking.        #
 -#                                                                     #
 -# If you use a compat cloaking mode then you must specify key1, key2, #
 -# key3, key4; the values must be less than 0x80000000 and should be   #
 -# picked at random. Prefix is mandatory, will default to network name #
 -# if not specified, and will always have a "-" appended.              #
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #
  #<cloak mode="half"
  #       key="secret"
  
  #-#-#-#-#-#-#-#-#-#-#-#- CLOSE MODULE #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Close module: Allows an oper to close all unregistered connections.
- # This module is oper-only and provides /close.
+ # This module is oper-only and provides the /CLOSE command.
  # To use, CLOSE must be in one of your oper class blocks.
  #<module name="m_close.so">
  
  #<module name="m_commonchans.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Conn-Join: Allows you to force users to join one or more channels
- # automatically upon connecting to the server, or join them in case
- # they aren't on any channels after being online for X seconds.
+ # Auto join on connect module: Allows you to force users to join one
 -# or more channels automatically upon connecting to the server.
++# or more channels automatically upon connecting to the server, or
++# join them in case they aren't on any channels after being online
++# for X seconds.
  #<module name="m_conn_join.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-#- CONNJOIN CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
  #
+ # If you have m_conn_join.so loaded, you can configure it using the
+ # following values, or set autojoin="#chat,#help" in <connect> blocks.
+ #
 +# Join users immediately after connection to #one #two and #three.
  #<autojoin channel="#one,#two,#three">
 +# Join users to #chat after 15 seconds if they aren't on any channels.
 +#<autojoin channel="#chat" delay="15">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Conn-Usermodes: Set modes on users when they connect
- # When this module is loaded <connect:allow> tags may have an optional
- # modes="" value, which contains modes to add or remove from users
- # when they connect to the server.
+ # Set modes on connect module: When this module is loaded <connect>
+ # blocks may have an optional modes="" value, which contains modes to
+ # add or remove from users when they connect to the server.
  #<module name="m_conn_umodes.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Conn-Wait-for-Pong: Don't let a user connect until they PONG
+ # Wait for PONG on connect module: Send a PING to all connecting users
+ # and don't let them connect until they reply with a PONG.
+ # This is useful to stop certain kinds of bots and proxies.
  #<module name="m_conn_waitpong.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-   WAITPONG CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
  # If you have the m_conn_waitpong.so module loaded, configure it with #
  # the <waitpong> tag:                                                 #
  #                                                                     #
- # sendsnotice    -   Whether to send a snotice on connect, like other #
- #                    older ircds                                      #
+ # sendsnotice    -   Whether to send a helpful notice to users on     #
+ #                    connect telling them how to connect, should      #
+ #                    their client not reply PONG automatically.       #
  #                                                                     #
  # killonbadreply -   Whether to kill the user if they send the wrong  #
  #                    PONG reply.                                      #
  #                                                                     #
  #<waitpong sendsnotice="yes" killonbadreply="yes">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Channel cycle module. Server side /hop, with +ilk etc. bypass.
+ # Channel cycle module: Adds the /CYCLE command which is a server-side
+ # /HOP that bypasses restrictive modes.
  #<module name="m_cycle.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Connectban: Provides IP connection throttling. Any IP range that connects
- # too many times (configurable) in an hour is zlined for a (configurable)
- # duration, and their count resets to 0.
+ # Connectban: Provides IP connection throttling. Any IP range that
+ # connects too many times (configurable) in an hour is Z-Lined for a
+ # (configurable) duration, and their count resets to 0.
+ #<module name="m_connectban.so">
  #
- # ipv4cidr and ipv6cidr allow you to turn the comparison from individual
- # IP addresses (32 and 128 bits) into CIDR masks, to allow for throttling
- # over whole ISPs/blocks of IPs, which may be needed to prevent attacks.
+ # ipv4cidr and ipv6cidr allow you to turn the comparison from
+ # individual IP addresses (32 and 128 bits) into CIDR masks, to allow
+ # for throttling over whole ISPs/blocks of IPs, which may be needed to
+ # prevent attacks.
  #
 -#<connectban threshold="10" duration="10m" ipv4cidr="32" ipv6cidr="128">
+ # This allows for 10 connections in an hour with a 10 minute ban if
+ # that is exceeded.
- #
- # <connectban threshold="10" duration="10m" ipv4cidr="32" ipv6cidr="128"
- #  banmessage="Your IP range has been attempting to connect too many times in too short a duration. Wait a while, and you will be able to connect.">
- #
- # This allows for 10 connections in an hour with a 10 minute ban if that is exceeded.
- #
- #<module name="m_connectban.so">
++#<connectban threshold="10" duration="10m" ipv4cidr="32" ipv6cidr="128"
 +# A custom ban message may optionally be specified.
++# banmessage="Your IP range has been attempting to connect too many times in too short a duration. Wait a while, and you will be able to connect.">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Connection throttle module. Configuration:
+ # Connection throttle module.
  #<module name="m_connflood.so">
  #
  #-#-#-#-#-#-#-#-#-#-#- CONNTHROTTLE CONFIGURATION  -#-#-#-#-#-#-#-#-#-#
  #   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
+ # name       The name of the mode, must be unique from other modes.
  # letter     The letter used for this mode. Required.
  # prefix     The prefix used for nicks with this mode. Not required.
- # rank       A numeric rank for this prefix, defining what permissions it gives
- #            VOICE_VALUE is 10000, HALFOP_VALUE is 20000, OP_VALUE is 30000
+ # rank       A numeric rank for this prefix, defining what permissions it gives.
+ #            The rank of voice, halfop and op is 10000, 20000, and 30000,
+ #            respectively.
  # ranktoset  The numeric rank required to set/unset this mode. Defaults to rank.
  # depriv     Can you remove the mode from yourself? Defaults to yes.
  #<customprefix name="founder" letter="q" prefix="~" rank="50000" 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.
+ # Do /RELOADMODULE m_customprefix.so after changing the settings of this module.
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Custom title module: Adds the /TITLE command which allows for trusted
- #                      users to gain a custom whois line and a optional
- #                      vhost can be specified.
+ # users to gain a custom whois line and an optional vhost can be
+ # specified.
  #<module name="m_customtitle.so">
  #
  #-#-#-#-#-#-#-#-#-#-  CUSTOM TITLE CONFIGURATION   -#-#-#-#-#-#-#-#-#-#
- #  name              - The username used to identify 
- #  password          - The password used to identify
- #  hash              - The hash for the specific user's password (optional)
- #                      m_password_hash.so and a hashing module must be loaded for this to work
- #  host              - Allowed hostmask [optional]
- #  title             - Title shown in whois
- #  vhost             - Displayed host [optional]
+ #  name     - The username used to identify.
+ #  password - The password used to identify.
+ #  hash     - The hash for the specific user's password (optional).
+ #             m_password_hash.so and a hashing module must be loaded
+ #             for this to work.
+ #  host     - Allowed hostmask (optional).
+ #  title    - Title shown in whois.
+ #  vhost    - Displayed host (optional).
  #
  #<title name="foo" password="bar" title="Official Chat Helper">
  #<title name="bar" password="foo" host="ident@host.name" title="Official Chat Helper" vhost="helper.network.chat">
  #<title name="foo" password="fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9" hash="sha256" title="Official Chat Helper">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # DCCALLOW module: Adds the /DCCALLOW command
+ # DCCALLOW module: Adds the /DCCALLOW command.
  #<module name="m_dccallow.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-  DCCALLOW CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
- #  blockchat         - Whether to block DCC CHAT as well as DCC SEND
- #  length            - Default duration of entries in DCCALLOW list
- #  action            - Default action to take if no action is specified
- #                      can be 'block' or 'allow'
+ #  blockchat         - Whether to block DCC CHAT as well as DCC SEND.
+ #  length            - Default duration of entries in DCCALLOW list.
+ #  action            - Default action to take if no action is
+ #                      specified, can be 'block' or 'allow'.
  #
  # File configuration:
- #  pattern           - The glob pattern to match against
+ #  pattern           - The glob pattern to match against.
  #  action            - Action to take if a user attempts to send a file
- #                      that matches this pattern, can be 'block' or 'allow'
+ #                      that matches this pattern, can be 'block' or
+ #                      'allow'.
  #
  #<dccallow blockchat="yes" length="5m" action="block">
  #<banfile pattern="*.exe" action="block">
  #<banfile pattern="*.txt" action="allow">
- #
- #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Deaf module: adds support for ircu style usermode +d - deaf to
- # channel messages and channel notices.
+ # Deaf module: Adds support for the usermode +d - deaf to channel
+ # messages and channel notices.
  #<module name="m_deaf.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Delay message module: Adds the channel mode +d which disallows a user
  # from talking in the channel unless they've been joined for X seconds.
- # Settable a la: /mode +d 30
+ # Settable using /MODE #chan +d 30
  #<module name="m_delaymsg.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Deny Channels: Deny Channels from being used by users
- #<module name="m_denychans.so"> 
+ # Deny channels module: Deny channels from being used by users.
+ #<module name="m_denychans.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-   DENYCHAN DEFINITIONS  -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # If you have the m_denychans.so module loaded, you need to specify   #
  # the channels to deny:                                               #
  #                                                                     #
- # name        -      The channel name to deny. (glob masks are ok)    #
- #                                                                     #
+ # name        -      The channel name to deny (glob masks are ok).    #
  # allowopers  -      If operators are allowed to override the deny.   #
- #                                                                     #
  # reason      -      Reason given for the deny.                       #
- #                                                                     #
- # redirect    -      Redirect the user to a different channel         #
+ # redirect    -      Redirect the user to a different channel.        #
  #                                                                     #
  #<badchan name="#gods*" allowopers="yes" reason="Tortoises!">         #
  #<badchan name="#heaven" redirect="#hell" reason="Nice try!">         #
  # Additionally, you may specify channels which are allowed, even if   #
  # a badchan tag specifies it would be denied:                         #
  #<goodchan name="#godsleeps">                                         #
- # Glob masks are accepted here also.
+ # Glob masks are accepted here also.                                  #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Devoice Module: Let users devoice themselves using /devoice #chan.
+ # Devoice module: Let users devoice themselves using /DEVOICE #chan.
  #<module name="m_devoice.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # DNS Blacklist Module: Provides support for looking up IPs on one or #
+ # DNS blacklist module: Provides support for looking up IPs on one or #
  # more blacklists.                                                    #
  #<module name="m_dnsbl.so">                                           #
  #                                                                     #
  # http://wiki.inspircd.org/Modules/dnsbl                              #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Exempt Channel Operators Module: Provides support for allowing      #
+ # Exempt channel operators module: Provides support for allowing      #
  # channel operators to be exempt from some channel modes.  Supported  #
  # modes are blockcaps, noctcp, blockcolor, nickflood, flood, censor,  #
  # filter, regmoderated, nonick, nonotice, and stripcolor.             #
  #<module name="m_exemptchanops.so">                                   #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Filter module: Provides message filtering, similar to SPAMFILTER.
+ # Filter module: Provides message filtering, similar to SPAMFILTER.   #
  #<module name="m_filter.so">
  #                                                                     #
  # This module depends upon a regex provider such as m_regex_pcre or   #
  #-#-#-#-#-#-#-#-#-#-#-  FILTER  CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # Optional - If you specify to use the m_filter module, then          #
- # specfiy below the path to the filter.conf file, or define some      #
+ # specify below the path to the filter.conf file, or define some      #
  # <filter> tags.                                                      #
  #                                                                     #
 -#<include file="conf/examples/filter.conf.example">
 +#<include file="examples/filter.conf.example">
 +
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# Flash Policy Daemon module: Allows Flash IRC clients (e.g. LightIRC)#
 +# to connect. If no file is specified, it'll serve a default policy   #
 +# allowing all IPs to connect to all plaintext IRC ports              #
 +#<bind address="" port="8430" type="flashpolicyd">                    #
 +#<flashpolicyd timeout="5" file="">                                   #
 +#<module name="m_flashpolicyd.so">                                    #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Gecosban: Implements extended ban r:, which stops anyone matching
+ # Gecos ban: Implements extended ban 'r', which stops anyone matching
  # a mask like +b r:*realname?here* from joining a channel.
  #<module name="m_gecosban.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # GeoIP module: Allows the server admin to match users by country code.
- # This modules is in extras. Re-run configure with: ./configure --enable-extras=m_geoip.cpp
+ # This module is in extras. Re-run configure with:
+ # ./configure --enable-extras=m_geoip.cpp
  # and run make install, then uncomment this module to enable it.
  # This module requires GeoIP to be installed on your system,
  # use your package manager to find the appropriate packages
  # classes for them to match.
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Globops module: gives /GLOBOPS and SNOMASK +g
+ # Globops module: Provides the /GLOBOPS command and snomask +g.
  # This module is oper-only.
  # To use, GLOBOPS must be in one of your oper class blocks.
  #<module name="m_globops.so">
  #<module name="m_globalload.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 -# Halfop module: Provides the +h (halfops) channel status mode.
 -#<module name="m_halfop.so">
 -
 -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 -# HELPOP module: Provides the /HELPOP command.
 +# HELPOP module: Provides the /HELPOP command
  #<module name="m_helpop.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-#-  HELPOP  CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
- # Optional - If you specify to use the m_helpop.so module, then       #
- # specify below the path to the helpop.conf file, or if you like to   #
- # make a mess, define your helpop tags in this conf.                  #
+ # If you specify to use the m_helpop.so module, then specify below    #
+ # the path to the helpop.conf file.                                   #
 -#<include file="conf/examples/inspircd.helpop-full.example">
 +#                                                                     #
 +#<include file="examples/inspircd.helpop-full.example">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # HIDECHANS module: Allows users to hide their channels list from non-
+ # Hide chans module: Allows users to hide their channels list from non-
  # opers by setting user mode +I on themselves.
  #<module name="m_hidechans.so">
  #
- # HIDECHANS can optionally prevent opers from seeing channels on a +I
+ # This mode can optionally prevent opers from seeing channels on a +I
  # user, for more privacy if set to true.
  # This setting is not recommended for most mainstream networks.
  #<hidechans affectsopers="false">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # HIDEOPER module: Allows opers to hide their oper status from non-
+ # Hide oper module: Allows opers to hide their oper status from non-
  # opers by setting user mode +H on themselves.
  # This module is oper-only.
  #<module name="m_hideoper.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Hostchange module: Allows a different style of cloaking
+ # Hostchange module: Allows a different style of cloaking.
  #<module name="m_hostchange.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-  HOSTCHANGE  CONFIGURATION  -#-#-#-#-#-#-#-#-#-#
  #                                                                     #
- # Optional - If you choose to use the m_hostchange.so module.         #
- # Config Help -  See http://wiki.inspircd.org/Modules/hostchange      #
+ # See http://wiki.inspircd.org/Modules/hostchange for help.           #
  #                                                                     #
  #<host suffix="polarbears.org" separator="." prefix="">
  #<hostchange mask="*@fbi.gov" action="addnick">
  #<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
+ # httpd module: Provides HTTP server support for InspIRCd.
  #<module name="m_httpd.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-#-  HTTPD   CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
  #<httpd timeout="20">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # http ACL module: Provides access control lists for m_httpd dependent
+ # HTTP ACL module: Provides access control lists for m_httpd dependent
  # modules. Use this module to restrict pages by IP address and by
  # password.
- #
  #<module name="m_httpd_acl.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-#- HTTPD ACL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
  #
  # Restrict access to the m_httpd_stats module to all but the local
  # network and when the correct password is specified:
- #
  # <httpdacl path="/stats*" types="password,whitelist"
  #    username="secretstuff" password="mypasshere" whitelist="127.0.0.*,10.*">
- # 
- # Deny all connections to all but the main index page:
  #
+ # Deny all connections to all but the main index page:
  # <httpdacl path="/*" types="blacklist" blacklist="*">
- #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # http config module: Allows the server's configuration to be viewed
- # over HTTP. Requires m_httpd.so to be loaded for it to function.
+ # HTTP config module: Allows the configuration of the server to be
+ # viewed over HTTP. Requires m_httpd.so to be loaded for it to function.
  #<module name="m_httpd_config.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # http stats module: Provides basic stats pages over HTTP
+ # HTTP stats module: Provides basic stats pages over HTTP.
  # Requires m_httpd.so to be loaded for it to function.
  #<module name="m_httpd_stats.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Ident: Provides RFC 1413 ident lookup support
+ # Ident: Provides RFC 1413 ident lookup support.
  # When this module is loaded <connect:allow> tags may have an optional
  # useident="yes|no" boolean value, determining whether or not to lookup
  # ident on users matching that connect tag.
  #                                                                     #
  # Optional - If you are using the m_ident.so module, then you can     #
  # specify the timeout for ident lookups here. If not defined, it will #
- # default to one second. This is a non-blocking timeout which holds   #
+ # default to 5 seconds. This is a non-blocking timeout which holds    #
  # the user in a 'connecting' state until the lookup is complete.      #
  # The bind value indicates which IP to bind outbound requests to.     #
- #                                                                     #
+ #
  #<ident timeout="5">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Invite except module: Adds support for channel invite exceptions (+I)
+ # Invite exception module: Adds support for channel invite exceptions
+ # (+I).
  #<module name="m_inviteexception.so">
  # Does a +I bypass channel +k in addition to +i?
  #<inviteexception bypasskey="yes">
  # enhancements to the client-to-server protocol. An extension is only
  # active for a client when the client specifically requests it, so this
  # module needs m_cap to work.
- # 
+ #
  # Further information on these extensions can be found at the IRCv3
  # working group website:
- # http://ircv3.atheme.org/extensions/
+ # http://ircv3.org/extensions/
  #
  #<module name="m_ircv3.so">
  # The following block can be used to control which extensions are
- # enabled.
+ # enabled. Note that extended-join can be incompatible with m_delayjoin
+ # and host cycling.
  #<ircv3 accountnotify="on" awaynotify="on" extendedjoin="on">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Join flood module: Adds support for join flood protection (+j)
+ # Join flood module: Adds support for join flood protection +j X:Y.
+ # Closes the channel for 60 seconds if X users join in Y seconds.
  #<module name="m_joinflood.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Jump Server module: Adds support for the RPL_REDIR numeric
+ # Jump server module: Adds support for the RPL_REDIR numeric.
  # This module is oper-only.
  # To use, JUMPSERVER must be in one of your oper class blocks.
  # If your server is redirecting new clients and you get disconnected,
  #<module name="m_jumpserver.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Anti-Auto-Rejoin: Adds support for prevention of auto-rejoin (+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
+ # Knock module: Adds the /KNOCK command and channel mode +K.
  #<module name="m_knock.so">
- # This setting specifes what to do when someone successfully /KNOCKs.
+ #
+ # This setting specifies what to do when someone successfully /KNOCKs.
  # If set to "notice", then a NOTICE will be sent to the channel.
  # This is the default and the compatible setting, as it requires no
  # special support from the clients.
  # If set to "both" then (surprise!) both will be sent.
  #<knock notify="notice">
  
- # This modules is in extras. Re-run configure with: ./configure --enable-extras=m_ldap.cpp
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# LDAP module: Allows other SQL modules to access a LDAP database
 +# through a unified API.
++# This modules is in extras. Re-run configure with:
++# ./configure --enable-extras=m_ldap.cpp
 +# and run make install, then uncomment this module to enable it.
 +#
 +#<module name="m_ldap.so">
 +#<database module="ldap" id="ldapdb" server="ldap://localhost" binddn="cn=Manager,dc=inspircd,dc=org" bindauth="mysecretpass" searchscope="subtree">
 +# The server parameter indicates the LDAP server to connect to. The   #
 +# ldap:// style scheme before the hostname proper is MANDATORY.       #
 +#                                                                     #
 +# The binddn and bindauth indicate the DN to bind to for searching,   #
 +# and the password for the distinguished name. Some LDAP servers will #
 +# allow anonymous searching in which case these two values do not     #
 +# need defining, otherwise they should be set similar to the examples #
 +# above.                                                              #
 +#                                                                     #
 +# The searchscope value indicates the subtree to search under. On our #
 +# test system this is 'subtree'. Your mileage may vary.               #
 +
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # LDAP authentication module: Adds the ability to authenticate users  #
 -# via LDAP. This is an extra module which must be enabled explicitly  #
 -# by symlinking it from modules/extra, and requires the OpenLDAP libs #
 -# This module is in extras. To enable it, Re-run configure with:      #
 -# ./configure --enable-extras=m_ldapauth.cpp                          #
 -# and run make install, then uncomment this module.                   #
 +# via LDAP.                                                           #
- #                                                                     #
- #<module name="m_ldapauth.so">                                        #
+ #<module name="m_ldapauth.so">
  #                                                                     #
  # Configuration:                                                      #
  #                                                                     #
 -# <ldapauth baserdn="ou=People,dc=brainbox,dc=cc"                     #
 +# <ldapauth dbid="ldapdb"                                             #
 +#           baserdn="ou=People,dc=brainbox,dc=cc"                     #
  #           attribute="uid"                                           #
 -#           server="ldap://brainwave.brainbox.cc"                     #
 -#           allowpattern="Guest*"                                     #
 +#           allowpattern="Guest* Bot*"                                #
  #           killreason="Access denied"                                #
 -#           searchscope="subtree"                                     #
 -#           binddn="cn=Manager,dc=brainbox,dc=cc"                     #
 -#           bindauth="mysecretpass"                                   #
  #           verbose="yes"                                             #
  #           host="$uid.$ou.inspircd.org">                             #
  #                                                                     #
  # The attribute value indicates the attribute which is used to locate #
  # a user account by name. On POSIX systems this is usually 'uid'.     #
  #                                                                     #
 -# The server parameter indicates the LDAP server to connect to. The   #
 -# ldap:// style scheme before the hostname proper is MANDATORY.       #
 -#                                                                     #
 -# The allowpattern value allows you to specify a wildcard mask which  #
 -# will always be allowed to connect regardless of if they have an     #
 -# account, for example guest users.                                   #
 +# The allowpattern value allows you to specify a space separated list #
 +# of wildcard masks which will always be allowed to connect           #
 +# regardless of if they have an account, for example guest and bot    #
 +# users.                                                              #
  #                                                                     #
  # Killreason indicates the QUIT reason to give to users if they fail  #
  # to authenticate.                                                    #
  #                                                                     #
 -# The searchscope value indicates the subtree to search under. On our #
 -# test system this is 'subtree'. Your mileage may vary.               #
 -#                                                                     #
  # Setting the verbose value causes an oper notice to be sent out for  #
  # every failed authentication to the server, with an error string.    #
  #                                                                     #
 -# The binddn and bindauth indicate the DN to bind to for searching,   #
 -# and the password for the distinguished name. Some LDAP servers will #
 -# allow anonymous searching in which case these two values do not     #
 -# need defining, otherwise they should be set similar to the examples #
 -# above.                                                              #
 -#                                                                     #
  # ldapwhitelist indicates that clients connecting from an IP in the   #
  # provided CIDR do not need to authenticate against LDAP. It can be   #
  # repeated to whitelist multiple CIDRs.                               #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # LDAP oper configuration module: Adds the ability to authenticate    #
 -# opers via LDAP. This is an extra module which must be enabled       #
 -# explicitly by symlinking it from modules/extra, and requires the    #
 -# OpenLDAP libs. Re-run configure with:                               #
 -# ./configure --enable-extras=m_ldapoper.cpp
 -# and run make install, then uncomment this module to enable it.      #
 +# opers via LDAP.                                                     #
- #                                                                     #
  #<module name="m_ldapoper.so">
  #                                                                     #
  # Configuration:                                                      #
  #                                                                     #
 -# <ldapoper baserdn="ou=People,dc=brainbox,dc=cc"
 -#           server="ldap://brainwave.brainbox.cc"
 -#           searchscope="subtree"
 -#           binddn="cn=Manager,dc=brainbox,dc=cc"
 -#           bindauth="mysecretpass"
 +# <ldapoper dbid="ldapdb"
 +#           baserdn="ou=People,dc=brainbox,dc=cc"
  #           attribute="uid">
  #                                                                     #
  # Available configuration items are identical to the same items in    #
  # not loaded the oper accounts are still protected by a password.     #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Lock server module: Adds /LOCKSERV and /UNLOCKSERV commands that is #
- # used to temporarily close/open for new connections to the server.   #
- # These commands require OPER status and that the LOCKSERV UNLOCKSERV #
+ # Lock server module: Adds /LOCKSERV and /UNLOCKSERV commands that    #
+ # are used to temporarily close/open the server for new connections.  #
+ # These commands require that the /LOCKSERV and /UNLOCKSERV commands  #
  # 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.                                             #
- #
  # This module is oper-only.
- #
  #<module name="m_lockserv.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #<module name="m_maphide.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Msg flood module: Adds message/notice flood protection (+f)
+ # Message flood module: Adds message/notice flood protection via
+ # channel mode +f.
  #<module name="m_messageflood.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # MLOCK module: Adds support for server-side enforcement of services
- # side MLOCKs.  Basically, this module suppresses any mode change that
+ # side MLOCKs. Basically, this module suppresses any mode change that
  # would likely be immediately bounced by services.
  #<module name="m_mlock.so">
  
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# Modenotice module: Adds the /MODENOTICE command that allows opers to
 +# send notices to all users having the given user mode(s) set.
 +#<module name="m_modenotice.so">
 +
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # MsSQL module: Allows other SQL modules to access MS SQL Server 
+ # MsSQL module: Allows other SQL modules to access MS SQL Server
  # through a unified API.
- # This modules is in extras. Re-run configure with: ./configure --enable-extras=m_mssql.cpp
+ # This module is in extras. Re-run configure with:
+ # ./configure --enable-extras=m_mssql.cpp
  # and run make install, then uncomment this module to enable it.
- #
  #<module name="m_mssql.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#-#
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # MySQL module: Allows other SQL modules to access MySQL databases
  # through a unified API.
- # This modules is in extras. Re-run configure with: ./configure --enable-extras=m_mysql.cpp
+ # This module is in extras. Re-run configure with:
+ # ./configure --enable-extras=m_mysql.cpp
  # and run make install, then uncomment this module to enable it.
- #
  #<module name="m_mysql.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#-#
  #<database module="mysql" name="mydb" user="myuser" pass="mypass" host="localhost" id="my_database2">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Named Modes module: This module allows for the display and set/unset
- # of channel settings and modes via long-form mode names in channels with
- # channelmode +Z set. For example, to set a channelban with named modes:
- # /mode #channel +Z ban=foo!bar@baz . Currently this doesn't serve much
- # purpose outside of making channel administration a bit easier in some
- # cases, but eventually modules will start using named modes only because
- # we're running out of channelmodes. :D
+ # Named modes module: Allows for the display and set/unset of channel
+ # modes via long-form mode names via +Z and the /PROP command.
+ # For example, to set a ban, do /mode #channel +Z ban=foo!bar@baz or
+ # /PROP #channel ban=foo!bar@baz
  #<module name="m_namedmodes.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # National characters module:
  # 1) Allows using national characters in nicknames.
  # 2) Allows using custom (national) casemapping over the network.
- # file -- filename of existing file in "locales" directory
- # casemapping -- custom value for 005 numeric (if you want it to be
- # different from the filename.
+ #<module name="m_nationalchars.so">
  #
+ # file - filename of existing file in "locales" directory
+ # casemapping - custom value for 005 numeric (if you want it to be
+ #               different from the filename).
  #<nationalchars file="bynets/russian-w1251-charlink" casemapping="ru_RU.cp1251-charlink">
- #<module name="m_nationalchars.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Nickchange flood protection module: Allows up to X nick changes in Y seconds.
- # Provides channel mode +F.
+ # Nickchange flood protection module: Provides channel mode +F X:Y
+ # which allows up to X nick changes in Y seconds.
  #<module name="m_nickflood.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #<module name="m_nicklock.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # No ctcp module: Adds the channel mode +C to block CTCPs and extban C
- # to block CTCPs sent by specific users.
+ # No CTCP module: Adds the channel mode +C to block CTCPs and extban
+ # 'C' to block CTCPs sent by specific users.
  #<module name="m_noctcp.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #<module name="m_nokicks.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # No nicks module: Adds the +N channel mode, as well as the N extban.
+ # 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">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # No part message module: adds extban 'p' to block part messages from #
+ # No part message module: Adds extban 'p' to block part messages from #
  # matching users.                                                     #
  #<module name="m_nopartmsg.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # No Notice module: adds the channel mode +T and the extban T to block
- # specific users from noticing the channel.
+ # No notice module: Adds the channel mode +T and the extban 'T' to
+ # block specific users from noticing the channel.
  #<module name="m_nonotice.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Network business 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">
  #
+ # Specify the prefix that +Y will grant here.
+ # Leave 'prefix' empty if you do not wish +Y to grant a prefix.
+ # If 'notice' is set to on, upon /OJOIN, the server will notice the
+ # channel saying that the oper is joining on network business.
+ # If 'op' is set to on, it will give them +o along with +Y.
  #<ojoin prefix="!" notice="yes" op="yes">
- # Specify the prefix that +Y will grant here
- # Leave prefix empty if you do not wish +Y to grant a prefix
- # If notice is set to on, upon ojoin, the server will notice
- # the channel saying that the oper is joining on network business
- # If op is set to on, it will give them +o along with +Y
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Oper channels mode: Adds the +O channel mode and +beI type O:<mask>
- # to ban, exempt, and invex given oper type masks.
- # e.g, /mode #channel +iI O:* is equivilant to chmode +O, but you
- # may also, e.g. /mode #channel +iI O:AdminTypeOnly to only allow admins.
- # +be work in a similar fashion.
- #
+ # Oper channels mode: Adds the +O channel mode and extban O:<mask>
+ # to ban, except, etc. specific oper types. For example
+ # /mode #channel +iI O:* is equivalent to channel mode +O, but you
+ # may also set +iI O:AdminTypeOnly to only allow admins.
+ # Modes +I and +e work in a similar fashion.
  #<module name="m_operchans.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Oper Join module: Auto-joins opers to a channel upon oper-up
+ # Oper join module: Auto-joins opers to a channel upon oper-up.
  # This module is oper-only. For the user equivalent, see m_conn_join.
  #<module name="m_operjoin.so">
  #
  # If you are using the m_operjoin.so module, specify options here:    #
  #                                                                     #
  # channel     -      The channel name to join, can also be a comma    #
- #                    separated list eg. "#channel1,#channel2".        #
+ #                    separated list e.g. "#channel1,#channel2".       #
  #                                                                     #
- # override    -      Lets the oper join walking thru any modes that   #
- #                    might be set, even bans. Use "yes" or "no".      #
+ # override    -      If on, lets the oper join walking thru any modes #
+ #                    that might be set, even bans.                    #
  #                                                                     #
  #<operjoin channel="#channel" override="no">
  #
  #<type name="Helper" autojoin="#help" classes="...">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Oper log module: Logs all oper commands to the ircd log at default
- # loglevel, and optionally to the 'r' SNOMASK.
+ # Oper log module: Logs all oper commands to the server log (with log
+ # type "m_operlog" at default loglevel), and optionally to the 'r'
+ # snomask.
  # This module is oper-only.
  #<module name="m_operlog.so">
  #
  #<operlog tosnomask="off">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Oper prefixing module: Gives IRC operators a prefix status character
- # on all channels they are in.
- #
+ # Oper prefixing module: Gives server operators a prefix status
+ # character on all channels they are in.
  #<module name="m_operprefix.so">
  #
  # You may additionally customise the prefix character.
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Oper MOTD module: Provides support for separate message of the day
- # on oper-up
+ # on oper-up.
  # This module is oper-only.
  #<module name="m_opermotd.so">
  #
  #-#-#-#-#-#-#-#-#-#-#   OPERMOTD CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
- # If you are using the m_opermotd.so module, specify the motd here    #
+ # If you are using the m_opermotd.so module, specify the motd here.   #
  #                                                                     #
- # onoper        - Should the message be sent on /OPER or only when    #
- #                 /OPERMOTD is used. Use "yes" or "no".               #
+ # onoper        - If on, the message is sent on /OPER, otherwise it's #
+ #                 only sent when /OPERMOTD is used.                   #
  #                                                                     #
  # processcolors - Allow color codes to be processed in the opermotd.  #
  #                 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
+ # Override module: Adds support for oper override.
  # This module is oper-only.
  #<module name="m_override.so">
  #
  # http://wiki.inspircd.org/Modules/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
+ # Oper levels module: Gives each oper a level and prevents actions
+ # being taken by lower level opers against higher level opers.
+ # Specify the level as the 'level' parameter of the <type> tag.
  # This module is oper-only.
  #<module name="m_operlevels.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Oper modes module: Allows you to specify modes to add/remove on oper
+ # Oper modes module: Allows you to specify modes to add/remove on oper.
  # Specify the modes as the 'modes' parameter of the <type> tag
  # and/or as the 'modes' parameter of the <oper> tag.
- # This module is oper-only. For the user equivalent, see m_conn_umodes
+ # This module is oper-only. For the user equivalent, see m_conn_umodes.
  #<module name="m_opermodes.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Password forwarding module: Forwards a password users can send on connect
- # to the specified client below. The client is usually nickserv and this
- # module is usually used to authenticate users with nickserv using their
- # connect password.
+ # Password forwarding module: Forwards a password users can send on
+ # connect to the specified client below. The client is usually NickServ
+ # and this module is usually used to authenticate users with NickServ
+ # using their connect password.
  #<module name="m_passforward.so">
  
  <passforward
                # 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 receives a connect
-               # password. 
+               # cmd: Command for the user to run when it receives a connect
+               # password.
                cmd="PRIVMSG $nickrequired :IDENTIFY $pass">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Password hash module: Allows hashed passwords to be used.
  # To be useful, a hashing module like m_sha256.so also needs to be loaded.
- # 
  #<module name="m_password_hash.so">
  #
  #-#-#-#-#-#-#-#-#-# PASSWORD HASH CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#
  #           hash="sha256"
  #           password="01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"
  #           type="NetAdmin">
- # 
+ #
  # Starting from 2.0, you can use a more secure salted hash that prevents simply
  # looking up the hash's value in a rainbow table built for the hash.
  #    hash="hmac-sha256" password="lkS1Nbtp$CyLd/WPQXizsbxFUTqFRoMvaC+zhOULEeZaQkUJj+Gg"
  #
- # Generate hashes using the /MKPASSWD command on the server. Don't run it on a
- # server you don't trust with your password.
+ # Generate hashes using the /MKPASSWD command on the server.
+ # Don't run it on a server you don't trust with your password.
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Permanent Channels module: Channels with the permanent channels mode
+ # Permanent channels module: Channels with the permanent channel mode
  # will remain open even after everyone else has left the channel, and
  # therefore keep things like modes, ban lists and topic. Permanent
  # channels -may- need support from your Services package to function
  #
  # If 'listmodes' is true then all list modes (+b, +I, +e, +g...) will be
  # saved. Defaults to false.
 -#<permchanneldb filename="data/permchannels.conf" listmodes="true">
 -#<include file="data/permchannels.conf">
 +#<permchanneldb filename="permchannels.conf" listmodes="true">
 +#<include file="permchannels.conf">
  #
  # You may also create channels on startup by using the <permchannels> block.
  # Don't forget to set them +P in the modes, or they won't stay permanent.
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # PostgreSQL module: Allows other SQL modules to access PgSQL databases
  # through a unified API.
- # This modules is in extras. Re-run configure with: ./configure --enable-extras=m_pgsql.cpp
+ # This module is in extras. Re-run configure with:
+ # ./configure --enable-extras=m_pgsql.cpp
  # and run make install, then uncomment this module to enable it.
- #
  #<module name="m_pgsql.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-#- SQL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#-#
  #<database module="pgsql" name="mydb" user="myuser" pass="mypass" host="localhost" id="my_database" ssl="no">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Muteban: Implements extended ban m:, which stops anyone matching
+ # Muteban: Implements extended ban 'm', which stops anyone matching
  # a mask like +b m:nick!user@host from speaking on channel.
  #<module name="m_muteban.so">
- #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Random Quote module: provides a random quote on connect.
- # NOTE: Some of these may mimic fatal errors and confuse users and 
- # opers alike! - BEWARE!
+ # Random quote module: Provides a random quote on connect.
+ # NOTE: Some of these may mimic fatal errors and confuse users and
+ # opers alike - BEWARE!
  #<module name="m_randquote.so">
  #
  #-#-#-#-#-#-#-#-#-#-  RANDOMQUOTES CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  # Optional - If you specify to use the m_randquote.so module, then    #
- # specify below the path to the randquotes.conf file.                 #
+ # specify below the path to the quotes file.                          #
  #                                                                     #
  #<randquote file="quotes.txt">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Redirect module: Adds channel redirection (mode +L)                 #
+ # Redirect module: Adds channel redirection mode +L.                  #
  # Optional: <redirect:antiredirect> to add usermode +L to stop forced #
  # redirection and instead print an error.                             #
  #                                                                     #
  #<redirect antiredirect="true">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Regular Expression Provider for Glob or wildcard (?/*) matching.
+ # Regular expression provider for glob or wildcard (?/*) matching.
  # You must have at least 1 provider loaded to use m_filter or m_rline
  # modules. This module has no additional requirements, as it uses the
  # matching already present in InspIRCd core.
  #<module name="m_regex_glob.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Regular Expression Provider for PCRE (Perl-Compatible Regular
+ # Regular expression provider for PCRE (Perl-Compatible Regular
  # Expressions). You need libpcre installed to compile and load this
  # module. You must have at least 1 provider loaded to use m_filter or
  # m_rline.
  #<module name="m_regex_pcre.so">
  
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# 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.
+ # Regular expression provider for POSIX regular expressions.
  # You shouldn't need any additional libraries on a POSIX-compatible
  # system (i.e.: any Linux, BSD, but not Windows). You must have at
  # least 1 provider loaded to use m_filter or m_rline.
  #<module name="m_regex_posix.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Regular Expression Provider for C++11 std::regex Regular Expressions.
+ # Regular expression provider for C++11 std::regex regular expressions.
  # This module works on any fully compliant implementation of the C++11
  # std::regex container. Examples for such are Visual C++ 2010 and newer
- # but not libstdc++ (which GCC uses)
+ # but not libstdc++ (which GCC uses).
  # You should verify that std::regex is supported by your setup before
  # using this module, as it may compile normally but won't do anything
  # on some implementations.
  #<module name="m_regex_stdlib.so">
- # Specify the Regular Expression engine to use here. Valid settings are
- # bre, ere, awk, grep, egrep, ecmascript (default if not specified)
+ #
+ # Specify the regular expression engine to use here. Valid settings are
+ # bre, ere, awk, grep, egrep, ecmascript (default if not specified).
  #<stdregex type="ecmascript">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Regular Expression Provider for TRE Regular Expressions.
+ # Regular expression provider for TRE regular expressions.
  # This is the same regular expression engine used by UnrealIRCd, so
  # if you are most familiar with the syntax of /spamfilter from there,
  # this is the provider you want. You need libtre installed in order
  #<module name="m_regex_tre.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Registered users only channel creation
- # Allows only registered users and opers to create new channels.
+ # Registered users only channel creation module. If enabled, only
+ # registered users and opers can create new channels.
  #
  # You probably *DO NOT* want to load this module on a public network.
  #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Remove module: Adds the /REMOVE command which is a peaceful
- # alternative to /KICK
+ # 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.
  #
  #<module name="m_restrictmsg.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Ban users through regular expression patterns
+ # R-Line module: Ban users through regular expression patterns.
  #<module name="m_rline.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-#- RLINE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
  #
  # If you wish to re-check a user when they change nickname (can be
  # useful under some situations, but *can* also use CPU with more users
- # on a server) then set the following configuration value:
+ # on a server) then set 'matchonnickchange' to yes.
  # Also, this is where you set what Regular Expression engine is to be
- # used. If you ever change it while running, all of your R-Lines will be
- # wiped. This is the regex engine used by all R-Lines set, and
- # m_regex_<engine>.so must be loaded, or rline will be nonfunctional
+ # used. If you ever change it while running, all of your R-Lines will
+ # be wiped. This is the regex engine used by all R-Lines set, and
+ # m_regex_<engine>.so must be loaded, or rline will be non-functional
  # until you load it or change the engine to one that is loaded.
  #
  #<rline matchonnickchange="yes" engine="pcre">
  #
  # Generally, you will NOT want to use 'glob' here, as this turns
  # rline into just another gline. The exceptions are that rline will
- # always use the full nick!user@host realname string, rather than only
+ # always use the full "nick!user@host realname" string, rather than only
  # user@host, but beware that only the ? and * wildcards are available,
  # and are the only way to specify where the space can occur if you do
  # 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
+ # SAJOIN module: Adds the /SAJOIN command which forcibly joins a user
+ # to the given channel.
  # This module is oper-only.
  # To use, SAJOIN must be in one of your oper class blocks.
 +# Opers need the users/sajoin-others priv to be able to /SAJOIN users
 +# other than themselves.
  #<module name="m_sajoin.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # SAKICK module: Adds the /SAKICK command
+ # SAKICK module: Adds the /SAKICK command which kicks a user from the
+ # given channel.
  # This module is oper-only.
  # To use, SAKICK must be in one of your oper class blocks.
  #<module name="m_sakick.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # SAMODE module: Adds the oper /SAMODE command
+ # SAMODE module: Adds the /SAMODE command which allows server operators
+ # to change modes on a channel without requiring them to have any
+ # channel priviliges. Also allows changing user modes for any user.
  # This module is oper-only.
  # To use, SAMODE must be in one of your oper class blocks.
  #<module name="m_samode.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # SANICK module: Allows opers to change user's nicks
+ # SANICK module: Adds the /SANICK command which allows opers to change
+ # users' nicks.
  # This module is oper-only.
  # To use, SANICK must be in one of your oper class blocks.
  #<module name="m_sanick.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # SAPART module: Adds the oper /SAPART command
+ # SAPART module: Adds the /SAPART command which forcibly parts a user
+ # from a channel.
  # This module is oper-only.
  # To use, SAPART must be in one of your oper class blocks.
  #<module name="m_sapart.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # SAQUIT module: Adds the oper /SAQUIT command (abusable!!!)
+ # SAQUIT module: Adds the /SAQUIT command which forcibly quits a user.
  # This module is oper-only.
  # To use, SAQUIT must be in one of your oper class blocks.
  #<module name="m_saquit.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # SATOPIC module: Adds the oper /SATOPIC command
+ # SATOPIC module: Adds the /SATOPIC command which allows changing the
+ # topic on a channel without requiring any channel priviliges.
  # This module is oper-only.
  # To use, SATOPIC must be in one of your oper class blocks.
  #<module name="m_satopic.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # SASL authentication module: Provides support for IRC Authentication 
+ # SASL authentication module: Provides support for IRC Authentication
  # Layer via AUTHENTICATE. Note: You also need to have m_cap.so loaded
  # for SASL to work.
  #<module name="m_sasl.so">
  #
  #-#-#-#-#-#-#-#-#-# SECURELIST CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
- # Securelist can be harmful to some irc search engines such as        #
+ # Securelist can be harmful to some IRC search engines such as        #
  # netsplit.de and searchirc.com. To prevent securelist blocking these #
  # sites from listing, define exception tags as shown below:           #
- <securehost exception="*@*.searchirc.org">
- <securehost exception="*@*.netsplit.de">
- <securehost exception="*@echo940.server4you.de">
- <securehost exception="*@*.ircdriven.com">
#<securehost exception="*@*.searchirc.org">
#<securehost exception="*@*.netsplit.de">
#<securehost exception="*@echo940.server4you.de">
#<securehost exception="*@*.ircdriven.com">
  #                                                                     #
  # Define the following variable to change how long a user must wait   #
  # before issuing a LIST. If not defined, defaults to 60 seconds.      #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Servprotect module: Provides support for Austhex style +k /
- # UnrealIRCD +S services mode
+ # UnrealIRCD +S services mode.
  #<module name="m_servprotect.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # See nicks module: Allow for SNOMASK +N which shows nick changes.
+ # See nicks module: Adds snomask +n and +N which show local and remote
+ # nick changes.
  # This module is oper-only.
  #<module name="m_seenicks.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Set Idle module: Adds a command for opers to change their
- # idle time (mainly a toy)
+ # Set idle module: Adds a command for opers to change their idle time.
  # This module is oper-only.
  # To use, SETIDLE must be in one of your oper class blocks.
  #<module name="m_setidle.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Services support module: Adds several usermodes such as +R and +M
- # this module implements the 'identified' state via account names (AC)
+ # Services support module: Adds several usermodes such as +R and +M.
+ # This module implements the 'identified' state via account names,
  # 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 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.
- #
  # Also of note is that this module implements three extbans:
  # +b R: (stop matching account names from joining)
  # +b M: (stop matching account names from speaking)
  # +b U:n!u@h (blocks matching unregistered users)
- #                                                                       
+ #
  #<module name="m_services_account.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Sethost module: Adds the /SETHOST command
+ # Sethost module: Adds the /SETHOST command.
  # This module is oper-only.
  # To use, SETHOST must be in one of your oper class blocks.
  # See m_chghost for how to customise valid chars for hostnames
  #<module name="m_sethost.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Setident module: Adds the /SETIDENT command
+ # Setident module: Adds the /SETIDENT command.
  # This module is oper-only.
  # To use, SETIDENT must be in one of your oper class blocks.
  #<module name="m_setident.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # SETNAME module: Adds the /SETNAME command
+ # SETNAME module: Adds the /SETNAME command.
  #<module name="m_setname.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Serverban: Implements extended ban s:, which stops anyone connected
+ # Serverban: Implements extended ban 's', which stops anyone connected
  # to a server matching a mask like +b s:server.mask.here from joining.
  #<module name="m_serverban.so">
  
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# Showfile: Provides support for showing a text file to users when    #
 +# they enter a command.                                               #
 +# This module adds one command for each <showfile> tag that shows the #
 +# given file to the user as a series of messages or numerics.         #
 +#<module name="m_showfile.so">                                        #
 +#                                                                     #
 +#-#-#-#-#-#-#-#-#-#-# SHOWFILE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#-#
 +#                                                                     #
 +# name    - The name of the command which displays this file. This is #
 +#           the only mandatory setting, all others are optional.      #
 +# file    - The text file to be shown to the user.                    #
 +#           By default same as the command name.                      #
 +# method  - How should the file be shown?                             #
 +#           * numeric: Send contents using a numeric                  #
 +#             (similiar to /MOTD; the default).                       #
 +#           * notice:  Send contents as a series of notices.          #
 +#           * msg:     Send contents as a series of private messages. #
 +# colors  - If true, color codes (\c, \b, \u, etc.) will be processed #
 +#           and sent as ANSI colors. If false (default) the file will #
 +#           be displayed as-is.                                       #
 +#                                                                     #
 +# When using the method "numeric", the following extra settings are   #
 +# available:                                                          #
 +#                                                                     #
 +# introtext    - Introductory line, "Showing <name>" by default.      #
 +# intronumeric - Numeric used for the introductory line.              #
 +# numeric      - Numeric used for sending the text itself.            #
 +# endtext      - Ending line, "End of <name>" by default.             #
 +# endnumeric   - Numeric used for the ending line.                    #
 +#                                                                     #
 +#<showfile name="RULES"
 +#          file="rules.txt"
 +#          colors="true"
 +#          introtext="Server rules:"
 +#          endtext="End of server rules.">
 +
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Show Whois module: Adds the +W usermode which allows opers
- # to see when they are whois'ed (can be annoying).
- # This module is oper-only.
+ # Show whois module: Adds the +W usermode which allows opers to see
+ # when they are /WHOIS'd.
+ # This module is oper-only by default.
  #<module name="m_showwhois.so">
  #
  # If you wish, you may also let users set this mode. Only opers with the
 -# users/auspex priv will see real hosts of people, though. This setting
 -# is not reloadable via /REHASH, changing it requires /RELOADMODULE.
 +# users/auspex priv will see real hosts of people, though.
  #<showwhois opersonly="yes"
  #
- # You may also set whether or not users should receive whois notices, should
- # they be /whois'd by an oper.
- # showfromopers="yes">
+ # You may also set whether or not users should receive whois notices,
+ # should they be /WHOIS'd by an oper.
+ #showfromopers="yes">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Shun module: Provides the /SHUN command, which stops a user from
  #<module name="m_shun.so">
  #
  # You may also configure which commands you wish a user to be able to
- # perform. It should be noted that if a shunned user issues QUIT or PART
- # then their message will be removed, as if they did not issue one.
+ # perform when shunned. It should be noted that if a shunned user
+ # issues QUIT or PART then their message will be removed, as if they
+ # did not issue one.
  #
- # You can (optionally) let the user know that their command was blocked.
+ # You can optionally let the user know that their command was blocked.
  #
  # You may also let SHUN affect opers (defaults to no).
  #<shun enabledcommands="PING PONG QUIT PART JOIN" notifyuser="yes" affectopers="no">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # SSL channel mode module: Adds support for SSL-only channels (+z).
- # does not do anything useful without a working SSL module (see below)
+ # SSL channel mode module: Adds support for SSL-only channels via
+ # channel mode +z and the 'z' extban which matches SSL client
+ # certificate fingerprints.
+ # Does not do anything useful without a working SSL module (see below).
  #<module name="m_sslmodes.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #
  #-#-#-#-#-#-#-#-#-#-#-  GNUTLS CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
- # m_ssl_gnutls.so is too complex it describe here, see the wiki:      #
+ # m_ssl_gnutls.so is too complex to describe here, see the wiki:      #
  # http://wiki.inspircd.org/Modules/ssl_gnutls                         #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # SSL Info module: Allows users to retrieve information about other
- # user's peer SSL certificates and keys. This can be used by client
+ # SSL info module: Allows users to retrieve information about other
+ # users' peer SSL certificates and keys. This can be used by client
  # scripts to validate users. For this to work, one of m_ssl_gnutls.so
- # or m_ssl_openssl.so must be loaded. This module also adds the 
+ # or m_ssl_openssl.so must be loaded. This module also adds the
  # "* <user> is using a secure connection" whois line, the ability for
- # opers to use SSL fingerprints to verify their identity and the ability
- # to force opers to use SSL connections in order to oper up.
- # It is highly recommended to load this module especially if
- # you use SSL on your network.
+ # opers to use SSL fingerprints to verify their identity and the
+ # ability to force opers to use SSL connections in order to oper up.
+ # It is highly recommended to load this module if you use SSL on your
+ # network.
  # For how to use the oper features, please see the first example <oper> tag
  # in opers.conf.example.
  #
  #
  #-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
- # m_ssl_openssl.so is too complex it describe here, see the wiki:     #
+ # m_ssl_openssl.so is too complex to describe here, see the wiki:     #
  # http://wiki.inspircd.org/Modules/ssl_openssl                        #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Strip color module: Adds the channel mode +S
+ # Strip color module: Adds channel mode +S that strips mIRC color
+ # codes from all messages sent to the channel.
  #<module name="m_stripcolor.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # SILENCE module: Adds support for /SILENCE
+ # Silence module: Adds support for the /SILENCE command, which allows
+ # users to have a server-side ignore list for their client.
  #<module name="m_silence.so">
  #
- # Configuration tags:
- #
+ # Set the maximum number of entries allowed on a user's silence list.
  #<silence maxentries="32">
- #
- # Sets the maximum number of entries on a users silence list.
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SQLite3 module: Allows other SQL modules to access SQLite3          #
- # databases through a unified API. 
- # This modules is in extras. Re-run configure with: ./configure --enable-extras=m_sqlite.cpp
+ # databases through a unified API.                                    #
+ # This module is in extras. Re-run configure with:                    #
+ # ./configure --enable-extras=m_sqlite.cpp
  # and run make install, then uncomment this module to enable it.      #
  #
  #<module name="m_sqlite3.so">
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SQL authentication module: Allows IRCd connections to be tied into
  # a database table (for example a forum).
- # This modules is in extras. Re-run configure with: ./configure --enable-extras=m_sqlauth.cpp
+ # This module is in extras. Re-run configure with:
+ # ./configure --enable-extras=m_sqlauth.cpp
  # and run make install, then uncomment this module to enable it.
  #
  #<module name="m_sqlauth.so">
  #
  #-#-#-#-#-#-#-#-#-#-#- SQLAUTH CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
- # m_sqlauth.so is too complex it describe here, see the wiki:         #
+ # m_sqlauth.so is too complex to describe here, see the wiki:         #
  # http://wiki.inspircd.org/Modules/sqlauth                            #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SQL oper module: Allows you to store oper credentials in an SQL table
- # This modules is in extras. Re-run configure with: ./configure --enable-extras=m_sqloper.cpp
+ # This module is in extras. Re-run configure with:
+ # ./configure --enable-extras=m_sqloper.cpp
  # and run make install, then uncomment this module to enable it.
  #
  #<module name="m_sqloper.so">
  #
  #-#-#-#-#-#-#-#-#-#-#- SQLOPER CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
- # dbid       - Database ID to use (see m_sql)                         #
- # hash       - Hashing provider to use for password hashing           #
+ # dbid       - Database ID to use (see SQL modules).                  #
+ # hash       - Hashing provider to use for password hashing.          #
  #                                                                     #
  # See also: http://wiki.inspircd.org/Modules/sqloper                  #
  #                                                                     #
  #<sqloper dbid="1" hash="md5">
  
 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 +# StartTLS module: Implements STARTTLS, which allows clients          #
 +# connected to non SSL enabled ports to enable SSL, if a proper SSL   #
 +# module is loaded (either m_ssl_gnutls or m_ssl_openssl).            #
 +#<module name="m_starttls.so">
 +
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # SVSHold module: Implements SVSHOLD. Like Q:Lines, but can only be   #
  # added/removed by Services.                                          #
  #<module name="m_svshold.so">
 -# If silent is true no snotices will be generated by SVSHOLD.
 +# SVSHOLD does not generate server notices by default, you can turn
 +# notices on by uncommenting the next line.
  #<svshold silent="false">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # SWHOIS module: Allows you to add arbitary lines to user WHOIS.
+ # SWHOIS module: Allows you to add arbitrary lines to user WHOIS.
  # This module is oper-only.
  # To use, SWHOIS must be in one of your oper class blocks.
  #<module name="m_swhois.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Test module: enable this to create a command useful in testing
+ # Test module: Enable this to create a command useful in testing
  # flood control. To avoid accidental use on live networks, the server
  # name must contain ".test" to load the module
  #<module name="m_testnet.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Timed bans module: Adds timed channel bans and the /TBAN command
+ # Timed bans module: Adds timed channel bans with the /TBAN command.
  #<module name="m_timedbans.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #<module name="m_uninvite.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Userip module: Adds the /USERIP command
+ # Userip module: Adds the /USERIP command.
  # Allows users to query their own IP, also allows opers to query the IP
  # of anyone else.
  #<module name="m_userip.so">
  #<vhost user="foo" password="fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9" hash="sha256" host="some.other.host">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Watch module: Adds the WATCH command, which is used by clients to 
+ # Watch module: Adds the WATCH command, which is used by clients to
  # maintain notify lists.
  #<module name="m_watch.so">
  #
- # Configuration tags:
- #
+ # Set the maximum number of entries on a user's watch list below.
  #<watch maxentries="32">
- #
- # Sets the maximum number of entries on a user's watch list.
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # XLine database: Stores all *Lines (G/Z/K/R/any added by other modules)
- # in a file "xline.db" which can be re-loaded on restart. This is useful
+ # in a file which is re-loaded on restart. This is useful
  # for two reasons: it keeps bans so users may not evade them, and on
  # bigger networks, server connections will take less time as there will
  # be a lot less bans to apply - as most of them will already be there.
  #<module name="m_xline_db.so">
  
- # Specify the filename for the xline database here
+ # Specify the filename for the xline database here.
  #<xlinedb filename="data/xline.db">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # module. If you don't do this, server links will NOT work at all.    #
  # This is by design, to allow for the implementation of other linking #
  # protocols in modules in the future.                                 #
- #                                                                     #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Spanning Tree module - allows linking of servers using the spanning
+ # Spanning tree module: Allows linking of servers using the spanning
  # tree protocol (see the READ THIS BIT section above).
  # You will almost always want to load this.
  #
  #<module name="m_spanningtree.so">
index afd54359c75492726f33c9f5f5bac09d02bff443,9518822de3b7b5028cae93c9fc2cd3283d54f88e..2bda9d84089be18b4609876dffc8b5eb5c35ae47
@@@ -1,7 -1,7 +1,7 @@@
  <module name="m_md5.so">
  <module name="m_sha256.so">
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Alias module: Allows you to define server-side command aliases
+ # Alias module: Allows you to define server-side command aliases.
  <module name="m_alias.so">
  <fantasy prefix="!" allowbots="no">
  # Aliases
  <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">
  <module name="m_commonchans.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Conn-Join: Allows you to force users to join one or more channels
- # automatically upon connecting to the server.
+ # Auto join on connect module: Allows you to force users to join one
+ # or more channels automatically upon connecting to the server.
  #<module name="m_conn_join.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-#- CONNJOIN CONFIGURATION  -#-#-#-#-#-#-#-#-#-#-#
  <module name="m_cycle.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Connection throttle module. Configuration:
+ # Connection throttle module.
  #<module name="m_connflood.so">
  #
  #-#-#-#-#-#-#-#-#-#-#- CONTHROTTLE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
  #   quitmsg="Throttled" bootwait="10">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # DCCALLOW module: Adds the /DCCALLOW command
+ # DCCALLOW module: Adds the /DCCALLOW command.
  <module name="m_dccallow.so">
  #
  #-#-#-#-#-#-#-#-#-#-#-  DCCALLOW CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
  <module name="m_operchans.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Oper Join module: Auto-joins opers to a channel upon oper-up
+ # Oper join module: Auto-joins opers to a channel upon oper-up.
  # This module is oper-only. For the user equivalent, see m_conn_join.
  <module name="m_operjoin.so">
  #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Oper MOTD module: Provides support for separate message of the day
- # on oper-up
+ # on oper-up.
  # This module is oper-only.
  #<module name="m_opermotd.so">
  #
  #                                                                     #
  # If you are using the m_opermotd.so module, specify the motd here    #
  #                                                                     #
- # onoper      -      Should the message be sent on /OPER or only when #
- #                    /OPERMOTD is used. Use "yes" or "no".            #
+ # onoper        - If on, the message is sent on /OPER, otherwise it's #
+ #                 only sent when /OPERMOTD is used.                   #
  #                                                                     #
  #<opermotd file="oper.motd" onoper="yes">
  
  <module name="m_override.so">
  #-#-#-#-#-#-#-#-#-#-#   OVERRIDE CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
- # m_override.so is too complex it describe here, see the wiki:        #
+ # m_override.so is too complex to describe here, see the wiki:        #
  # http://wiki.inspircd.org/Modules/override                           #
  
  <module name="m_operlevels.so">
  <module name="m_password_hash.so">
  <module name="m_muteban.so">
  
- #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Random Quote module: provides a random quote on connect.
- # NOTE: Some of these may mimic fatal errors and confuse users and 
- # opers alike! - BEWARE!
- #<module name="m_randquote.so">
- #
- #-#-#-#-#-#-#-#-#-#-  RANDOMQUOTES CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
- #                                                                     #
- # 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">
  <module name="m_redirect.so">
  <module name="m_regex_glob.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Regular Expression Provider for PCRE (Perl-Compatible Regular
+ # Regular expression provider for PCRE (Perl-Compatible Regular
  # Expressions). You need libpcre installed to compile and load this
  # module. You must have at least 1 provider loaded to use m_filter or
  # m_rline.
  #<module name="m_regex_pcre.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Regular Expression Provider for POSIX Regular Expressions.
+ # 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.
  #<module name="m_regex_posix.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Regular Expression Provider for TRE Regular Expressions.
+ # Regular expression provider for TRE Regular Expressions.
  # This is the same regular expression engine used by UnrealIRCd, so
  # if you are most familiar with the syntax of /spamfilter from there,
  # this is the provider you want. You need libtre installed in order
  #<module name="m_regex_tre.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Registered users only channel creation
- # Allows only registered users and opers to create new channels.
+ # Registered users only channel creation module. If enabled, only
+ # registered users and opers can create new channels.
  #
  # You probably *DO NOT* want to load this module on a public network.
  #
  <module name="m_shun.so">
  <shun enabledcommands="PING PONG QUIT PART JOIN" notifyuser="no" affectopers="no">
  
- <module name="m_spy.so">
  <module name="m_sslmodes.so">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #
  #-#-#-#-#-#-#-#-#-#-#-  GNUTLS CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
- # m_ssl_gnutls.so is too complex it describe here, see the wiki:      #
+ # m_ssl_gnutls.so is too complex to describe here, see the wiki:      #
  # http://wiki.inspircd.org/Modules/ssl_gnutls                         #
  
  <module name="m_sslinfo.so">
  #
  #-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
- # m_ssl_openssl.so is too complex it describe here, see the wiki:     #
+ # m_ssl_openssl.so is too complex to describe here, see the wiki:     #
  # http://wiki.inspircd.org/Modules/ssl_openssl                        #
  
  <module name="m_stripcolor.so">
  <watch maxentries="32">
  
  <module name="m_spanningtree.so">
index b42f3129a6193b4d7aa62c431db2891c89732e1d,d03d019c6d0ad50a33bc73202158a9eb7b6675e2..524ebce3422a1b1c41bd92188bdf316a94366d10
@@@ -8,25 -8,25 +8,26 @@@
  #  Note: It is possible to make a class which covers all available    #
  #  commands. To do this, specify commands="*". This is not really     #
  #  recommended, as it negates the whole purpose of the class system,  #
- #  however it is provided for fast configuration (e.g. in test nets)  #
+ #  however it is provided for fast configuration (e.g. in test nets). #
  #                                                                     #
  
  <class
       name="Shutdown"
  
-      # commands: oper commands that users of this class can run.
-      commands="DIE RESTART REHASH LOADMODULE UNLOADMODULE RELOAD GUNLOADMODULE GRELOADMODULE SAJOIN SAPART SANICK SAQUIT SATOPIC"
+      # commands: Oper-only commands that opers of this class can run.
+      commands="DIE RESTART REHASH LOADMODULE UNLOADMODULE RELOADMODULE GUNLOADMODULE GRELOADMODULE"
  
-      # privs: special privileges that users with this class may utilise.
+      # privs: Special privileges that users with this class may utilise.
       #  VIEWING:
       #   - channels/auspex: allows opers with this priv to see more detail about channels than normal users.
-      #   - users/auspex: allows opers with this priv to view more details about users than normal users.
+      #   - users/auspex: allows opers with this priv to view more details about users than normal users, e.g. real host and IP.
       #   - 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-fakelag: prevents opers from being penalized with fake lag for flooding (*NOTE)
       #   - users/flood/no-throttle: allows opers with this priv to send commands without being throttled (*NOTE)
       #   - users/flood/increased-buffers: allows opers with this priv to send and receive data without worrying about being disconnected for exceeding limits (*NOTE)
       #
       # chanmodes: Oper-only channel modes that opers with this class can use.
       chanmodes="*">
  
- <class name="ServerLink" commands="CONNECT SQUIT CONNECT MKPASSWD ALLTIME SWHOIS CLOSE JUMPSERVER LOCKSERV" usermodes="*" chanmodes="*" privs="servers/auspex">
- <class name="BanControl" commands="KILL GLINE KLINE ZLINE QLINE ELINE TLINE RLINE CHECK NICKLOCK SHUN CLONES CBAN CLEARCHAN" usermodes="*" chanmodes="*">
- <class name="OperChat" commands="WALLOPS GLOBOPS SETIDLE" usermodes="*" chanmodes="*" privs="users/mass-message">
- <class name="HostCloak" commands="SETHOST SETIDENT SETNAME CHGHOST CHGIDENT" usermodes="*" chanmodes="*" privs="users/auspex">
+ <class name="SACommands" commands="SAJOIN SAPART SANICK SAQUIT SATOPIC SAKICK SAMODE">
+ <class name="ServerLink" commands="CONNECT SQUIT RCONNECT RSQUIT MKPASSWD ALLTIME SWHOIS JUMPSERVER LOCKSERV UNLOCKSERV" usermodes="*" chanmodes="*" privs="servers/auspex">
+ <class name="BanControl" commands="KILL GLINE KLINE ZLINE QLINE ELINE TLINE RLINE CHECK NICKLOCK NICKUNLOCK SHUN CLONES CBAN CLOSE" usermodes="*" chanmodes="*">
+ <class name="OperChat" commands="WALLOPS GLOBOPS" usermodes="*" chanmodes="*" privs="users/mass-message">
+ <class name="HostCloak" commands="SETHOST SETIDENT CHGNAME CHGHOST CHGIDENT SETIDLE" usermodes="*" chanmodes="*" privs="users/auspex">
  
  
  #-#-#-#-#-#-#-#-#-#-#-#-  OPERATOR COMPOSITION   -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  
  <type
-     # name: Name of type. Used in actual olines below.
+     # name: Name of type. Used in actual server operator accounts below.
 -    # Cannot contain spaces. If you would like a space, use
 -    # the _ character instead and it will translate to a space on whois.
      name="NetAdmin"
  
-     # classes: classes (above blocks) that this type belongs to.
-     classes="OperChat BanControl HostCloak Shutdown ServerLink"
+     # classes: Classes (blocks above) that this type belongs to.
+     classes="SACommands OperChat BanControl HostCloak Shutdown ServerLink"
  
-     # vhost: host oper gets on oper-up. This is optional.
-     vhost="netadmin.omega.org.za"
+     # vhost: Host opers of this type get when they log in (oper up). This is optional.
+     vhost="netadmin.omega.example.org"
  
-     # modes: usermodes besides +o that are set on a oper of this type
+     # modes: User modes besides +o that are set on an oper of this type
      # when they oper up. Used for snomasks and other things.
      # Requires that m_opermodes.so be loaded.
      modes="+s +cCqQ">
  
- <type name="GlobalOp" classes="OperChat BanControl HostCloak ServerLink" vhost="ircop.omega.org.za">
- <type name="Helper" classes="HostCloak" vhost="helper.omega.org.za">
+ <type name="GlobalOp" classes="SACommands OperChat BanControl HostCloak ServerLink" vhost="ircop.omega.example.org">
+ <type name="Helper" classes="HostCloak" vhost="helper.omega.example.org">
  
  
  #-#-#-#-#-#-#-#-#-#-#-  OPERATOR CONFIGURATION   -#-#-#-#-#-#-#-#-#-#-#
  #                                                                     #
  #   Opers are defined here. This is a very important section.         #
- #   Remember to only make operators out of trust worthy people.       #
+ #   Remember to only make operators out of trustworthy people.        #
  #                                                                     #
  
- # oline with plain-text password
+ # Operator account with a plain-text password.
  <oper
-       # name: oper login that is used to oper up (/oper name password).
-       # Remember: This is case sensitive
-       name="Brain"
+       # name: Oper login that is used to oper up (/oper name password).
+       # Remember: This is case sensitive.
+       name="Attila"
  
-       # password: case-sensitive, unhashed...yea...self-explanatory.
+       # password: Case-sensitive, unhashed (plaintext).
        password="s3cret"
  
-       # host: What hostnames/IP's are allowed to oper up with this oline.
-       # Multiple options can be separated by spaces and CIDR's are allowed.
-       # You CAN use just * or *@* for this section, but it is not recommended
+       # host: What hostnames and IPs are allowed to use this operator account.
+       # Multiple options can be separated by spaces and CIDRs are allowed.
+       # You can use just * or *@* for this section, but it is not recommended
        # for security reasons.
-       host="yourident@dialup15.isp.com *@localhost *@example.com *@2001:db8::/32"
+       host="attila@inspircd.org *@2001:db8::/32"
  
        # ** ADVANCED ** This option is disabled by default.
        # fingerprint: When using the m_sslinfo module, you may specify
        # If m_sslinfo isn't loaded, this option will be ignored.
        #fingerprint="67cb9dc013248a829bb2171ed11becd4"
  
-       # autologin: if an SSL fingerprint for this oper is specified, you can
+       # autologin: If an SSL fingerprint for this oper is specified, you can
        # have the oper block automatically log in. This moves all security of the
        # oper block to the protection of the client certificate, so be sure that
        # the private key is well-protected! Requires m_sslinfo.
        #autologin="on"
  
-       # sslonly: This oper can only oper up if they're using a SSL connection.
+       # sslonly: If on, this oper can only oper up if they're using a SSL connection.
        # Setting this option adds a decent bit of security. Highly recommended
        # if the oper is on wifi, or specifically, unsecured wifi. Note that it
        # is redundant to specify this option if you specify a fingerprint.
        # This setting only takes effect if m_sslinfo is loaded.
        #sslonly="yes"
  
-       # vhost: overrides the vhost in the type block. Class and modes may also
-       # be overridden
-       vhost="brain.netadmin.omega"
+       # vhost: Overrides the vhost in the type block. Class and modes may also
+       # be overridden.
+       vhost="attila.example.org"
  
-       # type: What oper type this oline is. See the block above for list
-       # of types. NOTE: This is case-sensitive as well.
+       # type: Which type of operator this person is; see the block
+       # above for the list of types. NOTE: This is case-sensitive as well.
        type="NetAdmin">
  
- # oline with plain-text password and no comments..for all who like copy & paste
+ # Operator with a plaintext password and no comments, for easy copy & paste.
  <oper
        name="Brain"
        password="s3cret"
-       host="yourident@dialup15.isp.com *@localhost *@example.com *@2001:db8::/32"
+       host="brain@dialup15.isp.com *@localhost *@example.com *@2001:db8::/32"
        #fingerprint="67cb9dc013248a829bb2171ed11becd4"
        type="NetAdmin">
  
- # oline with hashed password. It is highly recommended to use hashed passwords.
+ # Operator with a hashed password. It is highly recommended to use hashed passwords.
  <oper
-       # name: oper login that is used to oper up (/oper name password).
-       # Remember: This is case sensitive
-       name="Brain"
+       # name: Oper login that is used to oper up (/oper name password).
+       # Remember: This is case sensitive.
+       name="Adam"
  
-       # hash: what hash this password is hashed with.
+       # 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"
+       # Create hashed passwords with: /mkpasswd <hash> <password>
+       hash="hmac-sha256"
  
-       # password: a hash of your password (see above option) hashed
-       # with /mkpasswd <hash> <password> . See m_password_hash in modules.conf
+       # password: A hash of the password (see above option) hashed
+       # with /mkpasswd <hash> <password>. See m_password_hash in modules.conf
        # for more information about password hashing.
-       password="1ec1c26b50d5d3c58d9583181af8076655fe00756bf7285940ba3670f99fcba0"
+       password="qQmv3LcF$Qh63wzmtUqWp9OXnLwe7yv1GcBwHpq59k2a0UrY8xe0"
  
-       # host: What hostnames/IP's are allowed to oper up with this oline.
-       # Multiple options can be separated by spaces and CIDR's are allowed.
-       # You CAN use just * or *@* for this section, but it is not recommended
+       # host: What hostnames and IPs are allowed to use this operator account.
+       # Multiple options can be separated by spaces and CIDRs are allowed.
+       # You can use just * or *@* for this section, but it is not recommended
        # for security reasons.
-       host="yourident@dialup15.isp.com *@localhost *@example.com *@2001:db8::/32"
+       host="*@127.0.0.1 *@192.0.2.40 *@198.51.100.4"
  
-       # type: What oper type this oline is. See the block above for list
-       # of types. NOTE: This is case-sensitive as well.
-       type="NetAdmin">
+       # type: Which type of operator this person is; see the block
+       # above for the list of types. NOTE: This is case-sensitive as well.
+       type="Helper">
diff --combined make/utilities.pm
index 48713fe9e9936bc7035ca718047c28296ebbe577,ebca57b686db343f6c09485d9b66902c0d9d6c6c..9d791870a9b27c796963aa9f06409d367f837fa7
  #
  
  
 -package make::utilities;
 +BEGIN {
 +      require 5.8.0;
 +}
  
 -require 5.8.0;
 +package make::utilities;
  
  use strict;
  use warnings FATAL => qw(all);
  
  use Exporter 'import';
 -use POSIX;
 -use Getopt::Long;
  use Fcntl;
 -our @EXPORT = qw(make_rpath pkgconfig_get_include_dirs pkgconfig_get_lib_dirs pkgconfig_check_version translate_functions promptstring);
 +use File::Path;
 +use File::Spec::Functions qw(rel2abs);
 +use Getopt::Long;
 +use POSIX;
 +
 +our @EXPORT = qw(module_installed prompt_bool prompt_dir prompt_string get_cpu_count make_rpath pkgconfig_get_include_dirs pkgconfig_get_lib_dirs pkgconfig_check_version translate_functions promptstring);
  
  # Parse the output of a *_config program,
  # such as pcre_config, take out the -L
  my %already_added = ();
  my $if_skip_lines = 0;
  
 +sub module_installed($)
 +{
 +      my $module = shift;
 +      eval("use $module;");
 +      return !$@;
 +}
 +
 +sub prompt_bool($$$) {
 +      my ($interactive, $question, $default) = @_;
 +      my $answer = prompt_string($interactive, $question, $default ? 'y' : 'n');
 +      return $answer =~ /y/i;
 +}
 +
 +sub prompt_dir($$$) {
 +      my ($interactive, $question, $default) = @_;
 +      my ($answer, $create) = (undef, 'y');
 +      do {
 +              $answer = rel2abs(prompt_string($interactive, $question, $default));
 +              $create = prompt_bool($interactive && !-d $answer, "$answer does not exist. Create it?", 'y');
 +              my $mkpath = eval {
 +                      mkpath($answer, 0, 0750);
 +                      return 1;
 +              };
 +              unless (defined $mkpath) {
 +                      print "Error: unable to create $answer!\n\n";
 +                      $create = 0;
 +              }
 +      } while (!$create);
 +      return $answer;
 +}
 +
 +sub prompt_string($$$) {
 +      my ($interactive, $question, $default) = @_;
 +      return $default unless $interactive;
 +      print $question, "\n";
 +      print "[\e[1;32m$default\e[0m] => ";
 +      chomp(my $answer = <STDIN>);
 +      print "\n";
 +      return $answer ? $answer : $default;
 +}
 +
 +sub get_cpu_count {
 +      my $count = 1;
 +      if ($^O =~ /bsd/) {
 +              $count = `sysctl -n hw.ncpu`;
 +      } elsif ($^O eq 'darwin') {
 +              $count = `sysctl -n hw.activecpu`;
 +      } elsif ($^O eq 'linux') {
 +              $count = `getconf _NPROCESSORS_ONLN`;
 +      } elsif ($^O eq 'solaris') {
 +              $count = `psrinfo -p`;
 +      }
 +      chomp($count);
 +      return $count;
 +}
 +
  sub promptstring($$$$$)
  {
        my ($prompt, $configitem, $default, $package, $commandlineswitch) = @_;
@@@ -147,7 -86,7 +147,7 @@@ sub make_rpath($;$
                        print "Adding extra library path to \e[1;32m$module\e[0m ... \e[1;32m$libpath\e[0m\n";
                        $already_added{$libpath} = 1;
                }
-               $output .= "-Wl,--rpath -Wl,$libpath -L$libpath " unless defined $main::opt_disablerpath;
+               $output .= "-Wl,-rpath -Wl,$libpath -L$libpath " unless defined $main::opt_disablerpath;
                $data =~ s/-L(\S+)//;
        }
        return $output;
@@@ -169,6 -108,15 +169,6 @@@ sub pkgconfig_get_include_dirs($$$;$
  {
        my ($packagename, $headername, $defaults, $module) = @_;
  
 -      my $key = "default_includedir_$packagename";
 -      if (exists $main::config{$key})
 -      {
 -              print "Locating include directory for package \e[1;32m$packagename\e[0m for module \e[1;32m$module\e[0m... ";
 -              my $ret = $main::config{$key};
 -              print "\e[1;32m$ret\e[0m (cached)\n";
 -              return $ret;
 -      }
 -
        extend_pkg_path();
  
        print "Locating include directory for package \e[1;32m$packagename\e[0m for module \e[1;32m$module\e[0m... ";
        if ((!defined $v) || ($v eq ""))
        {
                print "\e[31mCould not find $packagename via pkg-config\e[m (\e[1;32mplease install pkg-config\e[m)\n";
-               $foo = `locate "$headername" 2>/dev/null | head -n 1`;
+               my $locbin = $^O eq 'solaris' ? 'slocate' : 'locate';
+               $foo = `$locbin "$headername" 2>/dev/null | head -n 1`;
                my $find = $foo =~ /(.+)\Q$headername\E/ ? $1 : '';
                chomp($find);
                if ((defined $find) && ($find ne "") && ($find ne $packagename))
@@@ -275,6 -224,15 +276,6 @@@ sub pkgconfig_get_lib_dirs($$$;$
  {
        my ($packagename, $libname, $defaults, $module) = @_;
  
 -      my $key = "default_libdir_$packagename";
 -      if (exists $main::config{$key})
 -      {
 -              print "Locating library directory for package \e[1;32m$packagename\e[0m for module \e[1;32m$module\e[0m... ";
 -              my $ret = $main::config{$key};
 -              print "\e[1;32m$ret\e[0m (cached)\n";
 -              return $ret;
 -      }
 -
        extend_pkg_path();
  
        print "Locating library directory for package \e[1;32m$packagename\e[0m for module \e[1;32m$module\e[0m... ";
        my $foo = "";
        if ((!defined $v) || ($v eq ""))
        {
-               $foo = `locate "$libname" | head -n 1`;
+               my $locbin = $^O eq 'solaris' ? 'slocate' : 'locate';
+               $foo = `$locbin "$libname" | head -n 1`;
                $foo =~ /(.+)\Q$libname\E/;
                my $find = $1;
                chomp($find);
@@@ -348,6 -307,16 +350,6 @@@ sub translate_functions($$
                $module =~ /modules*\/(.+?)$/;
                $module = $1;
  
 -              # This is only a cursory check, just designed to catch casual accidental use of backticks.
 -              # There are pleanty of ways around it, but its not supposed to be for security, just checking
 -              # that people are using the new configuration api as theyre supposed to and not just using
 -              # backticks instead of eval(), being as eval has accountability. People wanting to get around
 -              # the accountability will do so anyway.
 -              if (($line =~ /`/) && ($line !~ /eval\(.+?`.+?\)/))
 -              {
 -                      die "Developers should no longer use backticks in configuration macros. Please use exec() and eval() macros instead. Offending line: $line (In module: $module)";
 -              }
 -
                if ($line =~ /ifuname\(\!"(\w+)"\)/)
                {
                        my $uname = $1;
                while ($line =~ /rpath\("(.+?)"\)/)
                {
                        my $replace = make_rpath($1,$module);
-                       $replace = "" if ($^O =~ /darwin/i);
                        $line =~ s/rpath\("(.+?)"\)/$replace/;
                }
        };
diff --combined src/channels.cpp
index c2060b90edb1a1905c5f3df49b8f3cc16b5ab58d,6c9bd7c85130f8927758c06895118f26861d09bc..4555deb96aa87551dde81a8adf52b3468ea296da
   */
  
  
 -/* $Core */
 -
  #include "inspircd.h"
 +#include "listmode.h"
  #include <cstdarg>
  #include "mode.h"
  
 -Channel::Channel(const std::string &cname, time_t ts)
 +namespace
  {
 -      if (!ServerInstance->chanlist->insert(std::make_pair(cname, this)).second)
 -              throw CoreException("Cannot create duplicate channel " + cname);
 -
 -      this->name = cname;
 -      this->age = ts ? ts : ServerInstance->Time();
 -
 -      maxbans = topicset = 0;
 -      modes.reset();
 +      ChanModeReference ban(NULL, "ban");
 +      ChanModeReference inviteonlymode(NULL, "inviteonly");
 +      ChanModeReference keymode(NULL, "key");
 +      ChanModeReference limitmode(NULL, "limit");
 +      ChanModeReference secretmode(NULL, "secret");
 +      ChanModeReference privatemode(NULL, "private");
 +      UserModeReference invisiblemode(NULL, "invisible");
  }
  
 -void Channel::SetMode(char mode,bool mode_on)
 +Channel::Channel(const std::string &cname, time_t ts)
 +      : name(cname), age(ts), topicset(0)
  {
 -      modes[mode-65] = mode_on;
 +      if (!ServerInstance->chanlist.insert(std::make_pair(cname, this)).second)
 +              throw CoreException("Cannot create duplicate channel " + cname);
  }
  
  void Channel::SetMode(ModeHandler* mh, bool on)
  {
 -      modes[mh->GetModeChar() - 65] = on;
 +      modes[mh->GetId()] = on;
  }
  
 -void Channel::SetModeParam(char mode, const std::string& parameter)
 +void Channel::SetTopic(User* u, const std::string& ntopic)
  {
 -      CustomModeList::iterator n = custom_mode_params.find(mode);
 -      // always erase, even if changing, so that the map gets the new value
 -      if (n != custom_mode_params.end())
 -              custom_mode_params.erase(n);
 -      if (parameter.empty())
 -      {
 -              modes[mode-65] = false;
 -      }
 -      else
 -      {
 -              custom_mode_params[mode] = parameter;
 -              modes[mode-65] = true;
 -      }
 -}
 -
 -void Channel::SetModeParam(ModeHandler* mode, const std::string& parameter)
 -{
 -      SetModeParam(mode->GetModeChar(), parameter);
 -}
 -
 -std::string Channel::GetModeParameter(char mode)
 -{
 -      CustomModeList::iterator n = custom_mode_params.find(mode);
 -      if (n != custom_mode_params.end())
 -              return n->second;
 -      return "";
 -}
 -
 -std::string Channel::GetModeParameter(ModeHandler* mode)
 -{
 -      CustomModeList::iterator n = custom_mode_params.find(mode->GetModeChar());
 -      if (n != custom_mode_params.end())
 -              return n->second;
 -      return "";
 -}
 -
 -int Channel::SetTopic(User *u, std::string &ntopic, bool forceset)
 -{
 -      if (!u)
 -              u = ServerInstance->FakeClient;
 -      if (IS_LOCAL(u) && !forceset)
 -      {
 -              ModResult res;
 -              FIRST_MOD_RESULT(OnPreTopicChange, res, (u,this,ntopic));
 -
 -              if (res == MOD_RES_DENY)
 -                      return CMD_FAILURE;
 -              if (res != MOD_RES_ALLOW)
 -              {
 -                      if (!this->HasUser(u))
 -                      {
 -                              u->WriteNumeric(442, "%s %s :You're not on that channel!",u->nick.c_str(), this->name.c_str());
 -                              return CMD_FAILURE;
 -                      }
 -                      if (IsModeSet('t') && !ServerInstance->OnCheckExemption(u,this,"topiclock").check(GetPrefixValue(u) >= HALFOP_VALUE))
 -                      {
 -                              u->WriteNumeric(482, "%s %s :You do not have access to change the topic on this channel", u->nick.c_str(), this->name.c_str());
 -                              return CMD_FAILURE;
 -                      }
 -              }
 -      }
 -
        this->topic.assign(ntopic, 0, ServerInstance->Config->Limits.MaxTopic);
        this->setby.assign(ServerInstance->Config->FullHostInTopic ? u->GetFullHost() : u->nick, 0, 128);
        this->WriteChannel(u, "TOPIC %s :%s", this->name.c_str(), this->topic.c_str());
 -
        this->topicset = ServerInstance->Time();
  
 -      FOREACH_MOD(I_OnPostTopicChange,OnPostTopicChange(u, this, this->topic));
 -
 -      return CMD_SUCCESS;
 -}
 -
 -long Channel::GetUserCounter()
 -{
 -      return userlist.size();
 +      FOREACH_MOD(OnPostTopicChange, (u, this, this->topic));
  }
  
  Membership* Channel::AddUser(User* user)
  {
 -      Membership* memb = new Membership(user, this);
 -      userlist[user] = memb;
 +      Membership*& memb = userlist[user];
 +      if (memb)
 +              return NULL;
 +
 +      memb = new Membership(user, this);
        return memb;
  }
  
  void Channel::DelUser(User* user)
  {
 -      UserMembIter a = userlist.find(user);
 +      UserMembIter it = userlist.find(user);
 +      if (it != userlist.end())
 +              DelUser(it);
 +}
  
 -      if (a != userlist.end())
 -      {
 -              a->second->cull();
 -              delete a->second;
 -              userlist.erase(a);
 -      }
 +void Channel::CheckDestroy()
 +{
 +      if (!userlist.empty())
 +              return;
  
 -      if (userlist.empty())
 -      {
 -              ModResult res;
 -              FIRST_MOD_RESULT(OnChannelPreDelete, res, (this));
 -              if (res == MOD_RES_DENY)
 -                      return;
 -              chan_hash::iterator iter = ServerInstance->chanlist->find(this->name);
 -              /* kill the record */
 -              if (iter != ServerInstance->chanlist->end())
 -              {
 -                      FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(this));
 -                      ServerInstance->chanlist->erase(iter);
 -              }
 +      ModResult res;
 +      FIRST_MOD_RESULT(OnChannelPreDelete, res, (this));
 +      if (res == MOD_RES_DENY)
 +              return;
  
 -              ClearInvites();
 -              ServerInstance->GlobalCulls.AddItem(this);
 +      chan_hash::iterator iter = ServerInstance->chanlist.find(this->name);
 +      /* kill the record */
 +      if (iter != ServerInstance->chanlist.end())
 +      {
 +              FOREACH_MOD(OnChannelDelete, (this));
 +              ServerInstance->chanlist.erase(iter);
        }
 +
 +      ClearInvites();
 +      ServerInstance->GlobalCulls.AddItem(this);
  }
  
 -bool Channel::HasUser(User* user)
 +void Channel::DelUser(const UserMembIter& membiter)
  {
 -      return (userlist.find(user) != userlist.end());
 +      Membership* memb = membiter->second;
 +      memb->cull();
 +      delete memb;
 +      userlist.erase(membiter);
 +
 +      // If this channel became empty then it should be removed
 +      CheckDestroy();
  }
  
  Membership* Channel::GetUser(User* user)
        return i->second;
  }
  
 -const UserMembList* Channel::GetUsers()
 -{
 -      return &userlist;
 -}
 -
  void Channel::SetDefaultModes()
  {
 -      ServerInstance->Logs->Log("CHANNELS", DEBUG, "SetDefaultModes %s",
 +      ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "SetDefaultModes %s",
                ServerInstance->Config->DefaultModes.c_str());
        irc::spacesepstream list(ServerInstance->Config->DefaultModes);
        std::string modeseq;
                ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL);
                if (mode)
                {
 +                      if (mode->IsPrefixMode())
 +                              continue;
 +
                        if (mode->GetNumParams(true))
                                list.GetToken(parameter);
                        else
   * add a channel to a user, creating the record for it if needed and linking
   * it to the user record
   */
 -Channel* Channel::JoinUser(User *user, const char* cn, bool override, const char* key, bool bursting, time_t TS)
 +Channel* Channel::JoinUser(LocalUser* user, std::string cname, bool override, const std::string& key)
  {
 -      // Fix: unregistered users could be joined using /SAJOIN
 -      if (!user || !cn || user->registered != REG_ALL)
 +      if (user->registered != REG_ALL)
 +      {
 +              ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "Attempted to join unregistered user " + user->uuid + " to channel " + cname);
                return NULL;
 -
 -      std::string privs;
 -      Channel *Ptr;
 +      }
  
        /*
         * We don't restrict the number of channels that remote users or users that are override-joining may be in.
         * We restrict local operators to OperMaxChans channels.
         * This is a lot more logical than how it was formerly. -- w00t
         */
 -      if (IS_LOCAL(user) && !override)
 +      if (!override)
        {
                if (user->HasPrivPermission("channels/high-join-limit"))
                {
                        if (user->chans.size() >= ServerInstance->Config->OperMaxChans)
                        {
 -                              user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s %s :You are on too many channels",user->nick.c_str(), cn);
 +                              user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s :You are on too many channels", cname.c_str());
                                return NULL;
                        }
                }
                                maxchans = ServerInstance->Config->MaxChans;
                        if (user->chans.size() >= maxchans)
                        {
 -                              user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s %s :You are on too many channels",user->nick.c_str(), cn);
 +                              user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s :You are on too many channels", cname.c_str());
                                return NULL;
                        }
                }
        }
  
 -      std::string cname;
 -      cname.assign(std::string(cn), 0, ServerInstance->Config->Limits.ChanMax);
 -      Ptr = ServerInstance->FindChan(cname);
 -      bool created_by_local = false;
 +      // Crop channel name if it's too long
 +      if (cname.length() > ServerInstance->Config->Limits.ChanMax)
 +              cname.resize(ServerInstance->Config->Limits.ChanMax);
 +
 +      Channel* chan = ServerInstance->FindChan(cname);
 +      bool created_by_local = (chan == NULL); // Flag that will be passed to modules in the OnUserJoin() hook later
 +      std::string privs; // Prefix mode(letter)s to give to the joining user
  
 -      if (!Ptr)
 +      if (!chan)
        {
 -              /*
 -               * Fix: desync bug was here, don't set @ on remote users - spanningtree handles their permissions. bug #358. -- w00t
 -               */
 -              if (!IS_LOCAL(user))
 -              {
 -                      if (!TS)
 -                              ServerInstance->Logs->Log("CHANNELS",DEBUG,"*** BUG *** Channel::JoinUser called for REMOTE user '%s' on channel '%s' but no TS given!", user->nick.c_str(), cn);
 -              }
 -              else
 -              {
 -                      privs = "o";
 -                      created_by_local = true;
 -              }
 +              privs = ServerInstance->Config->DefaultModes.substr(0, ServerInstance->Config->DefaultModes.find(' '));
  
 -              if (IS_LOCAL(user) && override == false)
 +              if (override == false)
                {
 +                      // Ask the modules whether they're ok with the join, pass NULL as Channel* as the channel is yet to be created
                        ModResult MOD_RESULT;
 -                      FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, NULL, cname.c_str(), privs, key ? key : ""));
 +                      FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, NULL, cname, privs, key));
                        if (MOD_RESULT == MOD_RES_DENY)
 -                              return NULL;
 +                              return NULL; // A module wasn't happy with the join, abort
                }
  
 -              Ptr = new Channel(cname, TS);
 +              chan = new Channel(cname, ServerInstance->Time());
 +              // Set the default modes on the channel (<options:defaultmodes>)
 +              chan->SetDefaultModes();
        }
        else
        {
                /* Already on the channel */
 -              if (Ptr->HasUser(user))
 +              if (chan->HasUser(user))
                        return NULL;
  
 -              /*
 -               * remote users are allowed us to bypass channel modes
 -               * and bans (used by servers)
 -               */
 -              if (IS_LOCAL(user) && override == false)
 +              if (override == false)
                {
                        ModResult MOD_RESULT;
 -                      FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, Ptr, cname.c_str(), privs, key ? key : ""));
 +                      FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, chan, cname, privs, key));
 +
 +                      // A module explicitly denied the join and (hopefully) generated a message
 +                      // describing the situation, so we may stop here without sending anything
                        if (MOD_RESULT == MOD_RES_DENY)
 -                      {
                                return NULL;
 -                      }
 -                      else if (MOD_RESULT == MOD_RES_PASSTHRU)
 +
 +                      // If no module returned MOD_RES_DENY or MOD_RES_ALLOW (which is the case
 +                      // most of the time) then proceed to check channel modes +k, +i, +l and bans,
 +                      // in this order.
 +                      // If a module explicitly allowed the join (by returning MOD_RES_ALLOW),
 +                      // then this entire section is skipped
 +                      if (MOD_RESULT == MOD_RES_PASSTHRU)
                        {
 -                              std::string ckey = Ptr->GetModeParameter('k');
 -                              bool invited = IS_LOCAL(user)->IsInvited(Ptr->name.c_str());
 +                              std::string ckey = chan->GetModeParameter(keymode);
 +                              bool invited = user->IsInvited(chan);
                                bool can_bypass = ServerInstance->Config->InvBypassModes && invited;
  
                                if (!ckey.empty())
                                {
 -                                      FIRST_MOD_RESULT(OnCheckKey, MOD_RESULT, (user, Ptr, key ? key : ""));
 -                                      if (!MOD_RESULT.check((key && ckey == key) || can_bypass))
 +                                      FIRST_MOD_RESULT(OnCheckKey, MOD_RESULT, (user, chan, key));
 +                                      if (!MOD_RESULT.check((ckey == key) || can_bypass))
                                        {
                                                // If no key provided, or key is not the right one, and can't bypass +k (not invited or option not enabled)
 -                                              user->WriteNumeric(ERR_BADCHANNELKEY, "%s %s :Cannot join channel (Incorrect channel key)",user->nick.c_str(), Ptr->name.c_str());
 +                                              user->WriteNumeric(ERR_BADCHANNELKEY, "%s :Cannot join channel (Incorrect channel key)", chan->name.c_str());
                                                return NULL;
                                        }
                                }
  
 -                              if (Ptr->IsModeSet('i'))
 +                              if (chan->IsModeSet(inviteonlymode))
                                {
 -                                      FIRST_MOD_RESULT(OnCheckInvite, MOD_RESULT, (user, Ptr));
 +                                      FIRST_MOD_RESULT(OnCheckInvite, MOD_RESULT, (user, chan));
                                        if (!MOD_RESULT.check(invited))
                                        {
 -                                              user->WriteNumeric(ERR_INVITEONLYCHAN, "%s %s :Cannot join channel (Invite only)",user->nick.c_str(), Ptr->name.c_str());
 +                                              user->WriteNumeric(ERR_INVITEONLYCHAN, "%s :Cannot join channel (Invite only)", chan->name.c_str());
                                                return NULL;
                                        }
                                }
  
 -                              std::string limit = Ptr->GetModeParameter('l');
 +                              std::string limit = chan->GetModeParameter(limitmode);
                                if (!limit.empty())
                                {
 -                                      FIRST_MOD_RESULT(OnCheckLimit, MOD_RESULT, (user, Ptr));
 -                                      if (!MOD_RESULT.check((Ptr->GetUserCounter() < atol(limit.c_str()) || can_bypass)))
 +                                      FIRST_MOD_RESULT(OnCheckLimit, MOD_RESULT, (user, chan));
 +                                      if (!MOD_RESULT.check((chan->GetUserCounter() < atol(limit.c_str()) || can_bypass)))
                                        {
 -                                              user->WriteNumeric(ERR_CHANNELISFULL, "%s %s :Cannot join channel (Channel is full)",user->nick.c_str(), Ptr->name.c_str());
 +                                              user->WriteNumeric(ERR_CHANNELISFULL, "%s :Cannot join channel (Channel is full)", chan->name.c_str());
                                                return NULL;
                                        }
                                }
  
 -                              if (Ptr->IsBanned(user) && !can_bypass)
 +                              if (chan->IsBanned(user) && !can_bypass)
                                {
 -                                      user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s %s :Cannot join channel (You're banned)",user->nick.c_str(), Ptr->name.c_str());
 +                                      user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s :Cannot join channel (You're banned)", chan->name.c_str());
                                        return NULL;
                                }
  
                                 */
                                if (invited)
                                {
 -                                      IS_LOCAL(user)->RemoveInvite(Ptr->name.c_str());
 +                                      user->RemoveInvite(chan);
                                }
                        }
                }
        }
  
 -      if (created_by_local)
 -      {
 -              /* As spotted by jilles, dont bother to set this on remote users */
 -              Ptr->SetDefaultModes();
 -      }
 -
 -      return Channel::ForceChan(Ptr, user, privs, bursting, created_by_local);
 +      // We figured that this join is allowed and also created the
 +      // channel if it didn't exist before, now do the actual join
 +      chan->ForceJoin(user, &privs, false, created_by_local);
 +      return chan;
  }
  
 -Channel* Channel::ForceChan(Channel* Ptr, User* user, const std::string &privs, bool bursting, bool created)
 +void Channel::ForceJoin(User* user, const std::string* privs, bool bursting, bool created_by_local)
  {
 -      std::string nick = user->nick;
 +      if (IS_SERVER(user))
 +      {
 +              ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "Attempted to join server user " + user->uuid + " to channel " + this->name);
 +              return;
 +      }
 +
 +      Membership* memb = this->AddUser(user);
 +      if (!memb)
 +              return; // Already on the channel
  
 -      Membership* memb = Ptr->AddUser(user);
 -      user->chans.insert(Ptr);
 +      user->chans.push_front(memb);
  
 -      for (std::string::const_iterator x = privs.begin(); x != privs.end(); x++)
 +      if (privs)
        {
 -              const char status = *x;
 -              ModeHandler* mh = ServerInstance->Modes->FindMode(status, MODETYPE_CHANNEL);
 -              if (mh)
 +              // If the user was granted prefix modes (in the OnUserPreJoin hook, or he's a
 +              // remote user and his own server set the modes), then set them internally now
 +              for (std::string::const_iterator i = privs->begin(); i != privs->end(); ++i)
                {
 -                      /* Set, and make sure that the mode handler knows this mode was now set */
 -                      Ptr->SetPrefix(user, mh->GetModeChar(), true);
 -                      mh->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, Ptr, nick, true);
 +                      PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(*i);
 +                      if (mh)
 +                      {
 +                              std::string nick = user->nick;
 +                              // Set the mode on the user
 +                              mh->OnModeChange(ServerInstance->FakeClient, NULL, this, nick, true);
 +                      }
                }
        }
  
 +      // Tell modules about this join, they have the chance now to populate except_list with users we won't send the JOIN (and possibly MODE) to
        CUList except_list;
 -      FOREACH_MOD(I_OnUserJoin,OnUserJoin(memb, bursting, created, except_list));
 +      FOREACH_MOD(OnUserJoin, (memb, bursting, created_by_local, except_list));
  
 -      Ptr->WriteAllExcept(user, false, 0, except_list, "JOIN :%s", Ptr->name.c_str());
 +      this->WriteAllExcept(user, false, 0, except_list, "JOIN :%s", this->name.c_str());
  
        /* Theyre not the first ones in here, make sure everyone else sees the modes we gave the user */
 -      if ((Ptr->GetUserCounter() > 1) && (!memb->modes.empty()))
 +      if ((GetUserCounter() > 1) && (!memb->modes.empty()))
        {
                std::string ms = memb->modes;
                for(unsigned int i=0; i < memb->modes.length(); i++)
                        ms.append(" ").append(user->nick);
  
                except_list.insert(user);
 -              Ptr->WriteAllExcept(user, !ServerInstance->Config->CycleHostsFromUser, 0, except_list, "MODE %s +%s", Ptr->name.c_str(), ms.c_str());
 +              this->WriteAllExcept(user, !ServerInstance->Config->CycleHostsFromUser, 0, except_list, "MODE %s +%s", this->name.c_str(), ms.c_str());
        }
  
        if (IS_LOCAL(user))
        {
 -              if (Ptr->topicset)
 +              if (this->topicset)
                {
 -                      user->WriteNumeric(RPL_TOPIC, "%s %s :%s", user->nick.c_str(), Ptr->name.c_str(), Ptr->topic.c_str());
 -                      user->WriteNumeric(RPL_TOPICTIME, "%s %s %s %lu", user->nick.c_str(), Ptr->name.c_str(), Ptr->setby.c_str(), (unsigned long)Ptr->topicset);
 +                      user->WriteNumeric(RPL_TOPIC, "%s :%s", this->name.c_str(), this->topic.c_str());
 +                      user->WriteNumeric(RPL_TOPICTIME, "%s %s %lu", this->name.c_str(), this->setby.c_str(), (unsigned long)this->topicset);
                }
 -              Ptr->UserList(user);
 +              this->UserList(user);
        }
 -      FOREACH_MOD(I_OnPostJoin,OnPostJoin(memb));
 -      return Ptr;
 +
 +      FOREACH_MOD(OnPostJoin, (memb));
  }
  
  bool Channel::IsBanned(User* user)
        if (result != MOD_RES_PASSTHRU)
                return (result == MOD_RES_DENY);
  
 -      for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
 +      ListModeBase* banlm = static_cast<ListModeBase*>(*ban);
 +      const ListModeBase::ModeList* bans = banlm->GetList(this);
 +      if (bans)
        {
 -              if (CheckBan(user, i->data))
 -                      return true;
 +              for (ListModeBase::ModeList::const_iterator it = bans->begin(); it != bans->end(); it++)
 +              {
 +                      if (CheckBan(user, it->mask))
 +                              return true;
 +              }
        }
        return false;
  }
@@@ -392,9 -446,10 +392,9 @@@ bool Channel::CheckBan(User* user, cons
        if (at == std::string::npos)
                return false;
  
 -      char tomatch[MAXBUF];
 -      snprintf(tomatch, MAXBUF, "%s!%s", user->nick.c_str(), user->ident.c_str());
 +      const std::string nickIdent = user->nick + "!" + user->ident;
        std::string prefix = mask.substr(0, at);
 -      if (InspIRCd::Match(tomatch, prefix, NULL))
 +      if (InspIRCd::Match(nickIdent, prefix, NULL))
        {
                std::string suffix = mask.substr(at + 1);
                if (InspIRCd::Match(user->host, suffix, NULL) ||
@@@ -411,14 -466,12 +411,14 @@@ ModResult Channel::GetExtBanStatus(Use
        FIRST_MOD_RESULT(OnExtBanCheck, rv, (user, this, type));
        if (rv != MOD_RES_PASSTHRU)
                return rv;
 -      for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
 +
 +      ListModeBase* banlm = static_cast<ListModeBase*>(*ban);
 +      const ListModeBase::ModeList* bans = banlm->GetList(this);
 +      if (bans)
        {
 -              if (i->data[0] == type && i->data[1] == ':')
 +              for (ListModeBase::ModeList::const_iterator it = bans->begin(); it != bans->end(); ++it)
                {
 -                      std::string val = i->data.substr(2);
 -                      if (CheckBan(user, val))
 +                      if (CheckBan(user, it->mask))
                                return MOD_RES_DENY;
                }
        }
  }
  
  /* Channel::PartUser
 - * remove a channel from a users record, and return the number of users left.
 - * Therefore, if this function returns 0 the caller should delete the Channel.
 + * Remove a channel from a users record, remove the reference to the Membership object
 + * from the channel and destroy it.
   */
  void Channel::PartUser(User *user, std::string &reason)
  {
 -      if (!user)
 -              return;
 -
 -      Membership* memb = GetUser(user);
 +      UserMembIter membiter = userlist.find(user);
  
 -      if (memb)
 +      if (membiter != userlist.end())
        {
 +              Membership* memb = membiter->second;
                CUList except_list;
 -              FOREACH_MOD(I_OnUserPart,OnUserPart(memb, reason, except_list));
 +              FOREACH_MOD(OnUserPart, (memb, reason, except_list));
  
                WriteAllExcept(user, false, 0, except_list, "PART %s%s%s", this->name.c_str(), reason.empty() ? "" : " :", reason.c_str());
  
 -              user->chans.erase(this);
 -              this->RemoveAllPrefixes(user);
 +              // Remove this channel from the user's chanlist
 +              user->chans.erase(memb);
 +              // Remove the Membership from this channel's userlist and destroy it
 +              this->DelUser(membiter);
        }
 -
 -      this->DelUser(user);
  }
  
 -void Channel::KickUser(User *src, User *user, const char* reason)
 +void Channel::KickUser(User* src, User* victim, const std::string& reason, Membership* srcmemb)
  {
 -      if (!src || !user || !reason)
 +      UserMembIter victimiter = userlist.find(victim);
 +      Membership* memb = ((victimiter != userlist.end()) ? victimiter->second : NULL);
 +
 +      if (!memb)
 +      {
 +              src->WriteNumeric(ERR_USERNOTINCHANNEL, "%s %s :They are not on that channel", victim->nick.c_str(), this->name.c_str());
                return;
 +      }
  
 -      Membership* memb = GetUser(user);
 +      // Do the following checks only if the KICK is done by a local user;
 +      // each server enforces its own rules.
        if (IS_LOCAL(src))
        {
 -              if (!memb)
 -              {
 -                      src->WriteNumeric(ERR_USERNOTINCHANNEL, "%s %s %s :They are not on that channel",src->nick.c_str(), user->nick.c_str(), this->name.c_str());
 -                      return;
 -              }
 -              if ((ServerInstance->ULine(user->server)) && (!ServerInstance->ULine(src->server)))
 -              {
 -                      src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :Only a u-line may kick a u-line from a channel.",src->nick.c_str(), this->name.c_str());
 -                      return;
 -              }
 -
 +              // Modules are allowed to explicitly allow or deny kicks done by local users
                ModResult res;
 -              if (ServerInstance->ULine(src->server))
 -                      res = MOD_RES_ALLOW;
 -              else
 -                      FIRST_MOD_RESULT(OnUserPreKick, res, (src,memb,reason));
 -
 +              FIRST_MOD_RESULT(OnUserPreKick, res, (src,memb,reason));
                if (res == MOD_RES_DENY)
                        return;
  
                if (res == MOD_RES_PASSTHRU)
                {
 -                      unsigned int them = this->GetPrefixValue(src);
 +                      if (!srcmemb)
 +                              srcmemb = GetUser(src);
 +                      unsigned int them = srcmemb ? srcmemb->getRank() : 0;
                        unsigned int req = HALFOP_VALUE;
                        for (std::string::size_type i = 0; i < memb->modes.length(); i++)
                        {
  
                        if (them < req)
                        {
 -                              src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must be a channel %soperator",
 -                                      src->nick.c_str(), this->name.c_str(), req > HALFOP_VALUE ? "" : "half-");
 +                              src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You must be a channel %soperator",
 +                                      this->name.c_str(), req > HALFOP_VALUE ? "" : "half-");
                                return;
                        }
                }
        }
  
 -      if (memb)
 -      {
 -              CUList except_list;
 -              FOREACH_MOD(I_OnUserKick,OnUserKick(src, memb, reason, except_list));
 -
 -              WriteAllExcept(src, false, 0, except_list, "KICK %s %s :%s", name.c_str(), user->nick.c_str(), reason);
 +      CUList except_list;
 +      FOREACH_MOD(OnUserKick, (src, memb, reason, except_list));
  
 -              user->chans.erase(this);
 -              this->RemoveAllPrefixes(user);
 -      }
 +      WriteAllExcept(src, false, 0, except_list, "KICK %s %s :%s", name.c_str(), victim->nick.c_str(), reason.c_str());
  
 -      this->DelUser(user);
 +      victim->chans.erase(memb);
 +      this->DelUser(victimiter);
  }
  
  void Channel::WriteChannel(User* user, const char* text, ...)
  {
 -      char textbuffer[MAXBUF];
 -      va_list argsPtr;
 -
 -      if (!user || !text)
 -              return;
 -
 -      va_start(argsPtr, text);
 -      vsnprintf(textbuffer, MAXBUF, text, argsPtr);
 -      va_end(argsPtr);
 -
 -      this->WriteChannel(user, std::string(textbuffer));
 +      std::string textbuffer;
 +      VAFORMAT(textbuffer, text, text);
 +      this->WriteChannel(user, textbuffer);
  }
  
  void Channel::WriteChannel(User* user, const std::string &text)
  {
 -      char tb[MAXBUF];
 -
 -      if (!user)
 -              return;
 -
 -      snprintf(tb,MAXBUF,":%s %s", user->GetFullHost().c_str(), text.c_str());
 -      std::string out = tb;
 +      const std::string message = ":" + user->GetFullHost() + " " + text;
  
        for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
        {
                if (IS_LOCAL(i->first))
 -                      i->first->Write(out);
 +                      i->first->Write(message);
        }
  }
  
  void Channel::WriteChannelWithServ(const std::string& ServName, const char* text, ...)
  {
 -      char textbuffer[MAXBUF];
 -      va_list argsPtr;
 -
 -      if (!text)
 -              return;
 -
 -      va_start(argsPtr, text);
 -      vsnprintf(textbuffer, MAXBUF, text, argsPtr);
 -      va_end(argsPtr);
 -
 -      this->WriteChannelWithServ(ServName, std::string(textbuffer));
 +      std::string textbuffer;
 +      VAFORMAT(textbuffer, text, text);
 +      this->WriteChannelWithServ(ServName, textbuffer);
  }
  
  void Channel::WriteChannelWithServ(const std::string& ServName, const std::string &text)
  {
 -      char tb[MAXBUF];
 -
 -      snprintf(tb,MAXBUF,":%s %s", ServName.empty() ? ServerInstance->Config->ServerName.c_str() : ServName.c_str(), text.c_str());
 -      std::string out = tb;
 +      const std::string message = ":" + (ServName.empty() ? ServerInstance->Config->ServerName : ServName) + " " + text;
  
        for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
        {
                if (IS_LOCAL(i->first))
 -                      i->first->Write(out);
 +                      i->first->Write(message);
        }
  }
  
   * for the sender (for privmsg etc) */
  void Channel::WriteAllExceptSender(User* user, bool serversource, char status, const char* text, ...)
  {
 -      char textbuffer[MAXBUF];
 -      va_list argsPtr;
 -
 -      if (!text)
 -              return;
 -
 -      va_start(argsPtr, text);
 -      vsnprintf(textbuffer, MAXBUF, text, argsPtr);
 -      va_end(argsPtr);
 -
 -      this->WriteAllExceptSender(user, serversource, status, std::string(textbuffer));
 +      std::string textbuffer;
 +      VAFORMAT(textbuffer, text, text);
 +      this->WriteAllExceptSender(user, serversource, status, textbuffer);
  }
  
  void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const char* text, ...)
  {
 -      char textbuffer[MAXBUF];
 -      va_list argsPtr;
 -
 -      if (!text)
 -              return;
 -
 -      int offset = snprintf(textbuffer,MAXBUF,":%s ", serversource ? ServerInstance->Config->ServerName.c_str() : user->GetFullHost().c_str());
 -
 -      va_start(argsPtr, text);
 -      vsnprintf(textbuffer + offset, MAXBUF - offset, text, argsPtr);
 -      va_end(argsPtr);
 -
 -      this->RawWriteAllExcept(user, serversource, status, except_list, std::string(textbuffer));
 +      std::string textbuffer;
 +      VAFORMAT(textbuffer, text, text);
 +      textbuffer = ":" + (serversource ? ServerInstance->Config->ServerName : user->GetFullHost()) + " " + textbuffer;
 +      this->RawWriteAllExcept(user, serversource, status, except_list, textbuffer);
  }
  
  void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &text)
  {
 -      char tb[MAXBUF];
 -
 -      snprintf(tb,MAXBUF,":%s %s", serversource ? ServerInstance->Config->ServerName.c_str() : user->GetFullHost().c_str(), text.c_str());
 -
 -      this->RawWriteAllExcept(user, serversource, status, except_list, std::string(tb));
 +      const std::string message = ":" + (serversource ? ServerInstance->Config->ServerName : user->GetFullHost()) + " " + text;
 +      this->RawWriteAllExcept(user, serversource, status, except_list, message);
  }
  
  void Channel::RawWriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &out)
        unsigned int minrank = 0;
        if (status)
        {
 -              ModeHandler* mh = ServerInstance->Modes->FindPrefix(status);
 +              PrefixMode* mh = ServerInstance->Modes->FindPrefix(status);
                if (mh)
                        minrank = mh->GetPrefixRank();
        }
@@@ -588,39 -698,59 +588,39 @@@ void Channel::WriteAllExceptSender(User
        this->WriteAllExcept(user, serversource, status, except_list, std::string(text));
  }
  
 -/*
 - * return a count of the users on a specific channel accounting for
 - * invisible users who won't increase the count. e.g. for /LIST
 - */
 -int Channel::CountInvisible()
 +const char* Channel::ChanModes(bool showkey)
  {
 -      int count = 0;
 -      for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
 -      {
 -              if (!i->first->quitting && !i->first->IsModeSet('i'))
 -                      count++;
 -      }
 +      static std::string scratch;
 +      std::string sparam;
  
 -      return count;
 -}
 -
 -char* Channel::ChanModes(bool showkey)
 -{
 -      static char scratch[MAXBUF];
 -      static char sparam[MAXBUF];
 -      char* offset = scratch;
 -      std::string extparam;
 -
 -      *scratch = '\0';
 -      *sparam = '\0';
 +      scratch.clear();
  
        /* This was still iterating up to 190, Channel::modes is only 64 elements -- Om */
        for(int n = 0; n < 64; n++)
        {
 -              if(this->modes[n])
 +              ModeHandler* mh = ServerInstance->Modes->FindMode(n + 65, MODETYPE_CHANNEL);
 +              if (mh && IsModeSet(mh))
                {
 -                      *offset++ = n + 65;
 -                      extparam.clear();
 +                      scratch.push_back(n + 65);
 +
 +                      ParamModeBase* pm = mh->IsParameterMode();
 +                      if (!pm)
 +                              continue;
 +
                        if (n == 'k' - 65 && !showkey)
                        {
 -                              extparam = "<key>";
 +                              sparam += " <key>";
                        }
                        else
                        {
 -                              extparam = this->GetModeParameter(n + 65);
 -                      }
 -                      if (!extparam.empty())
 -                      {
 -                              charlcat(sparam,' ',MAXBUF);
 -                              strlcat(sparam,extparam.c_str(),MAXBUF);
 +                              sparam += ' ';
 +                              pm->GetParameter(this, sparam);
                        }
                }
        }
  
 -      /* Null terminate scratch */
 -      *offset = '\0';
 -      strlcat(scratch,sparam,MAXBUF);
 -      return scratch;
 +      scratch += sparam;
 +      return scratch.c_str();
  }
  
  /* compile a userlist of a channel into a string, each nick seperated by
   */
  void Channel::UserList(User *user)
  {
 -      if (!IS_LOCAL(user))
 -              return;
 -
        bool has_privs = user->HasPrivPermission("channels/auspex");
 -
 -      if (this->IsModeSet('s') && !this->HasUser(user) && !has_privs)
 +      if (this->IsModeSet(secretmode) && !this->HasUser(user) && !has_privs)
        {
 -              user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), this->name.c_str());
 +              user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", this->name.c_str());
                return;
        }
  
 -      std::string list = user->nick;
 -      list.push_back(' ');
 -      list.push_back(this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=');
 +      std::string list;
 +      list.push_back(this->IsModeSet(secretmode) ? '@' : this->IsModeSet(privatemode) ? '*' : '=');
        list.push_back(' ');
        list.append(this->name).append(" :");
        std::string::size_type pos = list.size();
         */
        bool has_user = this->HasUser(user);
  
 -      const size_t maxlen = MAXBUF - 10 - ServerInstance->Config->ServerName.size();
++      const size_t maxlen = ServerInstance->Config->Limits.MaxLine - 10 - ServerInstance->Config->ServerName.size();
        std::string prefixlist;
        std::string nick;
        for (UserMembIter i = userlist.begin(); i != userlist.end(); ++i)
        {
 -              if (i->first->quitting)
 -                      continue;
 -              if ((!has_user) && (i->first->IsModeSet('i')) && (!has_privs))
 +              if ((!has_user) && (i->first->IsModeSet(invisiblemode)) && (!has_privs))
                {
                        /*
                         * user is +i, and source not on the channel, does not show
                        continue;
                }
  
 -              prefixlist = this->GetPrefixChar(i->first);
 +              Membership* memb = i->second;
 +
 +              prefixlist.clear();
 +              char prefix = memb->GetPrefixChar();
 +              if (prefix)
 +                      prefixlist.push_back(prefix);
                nick = i->first->nick;
  
 -              FOREACH_MOD(I_OnNamesListItem, OnNamesListItem(user, i->second, prefixlist, nick));
 +              FOREACH_MOD(OnNamesListItem, (user, memb, prefixlist, nick));
  
                /* Nick was nuked, a module wants us to skip it */
                if (nick.empty())
                        continue;
  
-               if (list.size() + prefixlist.length() + nick.length() + 1 > 480)
+               if (list.size() + prefixlist.length() + nick.length() + 1 > maxlen)
                {
                        /* list overflowed into multiple numerics */
                        user->WriteNumeric(RPL_NAMREPLY, list);
                user->WriteNumeric(RPL_NAMREPLY, list);
        }
  
 -      user->WriteNumeric(RPL_ENDOFNAMES, "%s %s :End of /NAMES list.", user->nick.c_str(), this->name.c_str());
 -}
 -
 -long Channel::GetMaxBans()
 -{
 -      /* Return the cached value if there is one */
 -      if (this->maxbans)
 -              return this->maxbans;
 -
 -      /* If there isnt one, we have to do some O(n) hax to find it the first time. (ick) */
 -      for (std::map<std::string,int>::iterator n = ServerInstance->Config->maxbans.begin(); n != ServerInstance->Config->maxbans.end(); n++)
 -      {
 -              if (InspIRCd::Match(this->name, n->first, NULL))
 -              {
 -                      this->maxbans = n->second;
 -                      return n->second;
 -              }
 -      }
 -
 -      /* Screw it, just return the default of 64 */
 -      this->maxbans = 64;
 -      return this->maxbans;
 -}
 -
 -void Channel::ResetMaxBans()
 -{
 -      this->maxbans = 0;
 +      user->WriteNumeric(RPL_ENDOFNAMES, "%s :End of /NAMES list.", this->name.c_str());
  }
  
  /* returns the status character for a given user on a channel, e.g. @ for op,
   * % for halfop etc. If the user has several modes set, the highest mode
   * the user has must be returned.
   */
 -const char* Channel::GetPrefixChar(User *user)
 +char Membership::GetPrefixChar() const
  {
 -      static char pf[2] = {0, 0};
 -      *pf = 0;
 +      char pf = 0;
        unsigned int bestrank = 0;
  
 -      UserMembIter m = userlist.find(user);
 -      if (m != userlist.end())
 +      for (std::string::const_iterator i = modes.begin(); i != modes.end(); ++i)
        {
 -              for(unsigned int i=0; i < m->second->modes.length(); i++)
 +              PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(*i);
 +              if (mh && mh->GetPrefixRank() > bestrank && mh->GetPrefix())
                {
 -                      char mchar = m->second->modes[i];
 -                      ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
 -                      if (mh && mh->GetPrefixRank() > bestrank && mh->GetPrefix())
 -                      {
 -                              bestrank = mh->GetPrefixRank();
 -                              pf[0] = mh->GetPrefix();
 -                      }
 +                      bestrank = mh->GetPrefixRank();
 +                      pf = mh->GetPrefix();
                }
        }
        return pf;
@@@ -726,23 -891,28 +727,23 @@@ unsigned int Membership::getRank(
        unsigned int rv = 0;
        if (mchar)
        {
 -              ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
 +              PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(mchar);
                if (mh)
                        rv = mh->GetPrefixRank();
        }
        return rv;
  }
  
 -const char* Channel::GetAllPrefixChars(User* user)
 +const char* Membership::GetAllPrefixChars() const
  {
        static char prefix[64];
        int ctr = 0;
  
 -      UserMembIter m = userlist.find(user);
 -      if (m != userlist.end())
 +      for (std::string::const_iterator i = modes.begin(); i != modes.end(); ++i)
        {
 -              for(unsigned int i=0; i < m->second->modes.length(); i++)
 -              {
 -                      char mchar = m->second->modes[i];
 -                      ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
 -                      if (mh && mh->GetPrefix())
 -                              prefix[ctr++] = mh->GetPrefix();
 -              }
 +              PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(*i);
 +              if (mh && mh->GetPrefix())
 +                      prefix[ctr++] = mh->GetPrefix();
        }
        prefix[ctr] = 0;
  
@@@ -757,33 -927,48 +758,33 @@@ unsigned int Channel::GetPrefixValue(Us
        return m->second->getRank();
  }
  
 -bool Channel::SetPrefix(User* user, char prefix, bool adding)
 +bool Membership::SetPrefix(PrefixMode* delta_mh, bool adding)
  {
 -      ModeHandler* delta_mh = ServerInstance->Modes->FindMode(prefix, MODETYPE_CHANNEL);
 -      if (!delta_mh)
 -              return false;
 -      UserMembIter m = userlist.find(user);
 -      if (m == userlist.end())
 -              return false;
 -      for(unsigned int i=0; i < m->second->modes.length(); i++)
 +      char prefix = delta_mh->GetModeChar();
 +      for (unsigned int i = 0; i < modes.length(); i++)
        {
 -              char mchar = m->second->modes[i];
 -              ModeHandler* mh = ServerInstance->Modes->FindMode(mchar, MODETYPE_CHANNEL);
 +              char mchar = modes[i];
 +              PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(mchar);
                if (mh && mh->GetPrefixRank() <= delta_mh->GetPrefixRank())
                {
 -                      m->second->modes =
 -                              m->second->modes.substr(0,i) +
 +                      modes = modes.substr(0,i) +
                                (adding ? std::string(1, prefix) : "") +
 -                              m->second->modes.substr(mchar == prefix ? i+1 : i);
 +                              modes.substr(mchar == prefix ? i+1 : i);
                        return adding != (mchar == prefix);
                }
        }
        if (adding)
 -              m->second->modes += std::string(1, prefix);
 +              modes.push_back(prefix);
        return adding;
  }
  
 -void Channel::RemoveAllPrefixes(User* user)
 -{
 -      UserMembIter m = userlist.find(user);
 -      if (m != userlist.end())
 -      {
 -              m->second->modes.clear();
 -      }
 -}
 -
  void Invitation::Create(Channel* c, LocalUser* u, time_t timeout)
  {
        if ((timeout != 0) && (ServerInstance->Time() >= timeout))
                // Expired, don't bother
                return;
  
 -      ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create chan=%s user=%s", c->name.c_str(), u->uuid.c_str());
 +      ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Create chan=%s user=%s", c->name.c_str(), u->uuid.c_str());
  
        Invitation* inv = Invitation::Find(c, u, false);
        if (inv)
                 if ((inv->expiry == 0) || (inv->expiry > timeout))
                        return;
                inv->expiry = timeout;
 -              ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create changed expiry in existing invitation %p", (void*) inv);
 +              ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Create changed expiry in existing invitation %p", (void*) inv);
        }
        else
        {
                inv = new Invitation(c, u, timeout);
 -              c->invites.push_back(inv);
 -              u->invites.push_back(inv);
 -              ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create created new invitation %p", (void*) inv);
 +              c->invites.push_front(inv);
 +              u->invites.push_front(inv);
 +              ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Create created new invitation %p", (void*) inv);
        }
  }
  
  Invitation* Invitation::Find(Channel* c, LocalUser* u, bool check_expired)
  {
 -      ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find chan=%s user=%s check_expired=%d", c ? c->name.c_str() : "NULL", u ? u->uuid.c_str() : "NULL", check_expired);
 +      ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Find chan=%s user=%s check_expired=%d", c ? c->name.c_str() : "NULL", u ? u->uuid.c_str() : "NULL", check_expired);
        if (!u || u->invites.empty())
                return NULL;
  
 -      InviteList locallist;
 -      locallist.swap(u->invites);
 -
        Invitation* result = NULL;
 -      for (InviteList::iterator i = locallist.begin(); i != locallist.end(); )
 +      for (InviteList::iterator i = u->invites.begin(); i != u->invites.end(); )
        {
                Invitation* inv = *i;
 +              ++i;
 +
                if ((check_expired) && (inv->expiry != 0) && (inv->expiry <= ServerInstance->Time()))
                {
                        /* Expired invite, remove it. */
 -                      std::string expiration = ServerInstance->TimeString(inv->expiry);
 -                      ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find ecountered expired entry: %p expired %s", (void*) inv, expiration.c_str());
 -                      i = locallist.erase(i);
 -                      inv->cull();
 +                      std::string expiration = InspIRCd::TimeString(inv->expiry);
 +                      ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Find ecountered expired entry: %p expired %s", (void*) inv, expiration.c_str());
                        delete inv;
                }
                else
                                result = inv;
                                break;
                        }
 -                      ++i;
                }
        }
  
 -      locallist.swap(u->invites);
 -      ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find result=%p", (void*) result);
 +      ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Find result=%p", (void*) result);
        return result;
  }
  
  Invitation::~Invitation()
  {
        // Remove this entry from both lists
 -      InviteList::iterator it = std::find(chan->invites.begin(), chan->invites.end(), this);
 -      if (it != chan->invites.end())
 -              chan->invites.erase(it);
 -      it = std::find(user->invites.begin(), user->invites.end(), this);
 -      if (it != user->invites.end())
 -              user->invites.erase(it);
 -}
 -
 -void InviteBase::ClearInvites()
 -{
 -      ServerInstance->Logs->Log("INVITEBASE", DEBUG, "InviteBase::ClearInvites %p", (void*) this);
 -      InviteList locallist;
 -      locallist.swap(invites);
 -      for (InviteList::const_iterator i = locallist.begin(); i != locallist.end(); ++i)
 -      {
 -              (*i)->cull();
 -              delete *i;
 -      }
 +      chan->invites.erase(this);
 +      user->invites.erase(this);
 +      ServerInstance->Logs->Log("INVITEBASE", LOG_DEBUG, "Invitation::~ %p", (void*) this);
  }
diff --combined src/cidr.cpp
index d6370b06a32264edb6fba4a9ff0cf0648b1ac732,3338756655b6e8e5f2e42abe0c1545e90969ce00..875b953044498ece026e15423b4c102cbd813c60
   */
  
  
 -/* $Core */
 -
  #include "inspircd.h"
  
- /* Used when comparing CIDR masks for the modulus bits left over.
-  * A lot of ircd's seem to do this:
-  * ((-1) << (8 - (mask % 8)))
-  * But imho, it sucks in comparison to a nice neat lookup table.
-  */
- const unsigned char inverted_bits[8] = {      0x00, /* 00000000 - 0 bits - never actually used */
-                               0x80, /* 10000000 - 1 bits */
-                               0xC0, /* 11000000 - 2 bits */
-                               0xE0, /* 11100000 - 3 bits */
-                               0xF0, /* 11110000 - 4 bits */
-                               0xF8, /* 11111000 - 5 bits */
-                               0xFC, /* 11111100 - 6 bits */
-                               0xFE  /* 11111110 - 7 bits */
- };
  /* Match CIDR strings, e.g. 127.0.0.1 to 127.0.0.0/8 or 3ffe:1:5:6::8 to 3ffe:1::0/32
-  * If you have a lot of hosts to match, youre probably better off building your mask once
-  * and then using the lower level MatchCIDRBits directly.
   *
   * This will also attempt to match any leading usernames or nicknames on the mask, using
   * match(), when match_with_username is true.
@@@ -81,8 -65,14 +63,14 @@@ bool irc::sockets::MatchCIDR(const std:
                cidr_copy.assign(cidr_mask);
        }
  
-       if (cidr_copy.find('/') == std::string::npos)
+       const std::string::size_type per_pos = cidr_copy.rfind('/');
+       if ((per_pos == std::string::npos) || (per_pos == cidr_copy.length()-1)
+               || (cidr_copy.find_first_not_of("0123456789", per_pos+1) != std::string::npos)
+               || (cidr_copy.find_first_not_of("0123456789abcdef.:") < per_pos))
+       {
+               // The CIDR mask is invalid
                return false;
+       }
  
        irc::sockets::sockaddrs addr;
        irc::sockets::aptosa(address_copy, 0, addr);
@@@ -92,3 -82,5 +80,3 @@@
  
        return mask == mask2;
  }
 -
 -
diff --combined src/command_parse.cpp
index 7133b3f053110347f9a9b2d30debd207e73b683c,1c72c7de46bb484be7f98b2e8c42f3c2ae422b05..d89d7cbb597097568a781d3f96ec9db7a2f1a4fb
  
  #include "inspircd.h"
  
 -int InspIRCd::PassCompare(Extensible* ex, const std::string &data, const std::string &input, const std::string &hashtype)
 +bool InspIRCd::PassCompare(Extensible* ex, const std::string& data, const std::string& input, const std::string& hashtype)
  {
        ModResult res;
        FIRST_MOD_RESULT(OnPassCompare, res, (ex, data, input, hashtype));
  
        /* Module matched */
        if (res == MOD_RES_ALLOW)
 -              return 0;
 +              return true;
  
        /* Module explicitly didnt match */
        if (res == MOD_RES_DENY)
 -              return 1;
 +              return false;
  
        /* We dont handle any hash types except for plaintext - Thanks tra26 */
        if (!hashtype.empty() && hashtype != "plaintext")
 -              /* See below. 1 because they dont match */
 -              return 1;
 +              return false;
  
 -      return (data != input); // this seems back to front, but returns 0 if they *match*, 1 else
 +      return (data == input);
  }
  
 -/* LoopCall is used to call a command classes handler repeatedly based on the contents of a comma seperated list.
 - * There are two overriden versions of this method, one of which takes two potential lists and the other takes one.
 - * We need a version which takes two potential lists for JOIN, because a JOIN may contain two lists of items at once,
 - * the channel names and their keys as follows:
 - * JOIN #chan1,#chan2,#chan3 key1,,key3
 - * Therefore, we need to deal with both lists concurrently. The first instance of this method does that by creating
 - * two instances of irc::commasepstream and reading them both together until the first runs out of tokens.
 - * The second version is much simpler and just has the one stream to read, and is used in NAMES, WHOIS, PRIVMSG etc.
 - * Both will only parse until they reach ServerInstance->Config->MaxTargets number of targets, to stop abuse via spam.
 - */
 -int CommandParser::LoopCall(User* user, Command* CommandObj, const std::vector<std::string>& parameters, unsigned int splithere, int extra, bool usemax)
 +bool CommandParser::LoopCall(User* user, Command* handler, const std::vector<std::string>& parameters, unsigned int splithere, int extra, bool usemax)
  {
        if (splithere >= parameters.size())
 -              return 0;
 -
 -      if (extra >= (signed)parameters.size())
 -              extra = -1;
 +              return false;
  
 -      /* First check if we have more than one item in the list, if we don't we return zero here and the handler
 +      /* First check if we have more than one item in the list, if we don't we return false here and the handler
         * which called us just carries on as it was.
         */
        if (parameters[splithere].find(',') == std::string::npos)
 -              return 0;
 +              return false;
  
        /** Some lame ircds will weed out dupes using some shitty O(n^2) algorithm.
         * By using std::set (thanks for the idea w00t) we can cut this down a ton.
         * ...VOOODOOOO!
 +       *
 +       * Only check for duplicates if there is one list (allow them in JOIN).
         */
        std::set<irc::string> dupes;
 +      bool check_dupes = (extra < 0);
  
 -      /* Create two lists, one for channel names, one for keys
 +      /* Create two sepstreams, if we have only one list, then initialize the second sepstream with
 +       * an empty string. The second parameter of the constructor of the sepstream tells whether
 +       * or not to allow empty tokens.
 +       * We allow empty keys, so "JOIN #a,#b ,bkey" will be interpreted as "JOIN #a", "JOIN #b bkey"
         */
        irc::commasepstream items1(parameters[splithere]);
 -      irc::commasepstream items2(extra >= 0 ? parameters[extra] : "");
 -      std::string extrastuff;
 +      irc::commasepstream items2(extra >= 0 ? parameters[extra] : "", true);
        std::string item;
        unsigned int max = 0;
 +      LocalUser* localuser = IS_LOCAL(user);
  
 -      /* Attempt to iterate these lists and call the command objech
 -       * which called us, for every parameter pair until there are
 -       * no more left to parse.
 +      /* Attempt to iterate these lists and call the command handler
 +       * for every parameter or parameter pair until there are no more
 +       * left to parse.
         */
        while (items1.GetToken(item) && (!usemax || max++ < ServerInstance->Config->MaxTargets))
        {
 -              if (dupes.find(item.c_str()) == dupes.end())
 +              if ((!check_dupes) || (dupes.insert(item.c_str()).second))
                {
                        std::vector<std::string> new_parameters(parameters);
 -
 -                      if (!items2.GetToken(extrastuff))
 -                              extrastuff.clear();
 -
                        new_parameters[splithere] = item;
 -                      if (extra >= 0)
 -                              new_parameters[extra] = extrastuff;
 -
 -                      CommandObj->Handle(new_parameters, user);
  
 -                      dupes.insert(item.c_str());
 -              }
 -      }
 -      return 1;
 -}
 -
 -bool CommandParser::IsValidCommand(const std::string &commandname, unsigned int pcnt, User * user)
 -{
 -      Commandtable::iterator n = cmdlist.find(commandname);
 -
 -      if (n != cmdlist.end())
 -      {
 -              if ((pcnt >= n->second->min_params))
 -              {
 -                      if (IS_LOCAL(user) && n->second->flags_needed)
 +                      if (extra >= 0)
                        {
 -                              if (user->IsModeSet(n->second->flags_needed))
 -                              {
 -                                      return (user->HasPermission(commandname));
 -                              }
 +                              // If we have two lists then get the next item from the second list.
 +                              // In case it runs out of elements then 'item' will be an empty string.
 +                              items2.GetToken(item);
 +                              new_parameters[extra] = item;
                        }
 -                      else
 +
 +                      CmdResult result = handler->Handle(new_parameters, user);
 +                      if (localuser)
                        {
 -                              return true;
 +                              // Run the OnPostCommand hook with the last parameter (original line) being empty
 +                              // to indicate that the command had more targets in its original form.
 +                              item.clear();
 +                              FOREACH_MOD(OnPostCommand, (handler, new_parameters, localuser, result, item));
                        }
                }
        }
 -      return false;
 +
 +      return true;
  }
  
  Command* CommandParser::GetHandler(const std::string &commandname)
  
  // calls a handler function for a command
  
 -CmdResult CommandParser::CallHandler(const std::string &commandname, const std::vector<std::string>& parameters, User *user)
 +CmdResult CommandParser::CallHandler(const std::string& commandname, const std::vector<std::string>& parameters, User* user, Command** cmd)
  {
        Commandtable::iterator n = cmdlist.find(commandname);
  
  
                        if (bOkay)
                        {
 +                              if (cmd)
 +                                      *cmd = n->second;
                                return n->second->Handle(parameters,user);
                        }
                }
        return CMD_INVALID;
  }
  
 -bool CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
 +void CommandParser::ProcessCommand(LocalUser *user, std::string &cmd)
  {
        std::vector<std::string> command_p;
        irc::tokenstream tokens(cmd);
        if (command[0] == ':')
                tokens.GetToken(command);
  
 -      while (tokens.GetToken(token) && (command_p.size() <= MAXPARAMETERS))
 +      while (tokens.GetToken(token))
                command_p.push_back(token);
  
        std::transform(command.begin(), command.end(), command.begin(), ::toupper);
  
        /* find the command, check it exists */
 -      Commandtable::iterator cm = cmdlist.find(command);
 +      Command* handler = GetHandler(command);
  
        /* Modify the user's penalty regardless of whether or not the command exists */
 -      bool do_more = true;
        if (!user->HasPrivPermission("users/flood/no-throttle"))
        {
                // If it *doesn't* exist, give it a slightly heftier penalty than normal to deter flooding us crap
 -              user->CommandFloodPenalty += cm != cmdlist.end() ? cm->second->Penalty * 1000 : 2000;
 +              user->CommandFloodPenalty += handler ? handler->Penalty * 1000 : 2000;
        }
  
 -
 -      if (cm == cmdlist.end())
 +      if (!handler)
        {
                ModResult MOD_RESULT;
                FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, false, cmd));
                if (MOD_RESULT == MOD_RES_DENY)
 -                      return true;
 +                      return;
  
                /*
                 * This double lookup is in case a module (abbreviation) wishes to change a command.
                 * Thanks dz for making me actually understand why this is necessary!
                 * -- w00t
                 */
 -              cm = cmdlist.find(command);
 -              if (cm == cmdlist.end())
 +              handler = GetHandler(command);
 +              if (!handler)
                {
                        if (user->registered == REG_ALL)
 -                              user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :Unknown command",user->nick.c_str(),command.c_str());
 +                              user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :Unknown command",command.c_str());
                        ServerInstance->stats->statsUnknown++;
 -                      return true;
 +                      return;
                }
        }
  
 -      if (cm->second->max_params && command_p.size() > cm->second->max_params)
 +      // If we were given more parameters than max_params then append the excess parameter(s)
 +      // to command_p[maxparams-1], i.e. to the last param that is still allowed
 +      if (handler->max_params && command_p.size() > handler->max_params)
        {
                /*
                 * command_p input (assuming max_params 1):
                 *      a
                 *      test
                 */
 -              std::string lparam;
  
 -              /*
 -               * The '-1' here is a clever trick, we'll go backwards throwing everything into a temporary param
 -               * and then just toss that into the array.
 -               * -- w00t
 -               */
 -              while (command_p.size() > (cm->second->max_params - 1))
 -              {
 -                      // BE CAREFUL: .end() returns past the end of the vector, hence decrement.
 -                      std::vector<std::string>::iterator it = command_p.end() - 1;
 +              // Iterator to the last parameter that will be kept
 +              const std::vector<std::string>::iterator lastkeep = command_p.begin() + (handler->max_params - 1);
 +              // Iterator to the first excess parameter
 +              const std::vector<std::string>::iterator firstexcess = lastkeep + 1;
  
 -                      lparam.insert(0, " " + *(it));
 -                      command_p.erase(it); // remove last element
 +              // Append all excess parameter(s) to the last parameter, seperated by spaces
 +              for (std::vector<std::string>::const_iterator i = firstexcess; i != command_p.end(); ++i)
 +              {
 +                      lastkeep->push_back(' ');
 +                      lastkeep->append(*i);
                }
  
 -              /* we now have (each iteration):
 -               *      ' test'
 -               *      ' a test'
 -               *      ' is a test' <-- final string
 -               * ...now remove the ' ' at the start...
 -               */
 -              lparam.erase(lparam.begin());
 -
 -              /* param is now 'is a test', which is exactly what we wanted! */
 -              command_p.push_back(lparam);
 +              // Erase the excess parameter(s)
 +              command_p.erase(firstexcess, command_p.end());
        }
  
        /*
        ModResult MOD_RESULT;
        FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, false, cmd));
        if (MOD_RESULT == MOD_RES_DENY)
 -              return true;
 +              return;
  
        /* activity resets the ping pending timer */
        user->nping = ServerInstance->Time() + user->MyClass->GetPingTime();
  
 -      if (cm->second->flags_needed)
 +      if (handler->flags_needed)
        {
 -              if (!user->IsModeSet(cm->second->flags_needed))
 +              if (!user->IsModeSet(handler->flags_needed))
                {
 -                      user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - You do not have the required operator privileges",user->nick.c_str());
 -                      return do_more;
 +                      user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - You do not have the required operator privileges");
 +                      return;
                }
 +
                if (!user->HasPermission(command))
                {
 -                      user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - Oper type %s does not have access to command %s",
 -                              user->nick.c_str(), user->oper->NameStr(), command.c_str());
 -                      return do_more;
 +                      user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - Oper type %s does not have access to command %s",
 +                              user->oper->name.c_str(), command.c_str());
 +                      return;
                }
        }
 -      if ((user->registered == REG_ALL) && (!IS_OPER(user)) && (cm->second->IsDisabled()))
 +
 +      if ((user->registered == REG_ALL) && (!user->IsOper()) && (handler->IsDisabled()))
        {
                /* command is disabled! */
                if (ServerInstance->Config->DisabledDontExist)
                {
 -                      user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :Unknown command",user->nick.c_str(),command.c_str());
 +                      user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :Unknown command", command.c_str());
                }
                else
                {
 -                      user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :This command has been disabled.",
 -                                                                              user->nick.c_str(), command.c_str());
 +                      user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :This command has been disabled.", command.c_str());
                }
  
-               ServerInstance->SNO->WriteToSnoMask('t', "%s denied for %s (%s@%s)",
+               ServerInstance->SNO->WriteToSnoMask('a', "%s denied for %s (%s@%s)",
                                command.c_str(), user->nick.c_str(), user->ident.c_str(), user->host.c_str());
 -              return do_more;
 +              return;
        }
  
 -      if ((!command_p.empty()) && (command_p.back().empty()) && (!cm->second->allow_empty_last_param))
 +      if ((!command_p.empty()) && (command_p.back().empty()) && (!handler->allow_empty_last_param))
                command_p.pop_back();
  
 -      if (command_p.size() < cm->second->min_params)
 +      if (command_p.size() < handler->min_params)
        {
 -              user->WriteNumeric(ERR_NEEDMOREPARAMS, "%s %s :Not enough parameters.", user->nick.c_str(), command.c_str());
 -              if ((ServerInstance->Config->SyntaxHints) && (user->registered == REG_ALL) && (cm->second->syntax.length()))
 -                      user->WriteNumeric(RPL_SYNTAX, "%s :SYNTAX %s %s", user->nick.c_str(), cm->second->name.c_str(), cm->second->syntax.c_str());
 -              return do_more;
 +              user->WriteNumeric(ERR_NEEDMOREPARAMS, "%s :Not enough parameters.", command.c_str());
 +              if ((ServerInstance->Config->SyntaxHints) && (user->registered == REG_ALL) && (handler->syntax.length()))
 +                      user->WriteNumeric(RPL_SYNTAX, ":SYNTAX %s %s", handler->name.c_str(), handler->syntax.c_str());
 +              return;
        }
 -      if ((user->registered != REG_ALL) && (!cm->second->WorksBeforeReg()))
 +
 +      if ((user->registered != REG_ALL) && (!handler->WorksBeforeReg()))
        {
                user->WriteNumeric(ERR_NOTREGISTERED, "%s :You have not registered",command.c_str());
 -              return do_more;
        }
        else
        {
                /* passed all checks.. first, do the (ugly) stats counters. */
 -              cm->second->use_count++;
 -              cm->second->total_bytes += cmd.length();
 +              handler->use_count++;
  
                /* module calls too */
                FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, true, cmd));
                if (MOD_RESULT == MOD_RES_DENY)
 -                      return do_more;
 +                      return;
  
                /*
                 * WARNING: be careful, the user may be deleted soon
                 */
 -              CmdResult result = cm->second->Handle(command_p, user);
 +              CmdResult result = handler->Handle(command_p, user);
  
 -              FOREACH_MOD(I_OnPostCommand,OnPostCommand(command, command_p, user, result,cmd));
 -              return do_more;
 +              FOREACH_MOD(OnPostCommand, (handler, command_p, user, result, cmd));
        }
  }
  
@@@ -327,23 -361,19 +327,23 @@@ void CommandParser::RemoveCommand(Comma
                cmdlist.erase(n);
  }
  
 +CommandBase::~CommandBase()
 +{
 +}
 +
  Command::~Command()
  {
        ServerInstance->Parser->RemoveCommand(this);
  }
  
 -bool CommandParser::ProcessBuffer(std::string &buffer,LocalUser *user)
 +void CommandParser::ProcessBuffer(std::string &buffer,LocalUser *user)
  {
 -      if (!user || buffer.empty())
 -              return true;
 +      if (buffer.empty())
 +              return;
  
 -      ServerInstance->Logs->Log("USERINPUT", RAWIO, "C[%s] I :%s %s",
 +      ServerInstance->Logs->Log("USERINPUT", LOG_RAWIO, "C[%s] I :%s %s",
                user->uuid.c_str(), user->nick.c_str(), buffer.c_str());
 -      return ProcessCommand(user,buffer);
 +      ProcessCommand(user,buffer);
  }
  
  bool CommandParser::AddCommand(Command *f)
@@@ -361,64 -391,88 +361,64 @@@ CommandParser::CommandParser(
  {
  }
  
 -int CommandParser::TranslateUIDs(const std::vector<TranslateType> to, const std::vector<std::string> &source, std::string &dest, bool prefix_final, Command* custom_translator)
 +std::string CommandParser::TranslateUIDs(const std::vector<TranslateType>& to, const std::vector<std::string>& source, bool prefix_final, CommandBase* custom_translator)
  {
        std::vector<TranslateType>::const_iterator types = to.begin();
 -      User* user = NULL;
 -      unsigned int i;
 -      int translations = 0;
 -      dest.clear();
 +      std::string dest;
  
 -      for(i=0; i < source.size(); i++)
 +      for (unsigned int i = 0; i < source.size(); i++)
        {
 -              TranslateType t;
 -              std::string item = source[i];
 -
 -              if (types == to.end())
 -                      t = TR_TEXT;
 -              else
 +              TranslateType t = TR_TEXT;
 +              // They might supply less translation types than parameters,
 +              // in that case pretend that all remaining types are TR_TEXT
 +              if (types != to.end())
                {
                        t = *types;
                        types++;
                }
  
 -              if (prefix_final && i == source.size() - 1)
 -                      dest.append(":");
 +              bool last = (i == (source.size() - 1));
 +              if (prefix_final && last)
 +                      dest.push_back(':');
  
 -              switch (t)
 -              {
 -                      case TR_NICK:
 -                              /* Translate single nickname */
 -                              user = ServerInstance->FindNick(item);
 -                              if (user)
 -                              {
 -                                      dest.append(user->uuid);
 -                                      translations++;
 -                              }
 -                              else
 -                                      dest.append(item);
 -                      break;
 -                      case TR_CUSTOM:
 -                              if (custom_translator)
 -                                      custom_translator->EncodeParameter(item, i);
 -                              dest.append(item);
 -                      break;
 -                      case TR_END:
 -                      case TR_TEXT:
 -                      default:
 -                              /* Do nothing */
 -                              dest.append(item);
 -                      break;
 -              }
 -              if (i != source.size() - 1)
 -                      dest.append(" ");
 +              TranslateSingleParam(t, source[i], dest, custom_translator, i);
 +
 +              if (!last)
 +                      dest.push_back(' ');
        }
  
 -      return translations;
 +      return dest;
  }
  
 -int CommandParser::TranslateUIDs(TranslateType to, const std::string &source, std::string &dest)
 +void CommandParser::TranslateSingleParam(TranslateType to, const std::string& item, std::string& dest, CommandBase* custom_translator, unsigned int paramnumber)
  {
 -      User* user = NULL;
 -      int translations = 0;
 -      dest.clear();
 -
        switch (to)
        {
                case TR_NICK:
 +              {
                        /* Translate single nickname */
 -                      user = ServerInstance->FindNick(source);
 +                      User* user = ServerInstance->FindNick(item);
                        if (user)
 +                              dest.append(user->uuid);
 +                      else
 +                              dest.append(item);
 +                      break;
 +              }
 +              case TR_CUSTOM:
 +              {
 +                      if (custom_translator)
                        {
 -                              dest = user->uuid;
 -                              translations++;
 +                              std::string translated = item;
 +                              custom_translator->EncodeParameter(translated, paramnumber);
 +                              dest.append(translated);
 +                              break;
                        }
 -                      else
 -                              dest = source;
 -              break;
 -              case TR_END:
 +                      // If no custom translator was given, fall through
 +              }
                case TR_TEXT:
                default:
                        /* Do nothing */
 -                      dest = source;
 +                      dest.append(item);
                break;
        }
 -
 -      return translations;
  }
diff --combined src/configreader.cpp
index baeffb015f4730c90625b0ad7c4830095b29be21,060f66d1620249b4cfb9fb4a33e6099df8f876af..cda5e03e0f8b708a77831ca12ee3013547474e79
  
  
  #include "inspircd.h"
 -#include <fstream>
  #include "xline.h"
 +#include "listmode.h"
  #include "exitcodes.h"
 -#include "commands/cmd_whowas.h"
  #include "configparser.h"
  #include <iostream>
 -#ifdef _WIN32
 -#include <Iphlpapi.h>
 -#pragma comment(lib, "Iphlpapi.lib")
 -#endif
  
  ServerConfig::ServerConfig()
  {
 -      WhoWasGroupSize = WhoWasMaxGroups = WhoWasMaxKeep = 0;
 -      RawLog = NoUserDns = HideBans = HideSplits = UndernetMsgPrefix = false;
 -      WildcardIPv6 = CycleHosts = InvBypassModes = true;
 +      RawLog = HideBans = HideSplits = UndernetMsgPrefix = false;
 +      WildcardIPv6 = InvBypassModes = true;
        dns_timeout = 5;
        MaxTargets = 20;
        NetBufferSize = 10240;
 -      SoftLimit = ServerInstance->SE->GetMaxFds();
 +      SoftLimit = SocketEngine::GetMaxFds();
        MaxConn = SOMAXCONN;
        MaxChans = 20;
        OperMaxChans = 30;
        c_ipv6_range = 128;
  }
  
 -void ServerConfig::Update005()
 -{
 -      std::stringstream out(data005);
 -      std::vector<std::string> data;
 -      std::string token;
 -      while (out >> token)
 -              data.push_back(token);
 -      sort(data.begin(), data.end());
 -
 -      std::string line5;
 -      isupport.clear();
 -      for(unsigned int i=0; i < data.size(); i++)
 -      {
 -              token = data[i];
 -              line5 = line5 + token + " ";
 -              if (i % 13 == 12)
 -              {
 -                      line5.append(":are supported by this server");
 -                      isupport.push_back(line5);
 -                      line5.clear();
 -              }
 -      }
 -      if (!line5.empty())
 -      {
 -              line5.append(":are supported by this server");
 -              isupport.push_back(line5);
 -      }
 -}
 -
 -void ServerConfig::Send005(User* user)
 -{
 -      for (std::vector<std::string>::iterator line = ServerInstance->Config->isupport.begin(); line != ServerInstance->Config->isupport.end(); line++)
 -              user->WriteNumeric(RPL_ISUPPORT, "%s %s", user->nick.c_str(), line->c_str());
 -}
 -
 -template<typename T, typename V>
 -static void range(T& value, V min, V max, V def, const char* msg)
 -{
 -      if (value >= (T)min && value <= (T)max)
 -              return;
 -      ServerInstance->Logs->Log("CONFIG", DEFAULT,
 -              "WARNING: %s value of %ld is not between %ld and %ld; set to %ld.",
 -              msg, (long)value, (long)min, (long)max, (long)def);
 -      value = def;
 -}
 -
 -
 -static void ValidIP(const std::string& ip, const std::string& key)
 -{
 -      irc::sockets::sockaddrs dummy;
 -      if (!irc::sockets::aptosa(ip, 0, dummy))
 -              throw CoreException("The value of "+key+" is not an IP address");
 -}
 -
  static void ValidHost(const std::string& p, const std::string& msg)
  {
        int num_dots = 0;
@@@ -85,6 -145,64 +85,6 @@@ bool ServerConfig::ApplyDisabledCommand
        return true;
  }
  
 -static void FindDNS(std::string& server)
 -{
 -      if (!server.empty())
 -              return;
 -#ifdef _WIN32
 -      // attempt to look up their nameserver from the system
 -      ServerInstance->Logs->Log("CONFIG",DEFAULT,"WARNING: <dns:server> not defined, attempting to find a working server in the system settings...");
 -
 -      PFIXED_INFO pFixedInfo;
 -      DWORD dwBufferSize = sizeof(FIXED_INFO);
 -      pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, sizeof(FIXED_INFO));
 -
 -      if(pFixedInfo)
 -      {
 -              if (GetNetworkParams(pFixedInfo, &dwBufferSize) == ERROR_BUFFER_OVERFLOW) {
 -                      HeapFree(GetProcessHeap(), 0, pFixedInfo);
 -                      pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, dwBufferSize);
 -              }
 -
 -              if(pFixedInfo) {
 -                      if (GetNetworkParams(pFixedInfo, &dwBufferSize) == NO_ERROR)
 -                              server = pFixedInfo->DnsServerList.IpAddress.String;
 -
 -                      HeapFree(GetProcessHeap(), 0, pFixedInfo);
 -              }
 -
 -              if(!server.empty())
 -              {
 -                      ServerInstance->Logs->Log("CONFIG",DEFAULT,"<dns:server> set to '%s' as first active resolver in the system settings.", server.c_str());
 -                      return;
 -              }
 -      }
 -
 -      ServerInstance->Logs->Log("CONFIG",DEFAULT,"No viable nameserver found! Defaulting to nameserver '127.0.0.1'!");
 -#else
 -      // attempt to look up their nameserver from /etc/resolv.conf
 -      ServerInstance->Logs->Log("CONFIG",DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
 -
 -      std::ifstream resolv("/etc/resolv.conf");
 -
 -      while (resolv >> server)
 -      {
 -              if (server == "nameserver")
 -              {
 -                      resolv >> server;
 -                      if (server.find_first_not_of("0123456789.") == std::string::npos)
 -                      {
 -                              ServerInstance->Logs->Log("CONFIG",DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",server.c_str());
 -                              return;
 -                      }
 -              }
 -      }
 -
 -      ServerInstance->Logs->Log("CONFIG",DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
 -#endif
 -      server = "127.0.0.1";
 -}
 -
  static void ReadXLine(ServerConfig* conf, const std::string& tag, const std::string& key, XLineFactory* make)
  {
        ConfigTagList tags = conf->ConfTags(tag);
@@@ -123,11 -241,13 +123,11 @@@ void ServerConfig::CrossCheckOperClassT
                std::string name = tag->getString("name");
                if (name.empty())
                        throw CoreException("<type:name> is missing from tag at " + tag->getTagLocation());
 -              if (!ServerInstance->IsNick(name.c_str(), Limits.NickMax))
 -                      throw CoreException("<type:name> is invalid (value '" + name + "')");
 -              if (oper_blocks.find(" " + name) != oper_blocks.end())
 +              if (OperTypes.find(name) != OperTypes.end())
                        throw CoreException("Duplicate type block with name " + name + " at " + tag->getTagLocation());
  
                OperInfo* ifo = new OperInfo;
 -              oper_blocks[" " + name] = ifo;
 +              OperTypes[name] = ifo;
                ifo->name = name;
                ifo->type_block = tag;
  
                        throw CoreException("<oper:name> missing from tag at " + tag->getTagLocation());
  
                std::string type = tag->getString("type");
 -              OperIndex::iterator tblk = oper_blocks.find(" " + type);
 -              if (tblk == oper_blocks.end())
 +              OperIndex::iterator tblk = OperTypes.find(type);
 +              if (tblk == OperTypes.end())
                        throw CoreException("Oper block " + name + " has missing type " + type);
                if (oper_blocks.find(name) != oper_blocks.end())
                        throw CoreException("Duplicate oper block with name " + name + " at " + tag->getTagLocation());
@@@ -299,7 -419,6 +299,7 @@@ void ServerConfig::CrossCheckConnectBlo
                        me->maxchans = tag->getInt("maxchans", me->maxchans);
                        me->maxconnwarn = tag->getBool("maxconnwarn", me->maxconnwarn);
                        me->limit = tag->getInt("limit", me->limit);
 +                      me->resolvehostnames = tag->getBool("resolvehostnames", me->resolvehostnames);
  
                        ClassMap::iterator oldMask = oldBlocksByMask.find(typeMask);
                        if (oldMask != oldBlocksByMask.end())
  
  /** Represents a deprecated configuration tag.
   */
 -struct Deprecated
 +struct DeprecatedConfig
  {
 -      /** Tag name
 -       */
 -      const char* tag;
 -      /** Tag value
 -       */
 -      const char* value;
 -      /** Reason for deprecation
 -       */
 -      const char* reason;
 +      /** Tag name. */
 +      std::string tag;
 +
 +      /** Attribute key. */
 +      std::string key;
 +
 +      /** Attribute value. */
 +      std::string value;
 +
 +      /** Reason for deprecation. */
 +      std::string reason;
  };
  
 -static const Deprecated ChangedConfig[] = {
 -      {"options", "hidelinks",                "has been moved to <security:hidelinks> as of 1.2a3"},
 -      {"options", "hidewhois",                "has been moved to <security:hidewhois> as of 1.2a3"},
 -      {"options", "userstats",                "has been moved to <security:userstats> as of 1.2a3"},
 -      {"options", "customversion",    "has been moved to <security:customversion> as of 1.2a3"},
 -      {"options", "hidesplits",               "has been moved to <security:hidesplits> as of 1.2a3"},
 -      {"options", "hidebans",         "has been moved to <security:hidebans> as of 1.2a3"},
 -      {"options", "hidekills",                "has been moved to <security:hidekills> as of 1.2a3"},
 -      {"options", "operspywhois",             "has been moved to <security:operspywhois> as of 1.2a3"},
 -      {"options", "announceinvites",  "has been moved to <security:announceinvites> as of 1.2a3"},
 -      {"options", "hidemodes",                "has been moved to <security:hidemodes> as of 1.2a3"},
 -      {"options", "maxtargets",               "has been moved to <security:maxtargets> as of 1.2a3"},
 -      {"options",     "nouserdns",            "has been moved to <performance:nouserdns> as of 1.2a3"},
 -      {"options",     "maxwho",               "has been moved to <performance:maxwho> as of 1.2a3"},
 -      {"options",     "softlimit",            "has been moved to <performance:softlimit> as of 1.2a3"},
 -      {"options", "somaxconn",                "has been moved to <performance:somaxconn> as of 1.2a3"},
 -      {"options", "netbuffersize",    "has been moved to <performance:netbuffersize> as of 1.2a3"},
 -      {"options", "maxwho",           "has been moved to <performance:maxwho> as of 1.2a3"},
 -      {"options",     "loglevel",             "1.2+ does not use the loglevel value. Please define <log> tags instead."},
 -      {"die",     "value",            "you need to reread your config"},
 -      {"bind",    "transport",                "has been moved to <bind:ssl> as of 2.0a1"},
 -      {"link",    "transport",                "has been moved to <link:ssl> as of 2.0a1"},
 -      {"link",        "autoconnect",          "2.0+ does not use the autoconnect value. Please define <autoconnect> tags instead."},
 +static const DeprecatedConfig ChangedConfig[] = {
 +      { "bind",        "transport",   "",                 "has been moved to <bind:ssl> as of 2.0" },
 +      { "die",         "value",       "",                 "you need to reread your config" },
 +      { "gnutls",      "starttls",    "",                 "has been replaced with m_starttls as of 2.2" },
 +      { "link",        "autoconnect", "",                 "2.0+ does not use this attribute - define <autoconnect> tags instead" },
 +      { "link",        "transport",   "",                 "has been moved to <link:ssl> as of 2.0" },
 +      { "module",      "name",        "m_chanprotect.so", "has been replaced with m_customprefix as of 2.2" },
 +      { "module",      "name",        "m_halfop.so",      "has been replaced with m_customprefix as of 2.2" },
 +      { "options",     "cyclehosts",  "",                 "has been replaced with m_hostcycle as of 2.2" },
 +      { "performance", "nouserdns",   "",                 "has been moved to <connect:resolvehostnames> as of 2.2" }
  };
  
  void ServerConfig::Fill()
        ConfigTag* security = ConfValue("security");
        if (sid.empty())
        {
 -              ServerName = ConfValue("server")->getString("name");
 -              sid = ConfValue("server")->getString("id");
 +              ServerName = ConfValue("server")->getString("name", "irc.example.com");
                ValidHost(ServerName, "<server:name>");
 -              if (!sid.empty() && !ServerInstance->IsSID(sid))
 +
 +              sid = ConfValue("server")->getString("id");
 +              if (!sid.empty() && !InspIRCd::IsSID(sid))
                        throw CoreException(sid + " is not a valid server ID. A server ID must be 3 characters long, with the first character a digit and the next two characters a digit or letter.");
        }
        else
        {
                if (ServerName != ConfValue("server")->getString("name"))
 -                      throw CoreException("You must restart to change the server name or SID");
 +                      throw CoreException("You must restart to change the server name");
 +
                std::string nsid = ConfValue("server")->getString("id");
                if (!nsid.empty() && nsid != sid)
 -                      throw CoreException("You must restart to change the server name or SID");
 -      }
 -      diepass = ConfValue("power")->getString("diepass");
 -      restartpass = ConfValue("power")->getString("restartpass");
 -      powerhash = ConfValue("power")->getString("hash");
 -      PrefixQuit = options->getString("prefixquit");
 -      SuffixQuit = options->getString("suffixquit");
 -      FixedQuit = options->getString("fixedquit");
 -      PrefixPart = options->getString("prefixpart");
 -      SuffixPart = options->getString("suffixpart");
 -      FixedPart = options->getString("fixedpart");
 -      SoftLimit = ConfValue("performance")->getInt("softlimit", ServerInstance->SE->GetMaxFds());
 +                      throw CoreException("You must restart to change the server id");
 +      }
 +      SoftLimit = ConfValue("performance")->getInt("softlimit", SocketEngine::GetMaxFds(), 10, SocketEngine::GetMaxFds());
 +      CCOnConnect = ConfValue("performance")->getBool("clonesonconnect", true);
        MaxConn = ConfValue("performance")->getInt("somaxconn", SOMAXCONN);
 -      MoronBanner = options->getString("moronbanner", "You're banned!");
 +      XLineMessage = options->getString("xlinemessage", options->getString("moronbanner", "You're banned!"));
        ServerDesc = ConfValue("server")->getString("description", "Configure Me");
        Network = ConfValue("server")->getString("network", "Network");
 -      AdminName = ConfValue("admin")->getString("name", "");
 -      AdminEmail = ConfValue("admin")->getString("email", "null@example.com");
 -      AdminNick = ConfValue("admin")->getString("nick", "admin");
 -      ModPath = ConfValue("path")->getString("moduledir", MOD_PATH);
 -      NetBufferSize = ConfValue("performance")->getInt("netbuffersize", 10240);
 +      NetBufferSize = ConfValue("performance")->getInt("netbuffersize", 10240, 1024, 65534);
        dns_timeout = ConfValue("dns")->getInt("timeout", 5);
        DisabledCommands = ConfValue("disabled")->getString("commands", "");
        DisabledDontExist = ConfValue("disabled")->getBool("fakenonexistant");
        UserStats = security->getString("userstats");
 -      CustomVersion = security->getString("customversion", Network + " IRCd");
 +      CustomVersion = security->getString("customversion");
        HideSplits = security->getBool("hidesplits");
        HideBans = security->getBool("hidebans");
        HideWhoisServer = security->getString("hidewhois");
        HideKillsServer = security->getString("hidekills");
        RestrictBannedUsers = security->getBool("restrictbannedusers", true);
        GenericOper = security->getBool("genericoper");
 -      NoUserDns = ConfValue("performance")->getBool("nouserdns");
        SyntaxHints = options->getBool("syntaxhints");
 -      CycleHosts = options->getBool("cyclehosts");
        CycleHostsFromUser = options->getBool("cyclehostsfromuser");
        UndernetMsgPrefix = options->getBool("ircumsgprefix");
        FullHostInTopic = options->getBool("hostintopic");
 -      MaxTargets = security->getInt("maxtargets", 20);
 -      DefaultModes = options->getString("defaultmodes", "nt");
 +      MaxTargets = security->getInt("maxtargets", 20, 1, 31);
 +      DefaultModes = options->getString("defaultmodes", "not");
        PID = ConfValue("pid")->getString("file");
 -      WhoWasGroupSize = ConfValue("whowas")->getInt("groupsize");
 -      WhoWasMaxGroups = ConfValue("whowas")->getInt("maxgroups");
 -      WhoWasMaxKeep = ServerInstance->Duration(ConfValue("whowas")->getString("maxkeep"));
        MaxChans = ConfValue("channels")->getInt("users", 20);
        OperMaxChans = ConfValue("channels")->getInt("opers", 60);
        c_ipv4_range = ConfValue("cidr")->getInt("ipv4clone", 32);
        Limits.ChanMax = ConfValue("limits")->getInt("maxchan", 64);
        Limits.MaxModes = ConfValue("limits")->getInt("maxmodes", 20);
        Limits.IdentMax = ConfValue("limits")->getInt("maxident", 11);
 +      Limits.MaxHost = ConfValue("limits")->getInt("maxhost", 64);
        Limits.MaxQuit = ConfValue("limits")->getInt("maxquit", 255);
        Limits.MaxTopic = ConfValue("limits")->getInt("maxtopic", 307);
        Limits.MaxKick = ConfValue("limits")->getInt("maxkick", 255);
        Limits.MaxGecos = ConfValue("limits")->getInt("maxgecos", 128);
        Limits.MaxAway = ConfValue("limits")->getInt("maxaway", 200);
 +      Limits.MaxLine = ConfValue("limits")->getInt("maxline", 512);
 +      Paths.Config = ConfValue("path")->getString("configdir", CONFIG_PATH);
 +      Paths.Data = ConfValue("path")->getString("datadir", DATA_PATH);
 +      Paths.Log = ConfValue("path")->getString("logdir", LOG_PATH);
 +      Paths.Module = ConfValue("path")->getString("moduledir", MOD_PATH);
        InvBypassModes = options->getBool("invitebypassmodes", true);
        NoSnoticeStack = options->getBool("nosnoticestack", false);
 -      WelcomeNotice = options->getBool("welcomenotice", true);
 -
 -      range(SoftLimit, 10, ServerInstance->SE->GetMaxFds(), ServerInstance->SE->GetMaxFds(), "<performance:softlimit>");
 -      if (ConfValue("performance")->getBool("limitsomaxconn", true))
 -              range(MaxConn, 0, SOMAXCONN, SOMAXCONN, "<performance:somaxconn>");
 -      range(MaxTargets, 1, 31, 20, "<security:maxtargets>");
 -      range(NetBufferSize, 1024, 65534, 10240, "<performance:netbuffersize>");
 -      range(WhoWasGroupSize, 0, 10000, 10, "<whowas:groupsize>");
 -      range(WhoWasMaxGroups, 0, 1000000, 10240, "<whowas:maxgroups>");
 -      range(WhoWasMaxKeep, 3600, INT_MAX, 3600, "<whowas:maxkeep>");
  
 -      ValidIP(DNSServer, "<dns:server>");
 +      if (Network.find(' ') != std::string::npos)
 +              throw CoreException(Network + " is not a valid network name. A network name must not contain spaces.");
  
        std::string defbind = options->getString("defaultbind");
        if (assign(defbind) == "ipv4")
                if (socktest < 0)
                        WildcardIPv6 = false;
                else
 -                      ServerInstance->SE->Close(socktest);
 -      }
 -      ConfigTagList tags = ConfTags("uline");
 -      for(ConfigIter i = tags.first; i != tags.second; ++i)
 -      {
 -              ConfigTag* tag = i->second;
 -              std::string server;
 -              if (!tag->readString("server", server))
 -                      throw CoreException("<uline> tag missing server at " + tag->getTagLocation());
 -              ulines[assign(server)] = tag->getBool("silent");
 -      }
 -
 -      tags = ConfTags("banlist");
 -      for(ConfigIter i = tags.first; i != tags.second; ++i)
 -      {
 -              ConfigTag* tag = i->second;
 -              std::string chan;
 -              if (!tag->readString("chan", chan))
 -                      throw CoreException("<banlist> tag missing chan at " + tag->getTagLocation());
 -              maxbans[chan] = tag->getInt("limit");
 +                      SocketEngine::Close(socktest);
        }
  
        ReadXLine(this, "badip", "ipmask", ServerInstance->XLines->GetFactory("Z"));
@@@ -498,7 -665,12 +498,7 @@@ void ServerConfig::Read(
        catch (CoreException& err)
        {
                valid = false;
 -              errstr << err.GetReason();
 -      }
 -      if (valid)
 -      {
 -              DNSServer = ConfValue("dns")->getString("server");
 -              FindDNS(DNSServer);
 +              errstr << err.GetReason() << std::endl;
        }
  }
  
@@@ -518,26 -690,16 +518,26 @@@ void ServerConfig::Apply(ServerConfig* 
        /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */
        try
        {
 -              for (int Index = 0; Index * sizeof(Deprecated) < sizeof(ChangedConfig); Index++)
 +              for (int index = 0; index * sizeof(DeprecatedConfig) < sizeof(ChangedConfig); index++)
                {
 -                      std::string dummy;
 -                      ConfigTagList tags = ConfTags(ChangedConfig[Index].tag);
 +                      std::string value;
 +                      ConfigTagList tags = ConfTags(ChangedConfig[index].tag);
                        for(ConfigIter i = tags.first; i != tags.second; ++i)
                        {
 -                              if (i->second->readString(ChangedConfig[Index].value, dummy, true))
 -                                      errstr << "Your configuration contains a deprecated value: <"
 -                                              << ChangedConfig[Index].tag << ":" << ChangedConfig[Index].value << "> - " << ChangedConfig[Index].reason
 -                                              << " (at " << i->second->getTagLocation() << ")\n";
 +                              if (i->second->readString(ChangedConfig[index].key, value, true)
 +                                      && (ChangedConfig[index].value.empty() || value == ChangedConfig[index].value))
 +                              {
 +                                      errstr << "Your configuration contains a deprecated value: <"  << ChangedConfig[index].tag;
 +                                      if (ChangedConfig[index].value.empty())
 +                                      {
 +                                              errstr << ':' << ChangedConfig[index].key;
 +                                      }
 +                                      else
 +                                      {
 +                                              errstr << ' ' << ChangedConfig[index].key << "=\"" << ChangedConfig[index].value << "\"";
 +                                      }
 +                                      errstr << "> - " << ChangedConfig[index].reason << " (at " << i->second->getTagLocation() << ")" << std::endl;
 +                              }
                        }
                }
  
        if (valid)
                ServerInstance->WritePID(this->PID);
  
 +      ConfigTagList binds = ConfTags("bind");
 +      if (binds.first == binds.second)
 +               errstr << "Possible configuration error: you have not defined any <bind> blocks." << std::endl
 +                       << "You will need to do this if you want clients to be able to connect!" << std::endl;
 +
        if (old)
        {
                // On first run, ports are bound later on
                ServerInstance->BindPorts(pl);
                if (pl.size())
                {
 -                      errstr << "Not all your client ports could be bound.\nThe following port(s) failed to bind:\n";
 +                      errstr << "Not all your client ports could be bound." << std::endl
 +                              << "The following port(s) failed to bind:" << std::endl;
  
                        int j = 1;
                        for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
                        {
 -                              char buf[MAXBUF];
 -                              snprintf(buf, MAXBUF, "%d.   Address: %s   Reason: %s\n", j, i->first.empty() ? "<all>" : i->first.c_str(), i->second.c_str());
 -                              errstr << buf;
 +                              errstr << j << ".\tAddress: " << (i->first.empty() ? "<all>" : i->first.c_str()) << "\tReason: "
 +                                      << i->second << std::endl;
                        }
                }
        }
        User* user = useruid.empty() ? NULL : ServerInstance->FindNick(useruid);
  
        if (!valid)
 -              ServerInstance->Logs->Log("CONFIG",DEFAULT, "There were errors in your configuration file:");
+       {
 +              ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "There were errors in your configuration file:");
+               Classes.clear();
+       }
  
        while (errstr.good())
        {
                ConfigFileCache::iterator file = this->Files.find(tag->getString("motd", "motd"));
                if (file != this->Files.end())
                      InspIRCd::ProcessColors(file->second);
 -
 -              file = this->Files.find(tag->getString("rules", "rules"));
 -              if (file != this->Files.end())
 -                    InspIRCd::ProcessColors(file->second);
        }
  
        /* No old configuration -> initial boot, nothing more to do here */
  
  void ServerConfig::ApplyModules(User* user)
  {
 -      Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
 -      if (whowas)
 -              WhowasRequest(NULL, whowas, WhowasRequest::WHOWAS_PRUNE).Send();
 -
 -      const std::vector<std::string> v = ServerInstance->Modules->GetAllModuleNames(0);
        std::vector<std::string> added_modules;
 -      std::set<std::string> removed_modules(v.begin(), v.end());
 +      ModuleManager::ModuleMap removed_modules = ServerInstance->Modules->GetModules();
  
        ConfigTagList tags = ConfTags("module");
        for(ConfigIter i = tags.first; i != tags.second; ++i)
                }
        }
  
 -      if (ConfValue("options")->getBool("allowhalfop") && removed_modules.erase("m_halfop.so") == 0)
 -              added_modules.push_back("m_halfop.so");
 -
 -      for (std::set<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
 +      for (ModuleManager::ModuleMap::iterator i = removed_modules.begin(); i != removed_modules.end(); ++i)
        {
 -              // Don't remove cmd_*.so, just remove m_*.so
 -              if (removing->c_str()[0] == 'c')
 +              const std::string& modname = i->first;
 +              // Don't remove core_*.so, just remove m_*.so
 +              if (modname.c_str()[0] == 'c')
                        continue;
 -              Module* m = ServerInstance->Modules->Find(*removing);
 -              if (m && ServerInstance->Modules->Unload(m))
 +              if (ServerInstance->Modules->Unload(i->second))
                {
 -                      ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH UNLOADED MODULE: %s",removing->c_str());
 +                      ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH UNLOADED MODULE: %s", modname.c_str());
  
                        if (user)
 -                              user->WriteNumeric(RPL_UNLOADEDMODULE, "%s %s :Module %s successfully unloaded.",user->nick.c_str(), removing->c_str(), removing->c_str());
 +                              user->WriteNumeric(RPL_UNLOADEDMODULE, "%s :Module %s successfully unloaded.", modname.c_str(), modname.c_str());
                        else
 -                              ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully unloaded.", removing->c_str());
 +                              ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully unloaded.", modname.c_str());
                }
                else
                {
                        if (user)
 -                              user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s %s :Failed to unload module %s: %s",user->nick.c_str(), removing->c_str(), removing->c_str(), ServerInstance->Modules->LastError().c_str());
 +                              user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :Failed to unload module %s: %s", modname.c_str(), modname.c_str(), ServerInstance->Modules->LastError().c_str());
                        else
 -                               ServerInstance->SNO->WriteGlobalSno('a', "Failed to unload module %s: %s", removing->c_str(), ServerInstance->Modules->LastError().c_str());
 +                               ServerInstance->SNO->WriteGlobalSno('a', "Failed to unload module %s: %s", modname.c_str(), ServerInstance->Modules->LastError().c_str());
                }
        }
  
                {
                        ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH LOADED MODULE: %s",adding->c_str());
                        if (user)
 -                              user->WriteNumeric(RPL_LOADEDMODULE, "%s %s :Module %s successfully loaded.",user->nick.c_str(), adding->c_str(), adding->c_str());
 +                              user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module %s successfully loaded.", adding->c_str(), adding->c_str());
                        else
                                ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully loaded.", adding->c_str());
                }
                else
                {
                        if (user)
 -                              user->WriteNumeric(ERR_CANTLOADMODULE, "%s %s :Failed to load module %s: %s",user->nick.c_str(), adding->c_str(), adding->c_str(), ServerInstance->Modules->LastError().c_str());
 +                              user->WriteNumeric(ERR_CANTLOADMODULE, "%s :Failed to load module %s: %s", adding->c_str(), adding->c_str(), ServerInstance->Modules->LastError().c_str());
                        else
                                ServerInstance->SNO->WriteGlobalSno('a', "Failed to load module %s: %s", adding->c_str(), ServerInstance->Modules->LastError().c_str());
                }
        }
  }
  
 -bool ServerConfig::StartsWithWindowsDriveLetter(const std::string &path)
 -{
 -      return (path.length() > 2 && isalpha(path[0]) && path[1] == ':');
 -}
 -
  ConfigTag* ServerConfig::ConfValue(const std::string &tag)
  {
        ConfigTagList found = config_data.equal_range(tag);
        ConfigTag* rv = found.first->second;
        found.first++;
        if (found.first != found.second)
 -              ServerInstance->Logs->Log("CONFIG",DEFAULT, "Multiple <" + tag + "> tags found; only first will be used "
 +              ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Multiple <" + tag + "> tags found; only first will be used "
                        "(first at " + rv->getTagLocation() + "; second at " + found.first->second->getTagLocation() + ")");
        return rv;
  }
@@@ -725,28 -902,35 +728,28 @@@ ConfigTagList ServerConfig::ConfTags(co
        return config_data.equal_range(tag);
  }
  
 -bool ServerConfig::FileExists(const char* file)
 +std::string ServerConfig::Escape(const std::string& str, bool xml)
  {
 -      struct stat sb;
 -      if (stat(file, &sb) == -1)
 -              return false;
 -
 -      if ((sb.st_mode & S_IFDIR) > 0)
 -              return false;
 -
 -      FILE *input = fopen(file, "r");
 -      if (input == NULL)
 -              return false;
 -      else
 +      std::string escaped;
 +      for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
        {
 -              fclose(input);
 -              return true;
 +              switch (*it)
 +              {
 +                      case '"':
 +                              escaped += xml ? "&quot;" : "\"";
 +                              break;
 +                      case '&':
 +                              escaped += xml ? "&amp;" : "&";
 +                              break;
 +                      case '\\':
 +                              escaped += xml ? "\\" : "\\\\";
 +                              break;
 +                      default:
 +                              escaped += *it;
 +                              break;
 +              }
        }
 -}
 -
 -const char* ServerConfig::CleanFilename(const char* name)
 -{
 -      const char* p = name + strlen(name);
 -      while ((p != name) && (*p != '/') && (*p != '\\')) p--;
 -      return (p != name ? ++p : p);
 -}
 -
 -const std::string& ServerConfig::GetSID()
 -{
 -      return sid;
 +      return escaped;
  }
  
  void ConfigReaderThread::Run()
  void ConfigReaderThread::Finish()
  {
        ServerConfig* old = ServerInstance->Config;
 -      ServerInstance->Logs->Log("CONFIG",DEBUG,"Switching to new configuration...");
 +      ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "Switching to new configuration...");
        ServerInstance->Config = this->Config;
        Config->Apply(old, TheUserUID);
  
                 */
                ServerInstance->XLines->CheckELines();
                ServerInstance->XLines->ApplyLines();
 -              ServerInstance->Res->Rehash();
 -              ServerInstance->ResetMaxBans();
 +              ChanModeReference ban(NULL, "ban");
 +              static_cast<ListModeBase*>(*ban)->DoRehash();
                Config->ApplyDisabledCommands(Config->DisabledCommands);
                User* user = ServerInstance->FindNick(TheUserUID);
 -              FOREACH_MOD(I_OnRehash, OnRehash(user));
 -              ServerInstance->BuildISupport();
 +
 +              ConfigStatus status(user);
 +              const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules();
 +              for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i)
 +                      i->second->ReadConfig(status);
 +
 +              ServerInstance->ISupport.Build();
  
                ServerInstance->Logs->CloseLogs();
                ServerInstance->Logs->OpenFileLogs();
index 260264faf0b0fb5cd627c5048d0a5592a696d399,0000000000000000000000000000000000000000..715f35d54343e80fde6a024a66ee7337665cfcfc
mode 100644,000000..100644
--- /dev/null
@@@ -1,86 -1,0 +1,86 @@@
-       if (!u || !c)
 +/*
 + * InspIRCd -- Internet Relay Chat Daemon
 + *
 + *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
 + *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
 + *
 + * This file is part of InspIRCd.  InspIRCd is free software: you can
 + * redistribute it and/or modify it under the terms of the GNU General Public
 + * License as published by the Free Software Foundation, version 2.
 + *
 + * This program is distributed in the hope that it will be useful, but WITHOUT
 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 + * details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 + */
 +
 +
 +#include "inspircd.h"
 +#include "core_channel.h"
 +
 +CommandKick::CommandKick(Module* parent)
 +      : Command(parent, "KICK", 2, 3)
 +{
 +      syntax = "<channel> <nick>{,<nick>} [<reason>]";
 +}
 +
 +/** Handle /KICK
 + */
 +CmdResult CommandKick::Handle (const std::vector<std::string>& parameters, User *user)
 +{
 +      std::string reason;
 +      Channel* c = ServerInstance->FindChan(parameters[0]);
 +      User* u;
 +
 +      if (CommandParser::LoopCall(user, this, parameters, 1))
 +              return CMD_SUCCESS;
 +
 +      if (IS_LOCAL(user))
 +              u = ServerInstance->FindNickOnly(parameters[1]);
 +      else
 +              u = ServerInstance->FindNick(parameters[1]);
 +
-               user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", u ? parameters[0].c_str() : parameters[1].c_str());
++      if ((!u) || (!c) || (u->registered != REG_ALL))
 +      {
++              user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", c ? parameters[1].c_str() : parameters[0].c_str());
 +              return CMD_FAILURE;
 +      }
 +
 +      Membership* srcmemb = NULL;
 +      if (IS_LOCAL(user))
 +      {
 +              srcmemb = c->GetUser(user);
 +              if (!srcmemb)
 +              {
 +                      user->WriteNumeric(ERR_NOTONCHANNEL, "%s :You're not on that channel!", parameters[0].c_str());
 +                      return CMD_FAILURE;
 +              }
 +
 +              if (u->server->IsULine())
 +              {
 +                      user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You may not kick a u-lined client", c->name.c_str());
 +                      return CMD_FAILURE;
 +              }
 +      }
 +
 +      if (parameters.size() > 2)
 +      {
 +              reason.assign(parameters[2], 0, ServerInstance->Config->Limits.MaxKick);
 +      }
 +      else
 +      {
 +              reason.assign(user->nick, 0, ServerInstance->Config->Limits.MaxKick);
 +      }
 +
 +      c->KickUser(user, u, reason, srcmemb);
 +
 +      return CMD_SUCCESS;
 +}
 +
 +RouteDescriptor CommandKick::GetRouting(User* user, const std::vector<std::string>& parameters)
 +{
 +      return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
 +}
index 81ada28bc10abd2f973e2aed0e299dca939080ce,0000000000000000000000000000000000000000..13d9123762d486ea30a4a7043e1d58bc00b93fbb
mode 100644,000000..100644
--- /dev/null
@@@ -1,62 -1,0 +1,57 @@@
-               if ((c->IsModeSet(secretmode)) && (!c->HasUser(user)))
-               {
-                     user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", c->name.c_str());
-                     return CMD_FAILURE;
-               }
 +/*
 + * InspIRCd -- Internet Relay Chat Daemon
 + *
 + *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
 + *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
 + *
 + * This file is part of InspIRCd.  InspIRCd is free software: you can
 + * redistribute it and/or modify it under the terms of the GNU General Public
 + * License as published by the Free Software Foundation, version 2.
 + *
 + * This program is distributed in the hope that it will be useful, but WITHOUT
 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 + * details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 + */
 +
 +
 +#include "inspircd.h"
 +#include "core_channel.h"
 +
 +CommandNames::CommandNames(Module* parent)
 +      : Command(parent, "NAMES", 0, 0)
 +      , secretmode(parent, "secret")
 +{
 +      syntax = "{<channel>{,<channel>}}";
 +}
 +
 +/** Handle /NAMES
 + */
 +CmdResult CommandNames::Handle (const std::vector<std::string>& parameters, User *user)
 +{
 +      Channel* c;
 +
 +      if (!parameters.size())
 +      {
 +              user->WriteNumeric(RPL_ENDOFNAMES, "* :End of /NAMES list.");
 +              return CMD_SUCCESS;
 +      }
 +
 +      if (CommandParser::LoopCall(user, this, parameters, 0))
 +              return CMD_SUCCESS;
 +
 +      c = ServerInstance->FindChan(parameters[0]);
 +      if (c)
 +      {
 +              c->UserList(user);
 +      }
 +      else
 +      {
 +              user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
 +      }
 +
 +      return CMD_SUCCESS;
 +}
index c7ead2a87f1c73dcfec9225413747338c43287c6,0000000000000000000000000000000000000000..53d2e1c49ef7c7eb5f1d4faa12839a5103b01c5e
mode 100644,000000..100644
--- /dev/null
@@@ -1,102 -1,0 +1,102 @@@
-               if (u)
 +/*
 + * InspIRCd -- Internet Relay Chat Daemon
 + *
 + *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
 + *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
 + *
 + * This file is part of InspIRCd.  InspIRCd is free software: you can
 + * redistribute it and/or modify it under the terms of the GNU General Public
 + * License as published by the Free Software Foundation, version 2.
 + *
 + * This program is distributed in the hope that it will be useful, but WITHOUT
 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 + * details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 + */
 +
 +
 +#include "inspircd.h"
 +
 +/** Handle /ISON.
 + */
 +class CommandIson : public Command
 +{
 + public:
 +      /** Constructor for ison.
 +       */
 +      CommandIson ( Module* parent) : Command(parent,"ISON", 1) {
 +              syntax = "<nick> {nick}";
 +      }
 +      /** Handle command.
 +       * @param parameters The parameters to the command
 +       * @param user The user issuing the command
 +       * @return A value from CmdResult to indicate command success or failure.
 +       */
 +      CmdResult Handle(const std::vector<std::string>& parameters, User *user);
 +};
 +
 +/** Handle /ISON
 + */
 +CmdResult CommandIson::Handle (const std::vector<std::string>& parameters, User *user)
 +{
 +      std::map<User*,User*> ison_already;
 +      User *u;
 +      std::string reply = "303 " + user->nick + " :";
 +
 +      for (unsigned int i = 0; i < parameters.size(); i++)
 +      {
 +              u = ServerInstance->FindNickOnly(parameters[i]);
 +              if (ison_already.find(u) != ison_already.end())
 +                      continue;
 +
-                                       if (u)
++              if ((u) && (u->registered == REG_ALL))
 +              {
 +                      reply.append(u->nick).append(" ");
 +                      if (reply.length() > 450)
 +                      {
 +                              user->WriteServ(reply);
 +                              reply = "303 " + user->nick + " :";
 +                      }
 +                      ison_already[u] = u;
 +              }
 +              else
 +              {
 +                      if ((i == parameters.size() - 1) && (parameters[i].find(' ') != std::string::npos))
 +                      {
 +                              /* Its a space seperated list of nicks (RFC1459 says to support this)
 +                               */
 +                              irc::spacesepstream list(parameters[i]);
 +                              std::string item;
 +
 +                              while (list.GetToken(item))
 +                              {
 +                                      u = ServerInstance->FindNickOnly(item);
 +                                      if (ison_already.find(u) != ison_already.end())
 +                                              continue;
 +
++                                      if ((u) && (u->registered == REG_ALL))
 +                                      {
 +                                              reply.append(u->nick).append(" ");
 +                                              if (reply.length() > 450)
 +                                              {
 +                                                      user->WriteServ(reply);
 +                                                      reply = "303 " + user->nick + " :";
 +                                              }
 +                                              ison_already[u] = u;
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (!reply.empty())
 +              user->WriteServ(reply);
 +
 +      return CMD_SUCCESS;
 +}
 +
 +
 +COMMAND_INIT(CommandIson)
index 2c120e5627551842f1decce0f72a03e8e9553f64,0000000000000000000000000000000000000000..6f4bc088e604419e56f4c51f3228a4c2fcfe44b4
mode 100644,000000..100644
--- /dev/null
@@@ -1,401 -1,0 +1,401 @@@
-                                               if (usingwildcards && (!oper->IsModeSet(invisiblemode)) && (!user->HasPrivPermission("users/auspex")))
 +/*
 + * InspIRCd -- Internet Relay Chat Daemon
 + *
 + *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
 + *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
 + *
 + * This file is part of InspIRCd.  InspIRCd is free software: you can
 + * redistribute it and/or modify it under the terms of the GNU General Public
 + * License as published by the Free Software Foundation, version 2.
 + *
 + * This program is distributed in the hope that it will be useful, but WITHOUT
 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 + * details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 + */
 +
 +
 +#include "inspircd.h"
 +
 +/** Handle /WHO.
 + */
 +class CommandWho : public Command
 +{
 +      bool CanView(Channel* chan, User* user);
 +      bool opt_viewopersonly;
 +      bool opt_showrealhost;
 +      bool opt_realname;
 +      bool opt_mode;
 +      bool opt_ident;
 +      bool opt_metadata;
 +      bool opt_port;
 +      bool opt_away;
 +      bool opt_local;
 +      bool opt_far;
 +      bool opt_time;
 +      ChanModeReference secretmode;
 +      ChanModeReference privatemode;
 +      UserModeReference invisiblemode;
 +
 +      Membership* get_first_visible_channel(User* u)
 +      {
 +              for (UCListIter i = u->chans.begin(); i != u->chans.end(); ++i)
 +              {
 +                      Membership* memb = *i;
 +                      if (!memb->chan->IsModeSet(secretmode))
 +                              return memb;
 +              }
 +              return NULL;
 +      }
 +
 + public:
 +      /** Constructor for who.
 +       */
 +      CommandWho(Module* parent)
 +              : Command(parent, "WHO", 1)
 +              , secretmode(parent, "secret")
 +              , privatemode(parent, "private")
 +              , invisiblemode(parent, "invisible")
 +      {
 +              syntax = "<server>|<nickname>|<channel>|<realname>|<host>|0 [ohurmMiaplf]";
 +      }
 +
 +      void SendWhoLine(User* user, const std::vector<std::string>& parms, const std::string& initial, Membership* memb, User* u, std::vector<std::string>& whoresults);
 +      /** Handle command.
 +       * @param parameters The parameters to the command
 +       * @param user The user issuing the command
 +       * @return A value from CmdResult to indicate command success or failure.
 +       */
 +      CmdResult Handle(const std::vector<std::string>& parameters, User *user);
 +      bool whomatch(User* cuser, User* user, const char* matchtext);
 +};
 +
 +bool CommandWho::whomatch(User* cuser, User* user, const char* matchtext)
 +{
 +      bool match = false;
 +      bool positive = false;
 +
 +      if (user->registered != REG_ALL)
 +              return false;
 +
 +      if (opt_local && !IS_LOCAL(user))
 +              return false;
 +      else if (opt_far && IS_LOCAL(user))
 +              return false;
 +
 +      if (opt_mode)
 +      {
 +              for (const char* n = matchtext; *n; n++)
 +              {
 +                      if (*n == '+')
 +                      {
 +                              positive = true;
 +                              continue;
 +                      }
 +                      else if (*n == '-')
 +                      {
 +                              positive = false;
 +                              continue;
 +                      }
 +                      if (user->IsModeSet(*n) != positive)
 +                              return false;
 +              }
 +              return true;
 +      }
 +      else
 +      {
 +              /*
 +               * This was previously one awesome pile of ugly nested if, when really, it didn't need
 +               * to be, since only one condition was ever checked, a chained if works just fine.
 +               * -- w00t
 +               */
 +              if (opt_metadata)
 +              {
 +                      match = false;
 +                      const Extensible::ExtensibleStore& list = user->GetExtList();
 +                      for(Extensible::ExtensibleStore::const_iterator i = list.begin(); i != list.end(); ++i)
 +                              if (InspIRCd::Match(i->first->name, matchtext))
 +                                      match = true;
 +              }
 +              else if (opt_realname)
 +                      match = InspIRCd::Match(user->fullname, matchtext);
 +              else if (opt_showrealhost)
 +                      match = InspIRCd::Match(user->host, matchtext, ascii_case_insensitive_map);
 +              else if (opt_ident)
 +                      match = InspIRCd::Match(user->ident, matchtext, ascii_case_insensitive_map);
 +              else if (opt_port)
 +              {
 +                      irc::portparser portrange(matchtext, false);
 +                      long portno = -1;
 +                      while ((portno = portrange.GetToken()))
 +                              if (IS_LOCAL(user) && portno == IS_LOCAL(user)->GetServerPort())
 +                              {
 +                                      match = true;
 +                                      break;
 +                              }
 +              }
 +              else if (opt_away)
 +                      match = InspIRCd::Match(user->awaymsg, matchtext);
 +              else if (opt_time)
 +              {
 +                      long seconds = InspIRCd::Duration(matchtext);
 +
 +                      // Okay, so time matching, we want all users connected `seconds' ago
 +                      if (user->age >= ServerInstance->Time() - seconds)
 +                              match = true;
 +              }
 +
 +              /*
 +               * Once the conditionals have been checked, only check dhost/nick/server
 +               * if they didn't match this user -- and only match if we don't find a match.
 +               *
 +               * This should make things minutely faster, and again, less ugly.
 +               * -- w00t
 +               */
 +              if (!match)
 +                      match = InspIRCd::Match(user->dhost, matchtext, ascii_case_insensitive_map);
 +
 +              if (!match)
 +                      match = InspIRCd::Match(user->nick, matchtext);
 +
 +              /* Don't allow server name matches if HideWhoisServer is enabled, unless the command user has the priv */
 +              if (!match && (ServerInstance->Config->HideWhoisServer.empty() || cuser->HasPrivPermission("users/auspex")))
 +                      match = InspIRCd::Match(user->server->GetName(), matchtext);
 +
 +              return match;
 +      }
 +}
 +
 +bool CommandWho::CanView(Channel* chan, User* user)
 +{
 +      if (!user || !chan)
 +              return false;
 +
 +      /* Bug #383 - moved higher up the list, because if we are in the channel
 +       * we can see all its users
 +       */
 +      if (chan->HasUser(user))
 +              return true;
 +      /* Opers see all */
 +      if (user->HasPrivPermission("users/auspex"))
 +              return true;
 +      /* Cant see inside a +s or a +p channel unless we are a member (see above) */
 +      else if (!chan->IsModeSet(secretmode) && !chan->IsModeSet(privatemode))
 +              return true;
 +
 +      return false;
 +}
 +
 +void CommandWho::SendWhoLine(User* user, const std::vector<std::string>& parms, const std::string& initial, Membership* memb, User* u, std::vector<std::string>& whoresults)
 +{
 +      if (!memb)
 +              memb = get_first_visible_channel(u);
 +
 +      std::string wholine = initial + (memb ? memb->chan->name : "*") + " " + u->ident + " " +
 +              (opt_showrealhost ? u->host : u->dhost) + " ";
 +      if (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
 +              wholine.append(ServerInstance->Config->HideWhoisServer);
 +      else
 +              wholine.append(u->server->GetName());
 +
 +      wholine.append(" " + u->nick + " ");
 +
 +      /* away? */
 +      if (u->IsAway())
 +      {
 +              wholine.append("G");
 +      }
 +      else
 +      {
 +              wholine.append("H");
 +      }
 +
 +      /* oper? */
 +      if (u->IsOper())
 +      {
 +              wholine.push_back('*');
 +      }
 +
 +      if (memb)
 +      {
 +              char prefix = memb->GetPrefixChar();
 +              if (prefix)
 +                      wholine.push_back(prefix);
 +      }
 +
 +      wholine.append(" :0 " + u->fullname);
 +
 +      FOREACH_MOD(OnSendWhoLine, (user, parms, u, memb, wholine));
 +
 +      if (!wholine.empty())
 +              whoresults.push_back(wholine);
 +}
 +
 +CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User *user)
 +{
 +      /*
 +       * XXX - RFC says:
 +       *   The <name> passed to WHO is matched against users' host, server, real
 +       *   name and nickname
 +       * Currently, we support WHO #chan, WHO nick, WHO 0, WHO *, and the addition of a 'o' flag, as per RFC.
 +       */
 +
 +      /* WHO options */
 +      opt_viewopersonly = false;
 +      opt_showrealhost = false;
 +      opt_realname = false;
 +      opt_mode = false;
 +      opt_ident = false;
 +      opt_metadata = false;
 +      opt_port = false;
 +      opt_away = false;
 +      opt_local = false;
 +      opt_far = false;
 +      opt_time = false;
 +
 +      std::vector<std::string> whoresults;
 +      std::string initial = "352 " + user->nick + " ";
 +
 +      /* Change '0' into '*' so the wildcard matcher can grok it */
 +      std::string matchtext = ((parameters[0] == "0") ? "*" : parameters[0]);
 +
 +      // WHO flags count as a wildcard
 +      bool usingwildcards = ((parameters.size() > 1) || (matchtext.find_first_of("*?.") != std::string::npos));
 +
 +      if (parameters.size() > 1)
 +      {
 +              for (std::string::const_iterator iter = parameters[1].begin(); iter != parameters[1].end(); ++iter)
 +              {
 +                      switch (*iter)
 +                      {
 +                              case 'o':
 +                                      opt_viewopersonly = true;
 +                                      break;
 +                              case 'h':
 +                                      if (user->HasPrivPermission("users/auspex"))
 +                                              opt_showrealhost = true;
 +                                      break;
 +                              case 'r':
 +                                      opt_realname = true;
 +                                      break;
 +                              case 'm':
 +                                      if (user->HasPrivPermission("users/auspex"))
 +                                              opt_mode = true;
 +                                      break;
 +                              case 'M':
 +                                      if (user->HasPrivPermission("users/auspex"))
 +                                              opt_metadata = true;
 +                                      break;
 +                              case 'i':
 +                                      opt_ident = true;
 +                                      break;
 +                              case 'p':
 +                                      if (user->HasPrivPermission("users/auspex"))
 +                                              opt_port = true;
 +                                      break;
 +                              case 'a':
 +                                      opt_away = true;
 +                                      break;
 +                              case 'l':
 +                                      if (user->HasPrivPermission("users/auspex") || ServerInstance->Config->HideWhoisServer.empty())
 +                                              opt_local = true;
 +                                      break;
 +                              case 'f':
 +                                      if (user->HasPrivPermission("users/auspex") || ServerInstance->Config->HideWhoisServer.empty())
 +                                              opt_far = true;
 +                                      break;
 +                              case 't':
 +                                      opt_time = true;
 +                                      break;
 +                      }
 +              }
 +      }
 +
 +
 +      /* who on a channel? */
 +      Channel* ch = ServerInstance->FindChan(matchtext);
 +
 +      if (ch)
 +      {
 +              if (CanView(ch,user))
 +              {
 +                      bool inside = ch->HasUser(user);
 +
 +                      /* who on a channel. */
 +                      const UserMembList *cu = ch->GetUsers();
 +
 +                      for (UserMembCIter i = cu->begin(); i != cu->end(); i++)
 +                      {
 +                              /* None of this applies if we WHO ourselves */
 +                              if (user != i->first)
 +                              {
 +                                      /* opers only, please */
 +                                      if (opt_viewopersonly && !i->first->IsOper())
 +                                              continue;
 +
 +                                      /* If we're not inside the channel, hide +i users */
 +                                      if (i->first->IsModeSet(invisiblemode) && !inside && !user->HasPrivPermission("users/auspex"))
 +                                              continue;
 +                              }
 +
 +                              SendWhoLine(user, parameters, initial, i->second, i->first, whoresults);
 +                      }
 +              }
 +      }
 +      else
 +      {
 +              /* Match against wildcard of nick, server or host */
 +              if (opt_viewopersonly)
 +              {
 +                      /* Showing only opers */
 +                      const UserManager::OperList& opers = ServerInstance->Users->all_opers;
 +                      for (UserManager::OperList::const_iterator i = opers.begin(); i != opers.end(); ++i)
 +                      {
 +                              User* oper = *i;
 +
 +                              if (whomatch(user, oper, matchtext.c_str()))
 +                              {
 +                                      if (!user->SharesChannelWith(oper))
 +                                      {
++                                              if (usingwildcards && (oper->IsModeSet(invisiblemode)) && (!user->HasPrivPermission("users/auspex")))
 +                                                      continue;
 +                                      }
 +
 +                                      SendWhoLine(user, parameters, initial, NULL, oper, whoresults);
 +                              }
 +                      }
 +              }
 +              else
 +              {
 +                      const user_hash& users = ServerInstance->Users->GetUsers();
 +                      for (user_hash::const_iterator i = users.begin(); i != users.end(); ++i)
 +                      {
 +                              if (whomatch(user, i->second, matchtext.c_str()))
 +                              {
 +                                      if (!user->SharesChannelWith(i->second))
 +                                      {
 +                                              if (usingwildcards && (i->second->IsModeSet(invisiblemode)) && (!user->HasPrivPermission("users/auspex")))
 +                                                      continue;
 +                                      }
 +
 +                                      SendWhoLine(user, parameters, initial, NULL, i->second, whoresults);
 +                              }
 +                      }
 +              }
 +      }
 +      /* Send the results out */
 +      for (std::vector<std::string>::const_iterator n = whoresults.begin(); n != whoresults.end(); n++)
 +              user->WriteServ(*n);
 +      user->WriteNumeric(RPL_ENDOFWHO, "%s :End of /WHO list.", *parameters[0].c_str() ? parameters[0].c_str() : "*");
 +
 +      // Penalize the user a bit for large queries
 +      // (add one unit of penalty per 200 results)
 +      if (IS_LOCAL(user))
 +              IS_LOCAL(user)->CommandFloodPenalty += whoresults.size() * 5;
 +      return CMD_SUCCESS;
 +}
 +
 +COMMAND_INIT(CommandWho)
diff --combined src/inspircd.cpp
index 5097960e88309e3fdee642e4f1b0bacd29a6f4d3,766aeaf8ef05d92cb4979252aac8423ce7825a0c..24072868a0c4c66b1e0cd61f133934c1113e842f
@@@ -26,7 -26,9 +26,7 @@@
   */
  
  
 -/* $Core */
  #include "inspircd.h"
 -#include "inspircd_version.h"
  #include <signal.h>
  
  #ifndef _WIN32
@@@ -76,26 -78,28 +76,26 @@@ 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
  };
  
 +#ifdef INSPIRCD_ENABLE_TESTSUITE
 +/** True if we have been told to run the testsuite from the commandline,
 + * rather than entering the mainloop.
 + */
 +static int do_testsuite = 0;
 +#endif
 +
  template<typename T> static void DeleteZero(T*&n)
  {
        T* 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];
        }
        ports.clear();
  
        /* Close all client sockets, or the new process inherits them */
 -      LocalUserList::reverse_iterator i = Users->local_users.rbegin();
 -      while (i != this->Users->local_users.rend())
 -      {
 -              User* u = *i++;
 -              Users->QuitUser(u, "Server shutdown");
 -      }
 +      LocalUserList& list = Users->local_users;
 +      for (LocalUserList::iterator i = list.begin(); i != list.end(); ++i)
 +              Users->QuitUser(*i, "Server shutdown");
  
        GlobalCulls.Apply();
        Modules->UnloadAll();
        /* Delete objects dynamically allocated in constructor (destructor would be more appropriate, but we're likely exiting) */
        /* Must be deleted before modes as it decrements modelines */
        if (FakeClient)
 +      {
 +              delete FakeClient->server;
                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();
 +      SocketEngine::Deinit();
 +      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
@@@ -178,8 -258,8 +178,8 @@@ bool InspIRCd::DaemonSeed(
        // Do not use QuickExit here: It will exit with status SIGTERM which would break e.g. daemon scripts
        signal(SIGTERM, VoidSignalHandler);
  
 -      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
@@@ -219,7 -299,7 +219,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
@@@ -243,35 -323,43 +243,35 @@@ 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;
 +      // Flag variables passed to getopt_long() later
        int do_version = 0, do_nofork = 0, do_debug = 0,
 -          do_nolog = 0, do_root = 0, do_testsuite = 0;    /* flag variables */
 -      int c = 0;
 +          do_nolog = 0, do_root = 0;
  
        // Initialize so that if we exit before proper initialization they're not deleted
        this->Logs = 0;
        this->Threads = 0;
        this->PI = 0;
        this->Users = 0;
 -      this->chanlist = 0;
        this->Config = 0;
        this->SNO = 0;
        this->BanCache = 0;
        this->Modules = 0;
        this->stats = 0;
 -      this->Timers = 0;
        this->Parser = 0;
        this->XLines = 0;
        this->Modes = 0;
 -      this->Res = 0;
        this->ConfigThread = NULL;
        this->FakeClient = NULL;
  
        // This must be created first, so other parts of Insp can use it while starting up
        this->Logs = new LogManager;
  
 -      SE = CreateSocketEngine();
 +      SocketEngine::Init();
  
        this->Threads = new ThreadEngine;
  
        /* 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;
        this->XLines = new XLineManager;
  
        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       },
                { "runasroot",  no_argument,            &do_root,       1       },
                { "version",    no_argument,            &do_version,    1       },
 +#ifdef INSPIRCD_ENABLE_TESTSUITE
                { "testsuite",  no_argument,            &do_testsuite,  1       },
 +#endif
                { 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;
 +                              ConfigFileName = ServerInstance->Config->Paths.PrependConfig(optarg);
                        break;
                        case 0:
                                /* getopt_long_only() set an int variable, just keep going */
                        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]" << std::endl;
                                Exit(EXIT_STATUS_ARGV);
                        break;
                }
        }
  
 +#ifdef INSPIRCD_ENABLE_TESTSUITE
        if (do_testsuite)
                do_nofork = do_debug = true;
 +#endif
  
        if (do_version)
        {
 -              std::cout << std::endl << VERSION << " r" << REVISION << std::endl;
 +              std::cout << std::endl << VERSION << " " << REVISION << std::endl;
                Exit(EXIT_STATUS_NOERROR);
        }
  
        /* Set the finished argument values */
        Config->cmdline.nofork = (do_nofork != 0);
        Config->cmdline.forcedebug = (do_debug != 0);
-       Config->cmdline.writelog = (!do_nolog != 0);
+       Config->cmdline.writelog = !do_nolog;
 -      Config->cmdline.TestSuite = (do_testsuite != 0);
  
        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()))
 +      if (!FileSystem::FileExists(ConfigFileName))
        {
  #ifdef _WIN32
                /* Windows can (and defaults to) hide file extensions, so let's play a bit nice for windows users. */
                std::string txtconf = this->ConfigFileName;
                txtconf.append(".txt");
  
 -              if (ServerConfig::FileExists(txtconf.c_str()))
 +              if (FileSystem::FileExists(txtconf))
                {
                        ConfigFileName = txtconf;
                }
  #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();
 +      SocketEngine::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 */
 -      this->FakeClient = new FakeUser(Config->sid, Config->ServerName);
 +      // Create the server user for this server
 +      this->FakeClient = new FakeUser(Config->sid, Config->ServerName, Config->ServerDesc);
  
 -      // 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())
                std::cout << std::endl << "Hint: Try using a public IP instead of blank or *" << std::endl;
        }
  
 -      std::cout << "InspIRCd is now running as '" << Config->ServerName << "'[" << Config->GetSID() << "] with " << SE->GetMaxFds() << " max open sockets" << std::endl;
 +      std::cout << "InspIRCd is now running as '" << Config->ServerName << "'[" << Config->GetSID() << "] with " << SocketEngine::GetMaxFds() << " max open sockets" << std::endl;
  
  #ifndef _WIN32
        if (!Config->cmdline.nofork)
                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));
                }
        }
  
         *
         *    -- nenolod
         */
 -      if ((!do_nofork) && (!do_testsuite) && (!Config->cmdline.forcedebug))
 +      if ((!do_nofork) && (!Config->cmdline.forcedebug))
        {
                int fd = open("/dev/null", O_RDWR);
  
                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(), SocketEngine::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(%s) failed (wrong group?): %s", SetGroup.c_str(), strerror(errno));
 +                      this->Logs->Log("STARTUP", LOG_DEFAULT, "getgrnam(%s) failed (wrong group?): %s", SetGroup.c_str(), strerror(errno));
                        this->QuickExit(0);
                }
  
  
                if (ret == -1)
                {
 -                      this->Logs->Log("SETGUID", DEFAULT, "setgid() failed (wrong group?): %s", strerror(errno));
 +                      this->Logs->Log("STARTUP", LOG_DEFAULT, "setgid() failed (wrong group?): %s", strerror(errno));
                        this->QuickExit(0);
                }
        }
  
                if (!u)
                {
 -                      this->Logs->Log("SETGUID", DEFAULT, "getpwnam(%s) failed (wrong user?): %s", SetUser.c_str(), strerror(errno));
 +                      this->Logs->Log("STARTUP", LOG_DEFAULT, "getpwnam(%s) failed (wrong user?): %s", SetUser.c_str(), strerror(errno));
                        this->QuickExit(0);
                }
  
  
                if (ret == -1)
                {
 -                      this->Logs->Log("SETGUID", DEFAULT, "setuid() failed (wrong user?): %s", strerror(errno));
 +                      this->Logs->Log("STARTUP", LOG_DEFAULT, "setuid() failed (wrong user?): %s", strerror(errno));
                        this->QuickExit(0);
                }
        }
@@@ -638,17 -753,15 +638,17 @@@ void InspIRCd::UpdateTime(
  #endif
  }
  
 -int InspIRCd::Run()
 +void InspIRCd::Run()
  {
 +#ifdef INSPIRCD_ENABLE_TESTSUITE
        /* See if we're supposed to be running the test suite rather than entering the mainloop */
 -      if (Config->cmdline.TestSuite)
 +      if (do_testsuite)
        {
                TestSuite* ts = new TestSuite;
                delete ts;
 -              Exit(0);
 +              return;
        }
 +#endif
  
        UpdateTime();
        time_t OLDTIME = TIME.tv_sec;
                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();
 +                      Timers.TickTimers(TIME.tv_sec);
 +                      Users->DoBackgroundUserStuff();
  
                        if ((TIME.tv_sec % 5) == 0)
                        {
 -                              FOREACH_MOD(I_OnBackgroundTimer,OnBackgroundTimer(TIME.tv_sec));
 +                              FOREACH_MOD(OnBackgroundTimer, (TIME.tv_sec));
                                SNO->FlushSnotices();
                        }
                }
                 * This will cause any read or write events to be
                 * dispatched to their handlers.
                 */
 -              this->SE->DispatchTrialWrites();
 -              this->SE->DispatchEvents();
 +              SocketEngine::DispatchTrialWrites();
 +              SocketEngine::DispatchEvents();
  
                /* if any users were quit, take them out */
                GlobalCulls.Apply();
                        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;
index ee024318b5ed6c1da43efab644954887422dd2c1,9a54ff80b7c6a0cb29a762d1dd3e4fadf320119b..7a8bdeeb00be52936a208dc2760a2a377516e878
  # 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 };
  
@@@ -60,400 -65,396 +60,406 @@@ char* get_error(
        return ERR_error_string(ERR_get_error(), NULL);
  }
  
 -static int error_callback(const char *str, size_t len, void *u);
 +static int OnVerify(int preverify_ok, X509_STORE_CTX* ctx);
  
 -/** Represents an SSL user's extra data
 - */
 -class issl_session
 +namespace OpenSSL
  {
 -public:
 -      SSL* sess;
 -      issl_status status;
 -      reference<ssl_cert> cert;
 -
 -      bool outbound;
 -      bool data_to_write;
 -
 -      issl_session()
 +      class Exception : public ModuleException
        {
 -              outbound = false;
 -              data_to_write = false;
 -      }
 -};
 -
 -static int OnVerify(int preverify_ok, X509_STORE_CTX *ctx)
 -{
 -      /* XXX: This will allow self signed certificates.
 -       * In the future if we want an option to not allow this,
 -       * we can just return preverify_ok here, and openssl
 -       * will boot off self-signed and invalid peer certs.
 -       */
 -      int ve = X509_STORE_CTX_get_error(ctx);
 -
 -      SelfSigned = (ve == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
 -
 -      return 1;
 -}
 -
 -class ModuleSSLOpenSSL : public Module
 -{
 -      issl_session* sessions;
 -
 -      SSL_CTX* ctx;
 -      SSL_CTX* clictx;
 +       public:
 +              Exception(const std::string& reason)
 +                      : ModuleException(reason) { }
 +      };
  
 -      std::string sslports;
 -      bool use_sha;
 -
 -      ServiceProvider iohook;
 - public:
 -
 -      ModuleSSLOpenSSL() : iohook(this, "ssl/openssl", SERVICE_IOHOOK)
 +      class DHParams
        {
 -              sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
 +              DH* dh;
  
 -              /* Global SSL library initialization*/
 -              SSL_library_init();
 -              SSL_load_error_strings();
 +       public:
 +              DHParams(const std::string& filename)
 +              {
 +                      FILE* dhpfile = fopen(filename.c_str(), "r");
 +                      if (dhpfile == NULL)
 +                              throw Exception("Couldn't open DH file " + filename + ": " + strerror(errno));
 +
 +                      dh = PEM_read_DHparams(dhpfile, NULL, NULL, NULL);
 +                      fclose(dhpfile);
 +                      if (!dh)
 +                              throw Exception("Couldn't read DH params from file " + filename);
 +              }
  
 -              /* 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() );
 +              ~DHParams()
 +              {
 +                      DH_free(dh);
 +              }
  
 -              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);
 +              DH* get()
 +              {
 +                      return dh;
 +              }
 +      };
  
 -              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);
 +      class Context
 +      {
 +              SSL_CTX* const ctx;
  
 -              const unsigned char session_id[] = "inspircd";
 -              SSL_CTX_set_session_id_context(ctx, session_id, sizeof(session_id) - 1);
 -      }
 +       public:
 +              Context(SSL_CTX* context)
 +                      : ctx(context)
 +              {
 +                      SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
 +                      SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
 -      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);
 -      }
++                      const unsigned char session_id[] = "inspircd";
++                      SSL_CTX_set_session_id_context(ctx, session_id, sizeof(session_id) - 1);
 +              }
  
 -      void OnHookIO(StreamSocket* user, ListenSocket* lsb)
 -      {
 -              if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "openssl")
 +              ~Context()
                {
 -                      /* Hook the user with our module */
 -                      user->AddIOHook(this);
 +                      SSL_CTX_free(ctx);
                }
 -      }
  
 -      void OnRehash(User* user)
 -      {
 -              sslports.clear();
 +              bool SetDH(DHParams& dh)
 +              {
 +                      return (SSL_CTX_set_tmp_dh(ctx, dh.get()) >= 0);
 +              }
  
 -              ConfigTag* Conf = ServerInstance->Config->ConfValue("openssl");
 +              bool SetCiphers(const std::string& ciphers)
 +              {
 +                      return SSL_CTX_set_cipher_list(ctx, ciphers.c_str());
 +              }
  
 -              if (Conf->getBool("showports", true))
 +              bool SetCerts(const std::string& filename)
                {
 -                      sslports = Conf->getString("advertisedports");
 -                      if (!sslports.empty())
 -                              return;
 +                      return SSL_CTX_use_certificate_chain_file(ctx, filename.c_str());
 +              }
  
 -                      for (size_t i = 0; i < ServerInstance->ports.size(); i++)
 -                      {
 -                              ListenSocket* port = ServerInstance->ports[i];
 -                              if (port->bind_tag->getString("ssl") != "openssl")
 -                                      continue;
 +              bool SetPrivateKey(const std::string& filename)
 +              {
 +                      return SSL_CTX_use_PrivateKey_file(ctx, filename.c_str(), SSL_FILETYPE_PEM);
 +              }
  
 -                              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());
 +              bool SetCA(const std::string& filename)
 +              {
 +                      return SSL_CTX_load_verify_locations(ctx, filename.c_str(), 0);
 +              }
  
 -                              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* CreateSession()
 +              {
 +                      return SSL_new(ctx);
                }
 -      }
 +      };
  
 -      void OnModuleRehash(User* user, const std::string &param)
 +      class Profile : public refcountbase
        {
 -              if (param != "ssl")
 -                      return;
 +              /** Name of this profile
 +               */
 +              const std::string name;
  
 -              std::string keyfile;
 -              std::string certfile;
 -              std::string cafile;
 -              std::string dhfile;
 -              OnRehash(user);
 +              /** DH parameters in use
 +               */
 +              DHParams dh;
  
 -              ConfigTag* conf = ServerInstance->Config->ConfValue("openssl");
 +              /** OpenSSL makes us have two contexts, one for servers and one for clients
 +               */
 +              Context ctx;
 +              Context clictx;
  
 -              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");
 +              /** Digest to use when generating fingerprints
 +               */
 +              const EVP_MD* digest;
  
 -              std::string ciphers = conf->getString("ciphers", "");
 +              /** Last error, set by error_callback()
 +               */
 +              std::string lasterr;
  
 -              if (!ciphers.empty())
 +              static int error_callback(const char* str, size_t len, void* u)
                {
 -                      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);
 -                      }
 +                      Profile* profile = reinterpret_cast<Profile*>(u);
 +                      profile->lasterr = std::string(str, len - 1);
 +                      return 0;
                }
  
 -              /* 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())))
 +       public:
 +              Profile(const std::string& profilename, ConfigTag* tag)
 +                      : name(profilename)
 +                      , dh(ServerInstance->Config->Paths.PrependConfig(tag->getString("dhfile", "dh.pem")))
 +                      , ctx(SSL_CTX_new(SSLv23_server_method()))
 +                      , clictx(SSL_CTX_new(SSLv23_client_method()))
                {
 -                      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);
 -              }
 +                      if ((!ctx.SetDH(dh)) || (!clictx.SetDH(dh)))
 +                              throw Exception("Couldn't set DH parameters");
  
 -              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)))
 -              {
 -                      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);
 -              }
 +                      std::string hash = tag->getString("hash", "md5");
 +                      digest = EVP_get_digestbyname(hash.c_str());
 +                      if (digest == NULL)
 +                              throw Exception("Unknown hash type " + hash);
  
 -              /* 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)))
 -              {
 -                      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);
 -              }
 +                      std::string ciphers = tag->getString("ciphers");
 +                      if (!ciphers.empty())
 +                      {
 +                              if ((!ctx.SetCiphers(ciphers)) || (!clictx.SetCiphers(ciphers)))
 +                              {
 +                                      ERR_print_errors_cb(error_callback, this);
 +                                      throw Exception("Can't set cipher list to \"" + ciphers + "\" " + lasterr);
 +                              }
 +                      }
  
 -              FILE* dhpfile = fopen(dhfile.c_str(), "r");
 -              DH* ret;
 +                      /* Load our keys and certificates
 +                       * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
 +                       */
 +                      std::string filename = ServerInstance->Config->Paths.PrependConfig(tag->getString("certfile", "cert.pem"));
 +                      if ((!ctx.SetCerts(filename)) || (!clictx.SetCerts(filename)))
 +                      {
 +                              ERR_print_errors_cb(error_callback, this);
 +                              throw Exception("Can't read certificate file: " + lasterr);
 +                      }
  
 -              if (dhpfile == NULL)
 -              {
 -                      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));
 -              }
 -              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))
 +                      filename = ServerInstance->Config->Paths.PrependConfig(tag->getString("keyfile", "key.pem"));
 +                      if ((!ctx.SetPrivateKey(filename)) || (!clictx.SetPrivateKey(filename)))
 +                      {
 +                              ERR_print_errors_cb(error_callback, this);
 +                              throw Exception("Can't read key file: " + lasterr);
 +                      }
 +
 +                      // Load the CAs we trust
 +                      filename = ServerInstance->Config->Paths.PrependConfig(tag->getString("cafile", "ca.pem"));
 +                      if ((!ctx.SetCA(filename)) || (!clictx.SetCA(filename)))
                        {
 -                              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);
 +                              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", filename.c_str(), lasterr.c_str());
                        }
 -                      DH_free(ret);
                }
  
 -              fclose(dhpfile);
 -      }
 +              const std::string& GetName() const { return name; }
 +              SSL* CreateServerSession() { return ctx.CreateSession(); }
 +              SSL* CreateClientSession() { return clictx.CreateSession(); }
 +              const EVP_MD* GetDigest() { return digest; }
 +      };
 +}
  
 -      void On005Numeric(std::string &output)
 -      {
 -              if (!sslports.empty())
 -                      output.append(" SSL=" + sslports);
 -      }
 +static int OnVerify(int preverify_ok, X509_STORE_CTX *ctx)
 +{
 +      /* XXX: This will allow self signed certificates.
 +       * In the future if we want an option to not allow this,
 +       * we can just return preverify_ok here, and openssl
 +       * will boot off self-signed and invalid peer certs.
 +       */
 +      int ve = X509_STORE_CTX_get_error(ctx);
  
 -      ~ModuleSSLOpenSSL()
 -      {
 -              SSL_CTX_free(ctx);
 -              SSL_CTX_free(clictx);
 -              delete[] sessions;
 -      }
 +      SelfSigned = (ve == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
 +
 +      return 1;
 +}
 +
 +class OpenSSLIOHook : public SSLIOHook
 +{
 + private:
 +      SSL* sess;
 +      issl_status status;
 +      const bool outbound;
 +      bool data_to_write;
 +      reference<OpenSSL::Profile> profile;
  
 -      void OnUserConnect(LocalUser* user)
 +      bool Handshake(StreamSocket* user)
        {
 -              if (user->eh.GetIOHook() == this)
 +              int ret;
 +
 +              if (outbound)
 +                      ret = SSL_connect(sess);
 +              else
 +                      ret = SSL_accept(sess);
 +
 +              if (ret < 0)
                {
 -                      if (sessions[user->eh.GetFd()].sess)
 +                      int err = SSL_get_error(sess, ret);
 +
 +                      if (err == SSL_ERROR_WANT_READ)
                        {
 -                              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));
 +                              SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
 +                              this->status = ISSL_HANDSHAKING;
 +                              return true;
 +                      }
 +                      else if (err == SSL_ERROR_WANT_WRITE)
 +                      {
 +                              SocketEngine::ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
 +                              this->status = ISSL_HANDSHAKING;
 +                              return true;
 +                      }
 +                      else
 +                      {
 +                              CloseSession();
                        }
 -              }
 -      }
  
 -      void OnCleanup(int target_type, void* item)
 -      {
 -              if (target_type == TYPE_USER)
 +                      return false;
 +              }
 +              else if (ret > 0)
                {
 -                      LocalUser* user = IS_LOCAL((User*)item);
 +                      // Handshake complete.
 +                      VerifyCertificate();
  
 -                      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");
 -                      }
 +                      status = ISSL_OPEN;
 +
 +                      SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
 +
 +                      return true;
 +              }
 +              else if (ret == 0)
 +              {
 +                      CloseSession();
 +                      return true;
                }
 -      }
  
 -      Version GetVersion()
 -      {
 -              return Version("Provides SSL support for clients", VF_VENDOR);
 +              return true;
        }
  
 -      void OnRequest(Request& request)
 +      void CloseSession()
        {
 -              if (strcmp("GET_SSL_CERT", request.id) == 0)
 +              if (sess)
                {
 -                      SocketCertificateRequest& req = static_cast<SocketCertificateRequest&>(request);
 -                      int fd = req.sock->GetFd();
 -                      issl_session* session = &sessions[fd];
 -
 -                      req.cert = session->cert;
 +                      SSL_shutdown(sess);
 +                      SSL_free(sess);
                }
 +              sess = NULL;
 +              certificate = NULL;
 +              status = ISSL_NONE;
 +              errno = EIO;
        }
  
 -      void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
 +      void VerifyCertificate()
        {
 -              int fd = user->GetFd();
 -
 -              issl_session* session = &sessions[fd];
 -
 -              session->sess = SSL_new(ctx);
 -              session->status = ISSL_NONE;
 -              session->outbound = false;
 -              session->cert = NULL;
 +              X509* cert;
 +              ssl_cert* certinfo = new ssl_cert;
 +              this->certificate = certinfo;
 +              unsigned int n;
 +              unsigned char md[EVP_MAX_MD_SIZE];
  
 -              if (session->sess == NULL)
 -                      return;
 +              cert = SSL_get_peer_certificate(sess);
  
 -              if (SSL_set_fd(session->sess, fd) == 0)
 +              if (!cert)
                {
 -                      ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
 +                      certinfo->error = "Could not get peer certificate: "+std::string(get_error());
                        return;
                }
  
 -              Handshake(user, session);
 -      }
 -
 -      void OnStreamSocketConnect(StreamSocket* user)
 -      {
 -              int fd = user->GetFd();
 -              /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
 -              if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() -1))
 -                      return;
 +              certinfo->invalid = (SSL_get_verify_result(sess) != X509_V_OK);
  
 -              issl_session* session = &sessions[fd];
 +              if (!SelfSigned)
 +              {
 +                      certinfo->unknownsigner = false;
 +                      certinfo->trusted = true;
 +              }
 +              else
 +              {
 +                      certinfo->unknownsigner = true;
 +                      certinfo->trusted = false;
 +              }
  
-               certinfo->dn = X509_NAME_oneline(X509_get_subject_name(cert),0,0);
-               certinfo->issuer = X509_NAME_oneline(X509_get_issuer_name(cert),0,0);
 -              session->sess = SSL_new(clictx);
 -              session->status = ISSL_NONE;
 -              session->outbound = true;
++              char buf[512];
++              X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
++              certinfo->dn = buf;
++              X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf));
++              certinfo->issuer = buf;
  
 -              if (session->sess == NULL)
 -                      return;
 +              if (!X509_digest(cert, profile->GetDigest(), md, &n))
 +              {
 +                      certinfo->error = "Out of memory generating fingerprint";
 +              }
 +              else
 +              {
 +                      certinfo->fingerprint = BinToHex(md, n);
 +              }
  
 -              if (SSL_set_fd(session->sess, fd) == 0)
 +              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))
                {
 -                      ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
 -                      return;
 +                      certinfo->error = "Not activated, or expired certificate";
                }
  
 -              Handshake(user, session);
 +              X509_free(cert);
        }
  
 -      void OnStreamSocketClose(StreamSocket* user)
 + public:
 +      OpenSSLIOHook(IOHookProvider* hookprov, StreamSocket* sock, bool is_outbound, SSL* session, const reference<OpenSSL::Profile>& sslprofile)
 +              : SSLIOHook(hookprov)
 +              , sess(session)
 +              , status(ISSL_NONE)
 +              , outbound(is_outbound)
 +              , data_to_write(false)
 +              , profile(sslprofile)
        {
 -              int fd = user->GetFd();
 -              /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
 -              if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1))
 +              if (sess == NULL)
                        return;
 +              if (SSL_set_fd(sess, sock->GetFd()) == 0)
 +                      throw ModuleException("Can't set fd with SSL_set_fd: " + ConvToStr(sock->GetFd()));
  
 -              CloseSession(&sessions[fd]);
 +              sock->AddIOHook(this);
 +              Handshake(sock);
        }
  
 -      int OnStreamSocketRead(StreamSocket* user, std::string& recvq)
 +      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 */
 -              if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1))
 -                      return -1;
 -
 -              issl_session* session = &sessions[fd];
 +              CloseSession();
 +      }
  
 -              if (!session->sess)
 +      int OnStreamSocketRead(StreamSocket* user, std::string& recvq) CXX11_OVERRIDE
 +      {
 +              if (!sess)
                {
 -                      CloseSession(session);
 +                      CloseSession();
                        return -1;
                }
  
 -              if (session->status == ISSL_HANDSHAKING)
 +              if (status == ISSL_HANDSHAKING)
                {
                        // The handshake isn't finished and it wants to read, try to finish it.
 -                      if (!Handshake(user, session))
 +                      if (!Handshake(user))
                        {
                                // Couldn't resume handshake.
 -                              if (session->status == ISSL_NONE)
 +                              if (status == ISSL_NONE)
                                        return -1;
                                return 0;
                        }
                }
  
 -              // If we resumed the handshake then session->status will be ISSL_OPEN
 +              // If we resumed the handshake then this->status will be ISSL_OPEN
  
 -              if (session->status == ISSL_OPEN)
 +              if (status == ISSL_OPEN)
                {
                        char* buffer = ServerInstance->GetReadBuffer();
                        size_t bufsiz = ServerInstance->Config->NetBufferSize;
 -                      int ret = SSL_read(session->sess, buffer, bufsiz);
 +                      int ret = SSL_read(sess, buffer, bufsiz);
  
                        if (ret > 0)
                        {
                                recvq.append(buffer, ret);
 -                              if (session->data_to_write)
 -                                      ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_SINGLE_WRITE);
 +                              if (data_to_write)
 +                                      SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_SINGLE_WRITE);
                                return 1;
                        }
                        else if (ret == 0)
                        {
                                // Client closed connection.
 -                              CloseSession(session);
 +                              CloseSession();
                                user->SetError("Connection closed");
                                return -1;
                        }
                        else if (ret < 0)
                        {
 -                              int err = SSL_get_error(session->sess, ret);
 +                              int err = SSL_get_error(sess, ret);
  
                                if (err == SSL_ERROR_WANT_READ)
                                {
 -                                      ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ);
 +                                      SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ);
                                        return 0;
                                }
                                else if (err == SSL_ERROR_WANT_WRITE)
                                {
 -                                      ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
 +                                      SocketEngine::ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
                                        return 0;
                                }
                                else
                                {
 -                                      CloseSession(session);
 +                                      CloseSession();
                                        return -1;
                                }
                        }
                return 0;
        }
  
 -      int OnStreamSocketWrite(StreamSocket* user, std::string& buffer)
 +      int OnStreamSocketWrite(StreamSocket* user, std::string& buffer) CXX11_OVERRIDE
        {
 -              int fd = user->GetFd();
 -
 -              issl_session* session = &sessions[fd];
 -
 -              if (!session->sess)
 +              if (!sess)
                {
 -                      CloseSession(session);
 +                      CloseSession();
                        return -1;
                }
  
 -              session->data_to_write = true;
 +              data_to_write = true;
  
 -              if (session->status == ISSL_HANDSHAKING)
 +              if (status == ISSL_HANDSHAKING)
                {
 -                      if (!Handshake(user, session))
 +                      if (!Handshake(user))
                        {
                                // Couldn't resume handshake.
 -                              if (session->status == ISSL_NONE)
 +                              if (status == ISSL_NONE)
                                        return -1;
                                return 0;
                        }
                }
  
 -              if (session->status == ISSL_OPEN)
 +              if (status == ISSL_OPEN)
                {
 -                      int ret = SSL_write(session->sess, buffer.data(), buffer.size());
 +                      int ret = SSL_write(sess, buffer.data(), buffer.size());
                        if (ret == (int)buffer.length())
                        {
 -                              session->data_to_write = false;
 -                              ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
 +                              data_to_write = false;
 +                              SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
                                return 1;
                        }
                        else if (ret > 0)
                        {
                                buffer = buffer.substr(ret);
 -                              ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
 +                              SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
                                return 0;
                        }
                        else if (ret == 0)
                        {
 -                              CloseSession(session);
 +                              CloseSession();
                                return -1;
                        }
                        else if (ret < 0)
                        {
 -                              int err = SSL_get_error(session->sess, ret);
 +                              int err = SSL_get_error(sess, ret);
  
                                if (err == SSL_ERROR_WANT_WRITE)
                                {
 -                                      ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
 +                                      SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
                                        return 0;
                                }
                                else if (err == SSL_ERROR_WANT_READ)
                                {
 -                                      ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ);
 +                                      SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ);
                                        return 0;
                                }
                                else
                                {
 -                                      CloseSession(session);
 +                                      CloseSession();
                                        return -1;
                                }
                        }
                return 0;
        }
  
 -      bool Handshake(StreamSocket* user, issl_session* session)
 +      void TellCiphersAndFingerprint(LocalUser* user)
        {
 -              int ret;
 +              if (sess)
 +              {
 +                      std::string text = "*** You are connected using SSL cipher '" + std::string(SSL_get_cipher(sess)) + "'";
 +                      const std::string& fingerprint = certificate->fingerprint;
 +                      if (!fingerprint.empty())
 +                              text += " and your SSL fingerprint is " + fingerprint;
  
 -              if (session->outbound)
 -                      ret = SSL_connect(session->sess);
 -              else
 -                      ret = SSL_accept(session->sess);
 +                      user->WriteNotice(text);
 +              }
 +      }
 +};
  
 -              if (ret < 0)
 +class OpenSSLIOHookProvider : public refcountbase, public IOHookProvider
 +{
 +      reference<OpenSSL::Profile> profile;
 +
 + public:
 +      OpenSSLIOHookProvider(Module* mod, reference<OpenSSL::Profile>& prof)
 +              : IOHookProvider(mod, "ssl/" + prof->GetName(), IOHookProvider::IOH_SSL)
 +              , profile(prof)
 +      {
 +              ServerInstance->Modules->AddService(*this);
 +      }
 +
 +      ~OpenSSLIOHookProvider()
 +      {
 +              ServerInstance->Modules->DelService(*this);
 +      }
 +
 +      void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
 +      {
 +              new OpenSSLIOHook(this, sock, false, profile->CreateServerSession(), profile);
 +      }
 +
 +      void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
 +      {
 +              new OpenSSLIOHook(this, sock, true, profile->CreateClientSession(), profile);
 +      }
 +};
 +
 +class ModuleSSLOpenSSL : public Module
 +{
 +      typedef std::vector<reference<OpenSSLIOHookProvider> > ProfileList;
 +
 +      ProfileList profiles;
 +
 +      void ReadProfiles()
 +      {
 +              ProfileList newprofiles;
 +              ConfigTagList tags = ServerInstance->Config->ConfTags("sslprofile");
 +              if (tags.first == tags.second)
                {
 -                      int err = SSL_get_error(session->sess, ret);
 +                      // Create a default profile named "openssl"
 +                      const std::string defname = "openssl";
 +                      ConfigTag* tag = ServerInstance->Config->ConfValue(defname);
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No <sslprofile> tags found, using settings from the <openssl> tag");
  
 -                      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)
 +                      try
                        {
 -                              ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
 -                              session->status = ISSL_HANDSHAKING;
 -                              return true;
 +                              reference<OpenSSL::Profile> profile(new OpenSSL::Profile(defname, tag));
 +                              newprofiles.push_back(new OpenSSLIOHookProvider(this, profile));
                        }
 -                      else
 +                      catch (OpenSSL::Exception& ex)
                        {
 -                              CloseSession(session);
 +                              throw ModuleException("Error while initializing the default SSL profile - " + ex.GetReason());
                        }
 -
 -                      return false;
                }
 -              else if (ret > 0)
 +
 +              for (ConfigIter i = tags.first; i != tags.second; ++i)
                {
 -                      // Handshake complete.
 -                      VerifyCertificate(session, user);
 +                      ConfigTag* tag = i->second;
 +                      if (tag->getString("provider") != "openssl")
 +                              continue;
  
 -                      session->status = ISSL_OPEN;
 +                      std::string name = tag->getString("name");
 +                      if (name.empty())
 +                      {
 +                              ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring <sslprofile> tag without name at " + tag->getTagLocation());
 +                              continue;
 +                      }
  
 -                      ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
 +                      reference<OpenSSL::Profile> profile;
 +                      try
 +                      {
 +                              profile = new OpenSSL::Profile(name, tag);
 +                      }
 +                      catch (CoreException& ex)
 +                      {
 +                              throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason());
 +                      }
  
 -                      return true;
 -              }
 -              else if (ret == 0)
 -              {
 -                      CloseSession(session);
 -                      return true;
 +                      newprofiles.push_back(new OpenSSLIOHookProvider(this, profile));
                }
  
 -              return true;
 +              profiles.swap(newprofiles);
        }
  
 -      void CloseSession(issl_session* session)
 + public:
 +      ModuleSSLOpenSSL()
        {
 -              if (session->sess)
 -              {
 -                      SSL_shutdown(session->sess);
 -                      SSL_free(session->sess);
 -              }
 -
 -              session->sess = NULL;
 -              session->status = ISSL_NONE;
 -              errno = EIO;
 +              // Initialize OpenSSL
 +              SSL_library_init();
 +              SSL_load_error_strings();
        }
  
 -      void VerifyCertificate(issl_session* session, StreamSocket* user)
 +      void init() CXX11_OVERRIDE
        {
 -              if (!session->sess || !user)
 -                      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();
 -
 -              cert = SSL_get_peer_certificate((SSL*)session->sess);
 +              ReadProfiles();
 +      }
  
 -              if (!cert)
 -              {
 -                      certinfo->error = "Could not get peer certificate: "+std::string(get_error());
 +      void OnModuleRehash(User* user, const std::string &param) CXX11_OVERRIDE
 +      {
 +              if (param != "ssl")
                        return;
 -              }
 -
 -              certinfo->invalid = (SSL_get_verify_result(session->sess) != X509_V_OK);
  
 -              if (!SelfSigned)
 +              try
                {
 -                      certinfo->unknownsigner = false;
 -                      certinfo->trusted = true;
 +                      ReadProfiles();
                }
 -              else
 +              catch (ModuleException& ex)
                {
 -                      certinfo->unknownsigner = true;
 -                      certinfo->trusted = false;
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, ex.GetReason() + " Not applying settings.");
                }
 +      }
  
 -              char buf[512];
 -              X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
 -              certinfo->dn = buf;
 -              X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf));
 -              certinfo->issuer = buf;
 +      void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
 +      {
 +              IOHook* hook = user->eh.GetIOHook();
 +              if (hook && hook->prov->creator == this)
 +                      static_cast<OpenSSLIOHook*>(hook)->TellCiphersAndFingerprint(user);
 +      }
  
 -              if (!X509_digest(cert, digest, md, &n))
 -              {
 -                      certinfo->error = "Out of memory generating fingerprint";
 -              }
 -              else
 +      void OnCleanup(int target_type, void* item) CXX11_OVERRIDE
 +      {
 +              if (target_type == TYPE_USER)
                {
 -                      certinfo->fingerprint = irc::hex(md, n);
 -              }
 +                      LocalUser* user = IS_LOCAL((User*)item);
  
 -              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))
 -              {
 -                      certinfo->error = "Not activated, or expired certificate";
 +                      if (user && user->eh.GetIOHook() && user->eh.GetIOHook()->prov->creator == 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() 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));
 -
 -      //
 -      // XXX: Remove this line, it causes valgrind warnings...
 -      //
 -      // MD_update(&m, buf, j);
 -      //
 -      //
 -      // ... ONLY JOKING! :-)
 -      //
 -
 -      return 0;
 -}
 -
  MODULE_INIT(ModuleSSLOpenSSL)
index b0be8d665d5d4d56cdfa6df581965cedc1578c70,2e25925410d6ef67d03f2a80b9b9da3da1d8999d..1a123e580082907f86a85e9295aca2a39c25f430
@@@ -23,7 -23,9 +23,7 @@@
  
  
  #include "inspircd.h"
 -#include "u_listmode.h"
 -
 -/* $ModDesc: Allows an extended ban (+b) syntax redirecting banned users to another channel */
 +#include "listmode.h"
  
  /* Originally written by Om, January 2009
   */
@@@ -41,20 -43,18 +41,20 @@@ class BanRedirectEntr
  };
  
  typedef std::vector<BanRedirectEntry> BanRedirectList;
 -typedef std::deque<std::string> StringDeque;
  
  class BanRedirect : public ModeWatcher
  {
 +      ChanModeReference ban;
   public:
        SimpleExtItem<BanRedirectList> extItem;
 -      BanRedirect(Module* parent) : ModeWatcher(parent, 'b', MODETYPE_CHANNEL),
 -              extItem("banredirect", parent)
 +      BanRedirect(Module* parent)
 +              : ModeWatcher(parent, "ban", MODETYPE_CHANNEL)
 +              , ban(parent, "ban")
 +              , extItem("banredirect", parent)
        {
        }
  
 -      bool BeforeMode(User* source, User* dest, Channel* channel, std::string &param, bool adding, ModeType type)
 +      bool BeforeMode(User* source, User* dest, Channel* channel, std::string &param, bool adding)
        {
                /* nick!ident@host -> nick!ident@host
                 * nick!ident@host#chan -> nick!ident@host#chan
                 * nick#chan -> nick!*@*#chan
                 */
  
 -              if(channel && (type == MODETYPE_CHANNEL) && param.length())
 +              if ((channel) && !param.empty())
                {
                        BanRedirectList* redirects;
  
                        std::string mask[4];
                        enum { NICK, IDENT, HOST, CHAN } current = NICK;
                        std::string::iterator start_pos = param.begin();
 -                      long maxbans = channel->GetMaxBans();
  
                        if (param.length() >= 2 && param[1] == ':')
                                return true;
  
 -                      if(adding && (channel->bans.size() > static_cast<unsigned>(maxbans)))
 +                      ListModeBase* banlm = static_cast<ListModeBase*>(*ban);
 +                      unsigned int maxbans = banlm->GetLimit(channel);
 +                      ListModeBase::ModeList* list = banlm->GetList(channel);
 +                      if ((list) && (adding) && (maxbans <= list->size()))
                        {
 -                              source->WriteNumeric(478, "%s %s :Channel ban list for %s is full (maximum entries for this channel is %ld)", source->nick.c_str(), channel->name.c_str(), channel->name.c_str(), maxbans);
 +                              source->WriteNumeric(ERR_BANLISTFULL, "%s :Channel ban list for %s is full (maximum entries for this channel is %u)", channel->name.c_str(), channel->name.c_str(), maxbans);
                                return false;
                        }
  
                                switch(*curr)
                                {
                                        case '!':
 +                                              if (current != NICK)
 +                                                      break;
                                                mask[current].assign(start_pos, curr);
                                                current = IDENT;
                                                start_pos = curr+1;
                                                break;
                                        case '@':
 +                                              if (current != IDENT)
 +                                                      break;
                                                mask[current].assign(start_pos, curr);
                                                current = HOST;
                                                start_pos = curr+1;
                                                break;
                                        case '#':
 -                                              /* bug #921: don't barf when redirecting to ## channels */
 -                                              if (current != CHAN)
 -                                              {
 -                                                      mask[current].assign(start_pos, curr);
 -                                                      current = CHAN;
 -                                                      start_pos = curr;
 -                                              }
 +                                              if (current == CHAN)
 +                                                      break;
 +                                              mask[current].assign(start_pos, curr);
 +                                              current = CHAN;
 +                                              start_pos = curr;
                                                break;
                                }
                        }
                        {
                                if (adding && IS_LOCAL(source))
                                {
 -                                      if (!ServerInstance->IsChannel(mask[CHAN].c_str(),  ServerInstance->Config->Limits.ChanMax))
 +                                      if (!ServerInstance->IsChannel(mask[CHAN]))
                                        {
 -                                              source->WriteNumeric(403, "%s %s :Invalid channel name in redirection (%s)", source->nick.c_str(), channel->name.c_str(), mask[CHAN].c_str());
 +                                              source->WriteNumeric(ERR_NOSUCHCHANNEL, "%s :Invalid channel name in redirection (%s)", channel->name.c_str(), mask[CHAN].c_str());
                                                return false;
                                        }
  
                                        Channel *c = ServerInstance->FindChan(mask[CHAN]);
                                        if (!c)
                                        {
 -                                              source->WriteNumeric(690, "%s :Target channel %s must exist to be set as a redirect.",source->nick.c_str(),mask[CHAN].c_str());
 +                                              source->WriteNumeric(690, ":Target channel %s must exist to be set as a redirect.", mask[CHAN].c_str());
                                                return false;
                                        }
                                        else if (adding && c->GetPrefixValue(source) < OP_VALUE)
                                        {
 -                                              source->WriteNumeric(690, "%s :You must be opped on %s to set it as a redirect.",source->nick.c_str(), mask[CHAN].c_str());
 +                                              source->WriteNumeric(690, ":You must be opped on %s to set it as a redirect.", mask[CHAN].c_str());
                                                return false;
                                        }
  
                                        if (assign(channel->name) == mask[CHAN])
                                        {
 -                                              source->WriteNumeric(690, "%s %s :You cannot set a ban redirection to the channel the ban is on", source->nick.c_str(), channel->name.c_str());
 +                                              source->WriteNumeric(690, "%s :You cannot set a ban redirection to the channel the ban is on", channel->name.c_str());
                                                return false;
                                        }
                                }
@@@ -217,19 -213,26 +217,19 @@@ class ModuleBanRedirect : public Modul
  {
        BanRedirect re;
        bool nofollow;
 +      ChanModeReference limitmode;
 +      ChanModeReference redirectmode;
  
   public:
        ModuleBanRedirect()
 -      : re(this)
 -      {
 -              nofollow = false;
 -      }
 -
 -
 -      void init()
 +              : re(this)
 +              , nofollow(false)
 +              , limitmode(this, "limit")
 +              , redirectmode(this, "redirect")
        {
 -              if(!ServerInstance->Modes->AddModeWatcher(&re))
 -                      throw ModuleException("Could not add mode watcher");
 -
 -              ServerInstance->Modules->AddService(re.extItem);
 -              Implementation list[] = { I_OnUserPreJoin };
 -              ServerInstance->Modules->Attach(list, this, sizeof(list)/sizeof(Implementation));
        }
  
 -      virtual void OnCleanup(int target_type, void* item)
 +      void OnCleanup(int target_type, void* item) CXX11_OVERRIDE
        {
                if(target_type == TYPE_CHANNEL)
                {
                        if(redirects)
                        {
                                irc::modestacker modestack(false);
 -                              StringDeque stackresult;
 -                              std::vector<std::string> mode_junk;
 -                              mode_junk.push_back(chan->name);
  
                                for(BanRedirectList::iterator i = redirects->begin(); i != redirects->end(); i++)
                                {
                                        modestack.Push('b', i->banmask);
                                }
  
 -                              while(modestack.GetStackedLine(stackresult))
 +                              std::vector<std::string> stackresult;
 +                              stackresult.push_back(chan->name);
 +                              while (modestack.GetStackedLine(stackresult))
                                {
 -                                      mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end());
 -                                      ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient);
 -                                      mode_junk.erase(mode_junk.begin() + 1, mode_junk.end());
 +                                      ServerInstance->Modes->Process(stackresult, ServerInstance->FakeClient, ModeParser::MODE_LOCALONLY);
 +                                      stackresult.erase(stackresult.begin() + 1, stackresult.end());
                                }
                        }
                }
        }
  
 -      virtual ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven)
 +      ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
        {
                if (chan)
                {
                                                std::string destlimit;
  
                                                if (destchan)
 -                                                      destlimit = destchan->GetModeParameter('l');
 +                                                      destlimit = destchan->GetModeParameter(limitmode);
  
 -                                              if(destchan && ServerInstance->Modules->Find("m_redirect.so") && destchan->IsModeSet('L') && !destlimit.empty() && (destchan->GetUserCounter() >= atoi(destlimit.c_str())))
 +                                              if(destchan && destchan->IsModeSet(redirectmode) && !destlimit.empty() && (destchan->GetUserCounter() >= atoi(destlimit.c_str())))
                                                {
 -                                                      user->WriteNumeric(474, "%s %s :Cannot join channel (You are banned)", user->nick.c_str(), chan->name.c_str());
 +                                                      user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s :Cannot join channel (You are banned)", chan->name.c_str());
                                                        return MOD_RES_DENY;
                                                }
                                                else
                                                {
 -                                                      user->WriteNumeric(474, "%s %s :Cannot join channel (You are banned)", user->nick.c_str(), chan->name.c_str());
 -                                                      user->WriteNumeric(470, "%s %s %s :You are banned from this channel, so you are automatically transferred to the redirected channel.", user->nick.c_str(), chan->name.c_str(), redir->targetchan.c_str());
 +                                                      user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s :Cannot join channel (You are banned)", chan->name.c_str());
-                                                       user->WriteNumeric(470, "%s %s :You are banned from this channel, so you are automatically transfered to the redirected channel.", chan->name.c_str(), redir->targetchan.c_str());
++                                                      user->WriteNumeric(470, "%s %s :You are banned from this channel, so you are automatically transferred to the redirected channel.", chan->name.c_str(), redir->targetchan.c_str());
                                                        nofollow = true;
 -                                                      Channel::JoinUser(user, redir->targetchan.c_str(), false, "", false, ServerInstance->Time());
 +                                                      Channel::JoinUser(user, redir->targetchan);
                                                        nofollow = false;
                                                        return MOD_RES_DENY;
                                                }
                return MOD_RES_PASSTHRU;
        }
  
 -      virtual ~ModuleBanRedirect()
 -      {
 -              /* XXX is this the best place to do this? */
 -              if (!ServerInstance->Modes->DelModeWatcher(&re))
 -                      ServerInstance->Logs->Log("m_banredirect.so", DEBUG, "Failed to delete modewatcher!");
 -      }
 -
 -      virtual Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Allows an extended ban (+b) syntax redirecting banned users to another channel", VF_COMMON|VF_VENDOR);
        }
index b3c664cf12d295ec7332c9e581195ff887b4d03b,9e0ec7705669708473bf6cc8fbc8c5e01a7cc3b7..8ee8472e676fb28c2d81e34710a1678cacb6954a
@@@ -22,6 -22,8 +22,6 @@@
   */
  
  
 -/* $ModDesc: Allows global loading of a module. */
 -
  #include "inspircd.h"
  
  /** Handle /GLOADMODULE
@@@ -33,6 -35,7 +33,6 @@@ class CommandGloadmodule : public Comma
        {
                flags_needed = 'o';
                syntax = "<modulename> [servermask]";
 -              TRANSLATE3(TR_TEXT, TR_TEXT, TR_END);
        }
  
        CmdResult Handle (const std::vector<std::string> &parameters, User *user)
                        if (ServerInstance->Modules->Load(parameters[0].c_str()))
                        {
                                ServerInstance->SNO->WriteToSnoMask('a', "NEW MODULE '%s' GLOBALLY LOADED BY '%s'",parameters[0].c_str(), user->nick.c_str());
 -                              user->WriteNumeric(975, "%s %s :Module successfully loaded.",user->nick.c_str(), parameters[0].c_str());
 +                              user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module successfully loaded.", parameters[0].c_str());
                        }
                        else
                        {
 -                              user->WriteNumeric(974, "%s %s :%s",user->nick.c_str(), parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
 +                              user->WriteNumeric(ERR_CANTLOADMODULE, "%s :%s", parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
                        }
                }
                else
@@@ -76,13 -79,6 +76,13 @@@ class CommandGunloadmodule : public Com
  
        CmdResult Handle (const std::vector<std::string> &parameters, User *user)
        {
 +              if (!ServerInstance->Config->ConfValue("security")->getBool("allowcoreunload") &&
 +                      InspIRCd::Match(parameters[0], "core_*.so", ascii_case_insensitive_map))
 +              {
 +                      user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :You cannot unload core commands!", parameters[0].c_str());
 +                      return CMD_FAILURE;
 +              }
 +
                std::string servername = parameters.size() > 1 ? parameters[1] : "*";
  
                if (InspIRCd::Match(ServerInstance->Config->ServerName.c_str(), servername))
                                }
                                else
                                {
 -                                      user->WriteNumeric(972, "%s %s :%s",user->nick.c_str(), parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
 +                                      user->WriteNumeric(ERR_CANTUNLOADMODULE, "%s :%s", parameters[0].c_str(), ServerInstance->Modules->LastError().c_str());
                                }
                        }
                        else
 -                              user->SendText(":%s 972 %s %s :No such module", ServerInstance->Config->ServerName.c_str(), user->nick.c_str(), parameters[0].c_str());
 +                              user->SendText(":%s %03d %s %s :No such module", ServerInstance->Config->ServerName.c_str(), ERR_CANTUNLOADMODULE, user->nick.c_str(), parameters[0].c_str());
                }
                else
                        ServerInstance->SNO->WriteToSnoMask('a', "MODULE '%s' GLOBAL UNLOAD BY '%s' (not unloaded here)",parameters[0].c_str(), user->nick.c_str());
@@@ -129,8 -125,8 +129,8 @@@ class GReloadModuleWorker : public Hand
                ServerInstance->SNO->WriteToSnoMask('a', "MODULE '%s' GLOBALLY RELOADED BY '%s'%s", name.c_str(), nick.c_str(), result ? "" : " (failed here)");
                User* user = ServerInstance->FindNick(uid);
                if (user)
 -                      user->WriteNumeric(975, "%s %s :Module %ssuccessfully reloaded.",
 -                              user->nick.c_str(), name.c_str(), result ? "" : "un");
 +                      user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module %ssuccessfully reloaded.",
 +                              name.c_str(), result ? "" : "un");
                ServerInstance->GlobalCulls.AddItem(this);
        }
  };
@@@ -153,10 -149,15 +153,15 @@@ class CommandGreloadmodule : public Com
                {
                        Module* m = ServerInstance->Modules->Find(parameters[0]);
                        if (m)
-                               ServerInstance->Modules->Reload(m, new GReloadModuleWorker(user->nick, user->uuid, parameters[0]));
+                       {
+                               GReloadModuleWorker* worker = NULL;
+                               if (m != creator)
+                                       worker = new GReloadModuleWorker(user->nick, user->uuid, parameters[0]);
+                               ServerInstance->Modules->Reload(m, worker);
+                       }
                        else
                        {
 -                              user->WriteNumeric(975, "%s %s :Could not find module by that name", user->nick.c_str(), parameters[0].c_str());
 +                              user->WriteNumeric(RPL_LOADEDMODULE, "%s :Could not find module by that name", parameters[0].c_str());
                                return CMD_FAILURE;
                        }
                }
@@@ -184,10 -185,22 +189,10 @@@ class ModuleGlobalLoad : public Modul
        {
        }
  
 -      void init()
 -      {
 -              ServerInstance->Modules->AddService(cmd1);
 -              ServerInstance->Modules->AddService(cmd2);
 -              ServerInstance->Modules->AddService(cmd3);
 -      }
 -
 -      ~ModuleGlobalLoad()
 -      {
 -      }
 -
 -      Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Allows global loading of a module.", VF_COMMON | VF_VENDOR);
        }
  };
  
  MODULE_INIT(ModuleGlobalLoad)
 -
diff --combined src/modules/m_httpd.cpp
index d0291b8cc0f98c251db1cec7ac0f16ebcaa05358,2a430d9674fb9669825604d91db5adf6355b6448..053f4b924b3d30b2ecd784fa6d1ec122be8e0ade
  
  
  #include "inspircd.h"
 -#include "httpd.h"
 -
 -/* $ModDesc: Provides HTTP serving facilities to modules */
 -/* $ModDep: httpd.h */
 +#include "iohook.h"
 +#include "modules/httpd.h"
  
  class ModuleHttpServer;
  
@@@ -65,8 -67,9 +65,8 @@@ class HttpServerSocket : public Buffere
        {
                InternalState = HTTP_SERVE_WAIT_REQUEST;
  
 -              FOREACH_MOD(I_OnHookIO, OnHookIO(this, via));
 -              if (GetIOHook())
 -                      GetIOHook()->OnStreamSocketAccept(this, client, server);
 +              if (via->iohookprov)
 +                      via->iohookprov->OnAccept(this, client, server);
        }
  
        ~HttpServerSocket()
@@@ -74,7 -77,7 +74,7 @@@
                sockets.erase(this);
        }
  
 -      virtual void OnError(BufferedSocketError)
 +      void OnError(BufferedSocketError) CXX11_OVERRIDE
        {
                ServerInstance->GlobalCulls.AddItem(this);
        }
                        case 300:
                                return "MULTIPLE CHOICES";
                        case 301:
-                               return "MOVED PERMENANTLY";
+                               return "MOVED PERMANENTLY";
                        case 302:
                                return "FOUND";
                        case 303:
  
                        if (reqbuffer.length() >= 8192)
                        {
 -                              ServerInstance->Logs->Log("m_httpd",DEBUG, "m_httpd dropped connection due to an oversized request buffer");
 +                              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "m_httpd dropped connection due to an oversized request buffer");
                                reqbuffer.clear();
                                SetError("Buffer");
                        }
        }
  };
  
 +class HTTPdAPIImpl : public HTTPdAPIBase
 +{
 + public:
 +      HTTPdAPIImpl(Module* parent)
 +              : HTTPdAPIBase(parent)
 +      {
 +      }
 +
 +      void SendResponse(HTTPDocumentResponse& resp) CXX11_OVERRIDE
 +      {
 +              claimed = true;
 +              resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers);
 +      }
 +};
 +
  class ModuleHttpServer : public Module
  {
 +      std::vector<HttpServerSocket *> httpsocks;
 +      HTTPdAPIImpl APIImpl;
        unsigned int timeoutsec;
  
   public:
 +      ModuleHttpServer()
 +              : APIImpl(this)
 +      {
 +      }
  
 -      void init()
 +      void init() CXX11_OVERRIDE
        {
                HttpModule = this;
 -              Implementation eventlist[] = { I_OnAcceptConnection, I_OnBackgroundTimer, I_OnRehash };
 -              ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
 -              OnRehash(NULL);
        }
  
 -      void OnRehash(User* user)
 +      void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("httpd");
                timeoutsec = tag->getInt("timeout");
        }
  
 -      void OnRequest(Request& request)
 -      {
 -              if (strcmp(request.id, "HTTP-DOC") != 0)
 -                      return;
 -              HTTPDocumentResponse& resp = static_cast<HTTPDocumentResponse&>(request);
 -              claimed = true;
 -              resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers);
 -      }
 -
 -      ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
 +      ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
        {
                if (from->bind_tag->getString("type") != "httpd")
                        return MOD_RES_PASSTHRU;
                return MOD_RES_ALLOW;
        }
  
 -      void OnBackgroundTimer(time_t curtime)
 +      void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE
        {
                if (!timeoutsec)
                        return;
                }
        }
  
 -      CullResult cull()
 +      CullResult cull() CXX11_OVERRIDE
        {
                std::set<HttpServerSocket*> local;
                local.swap(sockets);
                return Module::cull();
        }
  
 -      virtual Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides HTTP serving facilities to modules", VF_VENDOR);
        }
index 4fb4ae942bbcbe501f5c80f34aa8d843198bd2a8,62ddb6c67b160d187214431d4372f440d332750f..3bf4c8434352e985d3d39dda126489ce8ed6d628
   * Originally by Chernov-Phoenix Alexey (Phoenix@RusNet) mailto:phoenix /email address separator/ pravmail.ru
   */
  
 -/* $ModDesc: Gives opers cmode +y which provides a staff prefix. */
 -
  #include "inspircd.h"
  
  #define OPERPREFIX_VALUE 1000000
  
 -class OperPrefixMode : public ModeHandler
 +class OperPrefixMode : public PrefixMode
  {
        public:
 -              OperPrefixMode(Module* Creator) : ModeHandler(Creator, "operprefix", 'y', PARAM_ALWAYS, MODETYPE_CHANNEL)
 +              OperPrefixMode(Module* Creator) : PrefixMode(Creator, "operprefix", 'y')
                {
                        std::string pfx = ServerInstance->Config->ConfValue("operprefix")->getString("prefix", "!");
 -                      list = true;
                        prefix = pfx.empty() ? '!' : pfx[0];
 -                      levelrequired = OPERPREFIX_VALUE;
 -                      m_paramtype = TR_NICK;
 -              }
 -
 -              unsigned int GetPrefixRank()
 -              {
 -                      return OPERPREFIX_VALUE;
 +                      levelrequired = INT_MAX;
 +                      prefixrank = OPERPREFIX_VALUE;
                }
 -
 -              ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
 -              {
 -                      if (IS_SERVER(source) || ServerInstance->ULine(source->server))
 -                              return MODEACTION_ALLOW;
 -                      else
 -                      {
 -                              if (channel)
 -                                      source->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :Only servers are permitted to change channel mode '%c'", source->nick.c_str(), channel->name.c_str(), 'y');
 -                              return MODEACTION_DENY;
 -                      }
 -              }
 -
 -              bool NeedsOper() { return true; }
  };
  
  class ModuleOperPrefixMode;
  class HideOperWatcher : public ModeWatcher
  {
        ModuleOperPrefixMode* parentmod;
 +
   public:
 -      HideOperWatcher(ModuleOperPrefixMode* parent) : ModeWatcher((Module*) parent, 'H', MODETYPE_USER), parentmod(parent) {}
 -      void AfterMode(User* source, User* dest, Channel* channel, const std::string &parameter, bool adding, ModeType type);
 +      HideOperWatcher(ModuleOperPrefixMode* parent);
 +      void AfterMode(User* source, User* dest, Channel* channel, const std::string &parameter, bool adding);
  };
  
  class ModuleOperPrefixMode : public Module
  {
 - private:
        OperPrefixMode opm;
 -      bool mw_added;
        HideOperWatcher hideoperwatcher;
 +      UserModeReference hideopermode;
 +
   public:
        ModuleOperPrefixMode()
 -              : opm(this), mw_added(false), hideoperwatcher(this)
 +              : opm(this), hideoperwatcher(this)
 +              , hideopermode(this, "hideoper")
        {
 -      }
 -
 -      void init()
 -      {
 -              ServerInstance->Modules->AddService(opm);
 -
 -              Implementation eventlist[] = { I_OnUserPreJoin, I_OnPostOper, I_OnLoadModule, I_OnUnloadModule };
 -              ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
 -
                /* To give clients a chance to learn about the new prefix we don't give +y to opers
                 * right now. That means if the module was loaded after opers have joined channels
                 * they need to rejoin them in order to get the oper prefix.
                 */
 -
 -              if (ServerInstance->Modules->Find("m_hideoper.so"))
 -                      mw_added = ServerInstance->Modes->AddModeWatcher(&hideoperwatcher);
        }
  
 -      ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string& privs, const std::string& keygiven)
 +      ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
        {
 -              /* The user may have the +H umode on himself, but +H does not necessarily correspond
 -               * to the +H of m_hideoper.
 -               * However we only add the modewatcher when m_hideoper is loaded, so these
 -               * conditions (mw_added and the user being +H) together mean the user is a hidden oper.
 -               */
 -
 -              if (IS_OPER(user) && (!mw_added || !user->IsModeSet('H')))
 +              if ((user->IsOper()) && (!user->IsModeSet(hideopermode)))
                        privs.push_back('y');
                return MOD_RES_PASSTHRU;
        }
        {
                std::vector<std::string> modechange;
                modechange.push_back("");
 -              modechange.push_back(add ? "+y" : "-y");
 +              modechange.push_back(add ? "+" : "-");
 +              modechange[1].push_back(opm.GetModeChar());
                modechange.push_back(user->nick);
                for (UCListIter v = user->chans.begin(); v != user->chans.end(); v++)
                {
 -                      modechange[0] = (*v)->name;
 -                      ServerInstance->SendGlobalMode(modechange, ServerInstance->FakeClient);
 +                      modechange[0] = (*v)->chan->name;
 +                      ServerInstance->Modes->Process(modechange, ServerInstance->FakeClient);
                }
        }
  
 -      void OnPostOper(User* user, const std::string& opername, const std::string& opertype)
 +      void OnPostOper(User* user, const std::string& opername, const std::string& opertype) CXX11_OVERRIDE
        {
 -              if (IS_LOCAL(user) && (!mw_added || !user->IsModeSet('H')))
 +              if (IS_LOCAL(user) && (!user->IsModeSet(hideopermode)))
                        SetOperPrefix(user, true);
        }
  
 -      void OnLoadModule(Module* mod)
 -      {
 -              if ((!mw_added) && (mod->ModuleSourceFile == "m_hideoper.so"))
 -                      mw_added = ServerInstance->Modes->AddModeWatcher(&hideoperwatcher);
 -      }
 -
 -      void OnUnloadModule(Module* mod)
 -      {
 -              if ((mw_added) && (mod->ModuleSourceFile == "m_hideoper.so") && (ServerInstance->Modes->DelModeWatcher(&hideoperwatcher)))
 -                      mw_added = false;
 -      }
 -
 -      ~ModuleOperPrefixMode()
 -      {
 -              if (mw_added)
 -                      ServerInstance->Modes->DelModeWatcher(&hideoperwatcher);
 -      }
 -
 -      Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Gives opers cmode +y which provides a staff prefix.", VF_VENDOR);
        }
        }
  };
  
 -void HideOperWatcher::AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding, ModeType type)
 +HideOperWatcher::HideOperWatcher(ModuleOperPrefixMode* parent)
 +      : ModeWatcher(parent, "hideoper", MODETYPE_USER)
 +      , parentmod(parent)
 +{
 +}
 +
 +void HideOperWatcher::AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding)
  {
-       if (IS_LOCAL(dest))
+       // If hideoper is being unset because the user is deopering, don't set +y
 -      if (IS_LOCAL(dest) && IS_OPER(dest))
++      if (IS_LOCAL(dest) && dest->IsOper())
                parentmod->SetOperPrefix(dest, !adding);
  }
  
diff --combined src/modules/m_samode.cpp
index 129ad3f7ba7c824f8639c496876f430883196722,ea2ae24ab3d525cc5fc56a156e703a8056f9f0c3..14f79aaf7e66427532fdf0bf01075bc4f3a9ca94
@@@ -20,6 -20,8 +20,6 @@@
   */
  
  
 -/* $ModDesc: Provides command SAMODE to allow opers to change modes on channels and users */
 -
  #include "inspircd.h"
  
  /** Handle /SAMODE
@@@ -37,14 -39,18 +37,23 @@@ class CommandSamode : public Comman
  
        CmdResult Handle (const std::vector<std::string>& parameters, User *user)
        {
 -
+               if (parameters[0].c_str()[0] != '#')
+               {
+                       User* target = ServerInstance->FindNickOnly(parameters[0]);
+                       if ((!target) || (target->registered != REG_ALL))
+                       {
+                               user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), parameters[0].c_str());
+                               return CMD_FAILURE;
+                       }
+               }
 +              User* target = ServerInstance->FindNick(parameters[0]);
 +              if ((target) && (target != user))
 +              {
 +                      if (!user->HasPrivPermission("users/samode-usermodes", true))
 +                              return CMD_FAILURE;
 +              }
                this->active = true;
 -              ServerInstance->Parser->CallHandler("MODE", parameters, user);
 +              ServerInstance->Modes->Process(parameters, user);
                if (ServerInstance->Modes->GetLastParse().length())
                        ServerInstance->SNO->WriteGlobalSno('a', user->nick + " used SAMODE: " +ServerInstance->Modes->GetLastParse());
                this->active = false;
@@@ -61,12 -67,22 +70,12 @@@ class ModuleSaMode : public Modul
        {
        }
  
 -      void init()
 -      {
 -              ServerInstance->Modules->AddService(cmd);
 -              ServerInstance->Modules->Attach(I_OnPreMode, this);
 -      }
 -
 -      ~ModuleSaMode()
 -      {
 -      }
 -
 -      Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides command SAMODE to allow opers to change modes on channels and users", VF_VENDOR);
        }
  
 -      ModResult OnPreMode(User* source,User* dest,Channel* channel, const std::vector<std::string>& parameters)
 +      ModResult OnPreMode(User* source,User* dest,Channel* channel, const std::vector<std::string>& parameters) CXX11_OVERRIDE
        {
                if (cmd.active)
                        return MOD_RES_ALLOW;
diff --combined src/modules/m_saquit.cpp
index 3050e70d68193a72e6b2bbfff78c88d7fce17f4d,909a026abe306cacd77663f041b25233990d7d6a..aa6aa01809f238bef392d11659576ca6b99c70fa
@@@ -21,6 -21,8 +21,6 @@@
  
  #include "inspircd.h"
  
 -/* $ModDesc: Provides support for an SAQUIT command, exits user with a reason */
 -
  /** Handle /SAQUIT
   */
  class CommandSaquit : public Command
        CommandSaquit(Module* Creator) : Command(Creator, "SAQUIT", 2, 2)
        {
                flags_needed = 'o'; Penalty = 0; syntax = "<nick> <reason>";
 -              TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
 +              TRANSLATE2(TR_NICK, TR_TEXT);
        }
  
        CmdResult Handle (const std::vector<std::string>& parameters, User *user)
        {
                User* dest = ServerInstance->FindNick(parameters[0]);
-               if ((dest) && (!IS_SERVER(dest)))
+               if ((dest) && (!IS_SERVER(dest)) && (dest->registered == REG_ALL))
                {
 -                      if (ServerInstance->ULine(dest->server))
 +                      if (dest->server->IsULine())
                        {
 -                              user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Cannot use an SA command on a u-lined client",user->nick.c_str());
 +                              user->WriteNumeric(ERR_NOPRIVILEGES, ":Cannot use an SA command on a u-lined client");
                                return CMD_FAILURE;
                        }
  
                        // Pass the command on, so the client's server can quit it properly.
                        if (!IS_LOCAL(dest))
                                return CMD_SUCCESS;
 -                      
 +
                        ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used SAQUIT to make "+dest->nick+" quit with a reason of "+parameters[1]);
  
                        ServerInstance->Users->QuitUser(dest, parameters[1]);
@@@ -54,7 -56,7 +54,7 @@@
                }
                else
                {
 -                      user->WriteServ("NOTICE %s :*** Invalid nickname '%s'", user->nick.c_str(), parameters[0].c_str());
 +                      user->WriteNotice("*** Invalid nickname '" + parameters[0] + "'");
                        return CMD_FAILURE;
                }
        }
@@@ -77,10 -79,20 +77,10 @@@ class ModuleSaquit : public Modul
        {
        }
  
 -      void init()
 -      {
 -              ServerInstance->Modules->AddService(cmd);
 -      }
 -
 -      virtual ~ModuleSaquit()
 -      {
 -      }
 -
 -      virtual Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for an SAQUIT command, exits user with a reason", VF_OPTCOMMON | VF_VENDOR);
        }
 -
  };
  
  MODULE_INIT(ModuleSaquit)
index d7c0cdf1b72da5e96580850e6747cd5d7a1ace5a,0ea06a3cc8ce9ff79d8e21de18fcb24f26d3b6ce..1b020701be8ff0a35d3f98b325126ddaaaf58a63
  
  
  #include "inspircd.h"
 -#include "socket.h"
 -#include "xline.h"
 -#include "socketengine.h"
 -
 -#include "main.h"
  #include "utils.h"
 -#include "treeserver.h"
 -#include "treesocket.h"
 +#include "commands.h"
  
 -bool TreeSocket::Whois(const std::string &prefix, parameterlist &params)
 +CmdResult CommandIdle::HandleRemote(RemoteUser* issuer, std::vector<std::string>& params)
  {
 -      if (params.size() < 1)
 -              return true;
 -      User* u = ServerInstance->FindNick(prefix);
 -      if (u)
 +      /**
 +       * There are two forms of IDLE: request and reply. Requests have one parameter,
 +       * replies have more than one.
 +       *
 +       * If this is a request, 'issuer' did a /whois and its server wants to learn the
 +       * idle time of the user in params[0].
 +       *
 +       * If this is a reply, params[0] is the user who did the whois and params.back() is
 +       * the number of seconds 'issuer' has been idle.
 +       */
 +
 +      User* target = ServerInstance->FindUUID(params[0]);
-       if ((!target) || (IS_SERVER(target)))
++      if ((!target) || (IS_SERVER(target) || (target->registered != REG_ALL)))
 +              return CMD_FAILURE;
 +
 +      LocalUser* localtarget = IS_LOCAL(target);
 +      if (!localtarget)
        {
 -              // an incoming request
 -              if (params.size() == 1)
 -              {
 -                      User* x = ServerInstance->FindNick(params[0]);
 -                      if ((x) && (IS_LOCAL(x)))
 -                      {
 -                              long idle = abs((long)((x->idle_lastmsg) - ServerInstance->Time()));
 -                              parameterlist par;
 -                              par.push_back(prefix);
 -                              par.push_back(ConvToStr(x->signon));
 -                              par.push_back(ConvToStr(idle));
 -                              // ours, we're done, pass it BACK
 -                              Utils->DoOneToOne(params[0], "IDLE", par, u->server);
 -                      }
 -                      else
 -                      {
 -                              // not ours pass it on
 -                              if (x)
 -                                      Utils->DoOneToOne(prefix, "IDLE", params, x->server);
 -                      }
 -              }
 -              else if (params.size() == 3)
 -              {
 -                      std::string who_did_the_whois = params[0];
 -                      User* who_to_send_to = ServerInstance->FindNick(who_did_the_whois);
 -                      if ((who_to_send_to) && (IS_LOCAL(who_to_send_to)) && (who_to_send_to->registered == REG_ALL))
 -                      {
 -                              // an incoming reply to a whois we sent out
 -                              std::string nick_whoised = prefix;
 -                              unsigned long signon = atoi(params[1].c_str());
 -                              unsigned long idle = atoi(params[2].c_str());
 -                              if ((who_to_send_to) && (IS_LOCAL(who_to_send_to)))
 -                              {
 -                                      ServerInstance->DoWhois(who_to_send_to, u, signon, idle, nick_whoised.c_str());
 -                              }
 -                      }
 -                      else
 -                      {
 -                              // not ours, pass it on
 -                              if (who_to_send_to)
 -                                      Utils->DoOneToOne(prefix, "IDLE", params, who_to_send_to->server);
 -                      }
 -              }
 +              // Forward to target's server
 +              return CMD_SUCCESS;
        }
 -      return true;
 -}
  
 +      if (params.size() >= 2)
 +      {
 +              ServerInstance->Parser->CallHandler("WHOIS", params, issuer);
 +      }
 +      else
 +      {
 +              // A server is asking us the idle time of our user
 +              unsigned int idle;
 +              if (localtarget->idle_lastmsg >= ServerInstance->Time())
 +                      // Possible case when our clock ticked backwards
 +                      idle = 0;
 +              else
 +                      idle = ((unsigned int) (ServerInstance->Time() - localtarget->idle_lastmsg));
 +
 +              CmdBuilder reply(params[0], "IDLE");
 +              reply.push_back(issuer->uuid);
 +              reply.push_back(ConvToStr(target->signon));
 +              reply.push_back(ConvToStr(idle));
 +              reply.Unicast(issuer);
 +      }
  
 +      return CMD_SUCCESS;
 +}
index 9c262f1ea469b3aca0881b1ee5ce626692ea8557,c9729cc0f3a6837c96d3d290b1e44c0ad8e9887b..9da06e82912a72d8a3674ad6e4a2177ffd5883c4
  
  
  #include "inspircd.h"
 -#include "socket.h"
 -#include "xline.h"
 -#include "socketengine.h"
 +#include "iohook.h"
  
  #include "main.h"
 -#include "../spanningtree.h"
 +#include "modules/spanningtree.h"
  #include "utils.h"
  #include "treeserver.h"
  #include "link.h"
  #include "treesocket.h"
 -#include "resolvers.h"
 +#include "commands.h"
  
  /** Because most of the I/O gubbins are encapsulated within
   * BufferedSocket, we just call the superclass constructor for
   * most of the action, and append a few of our own values
   * to it.
   */
 -TreeSocket::TreeSocket(SpanningTreeUtilities* Util, Link* link, Autoconnect* myac, const std::string& ipaddr)
 -      : Utils(Util)
 +TreeSocket::TreeSocket(Link* link, Autoconnect* myac, const std::string& ipaddr)
 +      : linkID(assign(link->Name)), LinkState(CONNECTING), MyRoot(NULL), proto_version(0), ConnectionFailureShown(false)
 +      , age(ServerInstance->Time())
  {
 -      age = ServerInstance->Time();
 -      linkID = assign(link->Name);
        capab = new CapabData;
        capab->link = link;
        capab->ac = myac;
        capab->capab_phase = 0;
 -      MyRoot = NULL;
 -      proto_version = 0;
 -      ConnectionFailureShown = false;
 -      LinkState = CONNECTING;
 -      if (!link->Hook.empty())
 -      {
 -              ServiceProvider* prov = ServerInstance->Modules->FindService(SERVICE_IOHOOK, link->Hook);
 -              if (!prov)
 -              {
 -                      SetError("Could not find hook '" + link->Hook + "' for connection to " + linkID);
 -                      return;
 -              }
 -              AddIOHook(prov->creator);
 -      }
 +
        DoConnect(ipaddr, link->Port, link->Timeout, link->Bind);
        Utils->timeoutlist[this] = std::pair<std::string, int>(linkID, link->Timeout);
        SendCapabilities(1);
   * we must associate it with a socket without creating a new
   * connection. This constructor is used for this purpose.
   */
 -TreeSocket::TreeSocket(SpanningTreeUtilities* Util, int newfd, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
 -      : BufferedSocket(newfd), Utils(Util)
 +TreeSocket::TreeSocket(int newfd, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
 +      : BufferedSocket(newfd)
 +      , linkID("inbound from " + client->addr()), LinkState(WAIT_AUTH_1), MyRoot(NULL), proto_version(0)
 +      , ConnectionFailureShown(false), age(ServerInstance->Time())
  {
        capab = new CapabData;
        capab->capab_phase = 0;
 -      MyRoot = NULL;
 -      age = ServerInstance->Time();
 -      LinkState = WAIT_AUTH_1;
 -      proto_version = 0;
 -      ConnectionFailureShown = false;
 -      linkID = "inbound from " + client->addr();
  
 -      FOREACH_MOD(I_OnHookIO, OnHookIO(this, via));
 -      if (GetIOHook())
 -              GetIOHook()->OnStreamSocketAccept(this, client, server);
 +      if (via->iohookprov)
 +              via->iohookprov->OnAccept(this, client, server);
        SendCapabilities(1);
  
        Utils->timeoutlist[this] = std::pair<std::string, int>(linkID, 30);
@@@ -93,7 -114,8 +93,7 @@@ CullResult TreeSocket::cull(
  
  TreeSocket::~TreeSocket()
  {
 -      if (capab)
 -              delete capab;
 +      delete capab;
  }
  
  /** When an outbound connection finishes connecting, we receive
@@@ -106,17 -128,6 +106,17 @@@ void TreeSocket::OnConnected(
  {
        if (this->LinkState == CONNECTING)
        {
 +              if (!capab->link->Hook.empty())
 +              {
 +                      ServiceProvider* prov = ServerInstance->Modules->FindService(SERVICE_IOHOOK, capab->link->Hook);
 +                      if (!prov)
 +                      {
 +                              SetError("Could not find hook '" + capab->link->Hook + "' for connection to " + linkID);
 +                              return;
 +                      }
 +                      static_cast<IOHookProvider*>(prov)->OnConnect(this);
 +              }
 +
                ServerInstance->SNO->WriteGlobalSno('l', "Connection to \2%s\2[%s] started.", linkID.c_str(),
                        (capab->link->HiddenFromStats ? "<hidden>" : capab->link->IPAddr.c_str()));
                this->SendCapabilities(1);
@@@ -146,15 -157,16 +146,15 @@@ void TreeSocket::SendError(const std::s
   */
  void TreeSocket::SquitServer(std::string &from, TreeServer* Current, int& num_lost_servers, int& num_lost_users)
  {
 -      std::string servername = Current->GetName();
 -      ServerInstance->Logs->Log("m_spanningtree",DEBUG,"SquitServer for %s from %s",
 -              servername.c_str(), from.c_str());
 +      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "SquitServer for %s from %s", Current->GetName().c_str(), from.c_str());
        /* recursively squit the servers attached to 'Current'.
         * We're going backwards so we don't remove users
         * while we still need them ;)
         */
 -      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)
        {
 -              TreeServer* recursive_server = Current->GetChild(q);
 +              TreeServer* recursive_server = *i;
                this->SquitServer(from,recursive_server, num_lost_servers, num_lost_users);
        }
        /* Now we've whacked the kids, whack self */
@@@ -170,67 -182,46 +170,68 @@@ void TreeSocket::Squit(TreeServer* Curr
  {
        bool LocalSquit = false;
  
 -      if ((Current) && (Current != Utils->TreeRoot))
 +      if (!Current->IsRoot())
        {
                DelServerEvent(Utils->Creator, Current->GetName());
  
 -              if (!Current->GetSocket() || Current->GetSocket()->Introduced())
 -              {
 -                      parameterlist params;
 -                      params.push_back(Current->GetID());
 -                      params.push_back(":"+reason);
 -                      Utils->DoOneToAllButSender(Current->GetParent()->GetID(),"SQUIT",params,Current->GetID());
 -              }
 -
 -              if (Current->GetParent() == Utils->TreeRoot)
 +              if (Current->IsLocal())
                {
                        ServerInstance->SNO->WriteGlobalSno('l', "Server \002"+Current->GetName()+"\002 split: "+reason);
                        LocalSquit = true;
 +                      if (Current->GetSocket()->Introduced())
 +                      {
 +                              CmdBuilder params("SQUIT");
 +                              params.push_back(Current->GetID());
 +                              params.push_last(reason);
 +                              params.Broadcast();
 +                      }
                }
                else
                {
-                       ServerInstance->SNO->WriteGlobalSno('L', "Server \002"+Current->GetName()+"\002 split from server \002"+Current->GetParent()->GetName()+"\002 with reason: "+reason);
+                       ServerInstance->SNO->WriteToSnoMask('L', "Server \002"+Current->GetName()+"\002 split from server \002"+Current->GetParent()->GetName()+"\002 with reason: "+reason);
                }
                int num_lost_servers = 0;
                int num_lost_users = 0;
                std::string from = Current->GetParent()->GetName()+" "+Current->GetName();
 +
 +              ModuleSpanningTree* st = Utils->Creator;
 +              st->SplitInProgress = true;
                SquitServer(from, Current, num_lost_servers, num_lost_users);
 +              st->SplitInProgress = false;
 +
                ServerInstance->SNO->WriteToSnoMask(LocalSquit ? 'l' : 'L', "Netsplit complete, lost \002%d\002 user%s on \002%d\002 server%s.",
                        num_lost_users, num_lost_users != 1 ? "s" : "", num_lost_servers, num_lost_servers != 1 ? "s" : "");
                Current->Tidy();
                Current->GetParent()->DelChild(Current);
                Current->cull();
+               const bool ismyroot = (Current == MyRoot);
                delete Current;
-               if (Current == MyRoot)
+               if (ismyroot)
                {
                        MyRoot = NULL;
                        Close();
                }
        }
 -      else
 -              ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Squit from unknown server");
 +}
 +
 +CmdResult CommandSQuit::HandleServer(TreeServer* server, std::vector<std::string>& params)
 +{
 +      TreeServer* quitting = Utils->FindServer(params[0]);
 +      if (!quitting)
 +      {
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Squit from unknown server");
 +              return CMD_FAILURE;
 +      }
 +
 +      TreeSocket* sock = server->GetSocket();
 +      sock->Squit(quitting, params[1]);
 +
 +      // XXX: Return CMD_FAILURE when servers SQUIT themselves (i.e. :00S SQUIT 00S :Shutting down)
 +      // to avoid RouteCommand() being called. RouteCommand() requires a valid command source but we
 +      // do not have one because the server user is deleted when its TreeServer is destructed.
 +      // We generate a SQUIT in TreeSocket::Squit(), with our sid as the source and send it to the
 +      // remaining servers.
 +      return ((quitting == server) ? CMD_FAILURE : CMD_SUCCESS);
  }
  
  /** This function is called when we receive data from a remote
diff --combined src/modules/m_watch.cpp
index 6c7000a77b5df50a9547b0cfada915560673b3c0,0e532d65be1f9ae07e147a1b4336c7555b2a7d5d..57ca18a8fc22e84e9029cf4719684ed9caf414fe
@@@ -22,6 -22,8 +22,6 @@@
  
  #include "inspircd.h"
  
 -/* $ModDesc: Provides support for the /WATCH command */
 -
  
  /*
   * Okay, it's nice that this was documented and all, but I at least understood very little
   * of users using WATCH.
   */
  
 -/*
 - * Before you start screaming, this definition is only used here, so moving it to a header is pointless.
 - * Yes, it's horrid. Blame cl for being different. -- w00t
 - */
 -
 -typedef nspace::hash_map<irc::string, std::deque<User*>, irc::hash> watchentries;
 +typedef TR1NS::unordered_map<irc::string, std::deque<User*>, irc::hash> watchentries;
  typedef std::map<irc::string, std::string> watchlist;
  
  /* Who's watching each nickname.
@@@ -105,12 -112,12 +105,12 @@@ class CommandSVSWatch : public Comman
        CommandSVSWatch(Module* Creator) : Command(Creator,"SVSWATCH", 2)
        {
                syntax = "<target> [C|L|S]|[+|-<nick>]";
 -              TRANSLATE3(TR_NICK, TR_TEXT, TR_END); /* we watch for a nick. not a UID. */
 +              TRANSLATE2(TR_NICK, TR_TEXT); /* we watch for a nick. not a UID. */
        }
  
        CmdResult Handle (const std::vector<std::string> &parameters, User *user)
        {
 -              if (!ServerInstance->ULine(user->server))
 +              if (!user->server->IsULine())
                        return CMD_FAILURE;
  
                User *u = ServerInstance->FindNick(parameters[0]);
@@@ -144,9 -151,9 +144,9 @@@ class CommandWatch : public Comman
        CmdResult remove_watch(User* user, const char* nick)
        {
                // removing an item from the list
 -              if (!ServerInstance->IsNick(nick, ServerInstance->Config->Limits.NickMax))
 +              if (!ServerInstance->IsNick(nick))
                {
 -                      user->WriteNumeric(942, "%s %s :Invalid nickname", user->nick.c_str(), nick);
 +                      user->WriteNumeric(942, "%s :Invalid nickname", nick);
                        return CMD_FAILURE;
                }
  
                        if (n != wl->end())
                        {
                                if (!n->second.empty())
 -                                      user->WriteNumeric(602, "%s %s %s :stopped watching", user->nick.c_str(), n->first.c_str(), n->second.c_str());
 +                                      user->WriteNumeric(602, "%s %s :stopped watching", n->first.c_str(), n->second.c_str());
                                else
 -                                      user->WriteNumeric(602, "%s %s * * 0 :stopped watching", user->nick.c_str(), nick);
 +                                      user->WriteNumeric(602, "%s * * 0 :stopped watching", nick);
  
                                wl->erase(n);
                        }
  
        CmdResult add_watch(User* user, const char* nick)
        {
 -              if (!ServerInstance->IsNick(nick, ServerInstance->Config->Limits.NickMax))
 +              if (!ServerInstance->IsNick(nick))
                {
 -                      user->WriteNumeric(942, "%s %s :Invalid nickname",user->nick.c_str(),nick);
 +                      user->WriteNumeric(942, "%s :Invalid nickname", nick);
                        return CMD_FAILURE;
                }
  
  
                if (wl->size() == MAX_WATCH)
                {
 -                      user->WriteNumeric(512, "%s %s :Too many WATCH entries", user->nick.c_str(), nick);
 +                      user->WriteNumeric(512, "%s :Too many WATCH entries", nick);
                        return CMD_FAILURE;
                }
  
                        }
  
                        User* target = ServerInstance->FindNick(nick);
-                       if (target)
+                       if ((target) && (target->registered == REG_ALL))
                        {
                                (*wl)[nick] = std::string(target->ident).append(" ").append(target->dhost).append(" ").append(ConvToStr(target->age));
 -                              user->WriteNumeric(604, "%s %s %s :is online",user->nick.c_str(), nick, (*wl)[nick].c_str());
 -                              if (IS_AWAY(target))
 +                              user->WriteNumeric(604, "%s %s :is online", nick, (*wl)[nick].c_str());
 +                              if (target->IsAway())
                                {
 -                                      user->WriteNumeric(609, "%s %s %s %s %lu :is away", user->nick.c_str(), target->nick.c_str(), target->ident.c_str(), target->dhost.c_str(), (unsigned long) target->awaytime);
 +                                      user->WriteNumeric(609, "%s %s %s %lu :is away", target->nick.c_str(), target->ident.c_str(), target->dhost.c_str(), (unsigned long) target->awaytime);
                                }
                        }
                        else
                        {
                                (*wl)[nick].clear();
 -                              user->WriteNumeric(605, "%s %s * * 0 :is offline",user->nick.c_str(), nick);
 +                              user->WriteNumeric(605, "%s * * 0 :is offline", nick);
                        }
                }
  
        CommandWatch(Module* parent, unsigned int &maxwatch) : Command(parent,"WATCH", 0), MAX_WATCH(maxwatch), ext("watchlist", parent)
        {
                syntax = "[C|L|S]|[+|-<nick>]";
 -              TRANSLATE2(TR_TEXT, TR_END); /* we watch for a nick. not a UID. */
        }
  
        CmdResult Handle (const std::vector<std::string> &parameters, User *user)
                                for (watchlist::iterator q = wl->begin(); q != wl->end(); q++)
                                {
                                        if (!q->second.empty())
 -                                              user->WriteNumeric(604, "%s %s %s :is online", user->nick.c_str(), q->first.c_str(), q->second.c_str());
 +                                              user->WriteNumeric(604, "%s %s :is online", q->first.c_str(), q->second.c_str());
                                }
                        }
 -                      user->WriteNumeric(607, "%s :End of WATCH list",user->nick.c_str());
 +                      user->WriteNumeric(607, ":End of WATCH list");
                }
                else if (parameters.size() > 0)
                {
                                        {
                                                for (watchlist::iterator q = wl->begin(); q != wl->end(); q++)
                                                {
-                                                       if (!q->second.empty())
+                                                       User* targ = ServerInstance->FindNick(q->first.c_str());
+                                                       if (targ && !q->second.empty())
                                                        {
 -                                                              user->WriteNumeric(604, "%s %s %s :is online", user->nick.c_str(), q->first.c_str(), q->second.c_str());
 -                                                              if (IS_AWAY(targ))
 +                                                              user->WriteNumeric(604, "%s %s :is online", q->first.c_str(), q->second.c_str());
-                                                               User *targ = ServerInstance->FindNick(q->first.c_str());
 +                                                              if (targ->IsAway())
                                                                {
 -                                                                      user->WriteNumeric(609, "%s %s %s %s %lu :is away", user->nick.c_str(), targ->nick.c_str(), targ->ident.c_str(), targ->dhost.c_str(), (unsigned long) targ->awaytime);
 +                                                                      user->WriteNumeric(609, "%s %s %s %lu :is away", targ->nick.c_str(), targ->ident.c_str(), targ->dhost.c_str(), (unsigned long) targ->awaytime);
                                                                }
                                                        }
                                                        else
 -                                                              user->WriteNumeric(605, "%s %s * * 0 :is offline", user->nick.c_str(), q->first.c_str());
 +                                                              user->WriteNumeric(605, "%s * * 0 :is offline", q->first.c_str());
                                                }
                                        }
 -                                      user->WriteNumeric(607, "%s :End of WATCH list",user->nick.c_str());
 +                                      user->WriteNumeric(607, ":End of WATCH list");
                                }
                                else if (!strcasecmp(nick,"S"))
                                {
                                        if (i2 != whos_watching_me->end())
                                                youre_on = i2->second.size();
  
 -                                      user->WriteNumeric(603, "%s :You have %d and are on %d WATCH entries", user->nick.c_str(), you_have, youre_on);
 -                                      user->WriteNumeric(606, "%s :%s",user->nick.c_str(), list.c_str());
 -                                      user->WriteNumeric(607, "%s :End of WATCH S",user->nick.c_str());
 +                                      user->WriteNumeric(603, ":You have %d and are on %d WATCH entries", you_have, youre_on);
 +                                      user->WriteNumeric(606, ":%s", list.c_str());
 +                                      user->WriteNumeric(607, ":End of WATCH S");
                                }
                                else if (nick[0] == '-')
                                {
@@@ -374,14 -382,24 +374,14 @@@ class Modulewatch : public Modul
                whos_watching_me = new watchentries();
        }
  
 -      void init()
 -      {
 -              OnRehash(NULL);
 -              ServerInstance->Modules->AddService(cmdw);
 -              ServerInstance->Modules->AddService(sw);
 -              ServerInstance->Modules->AddService(cmdw.ext);
 -              Implementation eventlist[] = { I_OnRehash, I_OnGarbageCollect, I_OnUserQuit, I_OnPostConnect, I_OnUserPostNick, I_On005Numeric, I_OnSetAway };
 -              ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
 -      }
 -
 -      virtual void OnRehash(User* user)
 +      void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                maxwatch = ServerInstance->Config->ConfValue("watch")->getInt("maxentries", 32);
                if (!maxwatch)
                        maxwatch = 32;
        }
  
 -      virtual ModResult OnSetAway(User *user, const std::string &awaymsg)
 +      ModResult OnSetAway(User *user, const std::string &awaymsg) CXX11_OVERRIDE
        {
                std::string numeric;
                int inum;
                return MOD_RES_PASSTHRU;
        }
  
 -      virtual void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
 +      void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE
        {
                watchentries::iterator x = whos_watching_me->find(user->nick.c_str());
                if (x != whos_watching_me->end())
                {
                        for (std::deque<User*>::iterator n = x->second.begin(); n != x->second.end(); n++)
                        {
 -                              (*n)->WriteNumeric(601, "%s %s %s %s %lu :went offline", (*n)->nick.c_str() ,user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) ServerInstance->Time());
 +                              (*n)->WriteNumeric(601, "%s %s %s %lu :went offline", user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) ServerInstance->Time());
  
                                watchlist* wl = cmdw.ext.get(*n);
                                if (wl)
                }
        }
  
 -      virtual void OnGarbageCollect()
 +      void OnGarbageCollect()
        {
                watchentries* old_watch = whos_watching_me;
                whos_watching_me = new watchentries();
                delete old_watch;
        }
  
 -      virtual void OnPostConnect(User* user)
 +      void OnPostConnect(User* user) CXX11_OVERRIDE
        {
                watchentries::iterator x = whos_watching_me->find(user->nick.c_str());
                if (x != whos_watching_me->end())
                {
                        for (std::deque<User*>::iterator n = x->second.begin(); n != x->second.end(); n++)
                        {
 -                              (*n)->WriteNumeric(600, "%s %s %s %s %lu :arrived online", (*n)->nick.c_str(), user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) user->age);
 +                              (*n)->WriteNumeric(600, "%s %s %s %lu :arrived online", user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) user->age);
  
                                watchlist* wl = cmdw.ext.get(*n);
                                if (wl)
                }
        }
  
 -      virtual void OnUserPostNick(User* user, const std::string &oldnick)
 +      void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
        {
                watchentries::iterator new_offline = whos_watching_me->find(oldnick.c_str());
                watchentries::iterator new_online = whos_watching_me->find(user->nick.c_str());
                                watchlist* wl = cmdw.ext.get(*n);
                                if (wl)
                                {
 -                                      (*n)->WriteNumeric(601, "%s %s %s %s %lu :went offline", (*n)->nick.c_str(), oldnick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) user->age);
 +                                      (*n)->WriteNumeric(601, "%s %s %s %lu :went offline", oldnick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) user->age);
                                        (*wl)[oldnick.c_str()].clear();
                                }
                        }
                                if (wl)
                                {
                                        (*wl)[user->nick.c_str()] = std::string(user->ident).append(" ").append(user->dhost).append(" ").append(ConvToStr(user->age));
 -                                      (*n)->WriteNumeric(600, "%s %s %s :arrived online", (*n)->nick.c_str(), user->nick.c_str(), (*wl)[user->nick.c_str()].c_str());
 +                                      (*n)->WriteNumeric(600, "%s %s :arrived online", user->nick.c_str(), (*wl)[user->nick.c_str()].c_str());
                                }
                        }
                }
        }
  
 -      virtual void On005Numeric(std::string &output)
 +      void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
 -              // we don't really have a limit...
 -              output = output + " WATCH=" + ConvToStr(maxwatch);
 +              tokens["WATCH"] = ConvToStr(maxwatch);
        }
  
 -      virtual ~Modulewatch()
 +      ~Modulewatch()
        {
                delete whos_watching_me;
        }
  
 -      virtual Version GetVersion()
 +      Version GetVersion() CXX11_OVERRIDE
        {
                return Version("Provides support for the /WATCH command", VF_OPTCOMMON | VF_VENDOR);
        }
  };
  
  MODULE_INIT(Modulewatch)
 -
diff --combined src/socket.cpp
index ba35d2b0a632efa68f9677fcdfe7dec0004a8946,e73d01af9c37ed37fbdd1909b95dd83dbba0e5af..4ff43cde7434db185a0a490361dfab4a45afa33e
@@@ -46,7 -46,7 +46,7 @@@ bool InspIRCd::BindSocket(int sockfd, i
        else if (!irc::sockets::aptosa(addr, port, servaddr))
                return false;
  
 -      ret = SE->Bind(sockfd, servaddr);
 +      ret = SocketEngine::Bind(sockfd, servaddr);
  
        if (ret < 0)
        {
        {
                if (dolisten)
                {
 -                      if (SE->Listen(sockfd, Config->MaxConn) == -1)
 +                      if (SocketEngine::Listen(sockfd, Config->MaxConn) == -1)
                        {
 -                              this->Logs->Log("SOCKET",DEFAULT,"ERROR in listen(): %s",strerror(errno));
 +                              this->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR in listen(): %s",strerror(errno));
                                return false;
                        }
                        else
                        {
 -                              this->Logs->Log("SOCKET",DEBUG,"New socket binding for %d with listen: %s:%d", sockfd, addr, port);
 -                              SE->NonBlocking(sockfd);
 +                              this->Logs->Log("SOCKET", LOG_DEBUG, "New socket binding for %d with listen: %s:%d", sockfd, addr, port);
 +                              SocketEngine::NonBlocking(sockfd);
                                return true;
                        }
                }
                else
                {
 -                      this->Logs->Log("SOCKET",DEBUG,"New socket binding for %d without listen: %s:%d", sockfd, addr, port);
 +                      this->Logs->Log("SOCKET", LOG_DEBUG, "New socket binding for %d without listen: %s:%d", sockfd, addr, port);
                        return true;
                }
        }
@@@ -89,7 -89,7 +89,7 @@@ int InspIRCd::BindPorts(FailedPortList 
                std::string Addr = tag->getString("address");
  
                if (strncasecmp(Addr.c_str(), "::ffff:", 7) == 0)
 -                      this->Logs->Log("SOCKET",DEFAULT, "Using 4in6 (::ffff:) isn't recommended. You should bind IPv4 addresses directly instead.");
 +                      this->Logs->Log("SOCKET", LOG_DEFAULT, "Using 4in6 (::ffff:) isn't recommended. You should bind IPv4 addresses directly instead.");
  
                irc::portparser portrange(porttag, false);
                int portno = -1;
                                if ((**n).bind_desc == bind_readable)
                                {
                                        (*n)->bind_tag = tag; // Replace tag, we know addr and port match, but other info (type, ssl) may not
 +                                      (*n)->ResetIOHookProvider();
 +
                                        skip = true;
                                        old_ports.erase(n);
                                        break;
                        n++;
                if (n == ports.end())
                {
 -                      this->Logs->Log("SOCKET",DEFAULT,"Port bindings slipped out of vector, aborting close!");
 +                      this->Logs->Log("SOCKET", LOG_DEFAULT, "Port bindings slipped out of vector, aborting close!");
                        break;
                }
  
 -              this->Logs->Log("SOCKET",DEFAULT, "Port binding %s was removed from the config file, closing.",
 +              this->Logs->Log("SOCKET", LOG_DEFAULT, "Port binding %s was removed from the config file, closing.",
                        (**n).bind_desc.c_str());
                delete *n;
  
@@@ -219,26 -217,26 +219,24 @@@ bool irc::sockets::satoap(const irc::so
        return !addr.empty();
  }
  
- static const char all_zero[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
  std::string irc::sockets::sockaddrs::str() const
  {
 -      char buffer[MAXBUF];
        if (sa.sa_family == AF_INET)
        {
 -              const uint8_t* bits = reinterpret_cast<const uint8_t*>(&in4.sin_addr);
 -              sprintf(buffer, "%d.%d.%d.%d:%u", bits[0], bits[1], bits[2], bits[3], ntohs(in4.sin_port));
 +              char ipaddr[INET_ADDRSTRLEN];
 +              inet_ntop(AF_INET, &in4.sin_addr, ipaddr, sizeof(ipaddr));
 +              return InspIRCd::Format("%s:%u", ipaddr, ntohs(in4.sin_port));
        }
 -      else if (sa.sa_family == AF_INET6)
 +
 +      if (sa.sa_family == AF_INET6)
        {
 -              buffer[0] = '[';
 -              if (!inet_ntop(AF_INET6, &in6.sin6_addr, buffer+1, MAXBUF - 10))
 -                      return "<unknown>"; // should never happen, buffer is large enough
 -              int len = strlen(buffer);
 -              // no need for snprintf, buffer has at least 9 chars left, max short len = 5
 -              sprintf(buffer + len, "]:%u", ntohs(in6.sin6_port));
 +              char ipaddr[INET6_ADDRSTRLEN];
 +              inet_ntop(AF_INET6, &in6.sin6_addr, ipaddr, sizeof(ipaddr));
 +              return InspIRCd::Format("[%s]:%u", ipaddr, ntohs(in6.sin6_port));
        }
 -      else
 -              return "<unknown>";
 -      return std::string(buffer);
 +
 +      // This should never happen.
 +      return "<unknown>";
  }
  
  int irc::sockets::sockaddrs::sa_size() const
@@@ -369,3 -367,4 +367,3 @@@ bool irc::sockets::cidr_mask::match(con
        irc::sockets::cidr_mask tmp(addr, length);
        return tmp == *this;
  }
 -
index 79f1b36351cc5fe3120a9698d8ffbf2ab62bae37,0b5abaf304792b6d14978992ec1aa53d1cb77b86..be4a7d186be7df9718f56e7fba0054653be7de7e
@@@ -1,7 -1,6 +1,7 @@@
  /*
   * InspIRCd -- Internet Relay Chat Daemon
   *
 + *   Copyright (C) 2014 Adam <Adam@anope.org>
   *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
   *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
   *
@@@ -19,6 -18,8 +19,6 @@@
   */
  
  
 -#include "inspircd_config.h"
 -
  #include "inspircd.h"
  #include "socketengine.h"
  
  
  /** A specialisation of the SocketEngine class, designed to use traditional select().
   */
 -class SelectEngine : public SocketEngine
 +namespace
  {
        fd_set ReadSet, WriteSet, ErrSet;
 -      int MaxFD;
 -
 -public:
 -      /** Create a new SelectEngine
 -       */
 -      SelectEngine();
 -      /** Delete a SelectEngine
 -       */
 -      virtual ~SelectEngine();
 -      virtual bool AddFd(EventHandler* eh, int event_mask);
 -      virtual void DelFd(EventHandler* eh);
 -      void OnSetEvent(EventHandler* eh, int, int);
 -      virtual int DispatchEvents();
 -      virtual std::string GetName();
 -};
 -
 -SelectEngine::SelectEngine()
 +      int MaxFD = 0;
 +}
 +
 +void SocketEngine::Init()
  {
        MAX_DESCRIPTORS = FD_SETSIZE;
 -      CurrentSetSize = 0;
 -
 -      ref = new EventHandler* [GetMaxFds()];
 -      memset(ref, 0, GetMaxFds() * sizeof(EventHandler*));
  
        FD_ZERO(&ReadSet);
        FD_ZERO(&WriteSet);
        FD_ZERO(&ErrSet);
 -      MaxFD = 0;
  }
  
 -SelectEngine::~SelectEngine()
 +void SocketEngine::Deinit()
 +{
 +}
 +
 +void SocketEngine::RecoverFromFork()
  {
 -      delete[] ref;
  }
  
 -bool SelectEngine::AddFd(EventHandler* eh, int event_mask)
 +bool SocketEngine::AddFd(EventHandler* eh, int event_mask)
  {
        int fd = eh->GetFd();
        if ((fd < 0) || (fd > GetMaxFds() - 1))
                return false;
  
 -      if (ref[fd])
 +      if (!SocketEngine::AddFdRef(eh))
                return false;
  
 -      ref[fd] = eh;
 -
 -      SocketEngine::SetEventMask(eh, event_mask);
 +      eh->SetEventMask(event_mask);
        OnSetEvent(eh, 0, event_mask);
        FD_SET(fd, &ErrSet);
        if (fd > MaxFD)
                MaxFD = fd;
  
 -      CurrentSetSize++;
 -
 -      ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
 +      ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
        return true;
  }
  
 -void SelectEngine::DelFd(EventHandler* eh)
 +void SocketEngine::DelFd(EventHandler* eh)
  {
        int fd = eh->GetFd();
  
        if ((fd < 0) || (fd > GetMaxFds() - 1))
                return;
  
 -      CurrentSetSize--;
 -      ref[fd] = NULL;
 +      SocketEngine::DelFdRef(eh);
  
        FD_CLR(fd, &ReadSet);
        FD_CLR(fd, &WriteSet);
        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)
 +void SocketEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
  {
        int fd = eh->GetFd();
        int diff = old_mask ^ new_mask;
        }
  }
  
 -int SelectEngine::DispatchEvents()
 +int SocketEngine::DispatchEvents()
  {
-       static timeval tval = { 1, 0 };
+       timeval tval;
+       tval.tv_sec = 1;
+       tval.tv_usec = 0;
  
        fd_set rfdset = ReadSet, wfdset = WriteSet, errfdset = ErrSet;
  
        int sresult = select(MaxFD + 1, &rfdset, &wfdset, &errfdset, &tval);
        ServerInstance->UpdateTime();
  
 -      /* Nothing to process this time around */
 -      if (sresult < 1)
 -              return 0;
 -
        for (int i = 0, j = sresult; i <= MaxFD && j > 0; i++)
        {
                int has_read = FD_ISSET(i, &rfdset), has_write = FD_ISSET(i, &wfdset), has_error = FD_ISSET(i, &errfdset);
  
 -              if (has_read || has_write || has_error)
 -              {
 -                      --j;
 +              if (!(has_read || has_write || has_error))
 +                      continue;
  
 -                      EventHandler* ev = ref[i];
 -                      if (!ev)
 -                              continue;
 +              --j;
 +
 +              EventHandler* ev = GetRef(i);
 +              if (!ev)
 +                      continue;
  
 -                      if (has_error)
 -                      {
 -                              ErrorEvents++;
 +              if (has_error)
 +              {
 +                      stats.ErrorEvents++;
 +
 +                      socklen_t codesize = sizeof(int);
 +                      int errcode = 0;
 +                      if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
 +                              errcode = errno;
  
 -                              socklen_t codesize = sizeof(int);
 -                              int errcode = 0;
 -                              if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char*)&errcode, &codesize) < 0)
 -                                      errcode = errno;
 +                      ev->HandleEvent(EVENT_ERROR, errcode);
 +                      continue;
 +              }
  
 -                              ev->HandleEvent(EVENT_ERROR, errcode);
 +              if (has_read)
 +              {
 +                      stats.ReadEvents++;
 +                      ev->SetEventMask(ev->GetEventMask() & ~FD_READ_WILL_BLOCK);
 +                      ev->HandleEvent(EVENT_READ);
 +                      if (ev != GetRef(i))
                                continue;
 -                      }
 -
 -                      if (has_read)
 -                      {
 -                              ReadEvents++;
 -                              SetEventMask(ev, ev->GetEventMask() & ~FD_READ_WILL_BLOCK);
 -                              ev->HandleEvent(EVENT_READ);
 -                              if (ev != ref[i])
 -                                      continue;
 -                      }
 -                      if (has_write)
 -                      {
 -                              WriteEvents++;
 -                              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);
 -                      }
 +              }
 +
 +              if (has_write)
 +              {
 +                      stats.WriteEvents++;
 +                      int newmask = (ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
 +                      SocketEngine::OnSetEvent(ev, ev->GetEventMask(), newmask);
 +                      ev->SetEventMask(newmask);
 +                      ev->HandleEvent(EVENT_WRITE);
                }
        }
  
        return sresult;
  }
 -
 -std::string SelectEngine::GetName()
 -{
 -      return "select";
 -}
 -
 -SocketEngine* CreateSocketEngine()
 -{
 -      return new SelectEngine;
 -}