]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/m_dccallow.cpp
Convert the ISO 8859-2 nationalchars files to codepage configs.
[user/henk/code/inspircd.git] / src / modules / m_dccallow.cpp
index a18979878ff6738a32c25713b05f8f800825d8c3..98bcbf4cd46506083603d3fd5adb813a2907516b 100644 (file)
-/*       +------------------------------------+
- *       | Inspire Internet Relay Chat Daemon |
- *       +------------------------------------+
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
  *
- *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
- * See: http://www.inspircd.org/wiki/index.php/Credits
+ *   Copyright (C) 2019 Matt Schatz <genius3000@g3k.solutions>
+ *   Copyright (C) 2018 linuxdaemon <linuxdaemon.irc@gmail.com>
+ *   Copyright (C) 2016 Adam <Adam@anope.org>
+ *   Copyright (C) 2013, 2017-2020 Sadie Powell <sadie@witchery.services>
+ *   Copyright (C) 2012-2016 Attila Molnar <attilamolnar@hush.com>
+ *   Copyright (C) 2012, 2014, 2019 Robby <robby@chatbelgie.be>
+ *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2009 Matt Smith <dz@inspircd.org>
+ *   Copyright (C) 2008, 2010 Craig Edwards <brain@inspircd.org>
+ *   Copyright (C) 2008 John Brooks <special@inspircd.org>
+ *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2007-2008 Dennis Friis <peavey@inspircd.org>
+ *   Copyright (C) 2006 jamie <jamie@e03df62e-2008-0410-955e-edbf42e46eb7>
  *
- * This program is free but copyrighted software; see
- *            the file COPYING for details.
+ * 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"
 
-/* $ModDesc: Povides support for the /DCCALLOW command */
+enum
+{
+       // From ircd-ratbox.
+       RPL_HELPSTART = 704,
+       RPL_HELPTXT = 705,
+       RPL_ENDOFHELP = 706,
+
+       // InspIRCd-specific?
+       RPL_DCCALLOWSTART = 990,
+       RPL_DCCALLOWLIST = 991,
+       RPL_DCCALLOWEND = 992,
+       RPL_DCCALLOWTIMED = 993,
+       RPL_DCCALLOWPERMANENT = 994,
+       RPL_DCCALLOWREMOVED = 995,
+       ERR_DCCALLOWINVALID = 996,
+       RPL_DCCALLOWEXPIRED = 997,
+       ERR_UNKNOWNDCCALLOWCMD = 998
+};
 
-static ConfigReader *Conf;
+static const char* const helptext[] =
+{
+       "You may allow DCCs from specific users by specifying a",
+       "DCC allow for the user you want to receive DCCs from.",
+       "For example, to allow the user Brain to send you inspircd.exe",
+       "you would type:",
+       "/DCCALLOW +Brain",
+       "Brain would then be able to send you files. They would have to",
+       "resend the file again if the server gave them an error message",
+       "before you added them to your DCCALLOW list.",
+       "DCCALLOW entries will be temporary. If you want to add",
+       "them to your DCCALLOW list until you leave IRC, type:",
+       "/DCCALLOW +Brain 0",
+       "To remove the user from your DCCALLOW list, type:",
+       "/DCCALLOW -Brain",
+       "To see the users in your DCCALLOW list, type:",
+       "/DCCALLOW LIST",
+       "NOTE: If the user leaves IRC or changes their nickname",
+       "  they will be removed from your DCCALLOW list.",
+       "  Your DCCALLOW list will be deleted when you leave IRC."
+};
 
-class BannedFileList : public classbase
+class BannedFileList
 {
  public:
        std::string filemask;
        std::string action;
 };
 
-class DCCAllow : public classbase
+class DCCAllow
 {
  public:
        std::string nickname;
        std::string hostmask;
        time_t set_on;
-       long length;
+       unsigned long length;
 
        DCCAllow() { }
 
-       DCCAllow(const std::string &nick, const std::string &hm, const time_t so, const long ln) : nickname(nick), hostmask(hm), set_on(so), length(ln) { }
+       DCCAllow(const std::string& nick, const std::string& hm, time_t so, unsigned long ln)
+               : nickname(nick)
+               , hostmask(hm)
+               , set_on(so)
+               , length(ln)
+       {
+       }
 };
 
 typedef std::vector<User *> userlist;
