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. 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 unsigned long defaultlength;
110 CommandDccallow(Module* parent, DCCAllowExt& Ext)
111 : Command(parent, "DCCALLOW", 0)
114 syntax = "[(+|-)<nick> [<time>]]|[LIST|HELP]";
115 /* XXX we need to fix this so it can work with translation stuff (i.e. move +- into a seperate param */
118 CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
120 /* syntax: DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP] */
121 if (!parameters.size())
123 // display current DCCALLOW list
124 DisplayDCCAllowList(user);
127 else if (parameters.size() > 0)
129 char action = *parameters[0].c_str();
131 // if they didn't specify an action, this is probably a command
132 if (action != '+' && action != '-')
134 if (!strcasecmp(parameters[0].c_str(), "LIST"))
136 // list current DCCALLOW list
137 DisplayDCCAllowList(user);
140 else if (!strcasecmp(parameters[0].c_str(), "HELP"))
148 user->WriteNumeric(ERR_UNKNOWNDCCALLOWCMD, "DCCALLOW command not understood. For help on DCCALLOW, type /DCCALLOW HELP");
153 std::string nick(parameters[0], 1);
154 User *target = ServerInstance->FindNickOnly(nick);
156 if ((target) && (!target->quitting) && (target->registered == REG_ALL))
161 // check if it contains any entries
165 for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
167 // search through list
168 if (i->nickname == target->nick)
171 user->WriteNumeric(RPL_DCCALLOWREMOVED, user->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", target->nick.c_str()));
177 else if (action == '+')
181 user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, "You cannot add yourself to your own DCCALLOW list!");
188 dl = new dccallowlist;
190 // add this user to the userlist
194 if (dl->size() >= maxentries)
196 user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, "Too many nicks on DCCALLOW list");
200 for (dccallowlist::const_iterator k = dl->begin(); k != dl->end(); ++k)
202 if (k->nickname == target->nick)
204 user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, InspIRCd::Format("%s is already on your DCCALLOW list", target->nick.c_str()));
209 std::string mask = target->nick+"!"+target->ident+"@"+target->GetDisplayedHost();
210 unsigned long length;
211 if (parameters.size() < 2)
213 length = defaultlength;
215 else if (!InspIRCd::IsValidDuration(parameters[1]))
217 user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, InspIRCd::Format("%s is not a valid DCCALLOW duration", parameters[1].c_str()));
222 if (!InspIRCd::Duration(parameters[1], length))
224 user->WriteNotice("*** Invalid duration for DCC allow");
229 if (!InspIRCd::IsValidMask(mask))
234 dl->push_back(DCCAllow(target->nick, mask, ServerInstance->Time(), length));
238 user->WriteNumeric(RPL_DCCALLOWTIMED, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for %s", target->nick.c_str(), InspIRCd::DurationString(length).c_str()));
242 user->WriteNumeric(RPL_DCCALLOWPERMANENT, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for this session", target->nick.c_str()));
251 // nick doesn't exist
252 user->WriteNumeric(Numerics::NoSuchNick(nick));
259 RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
261 return ROUTE_BROADCAST;
264 void DisplayHelp(User* user)
266 user->WriteNumeric(RPL_HELPSTART, "*", "DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP]");
267 for (size_t i = 0; i < sizeof(helptext)/sizeof(helptext[0]); i++)
268 user->WriteNumeric(RPL_HELPTXT, "*", helptext[i]);
269 user->WriteNumeric(RPL_ENDOFHELP, "*", "End of DCCALLOW HELP");
271 LocalUser* localuser = IS_LOCAL(user);
273 localuser->CommandFloodPenalty += 4000;
276 void DisplayDCCAllowList(User* user)
278 // display current DCCALLOW list
279 user->WriteNumeric(RPL_DCCALLOWSTART, "Users on your DCCALLOW list:");
284 for (dccallowlist::const_iterator c = dl->begin(); c != dl->end(); ++c)
286 user->WriteNumeric(RPL_DCCALLOWLIST, user->nick, InspIRCd::Format("%s (%s)", c->nickname.c_str(), c->hostmask.c_str()));
290 user->WriteNumeric(RPL_DCCALLOWEND, "End of DCCALLOW list");
295 class ModuleDCCAllow : public Module
300 std::string defaultaction;
304 : ext("dccallow", ExtensionItem::EXT_USER, this)
310 void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE
312 dccallowlist* udl = ext.get(user);
314 // remove their DCCALLOW list if they have one
316 stdalgo::erase(ul, user);
318 // remove them from any DCCALLOW lists
319 // they are currently on
323 void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
328 ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
331 return MOD_RES_PASSTHRU;
333 if (target.type == MessageTarget::TYPE_USER)
335 User* u = target.Get<User>();
337 /* Always allow a user to dcc themselves (although... why?) */
339 return MOD_RES_PASSTHRU;
341 if ((details.text.length()) && (details.text[0] == '\1'))
345 // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :DCC SEND m_dnsbl.cpp 3232235786 52650 9676
346 // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :VERSION
348 if (strncmp(details.text.c_str(), "\1DCC ", 5) == 0)
351 if (dl && dl->size())
353 for (dccallowlist::const_iterator iter = dl->begin(); iter != dl->end(); ++iter)
354 if (InspIRCd::Match(user->GetFullHost(), iter->hostmask))
355 return MOD_RES_PASSTHRU;
358 std::string buf = details.text.substr(5);
359 size_t s = buf.find(' ');
360 if (s == std::string::npos)
361 return MOD_RES_PASSTHRU;
363 const std::string type = buf.substr(0, s);
365 if (stdalgo::string::equalsci(type, "SEND"))
369 buf = buf.substr(s + 1);
371 if (!buf.empty() && buf[0] == '"')
373 s = buf.find('"', 1);
375 if (s == std::string::npos || s <= 1)
376 return MOD_RES_PASSTHRU;
387 if (s == std::string::npos)
388 return MOD_RES_PASSTHRU;
390 std::string filename = buf.substr(first, s);
393 for (unsigned int i = 0; i < bfl.size(); i++)
395 if (InspIRCd::Match(filename, bfl[i].filemask, ascii_case_insensitive_map))
397 /* We have a matching badfile entry, override whatever the default action is */
398 if (stdalgo::string::equalsci(bfl[i].action, "allow"))
399 return MOD_RES_PASSTHRU;
408 /* only follow the default action if no badfile matches were found above */
409 if ((!found) && (defaultaction == "allow"))
410 return MOD_RES_PASSTHRU;
412 user->WriteNotice("The user " + u->nick + " is not accepting DCC SENDs from you. Your file " + filename + " was not sent.");
413 u->WriteNotice(user->nick + " (" + user->ident + "@" + user->GetDisplayedHost() + ") attempted to send you a file named " + filename + ", which was blocked.");
414 u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
417 else if ((blockchat) && (stdalgo::string::equalsci(type, "CHAT")))
419 user->WriteNotice("The user " + u->nick + " is not accepting DCC CHAT requests from you.");
420 u->WriteNotice(user->nick + " (" + user->ident + "@" + user->GetDisplayedHost() + ") attempted to initiate a DCC CHAT session, which was blocked.");
421 u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
427 return MOD_RES_PASSTHRU;
432 for (userlist::iterator iter = ul.begin(); iter != ul.end();)
434 User* u = (User*)(*iter);
440 dccallowlist::iterator iter2 = dl->begin();
441 while (iter2 != dl->end())
443 time_t expires = iter2->set_on + iter2->length;
444 if (iter2->length != 0 && expires <= ServerInstance->Time())
446 u->WriteNumeric(RPL_DCCALLOWEXPIRED, u->nick, InspIRCd::Format("DCCALLOW entry for %s has expired", iter2->nickname.c_str()));
447 iter2 = dl->erase(iter2);
459 iter = ul.erase(iter);
464 void RemoveNick(User* user)
466 /* Iterate through all DCCALLOW lists and remove user */
467 for (userlist::iterator iter = ul.begin(); iter != ul.end();)
469 User *u = (User*)(*iter);
475 for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
477 if (i->nickname == user->nick)
480 u->WriteNotice(i->nickname + " left the network or changed their nickname and has been removed from your DCCALLOW list");
481 u->WriteNumeric(RPL_DCCALLOWREMOVED, u->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", i->nickname.c_str()));
491 iter = ul.erase(iter);
496 void RemoveFromUserlist(User *user)
498 // remove user from userlist
499 for (userlist::iterator j = ul.begin(); j != ul.end(); ++j)
501 User* u = (User*)(*j);
510 void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
512 bannedfilelist newbfl;
513 ConfigTagList tags = ServerInstance->Config->ConfTags("banfile");
514 for (ConfigIter i = tags.first; i != tags.second; ++i)
517 bf.filemask = i->second->getString("pattern");
518 bf.action = i->second->getString("action");
519 newbfl.push_back(bf);
523 ConfigTag* tag = ServerInstance->Config->ConfValue("dccallow");
524 cmd.maxentries = tag->getUInt("maxentries", 20);
525 cmd.defaultlength = tag->getDuration("length", 0);
526 blockchat = tag->getBool("blockchat");
527 defaultaction = tag->getString("action");
530 Version GetVersion() CXX11_OVERRIDE
532 return Version("Provides the DCCALLOW command", VF_COMMON | VF_VENDOR);
536 MODULE_INIT(ModuleDCCAllow)