2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
5 * Copyright (C) 2008 John Brooks <john.brooks@dereferenced.net>
6 * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
7 * Copyright (C) 2006-2008 Craig Edwards <craigedwards@brainbox.cc>
8 * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
9 * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
10 * Copyright (C) 2006 Jamie ??? <???@???>
12 * This file is part of InspIRCd. InspIRCd is free software: you can
13 * redistribute it and/or modify it under the terms of the GNU General Public
14 * License as published by the Free Software Foundation, version 2.
16 * This program is distributed in the hope that it will be useful, but WITHOUT
17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
36 RPL_DCCALLOWSTART = 990,
37 RPL_DCCALLOWLIST = 991,
38 RPL_DCCALLOWEND = 992,
39 RPL_DCCALLOWTIMED = 993,
40 RPL_DCCALLOWPERMANENT = 994,
41 RPL_DCCALLOWREMOVED = 995,
42 ERR_DCCALLOWINVALID = 996,
43 RPL_DCCALLOWEXPIRED = 997,
44 ERR_UNKNOWNDCCALLOWCMD = 998
47 static const char* const helptext[] =
49 "You may allow DCCs from specific users by specifying a",
50 "DCC allow for the user you want to receive DCCs from.",
51 "For example, to allow the user Brain to send you inspircd.exe",
54 "Brain would then be able to send you files. They would have to",
55 "resend the file again if the server gave them an error message",
56 "before you added them to your DCCALLOW list.",
57 "DCCALLOW entries will be temporary by default, if you want to add",
58 "them to your DCCALLOW list until you leave IRC, type:",
60 "To remove the user from your DCCALLOW list, type:",
62 "To see the users in your DCCALLOW list, type:",
64 "NOTE: If the user leaves IRC or changes their nickname",
65 " they will be removed from your DCCALLOW list.",
66 " your DCCALLOW list will be deleted when you leave IRC."
86 DCCAllow(const std::string& nick, const std::string& hm, time_t so, unsigned long ln)
95 typedef std::vector<User *> userlist;
97 typedef std::vector<DCCAllow> dccallowlist;
99 typedef std::vector<BannedFileList> bannedfilelist;
101 typedef SimpleExtItem<dccallowlist> DCCAllowExt;
103 class CommandDccallow : public Command
108 unsigned int maxentries;
109 CommandDccallow(Module* parent, DCCAllowExt& Ext)
110 : Command(parent, "DCCALLOW", 0)
113 syntax = "[(+|-)<nick> [<time>]]|[LIST|HELP]";
114 /* XXX we need to fix this so it can work with translation stuff (i.e. move +- into a seperate param */
117 CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
119 /* syntax: DCCALLOW [+|-]<nick> (<time>) */
120 if (!parameters.size())
122 // display current DCCALLOW list
123 DisplayDCCAllowList(user);
126 else if (parameters.size() > 0)
128 char action = *parameters[0].c_str();
130 // if they didn't specify an action, this is probably a command
131 if (action != '+' && action != '-')
133 if (!strcasecmp(parameters[0].c_str(), "LIST"))
135 // list current DCCALLOW list
136 DisplayDCCAllowList(user);
139 else if (!strcasecmp(parameters[0].c_str(), "HELP"))
147 user->WriteNumeric(ERR_UNKNOWNDCCALLOWCMD, "DCCALLOW command not understood. For help on DCCALLOW, type /DCCALLOW HELP");
152 std::string nick(parameters[0], 1);
153 User *target = ServerInstance->FindNickOnly(nick);
155 if ((target) && (!target->quitting) && (target->registered == REG_ALL))
160 // check if it contains any entries
164 for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
166 // search through list
167 if (i->nickname == target->nick)
170 user->WriteNumeric(RPL_DCCALLOWREMOVED, user->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", target->nick.c_str()));
176 else if (action == '+')
180 user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, "You cannot add yourself to your own DCCALLOW list!");
187 dl = new dccallowlist;
189 // add this user to the userlist
193 if (dl->size() >= maxentries)
195 user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, "Too many nicks on DCCALLOW list");
199 for (dccallowlist::const_iterator k = dl->begin(); k != dl->end(); ++k)
201 if (k->nickname == target->nick)
203 user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, InspIRCd::Format("%s is already on your DCCALLOW list", target->nick.c_str()));
208 std::string mask = target->nick+"!"+target->ident+"@"+target->GetDisplayedHost();
209 std::string default_length = ServerInstance->Config->ConfValue("dccallow")->getString("length");
211 unsigned long length;
212 if (parameters.size() < 2)
214 length = InspIRCd::Duration(default_length);
216 else if (!InspIRCd::IsValidDuration(parameters[1]))
218 user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, InspIRCd::Format("%s is not a valid DCCALLOW duration", parameters[1].c_str()));
223 length = InspIRCd::Duration(parameters[1]);
226 if (!InspIRCd::IsValidMask(mask))
231 dl->push_back(DCCAllow(target->nick, mask, ServerInstance->Time(), length));
235 user->WriteNumeric(RPL_DCCALLOWTIMED, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for %ld seconds", target->nick.c_str(), length));
239 user->WriteNumeric(RPL_DCCALLOWPERMANENT, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for this session", target->nick.c_str()));
248 // nick doesn't exist
249 user->WriteNumeric(Numerics::NoSuchNick(nick));
256 RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
258 return ROUTE_BROADCAST;
261 void DisplayHelp(User* user)
263 user->WriteNumeric(RPL_HELPSTART, "*", "DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP]");
264 for (size_t i = 0; i < sizeof(helptext)/sizeof(helptext[0]); i++)
265 user->WriteNumeric(RPL_HELPTXT, "*", helptext[i]);
266 user->WriteNumeric(RPL_ENDOFHELP, "*", "End of DCCALLOW HELP");
268 LocalUser* localuser = IS_LOCAL(user);
270 localuser->CommandFloodPenalty += 4000;
273 void DisplayDCCAllowList(User* user)
275 // display current DCCALLOW list
276 user->WriteNumeric(RPL_DCCALLOWSTART, "Users on your DCCALLOW list:");
281 for (dccallowlist::const_iterator c = dl->begin(); c != dl->end(); ++c)
283 user->WriteNumeric(RPL_DCCALLOWLIST, user->nick, InspIRCd::Format("%s (%s)", c->nickname.c_str(), c->hostmask.c_str()));
287 user->WriteNumeric(RPL_DCCALLOWEND, "End of DCCALLOW list");
292 class ModuleDCCAllow : public Module
299 : ext("dccallow", ExtensionItem::EXT_USER, this)
304 void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE
306 dccallowlist* udl = ext.get(user);
308 // remove their DCCALLOW list if they have one
310 stdalgo::erase(ul, user);
312 // remove them from any DCCALLOW lists
313 // they are currently on
317 void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
322 ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
325 return MOD_RES_PASSTHRU;
327 if (target.type == MessageTarget::TYPE_USER)
329 User* u = target.Get<User>();
331 /* Always allow a user to dcc themselves (although... why?) */
333 return MOD_RES_PASSTHRU;
335 if ((details.text.length()) && (details.text[0] == '\1'))
339 // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :DCC SEND m_dnsbl.cpp 3232235786 52650 9676
340 // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :VERSION
342 if (strncmp(details.text.c_str(), "\1DCC ", 5) == 0)
345 if (dl && dl->size())
347 for (dccallowlist::const_iterator iter = dl->begin(); iter != dl->end(); ++iter)
348 if (InspIRCd::Match(user->GetFullHost(), iter->hostmask))
349 return MOD_RES_PASSTHRU;
352 std::string buf = details.text.substr(5);
353 size_t s = buf.find(' ');
354 if (s == std::string::npos)
355 return MOD_RES_PASSTHRU;
357 const std::string type = buf.substr(0, s);
359 ConfigTag* conftag = ServerInstance->Config->ConfValue("dccallow");
360 bool blockchat = conftag->getBool("blockchat");
362 if (stdalgo::string::equalsci(type, "SEND"))
366 buf = buf.substr(s + 1);
368 if (!buf.empty() && buf[0] == '"')
370 s = buf.find('"', 1);
372 if (s == std::string::npos || s <= 1)
373 return MOD_RES_PASSTHRU;
384 if (s == std::string::npos)
385 return MOD_RES_PASSTHRU;
387 std::string defaultaction = conftag->getString("action");
388 std::string filename = buf.substr(first, s);
391 for (unsigned int i = 0; i < bfl.size(); i++)
393 if (InspIRCd::Match(filename, bfl[i].filemask, ascii_case_insensitive_map))
395 /* We have a matching badfile entry, override whatever the default action is */
396 if (stdalgo::string::equalsci(bfl[i].action, "allow"))
397 return MOD_RES_PASSTHRU;
406 /* only follow the default action if no badfile matches were found above */
407 if ((!found) && (defaultaction == "allow"))
408 return MOD_RES_PASSTHRU;
410 user->WriteNotice("The user " + u->nick + " is not accepting DCC SENDs from you. Your file " + filename + " was not sent.");
411 u->WriteNotice(user->nick + " (" + user->ident + "@" + user->GetDisplayedHost() + ") attempted to send you a file named " + filename + ", which was blocked.");
412 u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
415 else if ((blockchat) && (stdalgo::string::equalsci(type, "CHAT")))
417 user->WriteNotice("The user " + u->nick + " is not accepting DCC CHAT requests from you.");
418 u->WriteNotice(user->nick + " (" + user->ident + "@" + user->GetDisplayedHost() + ") attempted to initiate a DCC CHAT session, which was blocked.");
419 u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
425 return MOD_RES_PASSTHRU;
430 for (userlist::iterator iter = ul.begin(); iter != ul.end();)
432 User* u = (User*)(*iter);
438 dccallowlist::iterator iter2 = dl->begin();
439 while (iter2 != dl->end())
441 time_t expires = iter2->set_on + iter2->length;
442 if (iter2->length != 0 && expires <= ServerInstance->Time())
444 u->WriteNumeric(RPL_DCCALLOWEXPIRED, u->nick, InspIRCd::Format("DCCALLOW entry for %s has expired", iter2->nickname.c_str()));
445 iter2 = dl->erase(iter2);
457 iter = ul.erase(iter);
462 void RemoveNick(User* user)
464 /* Iterate through all DCCALLOW lists and remove user */
465 for (userlist::iterator iter = ul.begin(); iter != ul.end();)
467 User *u = (User*)(*iter);
473 for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
475 if (i->nickname == user->nick)
478 u->WriteNotice(i->nickname + " left the network or changed their nickname and has been removed from your DCCALLOW list");
479 u->WriteNumeric(RPL_DCCALLOWREMOVED, u->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", i->nickname.c_str()));
489 iter = ul.erase(iter);
494 void RemoveFromUserlist(User *user)
496 // remove user from userlist
497 for (userlist::iterator j = ul.begin(); j != ul.end(); ++j)
499 User* u = (User*)(*j);
508 void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
510 ConfigTag* tag = ServerInstance->Config->ConfValue("dccallow");
511 cmd.maxentries = tag->getUInt("maxentries", 20);
514 ConfigTagList tags = ServerInstance->Config->ConfTags("banfile");
515 for (ConfigIter i = tags.first; i != tags.second; ++i)
518 bf.filemask = i->second->getString("pattern");
519 bf.action = i->second->getString("action");
524 Version GetVersion() CXX11_OVERRIDE
526 return Version("Provides support for the /DCCALLOW command", VF_COMMON | VF_VENDOR);
530 MODULE_INIT(ModuleDCCAllow)