]> 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 e0d0b96e16b3442d33e2b1ed693681a4059e4a1b..98bcbf4cd46506083603d3fd5adb813a2907516b 100644 (file)
- /* m_dccallow - Jamie Penman-Smithson <jamie@silverdream.org> - September 2006 */
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   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 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/>.
+ */
 
-using namespace std;
 
-#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
 {
  public:
-    std::string filemask;
-    std::string action;
+       std::string filemask;
+       std::string action;
 };
 
 class DCCAllow
 {
  public:
-    std::string nickname;
-    std::string hostmask;
-    time_t set_on;
-    long length;
-
-    DCCAllow() { }
-
-    DCCAllow(std::string nick, std::string hm, time_t so, long ln) : nickname(nick), hostmask(hm), set_on(so), length(ln) { }
+       std::string nickname;
+       std::string hostmask;
+       time_t set_on;
+       unsigned long length;
+
+       DCCAllow() { }
+
+       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)
-    {
-       this->source = "m_dccallow.so";
-       syntax = "{[+|-]<nick> <time>}";
-    }
-
-    CmdResult Handle(const char **parameters, int pcnt, userrec *user)
-    {
-       /* syntax: DCCALLOW [+|-]<nick> (<time>) */
-       
-       if (!pcnt)
+       unsigned int maxentries;
+
+       DCCAllowExt(Module* Creator)
+               : SimpleExtItem<dccallowlist>("dccallow", ExtensionItem::EXT_USER, Creator)
        {
-           // display current DCCALLOW list
-           DisplayDCCAllowList(user);
+       }
+
+       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);
+               }
 
-           return CMD_SUCCESS;
+               // The value was well formed.
+               set(user, list);
        }
