]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
Merge insp20
authorAttila Molnar <attilamolnar@hush.com>
Wed, 17 Aug 2016 10:49:48 +0000 (12:49 +0200)
committerAttila Molnar <attilamolnar@hush.com>
Wed, 17 Aug 2016 10:49:48 +0000 (12:49 +0200)
20 files changed:
1  2 
docs/conf/inspircd.conf.example
include/configreader.h
include/inspircd.h
make/template/inspircd
make/template/main.mk
src/configreader.cpp
src/coremods/core_list.cpp
src/coremods/core_oper/cmd_kill.cpp
src/coremods/core_stats.cpp
src/inspircd.cpp
src/mode.cpp
src/modules/extra/m_ssl_gnutls.cpp
src/modules/extra/m_ssl_openssl.cpp
src/modules/m_censor.cpp
src/modules/m_dccallow.cpp
src/modules/m_sasl.cpp
src/modules/m_securelist.cpp
src/modules/m_spanningtree/postcommand.cpp
src/usermanager.cpp
tools/travis-ci.sh

Simple merge
Simple merge
Simple merge
index 138de29a9cfdb4ef6eb060bff65fe11bbfca4007,b43ad60c9ecbb67981cf36258dc765964c36b860..cb2d2902d520eca9752a7cf0da4b4f4004c9a2c4
@@@ -153,8 -135,8 +153,8 @@@ sub cmd_rehash(
  
  sub cmd_cron()
  {
-       if (getstatus() == 0) { goto &cmd_start(); }
+       if (getstatus() == 0) { goto &cmd_start(@_); }
 -      exit();
 +      exit GENERIC_EXIT_UNSPECIFIED;
  }
  
  sub cmd_version()
index bff0657362364e0bb80a0f324d0e4a2ca67b8920,23daa7efc02a0d2069cc70f694b56958f1870571..818b4139d8681a7fbab038f6fa75fd12a83878de
@@@ -154,10 -165,10 +154,10 @@@ all: $(FOOTER
  
  target: $(HEADER)
        $(MAKEENV) perl make/calcdep.pl
-       cd $(BUILDPATH); $(MAKEENV) $(MAKE) -f real.mk $(TARGET)
+       cd "$(BUILDPATH)"; $(MAKEENV) $(MAKE) -f real.mk $(TARGET)
  
  debug:
 -      @${MAKE} D=1 all
 +      @${MAKE} INSPIRCD_DEBUG=1 all
  
  debug-header:
        @echo "*************************************"
@@@ -220,25 -231,14 +220,25 @@@ install: targe
        @-$(INSTALL) -d -m $(INSTMODE_DIR) $(BINPATH)
        @-$(INSTALL) -d -m $(INSTMODE_DIR) $(CONPATH)/examples/aliases
        @-$(INSTALL) -d -m $(INSTMODE_DIR) $(CONPATH)/examples/modules
 +      @-$(INSTALL) -d -m $(INSTMODE_DIR) $(MANPATH)
        @-$(INSTALL) -d -m $(INSTMODE_DIR) $(MODPATH)
-       [ $(BUILDPATH)/bin/ -ef $(BINPATH) ] || $(INSTALL) -m $(INSTMODE_BIN) $(BUILDPATH)/bin/inspircd $(BINPATH)
+       [ "$(BUILDPATH)/bin/" -ef $(BINPATH) ] || $(INSTALL) -m $(INSTMODE_BIN) "$(BUILDPATH)/bin/inspircd" $(BINPATH)
 -@IFNDEF PURE_STATIC
 +@IFNDEF INSPIRCD_STATIC
-       [ $(BUILDPATH)/modules/ -ef $(MODPATH) ] || $(INSTALL) -m $(INSTMODE_LIB) $(BUILDPATH)/modules/*.so $(MODPATH)
+       [ "$(BUILDPATH)/modules/" -ef $(MODPATH) ] || $(INSTALL) -m $(INSTMODE_LIB) "$(BUILDPATH)/modules/"*.so $(MODPATH)
  @ENDIF
 -      -$(INSTALL) -m $(INSTMODE_BIN) @STARTSCRIPT@ $(BASE) 2>/dev/null
 -      -$(INSTALL) -m $(INSTMODE_LIB) tools/gdbargs $(BASE)/.gdbargs 2>/dev/null
 +      -$(INSTALL) -m $(INSTMODE_BIN) @CONFIGURE_DIRECTORY@/inspircd $(BASE) 2>/dev/null
 +      -$(INSTALL) -m $(INSTMODE_LIB) .gdbargs $(BASE)/.gdbargs 2>/dev/null
 +@IFEQ $(SYSTEM) darwin
 +      -$(INSTALL) -m $(INSTMODE_BIN) @CONFIGURE_DIRECTORY@/org.inspircd.plist $(BASE) 2>/dev/null
 +@ENDIF
 +@IFEQ $(SYSTEM) linux
 +      -$(INSTALL) -m $(INSTMODE_LIB) @CONFIGURE_DIRECTORY@/inspircd.service $(BASE) 2>/dev/null
 +@ENDIF
 +      -$(INSTALL) -m $(INSTMODE_LIB) @CONFIGURE_DIRECTORY@/inspircd.1 $(MANPATH) 2>/dev/null
 +      -$(INSTALL) -m $(INSTMODE_LIB) @CONFIGURE_DIRECTORY@/inspircd-genssl.1 $(MANPATH) 2>/dev/null
 +      -$(INSTALL) -m $(INSTMODE_BIN) tools/genssl $(BINPATH)/inspircd-genssl 2>/dev/null
        -$(INSTALL) -m $(INSTMODE_LIB) docs/conf/*.example $(CONPATH)/examples
 +      -$(INSTALL) -m $(INSTMODE_LIB) *.pem $(CONPATH) 2>/dev/null
        -$(INSTALL) -m $(INSTMODE_LIB) docs/conf/aliases/*.example $(CONPATH)/examples/aliases
        -$(INSTALL) -m $(INSTMODE_LIB) docs/conf/modules/*.example $(CONPATH)/examples/modules
        @echo ""
@@@ -270,23 -272,25 +270,24 @@@ clean
  deinstall:
        -rm -f $(BINPATH)/inspircd
        -rm -rf $(CONPATH)/examples
 -      -rm -f $(MODPATH)/cmd_*.so
 +      -rm -f $(MANPATH)/inspircd.1
 +      -rm -f $(MANPATH)/inspircd-genssl.1
-       -rm -f $(MODPATH)/*.so
+       -rm -f $(MODPATH)/m_*.so
++      -rm -f $(MODPATH)/core_*.so
        -rm -f $(BASE)/.gdbargs
 +      -rm -f $(BASE)/inspircd.service
        -rm -f $(BASE)/org.inspircd.plist
  
 -squeakyclean: distclean
 -
  configureclean:
 -      rm -f .config.cache
 +      rm -f .gdbargs
        rm -f BSDmakefile
        rm -f GNUmakefile
 -      rm -f include/inspircd_config.h
 -      rm -f include/inspircd_version.h
 -      rm -f inspircd
 -      -rm -f org.inspircd.plist
 +      rm -f include/config.h
 +      rm -rf @CONFIGURE_DIRECTORY@
  
  distclean: clean configureclean
-       -rm -rf $(SOURCEPATH)/run
-       find $(SOURCEPATH)/src/modules -type l | xargs rm -f
+       -rm -rf "$(SOURCEPATH)/run"
+       find "$(SOURCEPATH)/src/modules" -type l | xargs rm -f
  
  help:
        @echo 'InspIRCd Makefile'
index e607c6e7d72e635564cbbae5ab7cdf9b3d968b4e,301db14e87187c849db0150bd8221461ee7f1386..8a432e82f6a74f836038a4ebfdfb1627dd473196
@@@ -409,27 -531,48 +409,28 @@@ void ServerConfig::Fill(
        HideBans = security->getBool("hidebans");
        HideWhoisServer = security->getString("hidewhois");
        HideKillsServer = security->getString("hidekills");
+       HideULineKills = security->getBool("hideulinekills");
        RestrictBannedUsers = security->getBool("restrictbannedusers", true);
        GenericOper = security->getBool("genericoper");
 -      NoUserDns = ConfValue("performance")->getBool("nouserdns");
        SyntaxHints = options->getBool("syntaxhints");
 -      CycleHosts = options->getBool("cyclehosts");
        CycleHostsFromUser = options->getBool("cyclehostsfromuser");
 -      UndernetMsgPrefix = options->getBool("ircumsgprefix");
        FullHostInTopic = options->getBool("hostintopic");
 -      MaxTargets = security->getInt("maxtargets", 20);
 -      DefaultModes = options->getString("defaultmodes", "nt");
 +      MaxTargets = security->getInt("maxtargets", 20, 1, 31);
 +      DefaultModes = options->getString("defaultmodes", "not");
        PID = ConfValue("pid")->getString("file");
 -      WhoWasGroupSize = ConfValue("whowas")->getInt("groupsize");
 -      WhoWasMaxGroups = ConfValue("whowas")->getInt("maxgroups");
 -      WhoWasMaxKeep = ServerInstance->Duration(ConfValue("whowas")->getString("maxkeep"));
        MaxChans = ConfValue("channels")->getInt("users", 20);
 -      OperMaxChans = ConfValue("channels")->getInt("opers", 60);
 +      OperMaxChans = ConfValue("channels")->getInt("opers");
        c_ipv4_range = ConfValue("cidr")->getInt("ipv4clone", 32);
        c_ipv6_range = ConfValue("cidr")->getInt("ipv6clone", 128);
 -      Limits.NickMax = ConfValue("limits")->getInt("maxnick", 32);
 -      Limits.ChanMax = ConfValue("limits")->getInt("maxchan", 64);
 -      Limits.MaxModes = ConfValue("limits")->getInt("maxmodes", 20);
 -      Limits.IdentMax = ConfValue("limits")->getInt("maxident", 11);
 -      Limits.MaxQuit = ConfValue("limits")->getInt("maxquit", 255);
 -      Limits.MaxTopic = ConfValue("limits")->getInt("maxtopic", 307);
 -      Limits.MaxKick = ConfValue("limits")->getInt("maxkick", 255);
 -      Limits.MaxGecos = ConfValue("limits")->getInt("maxgecos", 128);
 -      Limits.MaxAway = ConfValue("limits")->getInt("maxaway", 200);
 -      InvBypassModes = options->getBool("invitebypassmodes", true);
 +      Limits = ServerLimits(ConfValue("limits"));
 +      Paths.Config = ConfValue("path")->getString("configdir", INSPIRCD_CONFIG_PATH);
 +      Paths.Data = ConfValue("path")->getString("datadir", INSPIRCD_DATA_PATH);
 +      Paths.Log = ConfValue("path")->getString("logdir", INSPIRCD_LOG_PATH);
 +      Paths.Module = ConfValue("path")->getString("moduledir", INSPIRCD_MODULE_PATH);
        NoSnoticeStack = options->getBool("nosnoticestack", false);
 -      WelcomeNotice = options->getBool("welcomenotice", true);
 -
 -      range(SoftLimit, 10, ServerInstance->SE->GetMaxFds(), ServerInstance->SE->GetMaxFds(), "<performance:softlimit>");
 -      if (ConfValue("performance")->getBool("limitsomaxconn", true))
 -              range(MaxConn, 0, SOMAXCONN, SOMAXCONN, "<performance:somaxconn>");
 -      range(MaxTargets, 1, 31, 20, "<security:maxtargets>");
 -      range(NetBufferSize, 1024, 65534, 10240, "<performance:netbuffersize>");
 -      range(WhoWasGroupSize, 0, 10000, 10, "<whowas:groupsize>");
 -      range(WhoWasMaxGroups, 0, 1000000, 10240, "<whowas:maxgroups>");
 -      range(WhoWasMaxKeep, 3600, INT_MAX, 3600, "<whowas:maxkeep>");
  
 -      ValidIP(DNSServer, "<dns:server>");
 +      if (Network.find(' ') != std::string::npos)
 +              throw CoreException(Network + " is not a valid network name. A network name must not contain spaces.");
  
        std::string defbind = options->getString("defaultbind");
        if (assign(defbind) == "ipv4")
@@@ -567,13 -729,8 +568,13 @@@ void ServerConfig::Apply(ServerConfig* 
  
        // write once here, to try it out and make sure its ok
        if (valid)
-               ServerInstance->WritePID(this->PID);
+               ServerInstance->WritePID(this->PID, !old);
  
 +      ConfigTagList binds = ConfTags("bind");
 +      if (binds.first == binds.second)
 +               errstr << "Possible configuration error: you have not defined any <bind> blocks." << std::endl
 +                       << "You will need to do this if you want clients to be able to connect!" << std::endl;
 +
        if (old && valid)
        {
                // On first run, ports are bound later on
index 67829a55edf472715d7075e5c592ede7c6f2612a,0000000000000000000000000000000000000000..6a62d122f660e1e932b4e7d28463a19cbe13f61a
mode 100644,000000..100644
--- /dev/null
@@@ -1,118 -1,0 +1,117 @@@
-       /* Work around mIRC suckyness. YOU SUCK, KHALED! */
-       if (parameters.size() == 1)
 +/*
 + * InspIRCd -- Internet Relay Chat Daemon
 + *
 + *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
 + *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
 + *
 + * This file is part of InspIRCd.  InspIRCd is free software: you can
 + * redistribute it and/or modify it under the terms of the GNU General Public
 + * License as published by the Free Software Foundation, version 2.
 + *
 + * This program is distributed in the hope that it will be useful, but WITHOUT
 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 + * details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 + */
 +
 +
 +#include "inspircd.h"
 +
 +/** Handle /LIST.
 + */
 +class CommandList : public Command
 +{
 +      ChanModeReference secretmode;
 +      ChanModeReference privatemode;
 +
 + public:
 +      /** Constructor for list.
 +       */
 +      CommandList(Module* parent)
 +              : Command(parent,"LIST", 0, 0)
 +              , secretmode(creator, "secret")
 +              , privatemode(creator, "private")
 +      {
 +              Penalty = 5;
 +      }
 +
 +      /** Handle command.
 +       * @param parameters The parameters to the command
 +       * @param user The user issuing the command
 +       * @return A value from CmdResult to indicate command success or failure.
 +       */
 +      CmdResult Handle(const std::vector<std::string>& parameters, User *user);
 +};
 +
 +
 +/** Handle /LIST
 + */
 +CmdResult CommandList::Handle (const std::vector<std::string>& parameters, User *user)
 +{
 +      int minusers = 0, maxusers = 0;
 +
 +      user->WriteNumeric(RPL_LISTSTART, "Channel", "Users Name");
 +
++      if ((parameters.size() == 1) && (!parameters[0].empty()))
 +      {
 +              if (parameters[0][0] == '<')
 +              {
 +                      maxusers = atoi((parameters[0].c_str())+1);
 +              }
 +              else if (parameters[0][0] == '>')
 +              {
 +                      minusers = atoi((parameters[0].c_str())+1);
 +              }
 +      }
 +
 +      const bool has_privs = user->HasPrivPermission("channels/auspex");
 +      const bool match_name_topic = ((!parameters.empty()) && (!parameters[0].empty()) && (parameters[0][0] != '<') && (parameters[0][0] != '>'));
 +
 +      const chan_hash& chans = ServerInstance->GetChans();
 +      for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
 +      {
 +              Channel* const chan = i->second;
 +
 +              // attempt to match a glob pattern
 +              long users = chan->GetUserCounter();
 +
 +              bool too_few = (minusers && (users <= minusers));
 +              bool too_many = (maxusers && (users >= maxusers));
 +
 +              if (too_many || too_few)
 +                      continue;
 +
 +              if (match_name_topic)
 +              {
 +                      if (!InspIRCd::Match(chan->name, parameters[0]) && !InspIRCd::Match(chan->topic, parameters[0]))
 +                              continue;
 +              }
 +
 +              // if the channel is not private/secret, OR the user is on the channel anyway
 +              bool n = (has_privs || chan->HasUser(user));
 +
 +              // If we're not in the channel and +s is set on it, we want to ignore it
 +              if ((n) || (!chan->IsModeSet(secretmode)))
 +              {
 +                      if ((!n) && (chan->IsModeSet(privatemode)))
 +                      {
 +                              // Channel is private (+p) and user is outside/not privileged
 +                              user->WriteNumeric(RPL_LIST, '*', users, "");
 +                      }
 +                      else
 +                      {
 +                              /* User is in the channel/privileged, channel is not +s */
 +                              user->WriteNumeric(RPL_LIST, chan->name, users, InspIRCd::Format("[+%s] %s", chan->ChanModes(n), chan->topic.c_str()));
 +                      }
 +              }
 +      }
 +      user->WriteNumeric(RPL_LISTEND, "End of channel list.");
 +
 +      return CMD_SUCCESS;
 +}
 +
 +
 +COMMAND_INIT(CommandList)
index 0d111ce02e911f5cd13a3758b547c39b4b6a3efb,0000000000000000000000000000000000000000..e75566e2fcea19bb079eb8bf53c1d870607a0903
mode 100644,000000..100644
--- /dev/null
@@@ -1,156 -1,0 +1,156 @@@
-                       if (!user->server->IsULine())
 +/*
 + * InspIRCd -- Internet Relay Chat Daemon
 + *
 + *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
 + *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
 + *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
 + *
 + * This file is part of InspIRCd.  InspIRCd is free software: you can
 + * redistribute it and/or modify it under the terms of the GNU General Public
 + * License as published by the Free Software Foundation, version 2.
 + *
 + * This program is distributed in the hope that it will be useful, but WITHOUT
 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 + * details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 + */
 +
 +
 +#include "inspircd.h"
 +#include "core_oper.h"
 +
 +CommandKill::CommandKill(Module* parent)
 +      : Command(parent, "KILL", 2, 2)
 +{
 +      flags_needed = 'o';
 +      syntax = "<nickname> <reason>";
 +      TRANSLATE2(TR_CUSTOM, TR_CUSTOM);
 +}
 +
 +
 +/** Handle /KILL
 + */
 +CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User *user)
 +{
 +      /* Allow comma seperated lists of users for /KILL (thanks w00t) */
 +      if (CommandParser::LoopCall(user, this, parameters, 0))
 +      {
 +              // If we got a colon delimited list of nicks then the handler ran for each nick,
 +              // and KILL commands were broadcast for remote targets.
 +              return CMD_FAILURE;
 +      }
 +
 +      User *u = ServerInstance->FindNick(parameters[0]);
 +      if (u)
 +      {
 +              /*
 +               * Here, we need to decide how to munge kill messages. Whether to hide killer, what to show opers, etc.
 +               * We only do this when the command is being issued LOCALLY, for remote KILL, we just copy the message we got.
 +               *
 +               * This conditional is so that we only append the "Killed (" prefix ONCE. If killer is remote, then the kill
 +               * just gets processed and passed on, otherwise, if they are local, it gets prefixed. Makes sense :-) -- w00t
 +               */
 +
 +              if (IS_LOCAL(user))
 +              {
 +                      /*
 +                       * Moved this event inside the IS_LOCAL check also, we don't want half the network killing a user
 +                       * and the other half not. This would be a bad thing. ;p -- w00t
 +                       */
 +                      ModResult MOD_RESULT;
 +                      FIRST_MOD_RESULT(OnKill, MOD_RESULT, (user, u, parameters[1]));
 +
 +                      if (MOD_RESULT == MOD_RES_DENY)
 +                              return CMD_FAILURE;
 +
 +                      killreason = "Killed (";
 +                      if (!ServerInstance->Config->HideKillsServer.empty())
 +                      {
 +                              // hidekills is on, use it
 +                              killreason += ServerInstance->Config->HideKillsServer;
 +                      }
 +                      else
 +                      {
 +                              // hidekills is off, do nothing
 +                              killreason += user->nick;
 +                      }
 +
 +                      killreason += " (" + parameters[1] + "))";
 +              }
 +              else
 +              {
 +                      /* Leave it alone, remote server has already formatted it */
 +                      killreason.assign(parameters[1], 0, ServerInstance->Config->Limits.MaxQuit);
 +              }
 +
 +              /*
 +               * Now we need to decide whether or not to send a local or remote snotice. Currently this checking is a little flawed.
 +               * No time to fix it right now, so left a note. -- w00t
 +               */
 +              if (!IS_LOCAL(u))
 +              {
 +                      // remote kill
-                       if (!user->server->IsULine())
++                      if ((!ServerInstance->Config->HideULineKills) || (!user->server->IsULine()))
 +                              ServerInstance->SNO->WriteToSnoMask('K', "Remote kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
 +                      this->lastuuid = u->uuid;
 +              }
 +              else
 +              {
 +                      // local kill
 +                      /*
 +                       * XXX - this isn't entirely correct, servers A - B - C, oper on A, client on C. Oper kills client, A and B will get remote kill
 +                       * snotices, C will get a local kill snotice. this isn't accurate, and needs fixing at some stage. -- w00t
 +                       */
++                      if ((!ServerInstance->Config->HideULineKills) || (!user->server->IsULine()))
 +                      {
 +                              if (IS_LOCAL(user))
 +                                      ServerInstance->SNO->WriteGlobalSno('k',"Local Kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
 +                              else
 +                                      ServerInstance->SNO->WriteToSnoMask('k',"Local Kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
 +                      }
 +
 +                      ServerInstance->Logs->Log("KILL", LOG_DEFAULT, "LOCAL KILL: %s :%s!%s!%s (%s)", u->nick.c_str(), ServerInstance->Config->ServerName.c_str(), user->dhost.c_str(), user->nick.c_str(), parameters[1].c_str());
 +
 +                      u->Write(":%s KILL %s :%s!%s!%s (%s)", ServerInstance->Config->HideKillsServer.empty() ? user->GetFullHost().c_str() : ServerInstance->Config->HideKillsServer.c_str(),
 +                                      u->nick.c_str(),
 +                                      ServerInstance->Config->ServerName.c_str(),
 +                                      ServerInstance->Config->HideKillsServer.empty() ? user->dhost.c_str() : ServerInstance->Config->HideKillsServer.c_str(),
 +                                      ServerInstance->Config->HideKillsServer.empty() ? user->nick.c_str() : ServerInstance->Config->HideKillsServer.c_str(),
 +                                      parameters[1].c_str());
 +
 +                      this->lastuuid.clear();
 +              }
 +
 +              // send the quit out
 +              ServerInstance->Users->QuitUser(u, killreason);
 +      }
 +      else
 +      {
 +              user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
 +              return CMD_FAILURE;
 +      }
 +
 +      return CMD_SUCCESS;
 +}
 +
 +RouteDescriptor CommandKill::GetRouting(User* user, const std::vector<std::string>& parameters)
 +{
 +      // FindNick() doesn't work here because we quit the target user in Handle() which
 +      // removes it from the nicklist, so we check lastuuid: if it's empty then this KILL
 +      // was for a local user, otherwise it contains the uuid of the user who was killed.
 +      if (lastuuid.empty())
 +              return ROUTE_LOCALONLY;
 +      return ROUTE_BROADCAST;
 +}
 +
 +
 +void CommandKill::EncodeParameter(std::string& param, int index)
 +{
 +      // Manually translate the nick -> uuid (see above), and also the reason (params[1])
 +      // because we decorate it if the oper is local and want remote servers to see the
 +      // decorated reason not the original.
 +      param = ((index == 0) ? lastuuid : killreason);
 +}
index d890d19ea2f93e1516a611b6d97b92d048ddad81,0000000000000000000000000000000000000000..ee0c50db2c5378068a04d2d41a27c152c0a9cb7a
mode 100644,000000..100644
--- /dev/null
@@@ -1,409 -1,0 +1,409 @@@
-       CommandStats ( Module* parent) : Command(parent,"STATS",1,2) { syntax = "<stats-symbol> [<servername>]"; }
 +/*
 + * InspIRCd -- Internet Relay Chat Daemon
 + *
 + *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
 + *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
 + *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
 + *
 + * This file is part of InspIRCd.  InspIRCd is free software: you can
 + * redistribute it and/or modify it under the terms of the GNU General Public
 + * License as published by the Free Software Foundation, version 2.
 + *
 + * This program is distributed in the hope that it will be useful, but WITHOUT
 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 + * details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 + */
 +
 +
 +#include "inspircd.h"
 +#include "xline.h"
 +
 +#ifdef _WIN32
 +#include <psapi.h>
 +#pragma comment(lib, "psapi.lib") // For GetProcessMemoryInfo()
 +#endif
 +
 +/** Handle /STATS.
 + */
 +class CommandStats : public Command
 +{
 +      void DoStats(Stats::Context& stats);
 + public:
 +      /** Constructor for stats.
 +       */
++      CommandStats ( Module* parent) : Command(parent,"STATS",1,2) { allow_empty_last_param = false; syntax = "<stats-symbol> [<servername>]"; }
 +      /** Handle command.
 +       * @param parameters The parameters to the command
 +       * @param user The user issuing the command
 +       * @return A value from CmdResult to indicate command success or failure.
 +       */
 +      CmdResult Handle(const std::vector<std::string>& parameters, User *user);
 +      RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
 +      {
 +              if ((parameters.size() > 1) && (parameters[1].find('.') != std::string::npos))
 +                      return ROUTE_UNICAST(parameters[1]);
 +              return ROUTE_LOCALONLY;
 +      }
 +};
 +
 +static void GenerateStatsLl(Stats::Context& stats)
 +{
 +      stats.AddRow(211, InspIRCd::Format("nick[ident@%s] sendq cmds_out bytes_out cmds_in bytes_in time_open", (stats.GetSymbol() == 'l' ? "host" : "ip")));
 +
 +      const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
 +      for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
 +      {
 +              LocalUser* u = *i;
 +              stats.AddRow(211, u->nick+"["+u->ident+"@"+(stats.GetSymbol() == 'l' ? u->dhost : u->GetIPString())+"] "+ConvToStr(u->eh.getSendQSize())+" "+ConvToStr(u->cmds_out)+" "+ConvToStr(u->bytes_out)+" "+ConvToStr(u->cmds_in)+" "+ConvToStr(u->bytes_in)+" "+ConvToStr(ServerInstance->Time() - u->signon));
 +      }
 +}
 +
 +void CommandStats::DoStats(Stats::Context& stats)
 +{
 +      User* const user = stats.GetSource();
 +      const char statschar = stats.GetSymbol();
 +
 +      bool isPublic = ServerInstance->Config->UserStats.find(statschar) != std::string::npos;
 +      bool isRemoteOper = IS_REMOTE(user) && (user->IsOper());
 +      bool isLocalOperWithPrivs = IS_LOCAL(user) && user->HasPrivPermission("servers/auspex");
 +
 +      if (!isPublic && !isRemoteOper && !isLocalOperWithPrivs)
 +      {
 +              ServerInstance->SNO->WriteToSnoMask('t',
 +                              "%s '%c' denied for %s (%s@%s)",
 +                              (IS_LOCAL(user) ? "Stats" : "Remote stats"),
 +                              statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
 +              stats.AddRow(481, (std::string("Permission Denied - STATS ") + statschar + " requires the servers/auspex priv."));
 +              return;
 +      }
 +
 +      ModResult MOD_RESULT;
 +      FIRST_MOD_RESULT(OnStats, MOD_RESULT, (stats));
 +      if (MOD_RESULT == MOD_RES_DENY)
 +      {
 +              stats.AddRow(219, statschar, "End of /STATS report");
 +              ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",
 +                      (IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
 +              return;
 +      }
 +
 +      switch (statschar)
 +      {
 +              /* stats p (show listening ports) */
 +              case 'p':
 +              {
 +                      for (std::vector<ListenSocket*>::const_iterator i = ServerInstance->ports.begin(); i != ServerInstance->ports.end(); ++i)
 +                      {
 +                              ListenSocket* ls = *i;
 +                              std::string ip = ls->bind_addr;
 +                              if (ip.empty())
 +                                      ip.assign("*");
 +                              else if (ip.find_first_of(':') != std::string::npos)
 +                              {
 +                                      ip.insert(ip.begin(), '[');
 +                                      ip.insert(ip.end(),  ']');
 +                              }
 +                              std::string type = ls->bind_tag->getString("type", "clients");
 +                              std::string hook = ls->bind_tag->getString("ssl", "plaintext");
 +
 +                              stats.AddRow(249, ip + ":"+ConvToStr(ls->bind_port) + " (" + type + ", " + hook + ")");
 +                      }
 +              }
 +              break;
 +
 +              /* These stats symbols must be handled by a linking module */
 +              case 'n':
 +              case 'c':
 +              break;
 +
 +              case 'i':
 +              {
 +                      for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); ++i)
 +                      {
 +                              ConnectClass* c = *i;
 +                              Stats::Row row(215);
 +                              row.push("I").push(c->name);
 +
 +                              std::string param;
 +                              if (c->type == CC_ALLOW)
 +                                      param.push_back('+');
 +                              if (c->type == CC_DENY)
 +                                      param.push_back('-');
 +
 +                              if (c->type == CC_NAMED)
 +                                      param.push_back('*');
 +                              else
 +                                      param.append(c->host);
 +
 +                              row.push(param).push(c->config->getString("port", "*"));
 +                              row.push(ConvToStr(c->GetRecvqMax())).push(ConvToStr(c->GetSendqSoftMax())).push(ConvToStr(c->GetSendqHardMax())).push(ConvToStr(c->GetCommandRate()));
 +
 +                              param = ConvToStr(c->GetPenaltyThreshold());
 +                              if (c->fakelag)
 +                                      param.push_back('*');
 +                              row.push(param);
 +
 +                              stats.AddRow(row);
 +                      }
 +              }
 +              break;
 +
 +              case 'Y':
 +              {
 +                      int idx = 0;
 +                      for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
 +                      {
 +                              ConnectClass* c = *i;
 +                              stats.AddRow(215, 'i', "NOMATCH", '*', c->GetHost(), (c->limit ? c->limit : SocketEngine::GetMaxFds()), idx, ServerInstance->Config->ServerName, '*');
 +                              stats.AddRow(218, 'Y', idx, c->GetPingTime(), '0', c->GetSendqHardMax(), ConvToStr(c->GetRecvqMax())+" "+ConvToStr(c->GetRegTimeout()));
 +                              idx++;
 +                      }
 +              }
 +              break;
 +
 +              case 'P':
 +              {
 +                      unsigned int idx = 0;
 +                      const UserManager::OperList& opers = ServerInstance->Users->all_opers;
 +                      for (UserManager::OperList::const_iterator i = opers.begin(); i != opers.end(); ++i)
 +                      {
 +                              User* oper = *i;
 +                              if (!oper->server->IsULine())
 +                              {
 +                                      LocalUser* lu = IS_LOCAL(oper);
 +                                      stats.AddRow(249, oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " +
 +                                                      (lu ? ConvToStr(ServerInstance->Time() - lu->idle_lastmsg) + " secs" : "unavailable"));
 +                                      idx++;
 +                              }
 +                      }
 +                      stats.AddRow(249, ConvToStr(idx)+" OPER(s)");
 +              }
 +              break;
 +
 +              case 'k':
 +                      ServerInstance->XLines->InvokeStats("K",216,stats);
 +              break;
 +              case 'g':
 +                      ServerInstance->XLines->InvokeStats("G",223,stats);
 +              break;
 +              case 'q':
 +                      ServerInstance->XLines->InvokeStats("Q",217,stats);
 +              break;
 +              case 'Z':
 +                      ServerInstance->XLines->InvokeStats("Z",223,stats);
 +              break;
 +              case 'e':
 +                      ServerInstance->XLines->InvokeStats("E",223,stats);
 +              break;
 +              case 'E':
 +              {
 +                      const SocketEngine::Statistics& sestats = SocketEngine::GetStats();
 +                      stats.AddRow(249, "Total events: "+ConvToStr(sestats.TotalEvents));
 +                      stats.AddRow(249, "Read events:  "+ConvToStr(sestats.ReadEvents));
 +                      stats.AddRow(249, "Write events: "+ConvToStr(sestats.WriteEvents));
 +                      stats.AddRow(249, "Error events: "+ConvToStr(sestats.ErrorEvents));
 +                      break;
 +              }
 +
 +              /* stats m (list number of times each command has been used, plus bytecount) */
 +              case 'm':
 +              {
 +                      const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
 +                      for (CommandParser::CommandMap::const_iterator i = commands.begin(); i != commands.end(); ++i)
 +                      {
 +                              if (i->second->use_count)
 +                              {
 +                                      /* RPL_STATSCOMMANDS */
 +                                      stats.AddRow(212, i->second->name, i->second->use_count);
 +                              }
 +                      }
 +              }
 +              break;
 +
 +              /* stats z (debug and memory info) */
 +              case 'z':
 +              {
 +                      stats.AddRow(249, "Users: "+ConvToStr(ServerInstance->Users->GetUsers().size()));
 +                      stats.AddRow(249, "Channels: "+ConvToStr(ServerInstance->GetChans().size()));
 +                      stats.AddRow(249, "Commands: "+ConvToStr(ServerInstance->Parser.GetCommands().size()));
 +
 +                      float kbitpersec_in, kbitpersec_out, kbitpersec_total;
 +                      char kbitpersec_in_s[30], kbitpersec_out_s[30], kbitpersec_total_s[30];
 +
 +                      SocketEngine::GetStats().GetBandwidth(kbitpersec_in, kbitpersec_out, kbitpersec_total);
 +
 +                      snprintf(kbitpersec_total_s, 30, "%03.5f", kbitpersec_total);
 +                      snprintf(kbitpersec_out_s, 30, "%03.5f", kbitpersec_out);
 +                      snprintf(kbitpersec_in_s, 30, "%03.5f", kbitpersec_in);
 +
 +                      stats.AddRow(249, "Bandwidth total:  "+ConvToStr(kbitpersec_total_s)+" kilobits/sec");
 +                      stats.AddRow(249, "Bandwidth out:    "+ConvToStr(kbitpersec_out_s)+" kilobits/sec");
 +                      stats.AddRow(249, "Bandwidth in:     "+ConvToStr(kbitpersec_in_s)+" kilobits/sec");
 +
 +#ifndef _WIN32
 +                      /* Moved this down here so all the not-windows stuff (look w00tie, I didn't say win32!) is in one ifndef.
 +                       * Also cuts out some identical code in both branches of the ifndef. -- Om
 +                       */
 +                      rusage R;
 +
 +                      /* Not sure why we were doing '0' with a RUSAGE_SELF comment rather than just using RUSAGE_SELF -- Om */
 +                      if (!getrusage(RUSAGE_SELF,&R)) /* RUSAGE_SELF */
 +                      {
 +                              stats.AddRow(249, "Total allocation: "+ConvToStr(R.ru_maxrss)+"K");
 +                              stats.AddRow(249, "Signals:          "+ConvToStr(R.ru_nsignals));
 +                              stats.AddRow(249, "Page faults:      "+ConvToStr(R.ru_majflt));
 +                              stats.AddRow(249, "Swaps:            "+ConvToStr(R.ru_nswap));
 +                              stats.AddRow(249, "Context Switches: Voluntary; "+ConvToStr(R.ru_nvcsw)+" Involuntary; "+ConvToStr(R.ru_nivcsw));
 +
 +                              char percent[30];
 +
 +                              float n_elapsed = (ServerInstance->Time() - ServerInstance->stats.LastSampled.tv_sec) * 1000000
 +                                      + (ServerInstance->Time_ns() - ServerInstance->stats.LastSampled.tv_nsec) / 1000;
 +                              float n_eaten = ((R.ru_utime.tv_sec - ServerInstance->stats.LastCPU.tv_sec) * 1000000 + R.ru_utime.tv_usec - ServerInstance->stats.LastCPU.tv_usec);
 +                              float per = (n_eaten / n_elapsed) * 100;
 +
 +                              snprintf(percent, 30, "%03.5f%%", per);
 +                              stats.AddRow(249, std::string("CPU Use (now):    ")+percent);
 +
 +                              n_elapsed = ServerInstance->Time() - ServerInstance->startup_time;
 +                              n_eaten = (float)R.ru_utime.tv_sec + R.ru_utime.tv_usec / 100000.0;
 +                              per = (n_eaten / n_elapsed) * 100;
 +                              snprintf(percent, 30, "%03.5f%%", per);
 +                              stats.AddRow(249, std::string("CPU Use (total):  ")+percent);
 +                      }
 +#else
 +                      PROCESS_MEMORY_COUNTERS MemCounters;
 +                      if (GetProcessMemoryInfo(GetCurrentProcess(), &MemCounters, sizeof(MemCounters)))
 +                      {
 +                              stats.AddRow(249, "Total allocation: "+ConvToStr((MemCounters.WorkingSetSize + MemCounters.PagefileUsage) / 1024)+"K");
 +                              stats.AddRow(249, "Pagefile usage:   "+ConvToStr(MemCounters.PagefileUsage / 1024)+"K");
 +                              stats.AddRow(249, "Page faults:      "+ConvToStr(MemCounters.PageFaultCount));
 +                      }
 +
 +                      FILETIME CreationTime;
 +                      FILETIME ExitTime;
 +                      FILETIME KernelTime;
 +                      FILETIME UserTime;
 +                      LARGE_INTEGER ThisSample;
 +                      if(GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime) &&
 +                              QueryPerformanceCounter(&ThisSample))
 +                      {
 +                              KernelTime.dwHighDateTime += UserTime.dwHighDateTime;
 +                              KernelTime.dwLowDateTime += UserTime.dwLowDateTime;
 +                              double n_eaten = (double)( ( (uint64_t)(KernelTime.dwHighDateTime - ServerInstance->stats.LastCPU.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime - ServerInstance->stats.LastCPU.dwLowDateTime) )/100000;
 +                              double n_elapsed = (double)(ThisSample.QuadPart - ServerInstance->stats.LastSampled.QuadPart) / ServerInstance->stats.QPFrequency.QuadPart;
 +                              double per = (n_eaten/n_elapsed);
 +
 +                              char percent[30];
 +
 +                              snprintf(percent, 30, "%03.5f%%", per);
 +                              stats.AddRow(249, std::string("CPU Use (now):    ")+percent);
 +
 +                              n_elapsed = ServerInstance->Time() - ServerInstance->startup_time;
 +                              n_eaten = (double)(( (uint64_t)(KernelTime.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime))/100000;
 +                              per = (n_eaten / n_elapsed);
 +                              snprintf(percent, 30, "%03.5f%%", per);
 +                              stats.AddRow(249, std::string("CPU Use (total):  ")+percent));
 +                      }
 +#endif
 +              }
 +              break;
 +
 +              case 'T':
 +              {
 +                      stats.AddRow(249, "accepts "+ConvToStr(ServerInstance->stats.Accept)+" refused "+ConvToStr(ServerInstance->stats.Refused));
 +                      stats.AddRow(249, "unknown commands "+ConvToStr(ServerInstance->stats.Unknown));
 +                      stats.AddRow(249, "nick collisions "+ConvToStr(ServerInstance->stats.Collisions));
 +                      stats.AddRow(249, "dns requests "+ConvToStr(ServerInstance->stats.DnsGood+ServerInstance->stats.DnsBad)+" succeeded "+ConvToStr(ServerInstance->stats.DnsGood)+" failed "+ConvToStr(ServerInstance->stats.DnsBad));
 +                      stats.AddRow(249, "connection count "+ConvToStr(ServerInstance->stats.Connects));
 +                      stats.AddRow(249, InspIRCd::Format("bytes sent %5.2fK recv %5.2fK",
 +                              ServerInstance->stats.Sent / 1024.0, ServerInstance->stats.Recv / 1024.0));
 +              }
 +              break;
 +
 +              /* stats o */
 +              case 'o':
 +              {
 +                      ConfigTagList tags = ServerInstance->Config->ConfTags("oper");
 +                      for(ConfigIter i = tags.first; i != tags.second; ++i)
 +                      {
 +                              ConfigTag* tag = i->second;
 +                              stats.AddRow(243, 'O', tag->getString("host"), '*', tag->getString("name"), tag->getString("type"), '0');
 +                      }
 +              }
 +              break;
 +              case 'O':
 +              {
 +                      for (ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->OperTypes.begin(); i != ServerInstance->Config->OperTypes.end(); ++i)
 +                      {
 +                              OperInfo* tag = i->second;
 +                              tag->init();
 +                              std::string umodes;
 +                              std::string cmodes;
 +                              for(char c='A'; c <= 'z'; c++)
 +                              {
 +                                      ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_USER);
 +                                      if (mh && mh->NeedsOper() && tag->AllowedUserModes[c - 'A'])
 +                                              umodes.push_back(c);
 +                                      mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL);
 +                                      if (mh && mh->NeedsOper() && tag->AllowedChanModes[c - 'A'])
 +                                              cmodes.push_back(c);
 +                              }
 +                              stats.AddRow(243, 'O', tag->name, umodes, cmodes);
 +                      }
 +              }
 +              break;
 +
 +              /* stats l (show user I/O stats) */
 +              case 'l':
 +              /* stats L (show user I/O stats with IP addresses) */
 +              case 'L':
 +                      GenerateStatsLl(stats);
 +              break;
 +
 +              /* stats u (show server uptime) */
 +              case 'u':
 +              {
 +                      unsigned int up = static_cast<unsigned int>(ServerInstance->Time() - ServerInstance->startup_time);
 +                      stats.AddRow(242, InspIRCd::Format("Server up %u days, %.2u:%.2u:%.2u",
 +                              up / 86400, (up / 3600) % 24, (up / 60) % 60, up % 60));
 +              }
 +              break;
 +
 +              default:
 +              break;
 +      }
 +
 +      stats.AddRow(219, statschar, "End of /STATS report");
 +      ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",
 +              (IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
 +      return;
 +}
 +
 +CmdResult CommandStats::Handle (const std::vector<std::string>& parameters, User *user)
 +{
 +      if (parameters.size() > 1 && parameters[1] != ServerInstance->Config->ServerName)
 +      {
 +              // Give extra penalty if a non-oper does /STATS <remoteserver>
 +              LocalUser* localuser = IS_LOCAL(user);
 +              if ((localuser) && (!user->IsOper()))
 +                      localuser->CommandFloodPenalty += 2000;
 +              return CMD_SUCCESS;
 +      }
 +      Stats::Context stats(user, parameters[0][0]);
 +      DoStats(stats);
 +      const std::vector<Stats::Row>& rows = stats.GetRows();
 +      for (std::vector<Stats::Row>::const_iterator i = rows.begin(); i != rows.end(); ++i)
 +      {
 +              const Stats::Row& row = *i;
 +              user->WriteRemoteNumeric(row);
 +      }
 +
 +      return CMD_SUCCESS;
 +}
 +
 +COMMAND_INIT(CommandStats)
index eb24cdea0a2b0d924e8a116f6e4bbd7639c17bd5,0fa90fca5b6028ed764f94f6878d470b530320cb..5d4d8081f02772909c4b1a5cd934fc16da05bf43
@@@ -207,10 -307,12 +207,12 @@@ void InspIRCd::WritePID(const std::stri
                outfile.close();
        }
        else
 -      {\r
 -              if (exitonfail)\r
 +      {
-               std::cout << "Failed to write PID-file '" << fname << "', exiting." << std::endl;
-               this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to write PID-file '%s', exiting.",fname.c_str());
-               Exit(EXIT_STATUS_PID);
++              if (exitonfail)
+                       std::cout << "Failed to write PID-file '" << fname << "', exiting." << std::endl;
 -              this->Logs->Log("STARTUP",DEFAULT,"Failed to write PID-file '%s'%s",fname.c_str(), (exitonfail ? ", exiting." : ""));\r
++              this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to write PID-file '%s'%s", fname.c_str(), (exitonfail ? ", exiting." : ""));
+               if (exitonfail)
 -                      Exit(EXIT_STATUS_PID);\r
++                      Exit(EXIT_STATUS_PID);
        }
  #endif
  }
diff --cc src/mode.cpp
index b29eda828e197a01f5f9bb2a0a65f8c9c7c647ac,0f5d457834ba33538a28abb713c193751b9f6d71..3762dc52e14d9a25b537114532d1c58c43402dfe
@@@ -791,24 -837,35 +791,33 @@@ std::string ModeParser::GiveModeList(Mo
        return type1 + "," + type2 + "," + type3 + "," + type4;
  }
  
 -      bool operator()(ModeHandler* lhs, ModeHandler* rhs)
+ struct PrefixModeSorter
+ {
++      bool operator()(PrefixMode* lhs, PrefixMode* rhs)
+       {
+               return lhs->GetPrefixRank() < rhs->GetPrefixRank();
+       }
+ };
  std::string ModeParser::BuildPrefixes(bool lettersAndModes)
  {
        std::string mletters;
        std::string mprefixes;
-       insp::flat_map<int, std::pair<char, char> > prefixes;
 -      std::vector<ModeHandler*> prefixes;
++      std::vector<PrefixMode*> prefixes;
  
 -      for (unsigned char mode = 'A'; mode <= 'z'; mode++)
 +      const PrefixModeList& list = GetPrefixModes();
 +      for (PrefixModeList::const_iterator i = list.begin(); i != list.end(); ++i)
        {
 -              unsigned char pos = (mode-65) | MASK_CHANNEL;
 -
 -              if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix()))
 -              {
 -                      prefixes.push_back(modehandlers[pos]);
 -              }
 +              PrefixMode* pm = *i;
 +              if (pm->GetPrefix())
-                       prefixes[pm->GetPrefixRank()] = std::make_pair(pm->GetPrefix(), pm->GetModeChar());
++                      prefixes.push_back(pm);
        }
  
-       for (insp::flat_map<int, std::pair<char, char> >::reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); ++n)
+       std::sort(prefixes.begin(), prefixes.end(), PrefixModeSorter());
 -      for (std::vector<ModeHandler*>::const_reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); ++n)
