]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/m_dccallow.cpp
Add support for blocking tag messages with the deaf mode.
[user/henk/code/inspircd.git] / src / modules / m_dccallow.cpp
index 23d259f6c55b8bcc64cb761a21ac04d4194bbe76..98bcbf4cd46506083603d3fd5adb813a2907516b 100644 (file)
@@ -1,27 +1,76 @@
-/*       +------------------------------------+
- *       | Inspire Internet Relay Chat Daemon |
- *       +------------------------------------+
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
  *
- *  InspIRCd: (C) 2002-2007 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 <stdio.h>
-#include <vector>
-#include <string.h>
-#include "users.h"
-#include "channels.h"
-#include "modules.h"
+
 #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
 {
@@ -36,97 +85,167 @@ class DCCAllow
        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<userrec *> userlist;
+typedef std::vector<User *> userlist;
 userlist ul;
 typedef std::vector<DCCAllow> dccallowlist;
 dccallowlist* dl;
 typedef std::vector<BannedFileList> bannedfilelist;
 bannedfilelist bfl;
 
-class cmd_dccallow : public command_t
+class DCCAllowExt : public SimpleExtItem<dccallowlist>
 {
  public:
-       cmd_dccallow(InspIRCd* Me) : command_t(Me, "DCCALLOW", 0, 0)
+       unsigned int maxentries;
+
+       DCCAllowExt(Module* Creator)
+               : SimpleExtItem<dccallowlist>("dccallow", ExtensionItem::EXT_USER, Creator)
        {
-               this->source = "m_dccallow.so";
-               syntax = "{[+|-]<nick> <time>|HELP|LIST}";
        }
 
-       CmdResult Handle(const char **parameters, int pcnt, userrec *user)
+       void FromInternal(Extensible* container, const std::string& value) CXX11_OVERRIDE
        {
-               /* syntax: DCCALLOW [+|-]<nick> (<time>) */
-               if (!pcnt)
+               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:
+       DCCAllowExt& ext;
+       unsigned long defaultlength;
+       CommandDccallow(Module* parent, DCCAllowExt& Ext)
+               : Command(parent, "DCCALLOW", 0)
+               , ext(Ext)
+       {
+               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(User* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               /* syntax: DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP] */
+               if (!parameters.size())
                {
                        // display current DCCALLOW list
                        DisplayDCCAllowList(user);
                        return CMD_FAILURE;
                }
-               else if (pcnt > 0)
+               else if (parameters.size() > 0)
                {
-                       char action = *parameters[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], "LIST"))
+                               if (irc::equals(parameters[0], "LIST"))
                                {
                                        // list current DCCALLOW list
                                        DisplayDCCAllowList(user);
                                        return CMD_FAILURE;
-                               } 
-                               else if (!strcasecmp(parameters[0], "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] + 1;
-                       userrec *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 == '-')
                                {
-                                       user->GetExt("dccallow_list", dl);
                                        // check if it contains any entries
+                                       dl = ext.get(user);
                                        if (dl)
                                        {
-                                               if (dl->size())
-                                               {
-                                                       for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
-                                                       {
-                                                               // search through list
-                                                               if (i->nickname == target->nick)
-                                                               {
-                                                                       dl->erase(i);
-                                                                       user->WriteServ("995 %s %s :Removed %s from your DCCALLOW list", user->nick, user->nick, target->nick);
-                                                                       break;
-                                                               }
-                                                       }
-                                               }
-                                       }
-                                       else
-                                       {
-                                               DELETE(dl);
-                                               user->Shrink("dccallow_list");
-                               
-                                               // remove from userlist
-                                               for (userlist::iterator j = ul.begin(); j != ul.end(); ++j)
+                                               for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
                                                {
-                                                       userrec* u = (userrec*)(*j);
-                                                       if (u == user)
+                                                       // search through list
+                                                       if (i->nickname == target->nick)
                                                        {
-                                                               ul.erase(j);
+                                                               dl->erase(i);
+                                                               user->WriteNumeric(RPL_DCCALLOWREMOVED, user->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", target->nick.c_str()));
                                                                break;
                                                        }
                                                }
@@ -134,61 +253,70 @@ class cmd_dccallow : public command_t
                                }
                                else if (action == '+')
                                {
-                                       // fetch current DCCALLOW list
-                                       user->GetExt("dccallow_list", dl);
-                                       // they don't have one, create it
+                                       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->WriteServ("996 %s %s :%s is already on your DCCALLOW list", user->nick, user->nick, target->nick);
-                                                       return CMD_FAILURE;
-                                               }
-                                               else if (ServerInstance->MatchText(user->GetFullHost(), k->hostmask))
-                                               {
-                                                       user->WriteServ("996 %s %s :You cannot add yourself to your own DCCALLOW list!", user->nick, user->nick);
+                                                       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;
-                                       if (pcnt < 2)
+
+                                       std::string mask = target->nick+"!"+target->ident+"@"+target->GetDisplayedHost();
+                                       unsigned long length;
+                                       if (parameters.size() < 2)
                                        {
-                                               length = ServerInstance->Duration(default_length.c_str());
-                                       } 
-                                       else if (!atoi(parameters[1]))
+                                               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->WriteServ("993 %s %s :Added %s to DCCALLOW list for %d seconds", user->nick, user->nick, target->nick, 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->WriteServ("994 %s %s :Added %s to DCCALLOW list for this session", user->nick, user->nick, target->nick);
+                                               user->WriteNumeric(RPL_DCCALLOWPERMANENT, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for this session", target->nick.c_str()));
                                        }
 
                                        /* route it. */
@@ -198,234 +326,226 @@ class cmd_dccallow : public command_t
                        else
                        {
                                // nick doesn't exist
-                               user->WriteServ("401 %s %s :No such nick/channel", user->nick, nick.c_str());
+                               user->WriteNumeric(Numerics::NoSuchNick(nick));
                                return CMD_FAILURE;
                        }
                }
                return CMD_FAILURE;
        }
 
-       void DisplayHelp(userrec* user)
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               user->WriteServ("998 %s :DCCALLOW [<+|->nick [time]] [list] [help]", user->nick);
-               user->WriteServ("998 %s :You may allow DCCs from specific users by specifying a", user->nick);
-               user->WriteServ("998 %s :DCC allow for the user you want to receive DCCs from.", user->nick);
-               user->WriteServ("998 %s :For example, to allow the user Brain to send you inspircd.exe", user->nick);
-               user->WriteServ("998 %s :you would type:", user->nick);
-               user->WriteServ("998 %s :/DCCALLOW +Brain", user->nick);
-               user->WriteServ("998 %s :Brain would then be able to send you files. They would have to", user->nick);
-               user->WriteServ("998 %s :resend the file again if the server gave them an error message", user->nick);
-               user->WriteServ("998 %s :before you added them to your DCCALLOW list.", user->nick);
-               user->WriteServ("998 %s :DCCALLOW entries will be temporary by default, if you want to add", user->nick);
-               user->WriteServ("998 %s :them to your DCCALLOW list until you leave IRC, type:", user->nick);
-               user->WriteServ("998 %s :/DCCALLOW +Brain 0", user->nick);
-               user->WriteServ("998 %s :To remove the user from your DCCALLOW list, type:", user->nick);
-               user->WriteServ("998 %s :/DCCALLOW -Brain", user->nick);
-               user->WriteServ("998 %s :To see the users in your DCCALLOW list, type:", user->nick);
-               user->WriteServ("998 %s :/DCCALLOW LIST", user->nick);
-               user->WriteServ("998 %s :NOTE: If the user leaves IRC or changes their nickname", user->nick);
-               user->WriteServ("998 %s :  they will be removed from your DCCALLOW list.", user->nick);
-               user->WriteServ("998 %s :  your DCCALLOW list will be deleted when you leave IRC.", user->nick);
-               user->WriteServ("999 %s :End of DCCALLOW HELP", user->nick);
+               return ROUTE_BROADCAST;
        }
-       
-       void DisplayDCCAllowList(userrec* user)
+
+       void DisplayHelp(User* user)
+       {
+               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->WriteServ("990 %s :Users on your DCCALLOW list:", user->nick);
-               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->WriteServ("991 %s %s :%s (%s)", user->nick, user->nick, 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->WriteServ("992 %s :End of DCCALLOW list", user->nick);
-       }                       
+
+               user->WriteNumeric(RPL_DCCALLOWEND, "End of DCCALLOW list");
+       }
 
 };
-       
+
 class ModuleDCCAllow : public Module
 {
-       cmd_dccallow* mycommand;
- public:
+       DCCAllowExt ext;
+       CommandDccallow cmd;
+       bool blockchat;
+       std::string defaultaction;
 
-       ModuleDCCAllow(InspIRCd* Me)
-               : Module::Module(Me)
-       {
-               Conf = new ConfigReader(ServerInstance);
-               mycommand = new cmd_dccallow(ServerInstance);
-               ServerInstance->AddCommand(mycommand);
-               ReadFileConf();
-       }
-
-       void Implements(char* List)
+ public:
+       ModuleDCCAllow()
+               : ext(this)
+               , cmd(this, ext)
+               , blockchat(false)
        {
-               List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnUserQuit] = List[I_OnUserPreNick] = List[I_OnRehash] = 1;
        }
 
-       virtual void OnRehash(userrec* 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(userrec* user, const std::string &reason, const std::string &oper_message)
-       {
-               dccallowlist* dl;
-       
                // remove their DCCALLOW list if they have one
-               user->GetExt("dccallow_list", dl);
-               if (dl)
-               {
-                       DELETE(dl);
-                       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(userrec* user, const std::string &newnick)
+       void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
        {
                RemoveNick(user);
-               return 0;
        }
 
-       virtual int OnUserPreMessage(userrec* 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(userrec* 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)
                {
-                       userrec* u = (userrec*)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 (irc::equals(ctcpname, "DCC") && !ctcpbody.empty())
                                {
-                                       u->GetExt("dccallow_list", dl);
-               
+                                       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 (InspIRCd::Match(filename, bfl[i].filemask, ascii_case_insensitive_map))
                                                        {
-                                                               if (bfl[i].action == "allow")
-                                                                       return 0;
-                                                       }
-                                                       else
-                                                       {
-                                                               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, u->nick, filename.c_str());
-                                                       u->WriteServ("NOTICE %s :%s (%s@%s) attempted to send you a file named %s, which was blocked.", u->nick, user->nick, user->ident, user->dhost, 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, user->nick);
-                                                       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, u->nick);
-                                               u->WriteServ("NOTICE %s :%s (%s@%s) attempted to initiate a DCC CHAT session, which was blocked.", u->nick, user->nick, user->ident, user->dhost);
-                                               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, user->nick);
-                                               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();)
                {
-                       userrec* u = (userrec*)(*iter);
-                       u->GetExt("dccallow_list", dl);
-       
+                       User* u = (User*)(*iter);
+                       dl = ext.get(u);
                        if (dl)
                        {
                                if (dl->size())
                                {
-                                       dccallowlist::iterator iter = dl->begin();
-                                       while (iter != dl->end())
+                                       dccallowlist::iterator iter2 = dl->begin();
+                                       while (iter2 != dl->end())
                                        {
-                                               if ((iter->set_on + iter->length) <= ServerInstance->Time())
+                                               time_t expires = iter2->set_on + iter2->length;
+                                               if (iter2->length != 0 && expires <= ServerInstance->Time())
                                                {
-                                                       u->WriteServ("997 %s %s :DCCALLOW entry for %s has expired", u->nick, u->nick, iter->nickname.c_str());
-                                                       iter = dl->erase(iter);
+                                                       u->WriteNumeric(RPL_DCCALLOWEXPIRED, u->nick, InspIRCd::Format("DCCALLOW entry for %s has expired", iter2->nickname.c_str()));
+                                                       iter2 = dl->erase(iter2);
                                                }
                                                else
                                                {
-                                                       ++iter;
+                                                       ++iter2;
                                                }
                                        }
                                }
+                               ++iter;
                        }
                        else
                        {
-                               RemoveFromUserlist(u);
+                               iter = ul.erase(iter);
                        }
                }
        }
-       
-       void RemoveNick(userrec* user)
+
+       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();)
                {
-                       userrec *u = (userrec*)(*iter);
-                       u->GetExt("dccallow_list", dl);
-       
+                       User *u = (User*)(*iter);
+                       dl = ext.get(u);
                        if (dl)
                        {
                                if (dl->size())
@@ -434,28 +554,29 @@ 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, i->nickname.c_str());
-                                                       u->WriteServ("995 %s %s :Removed %s from your DCCALLOW list", u->nick, u->nick, 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);
                        }
                }
        }
 
-       void RemoveFromUserlist(userrec *user)
+       void RemoveFromUserlist(User *user)
        {
                // remove user from userlist
                for (userlist::iterator j = ul.begin(); j != ul.end(); ++j)
                {
-                       userrec* u = (userrec*)(*j);
+                       User* u = (User*)(*j);
                        if (u == user)
                        {
                                ul.erase(j);
@@ -464,50 +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,1,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);
        }
 };
 
-class ModuleDCCAllowFactory : public ModuleFactory
-{
- public:
-       ModuleDCCAllowFactory()
-       {
-       }
-
-       ~ModuleDCCAllowFactory()
-       {
-       }
-
-       virtual Module * CreateModule(InspIRCd* Me)
-       {
-               return new ModuleDCCAllow(Me);
-       }
-
-};
-
-extern "C" void * init_module( void )
-{
-       return new ModuleDCCAllowFactory;
-}
+MODULE_INIT(ModuleDCCAllow)