-       else if (pcnt > 0)
+
+       std::string ToInternal(const Extensible* container, void* item) const CXX11_OVERRIDE
        {
-           char action = *parameters[0];
-       
-           // if they didn't specify an action, this is probably a command
-           if (action != '+' && action != '-')
-           {
-               if (!strcasecmp(parameters[0], "LIST"))
+               dccallowlist* list = static_cast<dccallowlist*>(item);
+               std::string buf;
+               for (dccallowlist::const_iterator iter = list->begin(); iter != list->end(); ++iter)
                {
-                   // list current DCCALLOW list
-                   DisplayDCCAllowList(user);
-                   return CMD_SUCCESS;
-               } 
-               else if (!strcasecmp(parameters[0], "HELP"))
+                       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 help
-                   DisplayHelp(user);
-                   return CMD_SUCCESS;
+                       // display current DCCALLOW list
+                       DisplayDCCAllowList(user);
+                       return CMD_FAILURE;
                }
-           }
-               
-           std::string nick = parameters[0] + 1;
-           userrec *target = ServerInstance->FindNick(nick);
-
-           if (target)
-           {
-               ServerInstance->Log(DEBUG, "m_dccallow.so: got target %s and action %c", target->nick, action);
-                           
-               if (action == '-')
+               else if (parameters.size() > 0)
                {
-                   user->GetExt("dccallow_list", dl);
-                   // check if it contains any entries
-                   if (dl)
-                   {
-                       if (dl->size())
+                       char action = *parameters[0].c_str();
+
+                       // if they didn't specify an action, this is probably a command
+                       if (action != '+' && action != '-')
                        {
-                           for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
-                           {
-                               // search through list
-                               if (i->nickname == target->nick)
+                               if (irc::equals(parameters[0], "LIST"))
                                {
-                                   dl->erase(i);
-                                   user->WriteServ("995 %s %s :Removed %s from your DCCALLOW list", user->nick, user->nick, target->nick);
-                                   break;
+                                       // list current DCCALLOW list
+                                       DisplayDCCAllowList(user);
+                                       return CMD_FAILURE;
+                               }
+                               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;
                                }
-                           }
                        }
-                   }
-                   else
-                   {
-                       DELETE(dl);
-                       user->Shrink("dccallow_list");
-                       
-                       // remove from userlist
-                       for (userlist::iterator j = ul.begin(); j != ul.end(); ++j)
+
+                       std::string nick(parameters[0], 1);
+                       User *target = ServerInstance->FindNickOnly(nick);
+
+                       if ((target) && (!target->quitting) && (target->registered == REG_ALL))
                        {
-                           userrec* u = (userrec*)(*j);
-                           if (u->nick == user->nick)
-                           {
-                               ul.erase(j);
-                               break;
-                           }
+
+                               if (action == '-')
+                               {
+                                       // check if it contains any entries
+                                       dl = ext.get(user);
+                                       if (dl)
+                                       {
+                                               for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
+                                               {
+                                                       // search through list
+                                                       if (i->nickname == target->nick)
+                                                       {
+                                                               dl->erase(i);
+                                                               user->WriteNumeric(RPL_DCCALLOWREMOVED, user->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", target->nick.c_str()));
+                                                               break;
+                                                       }
+                                               }
+                                       }
+                               }
+                               else if (action == '+')
+                               {
+                                       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;
+                                               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(ERR_DCCALLOWINVALID, user->nick, InspIRCd::Format("%s is already on your DCCALLOW list", target->nick.c_str()));
+                                                       return CMD_FAILURE;
+                                               }
+                                       }
+
+                                       std::string mask = target->nick+"!"+target->ident+"@"+target->GetDisplayedHost();
+                                       unsigned long length;
+                                       if (parameters.size() < 2)
+                                       {
+                                               length = defaultlength;
+                                       }
+                                       else if (!InspIRCd::IsValidDuration(parameters[1]))
+                                       {
+                                               user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, InspIRCd::Format("%s is not a valid DCCALLOW duration", parameters[1].c_str()));
+                                               return CMD_FAILURE;
+                                       }
+                                       else
+                                       {
+                                               if (!InspIRCd::Duration(parameters[1], length))
+                                               {
+                                                       user->WriteNotice("*** Invalid duration for DCC allow");
+                                                       return CMD_FAILURE;
+                                               }
+                                       }
+
+                                       if (!InspIRCd::IsValidMask(mask))
+                                       {
+                                               return CMD_FAILURE;
+                                       }
+
+                                       dl->push_back(DCCAllow(target->nick, mask, ServerInstance->Time(), length));
+
+                                       if (length > 0)
+                                       {
+                                               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(RPL_DCCALLOWPERMANENT, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for this session", target->nick.c_str()));
+                                       }
+
+                                       /* route it. */
+                                       return CMD_SUCCESS;
+                               }
                        }
-                   }
-               }
-               else if (action == '+')
-               {
-                   // fetch current DCCALLOW list
-                   user->GetExt("dccallow_list", dl);
-                   // they don't have one, create it
-                   if (!dl)
-                   {
-                       dl = new dccallowlist;
-                       user->Extend(std::string("dccallow_list"), dl);
-                       // add this user to the userlist
-                       ul.push_back(user);
-                   }
-                   for (dccallowlist::const_iterator k = dl->begin(); k != dl->end(); ++k)
-                   {
-                       if (k->nickname == target->nick)
+                       else
                        {
-                           user->WriteServ("996 %s %s :%s is already on your DCCALLOW list", user->nick, user->nick, target->nick);
-                           return CMD_SUCCESS;
+                               // nick doesn't exist
+                               user->WriteNumeric(Numerics::NoSuchNick(nick));
+                               return CMD_FAILURE;
                        }
-                   }
-               
-                   std::string mask = std::string(target->nick)+"!"+std::string(target->ident)+"@"+std::string(target->dhost);
-                   const char* default_length = Conf->ReadValue("dccallow", "length", 0).c_str();
-
-                   long length;
-                   if (pcnt == 1 || ServerInstance->Duration(parameters[1]) < 1)
-                   {
-                       length = ServerInstance->Duration(default_length);
-                   } 
-                   else if (parameters[1] == 0)
-                   {
-                       length = 0;
-                   }
-                   else
-                   {
-                       length = ServerInstance->Duration(parameters[1]);
-                   }
-
-                   if (!ServerInstance->IsValidMask(mask.c_str()))
-                   {
-                       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);
-                   }
-                   else
-                   {
-                       user->WriteServ("994 %s %s :Added %s to DCCALLOW list for this session", user->nick, user->nick, target->nick);
-                   }
-                   
-                   return CMD_SUCCESS;
                }