++      for (std::vector<PrefixMode*>::const_reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); ++n)
        {
-               mletters = mletters + n->second.first;
-               mprefixes = mprefixes + n->second.second;
+               mletters += (*n)->GetPrefix();
+               mprefixes += (*n)->GetModeChar();
        }
  
        return lettersAndModes ? "(" + mprefixes + ")" + mletters : mletters;
index bda4e6a4878410b7c0ce8ee3c0c8c43b2520c4c6,2f4acf3f05edbb14454642940bf70fa37ae2759a..e5cb8ee9000ab3c1040db3d0a670ebce9f0c0035
@@@ -866,487 -972,15 +866,490 @@@ info_done_dealloc
                gnutls_x509_crt_deinit(cert);
        }
  
 -      void OnEvent(Event& ev)
 +      // Returns 1 if application I/O should proceed, 0 if it must wait for the underlying protocol to progress, -1 on fatal error
 +      int PrepareIO(StreamSocket* sock)
 +      {
 +              if (status == ISSL_HANDSHAKEN)
 +                      return 1;
 +              else if (status == ISSL_HANDSHAKING)
 +              {
 +                      // The handshake isn't finished, try to finish it
 +                      return Handshake(sock);
 +              }
 +
 +              CloseSession();
 +              sock->SetError("No SSL session");
 +              return -1;
 +      }
 +
 +#ifdef INSPIRCD_GNUTLS_HAS_CORK
 +      int FlushBuffer(StreamSocket* sock)
 +      {
 +              // If GnuTLS has some data buffered, write it
 +              if (gbuffersize)
 +                      return HandleWriteRet(sock, gnutls_record_uncork(this->sess, 0));
 +              return 1;
 +      }
 +#endif
 +
 +      int HandleWriteRet(StreamSocket* sock, int ret)
 +      {
 +              if (ret > 0)
 +              {
 +#ifdef INSPIRCD_GNUTLS_HAS_CORK
 +                      gbuffersize -= ret;
 +                      if (gbuffersize)
 +                      {
 +                              SocketEngine::ChangeEventMask(sock, FD_WANT_SINGLE_WRITE);
 +                              return 0;
 +                      }
 +#endif
 +                      return ret;
 +              }
 +              else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED || ret == 0)
 +              {
 +                      SocketEngine::ChangeEventMask(sock, FD_WANT_SINGLE_WRITE);
 +                      return 0;
 +              }
 +              else // (ret < 0)
 +              {
 +                      sock->SetError(gnutls_strerror(ret));
 +                      CloseSession();
 +                      return -1;
 +              }
 +      }
 +
 +      static const char* UnknownIfNULL(const char* str)
 +      {
 +              return str ? str : "UNKNOWN";
 +      }
 +
 +      static ssize_t gnutls_pull_wrapper(gnutls_transport_ptr_t session_wrap, void* buffer, size_t size)
 +      {
 +              StreamSocket* sock = reinterpret_cast<StreamSocket*>(session_wrap);
 +#ifdef _WIN32
 +              GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetModHook(thismod));
 +#endif
 +
 +              if (sock->GetEventMask() & FD_READ_WILL_BLOCK)
 +              {
 +#ifdef _WIN32
 +                      gnutls_transport_set_errno(session->sess, EAGAIN);
 +#else
 +                      errno = EAGAIN;
 +#endif
 +                      return -1;
 +              }
 +
 +              int rv = SocketEngine::Recv(sock, reinterpret_cast<char *>(buffer), size, 0);
 +
 +#ifdef _WIN32
 +              if (rv < 0)
 +              {
 +                      /* Windows doesn't use errno, but gnutls does, so check SocketEngine::IgnoreError()
 +                       * and then set errno appropriately.
 +                       * The gnutls library may also have a different errno variable than us, see
 +                       * gnutls_transport_set_errno(3).
 +                       */
 +                      gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
 +              }
 +#endif
 +
 +              if (rv < (int)size)
 +                      SocketEngine::ChangeEventMask(sock, FD_READ_WILL_BLOCK);
 +              return rv;
 +      }
 +
 +#ifdef INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
 +      static ssize_t VectorPush(gnutls_transport_ptr_t transportptr, const giovec_t* iov, int iovcnt)
 +      {
 +              StreamSocket* sock = reinterpret_cast<StreamSocket*>(transportptr);
 +#ifdef _WIN32
 +              GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetModHook(thismod));
 +#endif
 +
 +              if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK)
 +              {
 +#ifdef _WIN32
 +                      gnutls_transport_set_errno(session->sess, EAGAIN);
 +#else
 +                      errno = EAGAIN;
 +#endif
 +                      return -1;
 +              }
 +
 +              // Cast the giovec_t to iovec not to IOVector so the correct function is called on Windows
 +              int ret = SocketEngine::WriteV(sock, reinterpret_cast<const iovec*>(iov), iovcnt);
 +#ifdef _WIN32
 +              // See the function above for more info about the usage of gnutls_transport_set_errno() on Windows
 +              if (ret < 0)
 +                      gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
 +#endif
 +
 +              int size = 0;
 +              for (int i = 0; i < iovcnt; i++)
 +                      size += iov[i].iov_len;
 +
 +              if (ret < size)
 +                      SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
 +              return ret;
 +      }
 +
 +#else // INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
 +      static ssize_t gnutls_push_wrapper(gnutls_transport_ptr_t session_wrap, const void* buffer, size_t size)
        {
 -              if (starttls.enabled)
 -                      capHandler.HandleEvent(ev);
 +              StreamSocket* sock = reinterpret_cast<StreamSocket*>(session_wrap);
 +#ifdef _WIN32
 +              GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetModHook(thismod));
 +#endif
 +
 +              if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK)
 +              {
 +#ifdef _WIN32
 +                      gnutls_transport_set_errno(session->sess, EAGAIN);
 +#else
 +                      errno = EAGAIN;
 +#endif
 +                      return -1;
 +              }
 +
 +              int rv = SocketEngine::Send(sock, reinterpret_cast<const char *>(buffer), size, 0);
 +
 +#ifdef _WIN32
 +              if (rv < 0)
 +              {
 +                      /* Windows doesn't use errno, but gnutls does, so check SocketEngine::IgnoreError()
 +                       * and then set errno appropriately.
 +                       * The gnutls library may also have a different errno variable than us, see
 +                       * gnutls_transport_set_errno(3).
 +                       */
 +                      gnutls_transport_set_errno(session->sess, SocketEngine::IgnoreError() ? EAGAIN : errno);
 +              }
 +#endif
 +
 +              if (rv < (int)size)
 +                      SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
 +              return rv;
 +      }
 +#endif // INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
 +
 + public:
 +      GnuTLSIOHook(IOHookProvider* hookprov, StreamSocket* sock, inspircd_gnutls_session_init_flags_t flags, const reference<GnuTLS::Profile>& sslprofile)
 +              : SSLIOHook(hookprov)
 +              , sess(NULL)
 +              , status(ISSL_NONE)
 +              , profile(sslprofile)
 +#ifdef INSPIRCD_GNUTLS_HAS_CORK
 +              , gbuffersize(0)
 +#endif
 +      {
 +              gnutls_init(&sess, flags);
 +              gnutls_transport_set_ptr(sess, reinterpret_cast<gnutls_transport_ptr_t>(sock));
 +#ifdef INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
 +              gnutls_transport_set_vec_push_function(sess, VectorPush);
 +#else
 +              gnutls_transport_set_push_function(sess, gnutls_push_wrapper);
 +#endif
 +              gnutls_transport_set_pull_function(sess, gnutls_pull_wrapper);
 +              profile->SetupSession(sess);
 +
 +              sock->AddIOHook(this);
 +              Handshake(sock);
 +      }
 +
 +      void OnStreamSocketClose(StreamSocket* user) CXX11_OVERRIDE
 +      {
 +              CloseSession();
 +      }
 +
 +      int OnStreamSocketRead(StreamSocket* user, std::string& recvq) CXX11_OVERRIDE
 +      {
 +              // Finish handshake if needed
 +              int prepret = PrepareIO(user);
 +              if (prepret <= 0)
 +                      return prepret;
 +
 +              // If we resumed the handshake then this->status will be ISSL_HANDSHAKEN.
 +              {
 +                      GnuTLS::DataReader reader(sess);
 +                      int ret = reader.ret();
 +                      if (ret > 0)
 +                      {
 +                              reader.appendto(recvq);
++                              // Schedule a read if there is still data in the GnuTLS buffer
++                              if (gnutls_record_check_pending(sess) > 0)
++                                      SocketEngine::ChangeEventMask(user, FD_ADD_TRIAL_READ);
 +                              return 1;
 +                      }
 +                      else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
 +                      {
 +                              return 0;
 +                      }
 +                      else if (ret == 0)
 +                      {
 +                              user->SetError("Connection closed");
 +                              CloseSession();
 +                              return -1;
 +                      }
 +                      else
 +                      {
 +                              user->SetError(gnutls_strerror(ret));
 +                              CloseSession();
 +                              return -1;
 +                      }
 +              }
 +      }
 +
 +      int OnStreamSocketWrite(StreamSocket* user, StreamSocket::SendQueue& sendq) CXX11_OVERRIDE
 +      {
 +              // Finish handshake if needed
 +              int prepret = PrepareIO(user);
 +              if (prepret <= 0)
 +                      return prepret;
 +
 +              // Session is ready for transferring application data
 +
 +#ifdef INSPIRCD_GNUTLS_HAS_CORK
 +              while (true)
 +              {
 +                      // If there is something in the GnuTLS buffer try to send() it
 +                      int ret = FlushBuffer(user);
 +                      if (ret <= 0)
 +                              return ret; // Couldn't flush entire buffer, retry later (or close on error)
 +
 +                      // GnuTLS buffer is empty, if the sendq is empty as well then break to set FD_WANT_NO_WRITE
 +                      if (sendq.empty())
 +                              break;
 +
 +                      // GnuTLS buffer is empty but sendq is not, begin sending data from the sendq
 +                      gnutls_record_cork(this->sess);
 +                      while ((!sendq.empty()) && (gbuffersize < profile->GetOutgoingRecordSize()))
 +                      {
 +                              const StreamSocket::SendQueue::Element& elem = sendq.front();
 +                              gbuffersize += elem.length();
 +                              ret = gnutls_record_send(this->sess, elem.data(), elem.length());
 +                              if (ret < 0)
 +                              {
 +                                      CloseSession();
 +                                      return -1;
 +                              }
 +                              sendq.pop_front();
 +                      }
 +              }
 +#else
 +              int ret = 0;
 +
 +              while (!sendq.empty())
 +              {
 +                      FlattenSendQueue(sendq, profile->GetOutgoingRecordSize());
 +                      const StreamSocket::SendQueue::Element& buffer = sendq.front();
 +                      ret = HandleWriteRet(user, gnutls_record_send(this->sess, buffer.data(), buffer.length()));
 +
 +                      if (ret <= 0)
 +                              return ret;
 +                      else if (ret < (int)buffer.length())
 +                      {
 +                              sendq.erase_front(ret);
 +                              SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
 +                              return 0;
 +                      }
 +
 +                      // Wrote entire record, continue sending
 +                      sendq.pop_front();
 +              }
 +#endif
 +
 +              SocketEngine::ChangeEventMask(user, FD_WANT_NO_WRITE);
 +              return 1;
 +      }
 +
 +      void GetCiphersuite(std::string& out) const CXX11_OVERRIDE
 +      {
 +              if (!IsHandshakeDone())
 +                      return;
 +              out.append(UnknownIfNULL(gnutls_protocol_get_name(gnutls_protocol_get_version(sess)))).push_back('-');
 +              out.append(UnknownIfNULL(gnutls_kx_get_name(gnutls_kx_get(sess)))).push_back('-');
 +              out.append(UnknownIfNULL(gnutls_cipher_get_name(gnutls_cipher_get(sess)))).push_back('-');
 +              out.append(UnknownIfNULL(gnutls_mac_get_name(gnutls_mac_get(sess))));
 +      }
 +
 +      GnuTLS::Profile* GetProfile() { return profile; }
 +      bool IsHandshakeDone() const { return (status == ISSL_HANDSHAKEN); }
 +};
 +
 +int GnuTLS::X509Credentials::cert_callback(gnutls_session_t sess, const gnutls_datum_t* req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t* sign_algos, int sign_algos_length, cert_cb_last_param_type* st)
 +{
 +#ifndef GNUTLS_NEW_CERT_CALLBACK_API
 +      st->type = GNUTLS_CRT_X509;
 +#else
 +      st->cert_type = GNUTLS_CRT_X509;
 +      st->key_type = GNUTLS_PRIVKEY_X509;
 +#endif
 +      StreamSocket* sock = reinterpret_cast<StreamSocket*>(gnutls_transport_get_ptr(sess));
 +      GnuTLS::X509Credentials& cred = static_cast<GnuTLSIOHook*>(sock->GetModHook(thismod))->GetProfile()->GetX509Credentials();
 +
 +      st->ncerts = cred.certs.size();
 +      st->cert.x509 = cred.certs.raw();
 +      st->key.x509 = cred.key.get();
 +      st->deinit_all = 0;
 +
 +      return 0;
 +}
 +
 +class GnuTLSIOHookProvider : public refcountbase, public IOHookProvider
 +{
 +      reference<GnuTLS::Profile> profile;
 +
 + public:
 +      GnuTLSIOHookProvider(Module* mod, reference<GnuTLS::Profile>& prof)
 +              : IOHookProvider(mod, "ssl/" + prof->GetName(), IOHookProvider::IOH_SSL)
 +              , profile(prof)
 +      {
 +              ServerInstance->Modules->AddService(*this);
 +      }
 +
 +      ~GnuTLSIOHookProvider()
 +      {
 +              ServerInstance->Modules->DelService(*this);
 +      }
 +
 +      void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
 +      {
 +              new GnuTLSIOHook(this, sock, GNUTLS_SERVER, profile);
 +      }
 +
 +      void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
 +      {
 +              new GnuTLSIOHook(this, sock, GNUTLS_CLIENT, profile);
 +      }
 +};
 +
 +class ModuleSSLGnuTLS : public Module
 +{
 +      typedef std::vector<reference<GnuTLSIOHookProvider> > ProfileList;
 +
 +      // First member of the class, gets constructed first and destructed last
 +      GnuTLS::Init libinit;
 +      RandGen randhandler;
 +      ProfileList profiles;
 +
 +      void ReadProfiles()
 +      {
 +              // First, store all profiles in a new, temporary container. If no problems occur, swap the two
 +              // containers; this way if something goes wrong we can go back and continue using the current profiles,
 +              // avoiding unpleasant situations where no new SSL connections are possible.
 +              ProfileList newprofiles;
 +
 +              ConfigTagList tags = ServerInstance->Config->ConfTags("sslprofile");
 +              if (tags.first == tags.second)
 +              {
 +                      // No <sslprofile> tags found, create a profile named "gnutls" from settings in the <gnutls> block
 +                      const std::string defname = "gnutls";
 +                      ConfigTag* tag = ServerInstance->Config->ConfValue(defname);
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No <sslprofile> tags found; using settings from the <gnutls> tag");
 +
 +                      try
 +                      {
 +                              reference<GnuTLS::Profile> profile(GnuTLS::Profile::Create(defname, tag));
 +                              newprofiles.push_back(new GnuTLSIOHookProvider(this, profile));
 +                      }
 +                      catch (CoreException& ex)
 +                      {
 +                              throw ModuleException("Error while initializing the default SSL profile - " + ex.GetReason());
 +                      }
 +              }
 +
 +              for (ConfigIter i = tags.first; i != tags.second; ++i)
 +              {
 +                      ConfigTag* tag = i->second;
 +                      if (tag->getString("provider") != "gnutls")
 +                              continue;
 +
 +                      std::string name = tag->getString("name");
 +                      if (name.empty())
 +                      {
 +                              ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring <sslprofile> tag without name at " + tag->getTagLocation());
 +                              continue;
 +                      }
 +
 +                      reference<GnuTLS::Profile> profile;
 +                      try
 +                      {
 +                              profile = GnuTLS::Profile::Create(name, tag);
 +                      }
 +                      catch (CoreException& ex)
 +                      {
 +                              throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason());
 +                      }
 +
 +                      newprofiles.push_back(new GnuTLSIOHookProvider(this, profile));
 +              }
 +
 +              // New profiles are ok, begin using them
 +              // Old profiles are deleted when their refcount drops to zero
 +              profiles.swap(newprofiles);
 +      }
 +
 + public:
 +      ModuleSSLGnuTLS()
 +      {
 +#ifndef GNUTLS_HAS_RND
 +              gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
 +#endif
 +              thismod = this;
 +      }
 +
 +      void init() CXX11_OVERRIDE
 +      {
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "GnuTLS lib version %s module was compiled for " GNUTLS_VERSION, gnutls_check_version(NULL));
 +              ReadProfiles();
 +              ServerInstance->GenRandom = &randhandler;
 +      }
 +
 +      void OnModuleRehash(User* user, const std::string &param) CXX11_OVERRIDE
 +      {
 +              if(param != "ssl")
 +                      return;
 +
 +              try
 +              {
 +                      ReadProfiles();
 +              }
 +              catch (ModuleException& ex)
 +              {
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, ex.GetReason() + " Not applying settings.");
 +              }
 +      }
 +
 +      ~ModuleSSLGnuTLS()
 +      {
 +              ServerInstance->GenRandom = &ServerInstance->HandleGenRandom;
 +      }
 +
 +      void OnCleanup(int target_type, void* item) CXX11_OVERRIDE
 +      {
 +              if(target_type == TYPE_USER)
 +              {
 +                      LocalUser* user = IS_LOCAL(static_cast<User*>(item));
 +
 +                      if ((user) && (user->eh.GetModHook(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");
 +                      }
 +              }
 +      }
 +
 +      Version GetVersion() CXX11_OVERRIDE
 +      {
 +              return Version("Provides SSL support for clients", VF_VENDOR);
        }
  
 -      ModResult OnCheckReady(LocalUser* user)
 +      ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
        {
 -              if ((user->eh.GetIOHook() == this) && (sessions[user->eh.GetFd()].status != ISSL_HANDSHAKEN))
 +              const GnuTLSIOHook* const iohook = static_cast<GnuTLSIOHook*>(user->eh.GetModHook(this));
 +              if ((iohook) && (!iohook->IsHandshakeDone()))
                        return MOD_RES_DENY;
                return MOD_RES_PASSTHRU;
        }
index 4df0d8962e96b98367d1fc3d339bb2109eae9c5a,aee7a5e3428a86afc8e2817c21fea3566aed3365..8467cc6d416b5931efb721009aeca33611e7ea60
@@@ -631,8 -598,15 +631,14 @@@ class OpenSSLIOHook : public SSLIOHoo
                        if (ret > 0)
                        {
                                recvq.append(buffer, ret);
 -
+                               int mask = 0;
+                               // Schedule a read if there is still data in the OpenSSL buffer
 -                              if (SSL_pending(session->sess) > 0)
++                              if (SSL_pending(sess) > 0)
+                                       mask |= FD_ADD_TRIAL_READ;
 -                              if (session->data_to_write)
 +                              if (data_to_write)
-                                       SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_SINGLE_WRITE);
+                                       mask |= FD_WANT_POLL_READ | FD_WANT_SINGLE_WRITE;
+                               if (mask != 0)
 -                                      ServerInstance->SE->ChangeEventMask(user, mask);
++                                      SocketEngine::ChangeEventMask(user, mask);
                                return 1;
                        }
                        else if (ret == 0)
index 7d8c74024a8f0b5a5a877d6dcdabdbef0b0bb871,65b965df288f8acac65d152f05cded53d3a9eb86..c8b6b73a8836cda49cd90107e3bc31c995e46457
@@@ -79,7 -101,7 +79,7 @@@ class ModuleCensor : public Modul
                        {
                                if (index->second.empty())
                                {
-                                       user->WriteNumeric(ERR_WORDFILTERED, ((Channel*)dest)->name, index->first, "Your message contained a censored word, and was blocked");
 -                                      user->WriteNumeric(ERR_WORDFILTERED, "%s %s %s :Your message contained a censored word, and was blocked", user->nick.c_str(), ((target_type == TYPE_CHANNEL) ? ((Channel*)dest)->name.c_str() : ((User*)dest)->nick.c_str()), index->first.c_str());
++                                      user->WriteNumeric(ERR_WORDFILTERED, ((target_type == TYPE_CHANNEL) ? ((Channel*)dest)->name : ((User*)dest)->nick), index->first, "Your message contained a censored word, and was blocked");
                                        return MOD_RES_DENY;
                                }
  
Simple merge
Simple merge
index b6c6ce174f046570fd34e634b6ea3bf105f0b63d,5d11d27f7b0fa16b72a2e568d232745e279e0f42..b925c3f3733cea83d744a25677c253ffc2794855
@@@ -62,12 -75,12 +62,12 @@@ class ModuleSecureList : public Modul
                                        return MOD_RES_PASSTHRU;
  
                        /* Not exempt, BOOK EM DANNO! */
 -                      user->WriteServ("NOTICE %s :*** You cannot list within the first %lu seconds of connecting. Please try again later.",user->nick.c_str(), (unsigned long) WaitTime);
 +                      user->WriteNotice("*** You cannot list within the first " + ConvToStr(WaitTime) + " seconds of connecting. Please try again later.");
-                       /* Some crap clients (read: mIRC, various java chat applets) muck up if they don't
+                       /* Some clients (e.g. mIRC, various java chat applets) muck up if they don't
                         * receive these numerics whenever they send LIST, so give them an empty LIST to mull over.
                         */
 -                      user->WriteNumeric(321, "%s Channel :Users Name",user->nick.c_str());
 -                      user->WriteNumeric(323, "%s :End of channel list.",user->nick.c_str());
 +                      user->WriteNumeric(RPL_LISTSTART, "Channel", "Users Name");
 +                      user->WriteNumeric(RPL_LISTEND, "End of channel list.");
                        return MOD_RES_DENY;
                }
                return MOD_RES_PASSTHRU;
index 7b0478229990cf2dc592bf61398eac22d5bbc1cd,3f5d427e169a7ca90b68e82ae0648217f191c825..64ca729779249cd2a44e8bb8fc80db9119719d71
@@@ -30,42 -37,49 +30,42 @@@ void ModuleSpanningTree::OnPostCommand(
                Utils->RouteCommand(NULL, command, parameters, user);
  }
  
 -void SpanningTreeUtilities::RouteCommand(TreeServer* origin, const std::string &command, const parameterlist& parameters, User *user)
 +void SpanningTreeUtilities::RouteCommand(TreeServer* origin, CommandBase* thiscmd, const parameterlist& parameters, User* user)
  {
 -      if (!ServerInstance->Parser->IsValidCommand(command, parameters.size(), user))
 -              return;
 -
 -      /* We know it's non-null because IsValidCommand returned true */
 -      Command* thiscmd = ServerInstance->Parser->GetHandler(command);
 -
 +      const std::string& command = thiscmd->name;
        RouteDescriptor routing = thiscmd->GetRouting(user, parameters);
 -
 -      std::string sent_cmd = command;
 -      parameterlist params;
 -
        if (routing.type == ROUTE_TYPE_LOCALONLY)
 -      {
 -              /* Broadcast when it's a core command with the default route descriptor and the source is a
 -               * remote user or a remote server
 -               */
 +              return;
  
 -              Version ver = thiscmd->creator->GetVersion();
 -              if ((!(ver.Flags & VF_CORE)) || (IS_LOCAL(user)) || (IS_SERVER(user) == ServerInstance->FakeClient))
 -                      return;
 +      const bool encap = ((routing.type == ROUTE_TYPE_OPT_BCAST) || (routing.type == ROUTE_TYPE_OPT_UCAST));
 +      CmdBuilder params(user, encap ? "ENCAP" : command.c_str());
 +      TreeServer* sdest = NULL;
  
 -              routing = ROUTE_BROADCAST;
 -      }
 -      else if (routing.type == ROUTE_TYPE_OPT_BCAST)
 +      if (routing.type == ROUTE_TYPE_OPT_BCAST)
        {
 -              params.push_back("*");
 +              params.push('*');
                params.push_back(command);
 -              sent_cmd = "ENCAP";
        }
 -      else if (routing.type == ROUTE_TYPE_OPT_UCAST)
 +      else if (routing.type == ROUTE_TYPE_UNICAST || routing.type == ROUTE_TYPE_OPT_UCAST)
        {
 -              TreeServer* sdest = FindServer(routing.serverdest);
 +              sdest = static_cast<TreeServer*>(routing.server);
                if (!sdest)
                {
 -                      ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Trying to route ENCAP to nonexistent server %s",
 -                              routing.serverdest.c_str());
 -                      return;
 +                      // Assume the command handler already validated routing.serverdest and have only returned success if the target is something that the
 +                      // user executing the command is allowed to look up e.g. target is not an uuid if user is local.
 +                      sdest = FindRouteTarget(routing.serverdest);
 +                      if (!sdest)
 +                      {
-                               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Trying to route %s%s to nonexistant server %s", (encap ? "ENCAP " : ""), command.c_str(), routing.serverdest.c_str());
++                              ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Trying to route %s%s to nonexistent server %s", (encap ? "ENCAP " : ""), command.c_str(), routing.serverdest.c_str());
 +                              return;
 +                      }
 +              }
 +
 +              if (encap)
 +              {
 +                      params.push_back(sdest->GetID());
 +                      params.push_back(command);
                }
 -              params.push_back(sdest->GetID());
 -              params.push_back(command);
 -              sent_cmd = "ENCAP";
        }
        else
        {
index 7b4bbe281cbd3a2396ffa58c668e594f64c10f35,2cb7ad51117a4743eb8835f6cd5c79dba9456d6c..5891b42f014f61cf3db0b70b8ab5889bb59e63d5
@@@ -147,16 -139,21 +147,17 @@@ void UserManager::AddUser(int socket, L
                }
        }
  
 -      if (!ServerInstance->SE->AddFd(eh, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE))
 +      if (!SocketEngine::AddFd(eh, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE))
        {
 -              ServerInstance->Logs->Log("USERS", DEBUG,"Internal error on new connection");
 +              ServerInstance->Logs->Log("USERS", LOG_DEBUG, "Internal error on new connection");
                this->QuitUser(New, "Internal error handling connection");
+               return;
        }
  
 -      /* NOTE: even if dns lookups are *off*, we still need to display this.
 -       * BOPM and other stuff requires it.
 -       */
 -      New->WriteServ("NOTICE Auth :*** Looking up your hostname...");
        if (ServerInstance->Config->RawLog)
 -              New->WriteServ("NOTICE Auth :*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
 +              New->WriteNotice("*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
  
 -      FOREACH_MOD(I_OnSetUserIP,OnSetUserIP(New));
 +      FOREACH_MOD(OnSetUserIP, (New));
        if (New->quitting)
                return;
  
index 0000000000000000000000000000000000000000,6dbc823008449e4581d91818804ff2d2027b68f4..8828532b083c35264a36c959a4e49b7ae654c93d
mode 000000,100755..100755
--- /dev/null
@@@ -1,0 -1,20 +1,19 @@@
 -./configure --enable-extras=m_geoip.cpp,m_ldapauth.cpp,m_ldapoper.cpp,m_mysql.cpp,m_pgsql.cpp,m_regex_pcre.cpp,m_regex_posix.cpp,m_regex_tre.cpp,m_sqlite3.cpp,m_ssl_gnutls.cpp,m_ssl_openssl.cpp
 -./configure --with-cc=$CXX
 -make -j4 install
+ #!/bin/bash
+ set -v
+ if [ "$TRAVIS_OS_NAME" = "linux" ]
+ then
+       sudo apt-get update --assume-yes
+       sudo apt-get install --assume-yes libgeoip-dev libgnutls-dev libldap2-dev libmysqlclient-dev libpcre3-dev libpq-dev libsqlite3-dev libssl-dev libtre-dev
+ elif [ "$TRAVIS_OS_NAME" = "osx" ]
+ then
+       brew update
+       brew install geoip gnutls mysql-connector-c openssl pcre postgresql sqlite3 tre
+       brew link sqlite3 --force
+ else
+       >&2 echo "'$TRAVIS_OS_NAME' is an unknown Travis CI environment!"
+       exit 1
+ fi
+ set -e
++export TEST_BUILD_MODULES="m_geoip.cpp,m_ldap.cpp,m_mysql.cpp,m_pgsql.cpp,m_regex_pcre.cpp,m_regex_posix.cpp,m_regex_tre.cpp,m_sqlite3.cpp,m_ssl_gnutls.cpp,m_ssl_openssl.cpp"
++./tools/test-build $CXX
+ ./run/bin/inspircd --version