2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2019 Matt Schatz <genius3000@g3k.solutions>
5 * Copyright (C) 2018 linuxdaemon <linuxdaemon.irc@gmail.com>
6 * Copyright (C) 2016 Adam <Adam@anope.org>
7 * Copyright (C) 2013, 2017-2019 Sadie Powell <sadie@witchery.services>
8 * Copyright (C) 2012-2016 Attila Molnar <attilamolnar@hush.com>
9 * Copyright (C) 2012, 2014, 2019 Robby <robby@chatbelgie.be>
10 * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
11 * Copyright (C) 2009 Matt Smith <dz@inspircd.org>
12 * Copyright (C) 2008, 2010 Craig Edwards <brain@inspircd.org>
13 * Copyright (C) 2008 John Brooks <special@inspircd.org>
14 * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
15 * Copyright (C) 2007-2008 Dennis Friis <peavey@inspircd.org>
16 * Copyright (C) 2006 jamie <jamie@e03df62e-2008-0410-955e-edbf42e46eb7>
18 * This file is part of InspIRCd. InspIRCd is free software: you can
19 * redistribute it and/or modify it under the terms of the GNU General Public
20 * License as published by the Free Software Foundation, version 2.
22 * This program is distributed in the hope that it will be useful, but WITHOUT
23 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
24 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
42 RPL_DCCALLOWSTART = 990,
43 RPL_DCCALLOWLIST = 991,
44 RPL_DCCALLOWEND = 992,
45 RPL_DCCALLOWTIMED = 993,
46 RPL_DCCALLOWPERMANENT = 994,
47 RPL_DCCALLOWREMOVED = 995,
48 ERR_DCCALLOWINVALID = 996,
49 RPL_DCCALLOWEXPIRED = 997,
50 ERR_UNKNOWNDCCALLOWCMD = 998
53 static const char* const helptext[] =
55 "You may allow DCCs from specific users by specifying a",
56 "DCC allow for the user you want to receive DCCs from.",
57 "For example, to allow the user Brain to send you inspircd.exe",
60 "Brain would then be able to send you files. They would have to",
61 "resend the file again if the server gave them an error message",
62 "before you added them to your DCCALLOW list.",
63 "DCCALLOW entries will be temporary. If you want to add",
64 "them to your DCCALLOW list until you leave IRC, type:",
66 "To remove the user from your DCCALLOW list, type:",
68 "To see the users in your DCCALLOW list, type:",
70 "NOTE: If the user leaves IRC or changes their nickname",
71 " they will be removed from your DCCALLOW list.",
72 " Your DCCALLOW list will be deleted when you leave IRC."
92 DCCAllow(const std::string& nick, const std::string& hm, time_t so, unsigned long ln)
101 typedef std::vector<User *> userlist;
103 typedef std::vector<DCCAllow> dccallowlist;
105 typedef std::vector<BannedFileList> bannedfilelist;
108 class DCCAllowExt : public SimpleExtItem<dccallowlist>
111 unsigned int maxentries;
113 DCCAllowExt(Module* Creator)
114 : SimpleExtItem<dccallowlist>("dccallow", ExtensionItem::EXT_USER, Creator)
118 void FromInternal(Extensible* container, const std::string& value) CXX11_OVERRIDE
120 LocalUser* user = IS_LOCAL(static_cast<User*>(container));
124 // Remove the old list and create a new one.
126 dccallowlist* list = new dccallowlist();
128 irc::spacesepstream ts(value);
129 while (!ts.StreamEnd())
131 // Check we have space for another entry.
132 if (list->size() >= maxentries)
134 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Oversized DCC allow list received for %s: %s",
135 user->uuid.c_str(), value.c_str());
140 // Extract the fields.
142 if (!ts.GetToken(dccallow.nickname) ||
143 !ts.GetToken(dccallow.hostmask) ||
144 !ts.GetNumericToken(dccallow.set_on) ||
145 !ts.GetNumericToken(dccallow.length))
147 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Malformed DCC allow list received for %s: %s",
148 user->uuid.c_str(), value.c_str());
153 // Store the DCC allow entry.
154 list->push_back(dccallow);
159 std::string ToInternal(const Extensible* container, void* item) const CXX11_OVERRIDE
161 dccallowlist* list = static_cast<dccallowlist*>(item);
163 for (dccallowlist::const_iterator iter = list->begin(); iter != list->end(); ++iter)
165 if (iter != list->begin())
168 buf.append(iter->nickname);
170 buf.append(iter->hostmask);
172 buf.append(ConvToStr(iter->set_on));
174 buf.append(ConvToStr(iter->length));
180 class CommandDccallow : public Command
184 unsigned long defaultlength;
185 CommandDccallow(Module* parent, DCCAllowExt& Ext)
186 : Command(parent, "DCCALLOW", 0)
189 syntax = "[(+|-)<nick> [<time>]]|[LIST|HELP]";
190 /* XXX we need to fix this so it can work with translation stuff (i.e. move +- into a seperate param */
193 CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
195 /* syntax: DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP] */
196 if (!parameters.size())
198 // display current DCCALLOW list
199 DisplayDCCAllowList(user);
202 else if (parameters.size() > 0)
204 char action = *parameters[0].c_str();
206 // if they didn't specify an action, this is probably a command
207 if (action != '+' && action != '-')
209 if (irc::equals(parameters[0], "LIST"))
211 // list current DCCALLOW list
212 DisplayDCCAllowList(user);
215 else if (irc::equals(parameters[0], "HELP"))
223 user->WriteNumeric(ERR_UNKNOWNDCCALLOWCMD, "DCCALLOW command not understood. For help on DCCALLOW, type /DCCALLOW HELP");
228 std::string nick(parameters[0], 1);
229 User *target = ServerInstance->FindNickOnly(nick);
231 if ((target) && (!target->quitting) && (target->registered == REG_ALL))
236 // check if it contains any entries
240 for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
242 // search through list
243 if (i->nickname == target->nick)
246 user->WriteNumeric(RPL_DCCALLOWREMOVED, user->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", target->nick.c_str()));
252 else if (action == '+')
256 user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, "You cannot add yourself to your own DCCALLOW list!");
263 dl = new dccallowlist;
265 // add this user to the userlist
269 if (dl->size() >= ext.maxentries)
271 user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, "Too many nicks on DCCALLOW list");
275 for (dccallowlist::const_iterator k = dl->begin(); k != dl->end(); ++k)
277 if (k->nickname == target->nick)
279 user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, InspIRCd::Format("%s is already on your DCCALLOW list", target->nick.c_str()));
284 std::string mask = target->nick+"!"+target->ident+"@"+target->GetDisplayedHost();
285 unsigned long length;
286 if (parameters.size() < 2)
288 length = defaultlength;
290 else if (!InspIRCd::IsValidDuration(parameters[1]))
292 user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, InspIRCd::Format("%s is not a valid DCCALLOW duration", parameters[1].c_str()));
297 if (!InspIRCd::Duration(parameters[1], length))
299 user->WriteNotice("*** Invalid duration for DCC allow");
304 if (!InspIRCd::IsValidMask(mask))
309 dl->push_back(DCCAllow(target->nick, mask, ServerInstance->Time(), length));
313 user->WriteNumeric(RPL_DCCALLOWTIMED, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for %s", target->nick.c_str(), InspIRCd::DurationString(length).c_str()));
317 user->WriteNumeric(RPL_DCCALLOWPERMANENT, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for this session", target->nick.c_str()));
326 // nick doesn't exist
327 user->WriteNumeric(Numerics::NoSuchNick(nick));
334 RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
336 return ROUTE_BROADCAST;
339 void DisplayHelp(User* user)
341 user->WriteNumeric(RPL_HELPSTART, "*", "DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP]");
342 for (size_t i = 0; i < sizeof(helptext)/sizeof(helptext[0]); i++)
343 user->WriteNumeric(RPL_HELPTXT, "*", helptext[i]);
344 user->WriteNumeric(RPL_ENDOFHELP, "*", "End of DCCALLOW HELP");
346 LocalUser* localuser = IS_LOCAL(user);
348 localuser->CommandFloodPenalty += 4000;
351 void DisplayDCCAllowList(User* user)
353 // display current DCCALLOW list
354 user->WriteNumeric(RPL_DCCALLOWSTART, "Users on your DCCALLOW list:");
359 for (dccallowlist::const_iterator c = dl->begin(); c != dl->end(); ++c)
361 user->WriteNumeric(RPL_DCCALLOWLIST, user->nick, InspIRCd::Format("%s (%s)", c->nickname.c_str(), c->hostmask.c_str()));
365 user->WriteNumeric(RPL_DCCALLOWEND, "End of DCCALLOW list");
370 class ModuleDCCAllow : public Module
375 std::string defaultaction;
385 void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE
387 dccallowlist* udl = ext.get(user);
389 // remove their DCCALLOW list if they have one
391 stdalgo::erase(ul, user);
393 // remove them from any DCCALLOW lists
394 // they are currently on
398 void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
403 ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
406 return MOD_RES_PASSTHRU;
408 if (target.type == MessageTarget::TYPE_USER)
410 User* u = target.Get<User>();
412 /* Always allow a user to dcc themselves (although... why?) */
414 return MOD_RES_PASSTHRU;
416 if ((details.text.length()) && (details.text[0] == '\1'))
420 // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :DCC SEND m_dnsbl.cpp 3232235786 52650 9676
421 // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :VERSION
423 if (strncmp(details.text.c_str(), "\1DCC ", 5) == 0)
426 if (dl && dl->size())
428 for (dccallowlist::const_iterator iter = dl->begin(); iter != dl->end(); ++iter)
429 if (InspIRCd::Match(user->GetFullHost(), iter->hostmask))
430 return MOD_RES_PASSTHRU;
433 std::string buf = details.text.substr(5);
434 size_t s = buf.find(' ');
435 if (s == std::string::npos)
436 return MOD_RES_PASSTHRU;
438 const std::string type = buf.substr(0, s);
440 if (stdalgo::string::equalsci(type, "SEND"))
444 buf = buf.substr(s + 1);
446 if (!buf.empty() && buf[0] == '"')
448 s = buf.find('"', 1);
450 if (s == std::string::npos || s <= 1)
451 return MOD_RES_PASSTHRU;
462 if (s == std::string::npos)
463 return MOD_RES_PASSTHRU;
465 std::string filename = buf.substr(first, s);
468 for (unsigned int i = 0; i < bfl.size(); i++)
470 if (InspIRCd::Match(filename, bfl[i].filemask, ascii_case_insensitive_map))
472 /* We have a matching badfile entry, override whatever the default action is */
473 if (stdalgo::string::equalsci(bfl[i].action, "allow"))
474 return MOD_RES_PASSTHRU;
483 /* only follow the default action if no badfile matches were found above */
484 if ((!found) && (defaultaction == "allow"))
485 return MOD_RES_PASSTHRU;
487 user->WriteNotice("The user " + u->nick + " is not accepting DCC SENDs from you. Your file " + filename + " was not sent.");
488 u->WriteNotice(user->nick + " (" + user->ident + "@" + user->GetDisplayedHost() + ") attempted to send you a file named " + filename + ", which was blocked.");
489 u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
492 else if ((blockchat) && (stdalgo::string::equalsci(type, "CHAT")))
494 user->WriteNotice("The user " + u->nick + " is not accepting DCC CHAT requests from you.");
495 u->WriteNotice(user->nick + " (" + user->ident + "@" + user->GetDisplayedHost() + ") attempted to initiate a DCC CHAT session, which was blocked.");
496 u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
502 return MOD_RES_PASSTHRU;
507 for (userlist::iterator iter = ul.begin(); iter != ul.end();)
509 User* u = (User*)(*iter);
515 dccallowlist::iterator iter2 = dl->begin();
516 while (iter2 != dl->end())
518 time_t expires = iter2->set_on + iter2->length;
519 if (iter2->length != 0 && expires <= ServerInstance->Time())
521 u->WriteNumeric(RPL_DCCALLOWEXPIRED, u->nick, InspIRCd::Format("DCCALLOW entry for %s has expired", iter2->nickname.c_str()));
522 iter2 = dl->erase(iter2);
534 iter = ul.erase(iter);
539 void RemoveNick(User* user)
541 /* Iterate through all DCCALLOW lists and remove user */
542 for (userlist::iterator iter = ul.begin(); iter != ul.end();)
544 User *u = (User*)(*iter);
550 for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
552 if (i->nickname == user->nick)
555 u->WriteNotice(i->nickname + " left the network or changed their nickname and has been removed from your DCCALLOW list");
556 u->WriteNumeric(RPL_DCCALLOWREMOVED, u->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", i->nickname.c_str()));
566 iter = ul.erase(iter);
571 void RemoveFromUserlist(User *user)
573 // remove user from userlist
574 for (userlist::iterator j = ul.begin(); j != ul.end(); ++j)
576 User* u = (User*)(*j);
585 void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
587 bannedfilelist newbfl;
588 ConfigTagList tags = ServerInstance->Config->ConfTags("banfile");
589 for (ConfigIter i = tags.first; i != tags.second; ++i)
592 bf.filemask = i->second->getString("pattern");
593 bf.action = i->second->getString("action");
594 newbfl.push_back(bf);
598 ConfigTag* tag = ServerInstance->Config->ConfValue("dccallow");
599 cmd.ext.maxentries = tag->getUInt("maxentries", 20);
600 cmd.defaultlength = tag->getDuration("length", 0);
601 blockchat = tag->getBool("blockchat");
602 defaultaction = tag->getString("action");
605 Version GetVersion() CXX11_OVERRIDE
607 return Version("Provides the DCCALLOW command", VF_COMMON | VF_VENDOR);
611 MODULE_INIT(ModuleDCCAllow)