@@ -44,19 +105,96 @@ dccallowlist* dl;
 typedef std::vector<BannedFileList> bannedfilelist;
 bannedfilelist bfl;
 
+class DCCAllowExt : public SimpleExtItem<dccallowlist>
+{
+ public:
+       unsigned int maxentries;
+
+       DCCAllowExt(Module* Creator)
+               : SimpleExtItem<dccallowlist>("dccallow", ExtensionItem::EXT_USER, Creator)
+       {
+       }
+
+       void FromInternal(Extensible* container, const std::string& value) CXX11_OVERRIDE
+       {
+               LocalUser* user = IS_LOCAL(static_cast<User*>(container));
+               if (!user)
+                       return;
+
+               // Remove the old list and create a new one.
+               unset(user);
+               dccallowlist* list = new dccallowlist();
+
+               irc::spacesepstream ts(value);
+               while (!ts.StreamEnd())
+               {
+                       // Check we have space for another entry.
+                       if (list->size() >= maxentries)
+                       {
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Oversized DCC allow list received for %s: %s",
+                                       user->uuid.c_str(), value.c_str());
+                               delete list;
+                               return;
+                       }
+
+                       // Extract the fields.
+                       DCCAllow dccallow;
+                       if (!ts.GetToken(dccallow.nickname) ||
+                               !ts.GetToken(dccallow.hostmask) ||
+                               !ts.GetNumericToken(dccallow.set_on) ||
+                               !ts.GetNumericToken(dccallow.length))
+                       {
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Malformed DCC allow list received for %s: %s",
+                                       user->uuid.c_str(), value.c_str());
+                               delete list;
+                               return;
+                       }
+
+                       // Store the DCC allow entry.
+                       list->push_back(dccallow);
+               }
+
+               // The value was well formed.
+               set(user, list);
+       }
+
+       std::string ToInternal(const Extensible* container, void* item) const CXX11_OVERRIDE
+       {
+               dccallowlist* list = static_cast<dccallowlist*>(item);
+               std::string buf;
+               for (dccallowlist::const_iterator iter = list->begin(); iter != list->end(); ++iter)
+               {
+                       if (iter != list->begin())
+                               buf.push_back(' ');
+
+                       buf.append(iter->nickname);
+                       buf.push_back(' ');
+                       buf.append(iter->hostmask);
+                       buf.push_back(' ');
+                       buf.append(ConvToStr(iter->set_on));
+                       buf.push_back(' ');
+                       buf.append(ConvToStr(iter->length));
+               }
+               return buf;
+       }
+};
+
 class CommandDccallow : public Command
 {
  public:
-       CommandDccallow(InspIRCd* Me) : Command(Me, "DCCALLOW", 0, 0)
+       DCCAllowExt& ext;
+       unsigned long defaultlength;
+       CommandDccallow(Module* parent, DCCAllowExt& Ext)
+               : Command(parent, "DCCALLOW", 0)
+               , ext(Ext)
        {
-               this->source = "m_dccallow.so";
-               syntax = "{[+|-]<nick> <time>|HELP|LIST}";
-               /* XXX we need to fix this so it can work with translation stuff (i.e. move +- into a seperate param */
+               syntax = "[(+|-)<nick> [<time>]]|[LIST|HELP]";
+               /* XXX we need to fix this so it can work with translation stuff (i.e. move +- into a separate param */
        }
 
-       CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               /* syntax: DCCALLOW [+|-]<nick> (<time>) */
+               /* syntax: DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP] */
                if (!parameters.size())
                {
                        // display current DCCALLOW list
@@ -66,34 +204,40 @@ class CommandDccallow : public Command
                else if (parameters.size() > 0)
                {
                        char action = *parameters[0].c_str();
-               
+
                        // if they didn't specify an action, this is probably a command
                        if (action != '+' && action != '-')
                        {
-                               if (!strcasecmp(parameters[0].c_str(), "LIST"))
+                               if (irc::equals(parameters[0], "LIST"))
                                {
                                        // list current DCCALLOW list
                                        DisplayDCCAllowList(user);
                                        return CMD_FAILURE;
-                               } 
-                               else if (!strcasecmp(parameters[0].c_str(), "HELP"))
+                               }
+                               else if (irc::equals(parameters[0], "HELP"))
                                {
                                        // display help
                                        DisplayHelp(user);
                                        return CMD_FAILURE;
                                }
+                               else
+                               {
+                                       user->WriteNumeric(ERR_UNKNOWNDCCALLOWCMD, "DCCALLOW command not understood. For help on DCCALLOW, type /DCCALLOW HELP");
+                                       return CMD_FAILURE;
+                               }
                        }
-                       
-                       std::string nick = parameters[0].substr(1);
-                       User *target = ServerInstance->FindNick(nick);
-       
-                       if (target)
+
+                       std::string nick(parameters[0], 1);
+                       User *target = ServerInstance->FindNickOnly(nick);
+
+                       if ((target) && (!target->quitting) && (target->registered == REG_ALL))
                        {
-                               
+
                                if (action == '-')
                                {
                                        // check if it contains any entries
-                                       if (user->GetExt("dccallow_list", dl))
+                                       dl = ext.get(user);
+                                       if (dl)
                                        {
                                                for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
                                                {
@@ -101,23 +245,7 @@ class CommandDccallow : public Command
                                                        if (i->nickname == target->nick)
                                                        {
                                                                dl->erase(i);
-                                                               user->WriteNumeric(995, "%s %s :Removed %s from your DCCALLOW list", user->nick.c_str(), user->nick.c_str(), target->nick.c_str());
-                                                               break;
-                                                       }
-                                               }
-                                       }
-                                       else
-                                       {
-                                               delete  dl;
-                                               user->Shrink("dccallow_list");
-                               
-                                               // remove from userlist
-                                               for (userlist::iterator j = ul.begin(); j != ul.end(); ++j)
-                                               {
-                                                       User* u = (User*)(*j);
-                                                       if (u == user)
-                                                       {
-                                                               ul.erase(j);
+                                                               user->WriteNumeric(RPL_DCCALLOWREMOVED, user->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", target->nick.c_str()));
                                                                break;
                                                        }
                                                }
@@ -125,58 +253,70 @@ class CommandDccallow : public Command
                                }
                                else if (action == '+')
                                {
-                                       if (!user->GetExt("dccallow_list", dl))
+                                       if (target == user)
+                                       {
+                                               user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, "You cannot add yourself to your own DCCALLOW list!");
+                                               return CMD_FAILURE;
+                                       }
+
+                                       dl = ext.get(user);
+                                       if (!dl)
                                        {
                                                dl = new dccallowlist;
-                                               user->Extend("dccallow_list", dl);
+                                               ext.set(user, dl);
                                                // add this user to the userlist
                                                ul.push_back(user);
                                        }
+
+                                       if (dl->size() >= ext.maxentries)
+                                       {
+                                               user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, "Too many nicks on DCCALLOW list");
+                                               return CMD_FAILURE;
+                                       }
+
                                        for (dccallowlist::const_iterator k = dl->begin(); k != dl->end(); ++k)
                                        {
                                                if (k->nickname == target->nick)
                                                {
-                                                       user->WriteNumeric(996, "%s %s :%s is already on your DCCALLOW list", user->nick.c_str(), user->nick.c_str(), target->nick.c_str());
-                                                       return CMD_FAILURE;
-                                               }
-                                               else if (ServerInstance->MatchText(user->GetFullHost(), k->hostmask))
-                                               {
-                                                       user->WriteNumeric(996, "%s %s :You cannot add yourself to your own DCCALLOW list!", user->nick.c_str(), user->nick.c_str());
+                                                       user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, InspIRCd::Format("%s is already on your DCCALLOW list", target->nick.c_str()));
                                                        return CMD_FAILURE;
                                                }
                                        }
-                               
-                                       std::string mask = std::string(target->nick)+"!"+std::string(target->ident)+"@"+std::string(target->dhost);
-                                       std::string default_length = Conf->ReadValue("dccallow", "length", 0);
-               
-                                       long length;
+
+                                       std::string mask = target->nick+"!"+target->ident+"@"+target->GetDisplayedHost();
+                                       unsigned long length;
                                        if (parameters.size() < 2)
                                        {
-                                               length = ServerInstance->Duration(default_length);
-                                       } 
-                                       else if (!atoi(parameters[1].c_str()))
+                                               length = defaultlength;
+                                       }
+                                       else if (!InspIRCd::IsValidDuration(parameters[1]))
                                        {
-                                               length = 0;
+                                               user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, InspIRCd::Format("%s is not a valid DCCALLOW duration", parameters[1].c_str()));
+                                               return CMD_FAILURE;
                                        }
                                        else
                                        {
-                                               length = ServerInstance->Duration(parameters[1]);
+                                               if (!InspIRCd::Duration(parameters[1], length))
+                                               {
+                                                       user->WriteNotice("*** Invalid duration for DCC allow");
+                                                       return CMD_FAILURE;
+                                               }
                                        }
-       
-                                       if (!ServerInstance->IsValidMask(mask.c_str()))
+
+                                       if (!InspIRCd::IsValidMask(mask))
                                        {
                                                return CMD_FAILURE;
                                        }
-                       
+
                                        dl->push_back(DCCAllow(target->nick, mask, ServerInstance->Time(), length));
-                       
+
                                        if (length > 0)
                                        {
-                                               user->WriteNumeric(993, "%s %s :Added %s to DCCALLOW list for %ld seconds", user->nick.c_str(), user->nick.c_str(), target->nick.c_str(), length);
+                                               user->WriteNumeric(RPL_DCCALLOWTIMED, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for %s", target->nick.c_str(), InspIRCd::DurationString(length).c_str()));
                                        }
                                        else
                                        {
-                                               user->WriteNumeric(994, "%s %s :Added %s to DCCALLOW list for this session", user->nick.c_str(), user->nick.c_str(), target->nick.c_str());
+                                               user->WriteNumeric(RPL_DCCALLOWPERMANENT, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for this session", target->nick.c_str()));
                                        }
 
                                        /* route it. */
@@ -186,202 +326,202 @@ class CommandDccallow : public Command
                        else
                        {
                                // nick doesn't exist
-                               user->WriteNumeric(401, "%s %s :No such nick/channel", user->nick.c_str(), nick.c_str());
+                               user->WriteNumeric(Numerics::NoSuchNick(nick));
                                return CMD_FAILURE;
                        }
                }
                return CMD_FAILURE;
        }
 
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               return ROUTE_BROADCAST;
+       }
+
        void DisplayHelp(User* user)
        {
-               user->WriteNumeric(998, "%s :DCCALLOW [<+|->nick.c_str() [time]] [list] [help]", user->nick.c_str());
-               user->WriteNumeric(998, "%s :You may allow DCCs from specific users by specifying a", user->nick.c_str());
-               user->WriteNumeric(998, "%s :DCC allow for the user you want to receive DCCs from.", user->nick.c_str());
-               user->WriteNumeric(998, "%s :For example, to allow the user Brain to send you inspircd.exe", user->nick.c_str());
-               user->WriteNumeric(998, "%s :you would type:", user->nick.c_str());
-               user->WriteNumeric(998, "%s :/DCCALLOW +Brain", user->nick.c_str());
-               user->WriteNumeric(998, "%s :Brain would then be able to send you files. They would have to", user->nick.c_str());
-               user->WriteNumeric(998, "%s :resend the file again if the server gave them an error message", user->nick.c_str());
-               user->WriteNumeric(998, "%s :before you added them to your DCCALLOW list.", user->nick.c_str());
-               user->WriteNumeric(998, "%s :DCCALLOW entries will be temporary by default, if you want to add", user->nick.c_str());
-               user->WriteNumeric(998, "%s :them to your DCCALLOW list until you leave IRC, type:", user->nick.c_str());
-               user->WriteNumeric(998, "%s :/DCCALLOW +Brain 0", user->nick.c_str());
-               user->WriteNumeric(998, "%s :To remove the user from your DCCALLOW list, type:", user->nick.c_str());
-               user->WriteNumeric(998, "%s :/DCCALLOW -Brain", user->nick.c_str());
-               user->WriteNumeric(998, "%s :To see the users in your DCCALLOW list, type:", user->nick.c_str());
-               user->WriteNumeric(998, "%s :/DCCALLOW LIST", user->nick.c_str());
-               user->WriteNumeric(998, "%s :NOTE: If the user leaves IRC or changes their nickname", user->nick.c_str());
-               user->WriteNumeric(998, "%s :  they will be removed from your DCCALLOW list.", user->nick.c_str());
-               user->WriteNumeric(998, "%s :  your DCCALLOW list will be deleted when you leave IRC.", user->nick.c_str());
-               user->WriteNumeric(999, "%s :End of DCCALLOW HELP", user->nick.c_str());
+               user->WriteNumeric(RPL_HELPSTART, "*", "DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP]");
+               for (size_t i = 0; i < sizeof(helptext)/sizeof(helptext[0]); i++)
+                       user->WriteNumeric(RPL_HELPTXT, "*", helptext[i]);
+               user->WriteNumeric(RPL_ENDOFHELP, "*", "End of DCCALLOW HELP");
+
+               LocalUser* localuser = IS_LOCAL(user);
+               if (localuser)
+                       localuser->CommandFloodPenalty += 4000;
        }
