]> 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 --cc configure
index 4db9064b986f6d1d3f3381d7065cbf859d58df0e,27f7e6f1ec4290a7d310f491040243f8ff5dd90e..0a91e65c5bc4e24bd75693f43fd0ad3274a804a8
+++ b/configure
@@@ -253,17 -456,41 +253,17 @@@ if (defined($config{HAS_OPENSSL}) && ((
        $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    ***
  # 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]+/)
        {
index c7d672107b357537e9758bb2cbfd1db7359bff44,556d4c1a832d662ed692675b379688c5e2a3758e..7b819845ff3c960130ff118f92cb5066f5b3d9ec
@@@ -97,17 -97,12 +97,17 @@@ 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]
  
Simple merge
index 49559ffee8eef1fac524b002e0cc935f4f9e39e0,2617d1e6c04467d9e0c92c94b3c678784ee18cf7..fd625aef1a45fa2873c9052beb999c2bb0cedf69
  #   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         #
           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"
Simple merge
index cfd55d84e6b65e50c6df7d4fbb95cb51411c3e3c,b4a5afab6f70af891ee945348064d0a1c84aaaa6..b17fd73fb135c2b0a0e1473d1c6ffd7bd8a2ece5
  # 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"
        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">
  #                  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"
  #<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">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  #<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">
  #-#-#-#-#-#-#-#-#-#-#-  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">
  
  #
  #-#-#-#-#-#-#-#-#-#-#-#-  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">
  #
  #<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  -#-#-#-#-#-#-#-#-#-#-#
  #<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:                                                      #
  #                                                                     #
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # 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:                                                      #
  # 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   -#-#-#-#-#-#-#-#-#-#-#-#-#
  #                 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">
  #
  # 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.
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # 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.
  #
  # 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">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # 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
index b42f3129a6193b4d7aa62c431db2891c89732e1d,d03d019c6d0ad50a33bc73202158a9eb7b6675e2..524ebce3422a1b1c41bd92188bdf316a94366d10
       #   - 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)
       #
  #                                                                     #
  
  <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">
Simple merge
index c2060b90edb1a1905c5f3df49b8f3cc16b5ab58d,6c9bd7c85130f8927758c06895118f26861d09bc..4555deb96aa87551dde81a8adf52b3468ea296da
@@@ -648,6 -783,7 +648,7 @@@ void Channel::UserList(User *user
         */
        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)
diff --cc 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.
index 7133b3f053110347f9a9b2d30debd207e73b683c,1c72c7de46bb484be7f98b2e8c42f3c2ae422b05..d89d7cbb597097568a781d3f96ec9db7a2f1a4fb
@@@ -278,28 -309,29 +278,28 @@@ void CommandParser::ProcessCommand(Loca
                }
                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
        {
index baeffb015f4730c90625b0ad7c4830095b29be21,060f66d1620249b4cfb9fb4a33e6099df8f876af..cda5e03e0f8b708a77831ca12ee3013547474e79
@@@ -586,7 -743,10 +586,10 @@@ void ServerConfig::Apply(ServerConfig* 
        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())
        {
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)
index 5097960e88309e3fdee642e4f1b0bacd29a6f4d3,766aeaf8ef05d92cb4979252aac8423ce7825a0c..24072868a0c4c66b1e0cd61f133934c1113e842f
@@@ -382,7 -476,8 +382,7 @@@ InspIRCd::InspIRCd(int argc, char** arg
        /* 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)
        {
index ee024318b5ed6c1da43efab644954887422dd2c1,9a54ff80b7c6a0cb29a762d1dd3e4fadf320119b..7a8bdeeb00be52936a208dc2760a2a377516e878
@@@ -60,346 -65,342 +60,352 @@@ 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;
                }
  
index b0be8d665d5d4d56cdfa6df581965cedc1578c70,2e25925410d6ef67d03f2a80b9b9da3da1d8999d..1a123e580082907f86a85e9295aca2a39c25f430
@@@ -315,10 -320,10 +315,10 @@@ class ModuleBanRedirect : public Modul
                                                }
                                                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;
                                                }
index b3c664cf12d295ec7332c9e581195ff887b4d03b,9e0ec7705669708473bf6cc8fbc8c5e01a7cc3b7..8ee8472e676fb28c2d81e34710a1678cacb6954a
@@@ -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;
                        }
                }
Simple merge
index 4fb4ae942bbcbe501f5c80f34aa8d843198bd2a8,62ddb6c67b160d187214431d4372f440d332750f..3bf4c8434352e985d3d39dda126489ce8ed6d628
@@@ -105,15 -160,10 +105,16 @@@ class ModuleOperPrefixMode : public Mod
        }
  };
  
 -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);
  }
  
index 129ad3f7ba7c824f8639c496876f430883196722,ea2ae24ab3d525cc5fc56a156e703a8056f9f0c3..14f79aaf7e66427532fdf0bf01075bc4f3a9ca94
@@@ -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;
index 3050e70d68193a72e6b2bbfff78c88d7fce17f4d,909a026abe306cacd77663f041b25233990d7d6a..aa6aa01809f238bef392d11659576ca6b99c70fa
@@@ -35,11 -37,11 +35,11 @@@ class CommandSaquit : public Comman
        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;
                        }
  
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 6c7000a77b5df50a9547b0cfada915560673b3c0,0e532d65be1f9ae07e147a1b4336c7555b2a7d5d..57ca18a8fc22e84e9029cf4719684ed9caf414fe
@@@ -231,13 -238,13 +231,13 @@@ class CommandWatch : public Comman
                        }
  
                        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
                                        {
                                                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
diff --cc src/socket.cpp
index ba35d2b0a632efa68f9677fcdfe7dec0004a8946,e73d01af9c37ed37fbdd1909b95dd83dbba0e5af..4ff43cde7434db185a0a490361dfab4a45afa33e
@@@ -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
index 79f1b36351cc5fe3120a9698d8ffbf2ab62bae37,0b5abaf304792b6d14978992ec1aa53d1cb77b86..be4a7d186be7df9718f56e7fba0054653be7de7e
@@@ -109,9 -130,11 +109,11 @@@ void SocketEngine::OnSetEvent(EventHand
        }
  }
  
 -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;