-           }
-           else
-           {
-               // nick doesn't exist
-               user->WriteServ("401 %s %s :No such nick/channel", user->nick, nick.c_str());
                return CMD_FAILURE;
-           }
        }
-    return CMD_SUCCESS;
-    }
-
-    void DisplayHelp(userrec* user)
-    {
-       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);
-    }
-
-    void DisplayDCCAllowList(userrec* user)
-    {
-        // display current DCCALLOW list
-       user->WriteServ("990 %s :Users on your DCCALLOW list:", user->nick);
-       user->GetExt("dccallow_list", dl);
-       
-       if (dl)
+
+       RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
+       {
+               return ROUTE_BROADCAST;
+       }
+
+       void DisplayHelp(User* user)
        {
-           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_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(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(RPL_DCCALLOWLIST, user->nick, InspIRCd::Format("%s (%s)", c->nickname.c_str(), c->hostmask.c_str()));
+                       }
+               }
+
+               user->WriteNumeric(RPL_DCCALLOWEND, "End of DCCALLOW list");
        }
-       
-       user->WriteServ("992 %s :End of DCCALLOW list", user->nick);
-    }                  
 
 };
-       
+
 class ModuleDCCAllow : public Module
 {
-    cmd_dccallow* mycommand;
+       DCCAllowExt ext;
+       CommandDccallow cmd;
+       bool blockchat;
+       std::string defaultaction;
 
  public:
-    ModuleDCCAllow(InspIRCd* Me)
-       : Module::Module(Me)
-    {
-       Conf = new ConfigReader(ServerInstance);
-       mycommand = new cmd_dccallow(ServerInstance);
-       ServerInstance->AddCommand(mycommand);
-       ReadFileConf();
-    }
-
-    void Implements(char* List)
-    {
-       List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnUserQuit] = List[I_OnUserPreNick] = List[I_OnRehash] = 1;
-    }
-
-    virtual void OnRehash(const std::string &parameter)
-    {
-               delete Conf;
-       Conf = new ConfigReader(ServerInstance);
-    }
-
-    virtual void OnUserQuit(userrec* user, const std::string &reason)
-    {
-       dccallowlist* dl;
-
-       // remove their DCCALLOW list if they have one
-       user->GetExt("dccallow_list", dl);
-       if (dl)
+       ModuleDCCAllow()
+               : ext(this)
+               , cmd(this, ext)
+               , blockchat(false)
        {
-           DELETE(dl);
-           user->Shrink("dccallow_list");
-           RemoveFromUserlist(user);
        }
-       
-       // remove them from any DCCALLOW lists
-       // they are currently on
-       RemoveNick(user);
-    }
-
-
-    virtual int OnUserPreNick(userrec* user, const std::string &newnick)
-    {
-       RemoveNick(user);
-       return 0;
-    }
-    
-    virtual int OnUserPreMessage(userrec* user, void* dest, int target_type, std::string &text, char status)
-    {
-       return OnUserPreNotice(user, dest, target_type, text, status);
-    }
-
-    virtual int OnUserPreNotice(userrec* user, void* dest, int target_type, std::string &text, char status)
-    {
-       Expire();
-
-       if (target_type == TYPE_USER)
+
+       void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE
        {
-           userrec* u = (userrec*)dest;
-           
-           if ((text.length()) && (text[0] == '\1'))
-           {
-               // :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)
-               {
-                   u->GetExt("dccallow_list", dl);
+               dccallowlist* udl = ext.get(user);
 
-                   if (dl)
-                   {
-                       if (dl->size())
-                       {
-                           for (dccallowlist::const_iterator iter = dl->begin(); iter != dl->end(); ++iter)
-                           {
-                               if (ServerInstance->MatchText(user->GetFullHost(), iter->hostmask))
-                               {
-                                   return 0;
-                               }
-                           }
-                       }
-                   }
+               // remove their DCCALLOW list if they have one
+               if (udl)
+                       stdalgo::erase(ul, user);
 
-                   // tokenize
-                   stringstream ss(text);
-                   std::string buf;
-                   vector<string> tokens;
+               // remove them from any DCCALLOW lists
+               // they are currently on
+               RemoveNick(user);
+       }
 
-                   while (ss >> buf)
-                       tokens.push_back(buf);
+       void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
+       {
+               RemoveNick(user);
+       }
+
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
+       {
+               if (!IS_LOCAL(user))
+                       return MOD_RES_PASSTHRU;
 
-                   irc::string type = tokens[1].c_str();
-                   ServerInstance->Log(DEBUG, "m_dccallow.so: got DCC type %s", type.c_str());
+               if (target.type == MessageTarget::TYPE_USER)
+               {
+                       User* u = target.Get<User>();
 
-                   bool blockchat = Conf->ReadValue("dccallow", "blockchat", 0);
-                   ServerInstance->Log(DEBUG, "m_dccallow.so: got blockchat: %s", blockchat);
+                       /* Always allow a user to dcc themselves (although... why?) */
+                       if (user == u)
+                               return MOD_RES_PASSTHRU;
 
-                   if (type == "SEND")
-                   {
-                       std::string defaultaction = Conf->ReadValue("dccallow", "action", 0);
-                       std::string filename = tokens[2];
-                       
-                       if (strcmp(defaultaction, "allow") == 0) 
-                       {
-                           return 0;
-                       }
-                       
-                       for (unsigned int i = 0; i < bfl.size(); i++)
+                       std::string ctcpname;
+                       std::string ctcpbody;
+                       if (details.IsCTCP(ctcpname, ctcpbody))
                        {
-                           if (ServerInstance->MatchText(filename, bfl[i].filemask))
-                           {
-                               if (strcmp(bfl[i].action.c_str(), "allow") == 0)
+                               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 (irc::equals(ctcpname, "DCC") && !ctcpbody.empty())
                                {
-                                   return 0;
+                                       dl = ext.get(u);
+                                       if (dl && dl->size())
+                                       {
+                                               for (dccallowlist::const_iterator iter = dl->begin(); iter != dl->end(); ++iter)
+                                                       if (InspIRCd::Match(user->GetFullHost(), iter->hostmask))
+                                                               return MOD_RES_PASSTHRU;
+                                       }
+
+                                       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"))
+                                       {
+                                               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 (InspIRCd::Match(filename, bfl[i].filemask, ascii_case_insensitive_map))
+                                                       {
+                                                               /* 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;
+                                                               }
+                                                       }
+                                               }
+
+                                               /* 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 (blockchat && irc::equals(type, "CHAT"))
+                                       {
+                                               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;
+                                       }
                                }
-                           }
-                           else
-                           {
-                               if (strcmp(defaultaction, "allow") == 0)
+                       }
+               }
+               return MOD_RES_PASSTHRU;
+       }
+
+       void Expire()
+       {
+               for (userlist::iterator iter = ul.begin(); iter != ul.end();)
+               {
+                       User* u = (User*)(*iter);
+                       dl = ext.get(u);
+                       if (dl)
+                       {
+                               if (dl->size())
                                {
-                                   return 0;
+                                       dccallowlist::iterator iter2 = dl->begin();
+                                       while (iter2 != dl->end())
+                                       {
+                                               time_t expires = iter2->set_on + iter2->length;
+                                               if (iter2->length != 0 && expires <= ServerInstance->Time())
+                                               {
+                                                       u->WriteNumeric(RPL_DCCALLOWEXPIRED, u->nick, InspIRCd::Format("DCCALLOW entry for %s has expired", iter2->nickname.c_str()));
+                                                       iter2 = dl->erase(iter2);
+                                               }
+                                               else
+                                               {
+                                                       ++iter2;
+                                               }
+                                       }
                                }
-                           }
-                           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);
+                               ++iter;
+                       }
+                       else
+                       {
+                               iter = ul.erase(iter);
                        }
-                   }
-                   else if ((type == "CHAT") && (strcmp(blockchat, "yes") == 0))
-                   {
-                       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;    
                }
-           }
        }
-       return 0;
-    }
 
-    void Expire()
-    {
-       for (userlist::iterator iter = ul.begin(); iter != ul.end(); ++iter)
+       void RemoveNick(User* user)
        {
-           userrec* u = (userrec*)(*iter);
-           u->GetExt("dccallow_list", dl);
-
-           if (dl)
-           {
-               if (dl->size())
+               /* Iterate through all DCCALLOW lists and remove user */
+               for (userlist::iterator iter = ul.begin(); iter != ul.end();)
                {
-                   dccallowlist::iterator iter = dl->begin();
-                   while (iter != dl->end())
-                   {
-                       if ((iter->set_on + iter->length) <= ServerInstance->Time())
+                       User *u = (User*)(*iter);
+                       dl = ext.get(u);
+                       if (dl)
                        {
-                           u->WriteServ("997 %s %s :DCCALLOW entry for %s has expired", u->nick, u->nick, iter->nickname.c_str());
-                           iter = dl->erase(iter);
+                               if (dl->size())
+                               {
+                                       for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
+                                       {
+                                               if (i->nickname == user->nick)
+                                               {
+
+                                                       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
                        {
-                           ++iter;
+                               iter = ul.erase(iter);
                        }
-                   }
                }
-           }
-           else
-           {
-               DELETE(dl);
-               u->Shrink("dccallow_list");
-               RemoveFromUserlist(u);
-               ServerInstance->Log(DEBUG, "m_dccallow.so: UH OH! Couldn't get DCCALLOW list for %s", u->nick);
-           }
        }
-    }
 
-    void RemoveNick(userrec* user)
-    {
-       /* Iterate through all DCCALLOW lists and remove user */
-       for (userlist::iterator iter = ul.begin(); iter != ul.end(); ++iter)
+       void RemoveFromUserlist(User *user)
        {
-           userrec *u = (userrec*)(*iter);
-           u->GetExt("dccallow_list", dl);
-
-           if (dl)
-           {
-               if (dl->size())
+               // remove user from userlist
+               for (userlist::iterator j = ul.begin(); j != ul.end(); ++j)
                {
-                   for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
-                   {
-                       if (i->nickname == user->nick)
+                       User* u = (User*)(*j);
+                       if (u == user)
                        {
-                           
-                           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());
-                           dl->erase(i);
-                           break;
+                               ul.erase(j);
+                               break;
                        }
-                   }
                }
-           }
-           else
-           {
-               DELETE(dl);
-               u->Shrink("dccallow_list");
-               RemoveFromUserlist(u);
-           }
        }
-    }
 
-    void RemoveFromUserlist(userrec *user)
-    {
-       // remove user from userlist
-       for (userlist::iterator j = ul.begin(); j != ul.end(); ++j)
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-           userrec* u = (userrec*)(*j);
-           if (u->nick == user->nick)
-           {
-               ul.erase(j);
-               break;
-           }
+               bannedfilelist newbfl;
+               ConfigTagList tags = ServerInstance->Config->ConfTags("banfile");
+               for (ConfigIter i = tags.first; i != tags.second; ++i)
+               {
+                       BannedFileList bf;
+                       bf.filemask = i->second->getString("pattern");
+                       bf.action = i->second->getString("action");
+                       newbfl.push_back(bf);
+               }
+               bfl.swap(newbfl);
+
+               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");
        }
-    }
 
-    void ReadFileConf()
-    {
-       bfl.clear();
-       for (int i = 0; i < Conf->Enumerate("banfile"); i++)
+       Version GetVersion() CXX11_OVERRIDE
        {
-           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);
+               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);
        }
-    
-    }
-
-    virtual ~ModuleDCCAllow()
-    {
-    }
-
-    virtual Version GetVersion()
-    {
-       return Version(1,0,0,0,VF_COMMON,API_VERSION);
-    }
 };
 
-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)