-       
+
        void DisplayDCCAllowList(User* user)
        {
                 // display current DCCALLOW list
-               user->WriteNumeric(990, "%s :Users on your DCCALLOW list:", user->nick.c_str());
-       
-               if (user->GetExt("dccallow_list", dl))
+               user->WriteNumeric(RPL_DCCALLOWSTART, "Users on your DCCALLOW list:");
+
+               dl = ext.get(user);
+               if (dl)
                {
                        for (dccallowlist::const_iterator c = dl->begin(); c != dl->end(); ++c)
                        {
-                               user->WriteNumeric(991, "%s %s :%s (%s)", user->nick.c_str(), user->nick.c_str(), c->nickname.c_str(), c->hostmask.c_str());
+                               user->WriteNumeric(RPL_DCCALLOWLIST, user->nick, InspIRCd::Format("%s (%s)", c->nickname.c_str(), c->hostmask.c_str()));
                        }
                }
-               
-               user->WriteNumeric(992, "%s :End of DCCALLOW list", user->nick.c_str());
-       }                       
+
+               user->WriteNumeric(RPL_DCCALLOWEND, "End of DCCALLOW list");
+       }
 
 };
-       
+
 class ModuleDCCAllow : public Module
 {
-       CommandDccallow* mycommand;
- public:
+       DCCAllowExt ext;
+       CommandDccallow cmd;
+       bool blockchat;
+       std::string defaultaction;
 
-       ModuleDCCAllow(InspIRCd* Me)
-               : Module(Me)
+ public:
+       ModuleDCCAllow()
+               : ext(this)
+               , cmd(this, ext)
+               , blockchat(false)
        {
-               Conf = new ConfigReader(ServerInstance);
-               mycommand = new CommandDccallow(ServerInstance);
-               ServerInstance->AddCommand(mycommand);
-               ReadFileConf();
-               Implementation eventlist[] = { I_OnUserPreMessage, I_OnUserPreNotice, I_OnUserQuit, I_OnUserPreNick, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, this, 5);
        }
 
-
-       virtual void OnRehash(User* user, const std::string &parameter)
+       void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE
        {
-               delete Conf;
-               Conf = new ConfigReader(ServerInstance);
-       }
+               dccallowlist* udl = ext.get(user);
 
-       virtual void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
-       {
-               dccallowlist* udl;
-       
                // remove their DCCALLOW list if they have one
-               if (user->GetExt("dccallow_list", udl))
-               {
-                       delete udl;
-                       user->Shrink("dccallow_list");
-                       RemoveFromUserlist(user);
-               }
-               
+               if (udl)
+                       stdalgo::erase(ul, user);
+
                // remove them from any DCCALLOW lists
                // they are currently on
                RemoveNick(user);
        }
 
-
-       virtual int OnUserPreNick(User* user, const std::string &newnick)
+       void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
        {
                RemoveNick(user);
-               return 0;
        }
 
-       virtual int OnUserPreMessage(User* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list)
-       {
-               return OnUserPreNotice(user, dest, target_type, text, status, exempt_list);
-       }
-
-       virtual int OnUserPreNotice(User* user, void* dest, int target_type, std::string &text, char status, CUList &exempt_list)
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
        {
                if (!IS_LOCAL(user))
-                       return 0;
+                       return MOD_RES_PASSTHRU;
 
-               if (target_type == TYPE_USER)
+               if (target.type == MessageTarget::TYPE_USER)
                {
-                       User* u = (User*)dest;
+                       User* u = target.Get<User>();
 
                        /* Always allow a user to dcc themselves (although... why?) */
                        if (user == u)
-                               return 0;
-               
-                       if ((text.length()) && (text[0] == '\1'))
+                               return MOD_RES_PASSTHRU;
+
+                       std::string ctcpname;
+                       std::string ctcpbody;
+                       if (details.IsCTCP(ctcpname, ctcpbody))
                        {
                                Expire();
 
                                // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :DCC SEND m_dnsbl.cpp 3232235786 52650 9676
                                // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :VERSION
-                                       
-                               if (strncmp(text.c_str(), "\1DCC ", 5) == 0)
-                               {       
-                                       if (u->GetExt("dccallow_list", dl) && dl->size())
+
+                               if (irc::equals(ctcpname, "DCC") && !ctcpbody.empty())
+                               {
+                                       dl = ext.get(u);
+                                       if (dl && dl->size())
                                        {
                                                for (dccallowlist::const_iterator iter = dl->begin(); iter != dl->end(); ++iter)
-                                                       if (ServerInstance->MatchText(user->GetFullHost(), iter->hostmask))
-                                                               return 0;
+                                                       if (InspIRCd::Match(user->GetFullHost(), iter->hostmask))
+                                                               return MOD_RES_PASSTHRU;
                                        }
-               
-                                       // tokenize
-                                       std::stringstream ss(text);
-                                       std::string buf;
-                                       std::vector<std::string> tokens;
-               
-                                       while (ss >> buf)
-                                               tokens.push_back(buf);
-               
-                                       irc::string type = tokens[1].c_str();
-               
-                                       bool blockchat = Conf->ReadFlag("dccallow", "blockchat", 0);
-               
-                                       if (type == "SEND")
+
+                                       size_t s = ctcpbody.find(' ');
+                                       if (s == std::string::npos)
+                                               return MOD_RES_PASSTHRU;
+
+                                       const std::string type = ctcpbody.substr(0, s);
+
+                                       if (irc::equals(type, "SEND"))
                                        {
-                                               std::string defaultaction = Conf->ReadValue("dccallow", "action", 0);
-                                               std::string filename = tokens[2];
-                                       
-                                               if (defaultaction == "allow") 
-                                                       return 0;
-                               
+                                               size_t first;
+
+                                               std::string buf = ctcpbody.substr(s + 1);
+
+                                               if (!buf.empty() && buf[0] == '"')
+                                               {
+                                                       s = buf.find('"', 1);
+
+                                                       if (s == std::string::npos || s <= 1)
+                                                               return MOD_RES_PASSTHRU;
+
+                                                       --s;
+                                                       first = 1;
+                                               }
+                                               else
+                                               {
+                                                       s = buf.find(' ');
+                                                       first = 0;
+                                               }
+
+                                               if (s == std::string::npos)
+                                                       return MOD_RES_PASSTHRU;
+
+                                               std::string filename = buf.substr(first, s);
+
+                                               bool found = false;
                                                for (unsigned int i = 0; i < bfl.size(); i++)
                                                {
-                                                       if (ServerInstance->MatchText(filename, bfl[i].filemask))
-                                                       {
-                                                               if (bfl[i].action == "allow")
-                                                                       return 0;
-                                                       }
-                                                       else
+                                                       if (InspIRCd::Match(filename, bfl[i].filemask, ascii_case_insensitive_map))
                                                        {
-                                                               if (defaultaction == "allow")
-                                                                       return 0;
+                                                               /* We have a matching badfile entry, override whatever the default action is */
+                                                               if (stdalgo::string::equalsci(bfl[i].action, "allow"))
+                                                                       return MOD_RES_PASSTHRU;
+                                                               else
+                                                               {
+                                                                       found = true;
+                                                                       break;
+                                                               }
                                                        }
-                                                       user->WriteServ("NOTICE %s :The user %s is not accepting DCC SENDs from you. Your file %s was not sent.", user->nick.c_str(), u->nick.c_str(), filename.c_str());
-                                                       u->WriteServ("NOTICE %s :%s (%s@%s) attempted to send you a file named %s, which was blocked.", u->nick.c_str(), user->nick.c_str(), user->ident.c_str(), user->dhost.c_str(), filename.c_str());
-                                                       u->WriteServ("NOTICE %s :If you trust %s and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.", u->nick.c_str(), user->nick.c_str());
-                                                       return 1;
                                                }
+
+                                               /* only follow the default action if no badfile matches were found above */
+                                               if ((!found) && (defaultaction == "allow"))
+                                                       return MOD_RES_PASSTHRU;
+
+                                               user->WriteNotice("The user " + u->nick + " is not accepting DCC SENDs from you. Your file " + filename + " was not sent.");
+                                               u->WriteNotice(user->nick + " (" + user->ident + "@" + user->GetDisplayedHost() + ") attempted to send you a file named " + filename + ", which was blocked.");
+                                               u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
+                                               return MOD_RES_DENY;
                                        }
-                                       else if ((type == "CHAT") && (blockchat))
+                                       else if (blockchat && irc::equals(type, "CHAT"))
                                        {
-                                               user->WriteServ("NOTICE %s :The user %s is not accepting DCC CHAT requests from you.", user->nick.c_str(), u->nick.c_str());
-                                               u->WriteServ("NOTICE %s :%s (%s@%s) attempted to initiate a DCC CHAT session, which was blocked.", u->nick.c_str(), user->nick.c_str(), user->ident.c_str(), user->dhost.c_str());
-                                               u->WriteServ("NOTICE %s :If you trust %s and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.", u->nick.c_str(), user->nick.c_str());
-                                               return 1;
+                                               user->WriteNotice("The user " + u->nick + " is not accepting DCC CHAT requests from you.");
+                                               u->WriteNotice(user->nick + " (" + user->ident + "@" + user->GetDisplayedHost() + ") attempted to initiate a DCC CHAT session, which was blocked.");
+                                               u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
+                                               return MOD_RES_DENY;
                                        }
                                }
                        }
                }
-               return 0;
+               return MOD_RES_PASSTHRU;
        }
-       
+
        void Expire()
        {
-               for (userlist::iterator iter = ul.begin(); iter != ul.end(); ++iter)
+               for (userlist::iterator iter = ul.begin(); iter != ul.end();)
                {
                        User* u = (User*)(*iter);
-                       if (u->GetExt("dccallow_list", dl))
+                       dl = ext.get(u);
+                       if (dl)
                        {
                                if (dl->size())
                                {
                                        dccallowlist::iterator iter2 = dl->begin();
                                        while (iter2 != dl->end())
                                        {
-                                               if (iter2->length != 0 && (iter2->set_on + iter2->length) <= ServerInstance->Time())
+                                               time_t expires = iter2->set_on + iter2->length;
+                                               if (iter2->length != 0 && expires <= ServerInstance->Time())
                                                {
-                                                       u->WriteNumeric(997, "%s %s :DCCALLOW entry for %s has expired", u->nick.c_str(), u->nick.c_str(), iter2->nickname.c_str());
+                                                       u->WriteNumeric(RPL_DCCALLOWEXPIRED, u->nick, InspIRCd::Format("DCCALLOW entry for %s has expired", iter2->nickname.c_str()));
                                                        iter2 = dl->erase(iter2);
                                                }
                                                else
@@ -390,21 +530,23 @@ class ModuleDCCAllow : public Module
                                                }
                                        }
                                }
+                               ++iter;
                        }
                        else
                        {
-                               RemoveFromUserlist(u);
+                               iter = ul.erase(iter);
                        }
                }
        }
-       
+
        void RemoveNick(User* user)
        {
                /* Iterate through all DCCALLOW lists and remove user */
-               for (userlist::iterator iter = ul.begin(); iter != ul.end(); ++iter)
+               for (userlist::iterator iter = ul.begin(); iter != ul.end();)
                {
                        User *u = (User*)(*iter);
-                       if (u->GetExt("dccallow_list", dl))
+                       dl = ext.get(u);
+                       if (dl)
                        {
                                if (dl->size())
                                {
@@ -412,18 +554,19 @@ class ModuleDCCAllow : public Module
                                        {
                                                if (i->nickname == user->nick)
                                                {
-                                       
-                                                       u->WriteServ("NOTICE %s :%s left the network or changed their nickname and has been removed from your DCCALLOW list", u->nick.c_str(), i->nickname.c_str());
-                                                       u->WriteNumeric(995, "%s %s :Removed %s from your DCCALLOW list", u->nick.c_str(), u->nick.c_str(), i->nickname.c_str());
+
+                                                       u->WriteNotice(i->nickname + " left the network or changed their nickname and has been removed from your DCCALLOW list");
+                                                       u->WriteNumeric(RPL_DCCALLOWREMOVED, u->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", i->nickname.c_str()));
                                                        dl->erase(i);
                                                        break;
                                                }
                                        }
                                }
+                               ++iter;
                        }
                        else
                        {
-                               RemoveFromUserlist(u);
+                               iter = ul.erase(iter);
                        }
                }
        }
@@ -442,30 +585,30 @@ class ModuleDCCAllow : public Module
                }
        }
 
-       void ReadFileConf()
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               bfl.clear();
-               for (int i = 0; i < Conf->Enumerate("banfile"); i++)
+               bannedfilelist newbfl;
+               ConfigTagList tags = ServerInstance->Config->ConfTags("banfile");
+               for (ConfigIter i = tags.first; i != tags.second; ++i)
                {
                        BannedFileList bf;
-                       std::string fileglob = Conf->ReadValue("banfile", "pattern", i);
-                       std::string action = Conf->ReadValue("banfile", "action", i);
-                       bf.filemask = fileglob;
-                       bf.action = action;
-                       bfl.push_back(bf);
+                       bf.filemask = i->second->getString("pattern");
+                       bf.action = i->second->getString("action");
+                       newbfl.push_back(bf);
                }
-       
-       }
+               bfl.swap(newbfl);
 
-       virtual ~ModuleDCCAllow()
-       {
+               ConfigTag* tag = ServerInstance->Config->ConfValue("dccallow");
+               cmd.ext.maxentries = tag->getUInt("maxentries", 20);
+               cmd.defaultlength = tag->getDuration("length", 0);
+               blockchat = tag->getBool("blockchat");
+               defaultaction = tag->getString("action");
        }
 
-       virtual Version GetVersion()
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return Version(1, 2, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
+               return Version("Allows the server administrator to configure what files are allowed to be sent via DCC SEND and allows users to configure who can send them DCC CHAT and DCC SEND requests.", VF_COMMON | VF_VENDOR);
        }
 };
 
 MODULE_INIT(ModuleDCCAllow)
-