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;
102 class DCCAllowExt : public SimpleExtItem<dccallowlist>
105 unsigned int maxentries;
107 DCCAllowExt(Module* Creator)
108 : SimpleExtItem<dccallowlist>("dccallow", ExtensionItem::EXT_USER, Creator)
112 void FromInternal(Extensible* container, const std::string& value) CXX11_OVERRIDE
114 LocalUser* user = IS_LOCAL(static_cast<User*>(container));
118 // Remove the old list and create a new one.
120 dccallowlist* list = new dccallowlist();
122 irc::spacesepstream ts(value);
123 while (!ts.StreamEnd())
125 // Check we have space for another entry.
126 if (list->size() >= maxentries)
128 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Oversized DCC allow list received for %s: %s",
129 user->uuid.c_str(), value.c_str());
134 // Extract the fields.
136 if (!ts.GetToken(dccallow.nickname) ||
137 !ts.GetToken(dccallow.hostmask) ||
138 !ts.GetNumericToken(dccallow.set_on) ||
139 !ts.GetNumericToken(dccallow.length))
141 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Malformed DCC allow list received for %s: %s",
142 user->uuid.c_str(), value.c_str());
147 // Store the DCC allow entry.
148 list->push_back(dccallow);
153 std::string ToInternal(const Extensible* container, void* item) const CXX11_OVERRIDE
155 dccallowlist* list = static_cast<dccallowlist*>(item);
157 for (dccallowlist::const_iterator iter = list->begin(); iter != list->end(); ++iter)
159 if (iter != list->begin())
162 buf.append(iter->nickname);
164 buf.append(iter->hostmask);
166 buf.append(ConvToStr(iter->set_on));
168 buf.append(ConvToStr(iter->length));
174 class CommandDccallow : public Command
178 unsigned long defaultlength;
179 CommandDccallow(Module* parent, DCCAllowExt& Ext)
180 : Command(parent, "DCCALLOW", 0)
183 syntax = "[(+|-)<nick> [<time>]]|[LIST|HELP]";
184 /* XXX we need to fix this so it can work with translation stuff (i.e. move +- into a seperate param */
187 CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
189 /* syntax: DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP] */
190 if (!parameters.size())
192 // display current DCCALLOW list
193 DisplayDCCAllowList(user);
196 else if (parameters.size() > 0)
198 char action = *parameters[0].c_str();
200 // if they didn't specify an action, this is probably a command
201 if (action != '+' && action != '-')
203 if (!strcasecmp(parameters[0].c_str(), "LIST"))
205 // list current DCCALLOW list
206 DisplayDCCAllowList(user);
209 else if (!strcasecmp(parameters[0].c_str(), "HELP"))
217 user->WriteNumeric(ERR_UNKNOWNDCCALLOWCMD, "DCCALLOW command not understood. For help on DCCALLOW, type /DCCALLOW HELP");
222 std::string nick(parameters[0], 1);
223 User *target = ServerInstance->FindNickOnly(nick);
225 if ((target) && (!target->quitting) && (target->registered == REG_ALL))
230 // check if it contains any entries
234 for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
236 // search through list
237 if (i->nickname == target->nick)
240 user->WriteNumeric(RPL_DCCALLOWREMOVED, user->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", target->nick.c_str()));
246 else if (action == '+')
250 user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, "You cannot add yourself to your own DCCALLOW list!");
257 dl = new dccallowlist;
259 // add this user to the userlist
263 if (dl->size() >= ext.maxentries)
265 user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, "Too many nicks on DCCALLOW list");
269 for (dccallowlist::const_iterator k = dl->begin(); k != dl->end(); ++k)
271 if (k->nickname == target->nick)
273 user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, InspIRCd::Format("%s is already on your DCCALLOW list", target->nick.c_str()));
278 std::string mask = target->nick+"!"+target->ident+"@"+target->GetDisplayedHost();
279 unsigned long length;
280 if (parameters.size() < 2)
282 length = defaultlength;
284 else if (!InspIRCd::IsValidDuration(parameters[1]))
286 user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, InspIRCd::Format("%s is not a valid DCCALLOW duration", parameters[1].c_str()));
291 if (!InspIRCd::Duration(parameters[1], length))
293 user->WriteNotice("*** Invalid duration for DCC allow");
298 if (!InspIRCd::IsValidMask(mask))
303 dl->push_back(DCCAllow(target->nick, mask, ServerInstance->Time(), length));
307 user->WriteNumeric(RPL_DCCALLOWTIMED, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for %s", target->nick.c_str(), InspIRCd::DurationString(length).c_str()));
311 user->WriteNumeric(RPL_DCCALLOWPERMANENT, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for this session", target->nick.c_str()));
320 // nick doesn't exist
321 user->WriteNumeric(Numerics::NoSuchNick(nick));
328 RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
330 return ROUTE_BROADCAST;
333 void DisplayHelp(User* user)
335 user->WriteNumeric(RPL_HELPSTART, "*", "DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP]");
336 for (size_t i = 0; i < sizeof(helptext)/sizeof(helptext[0]); i++)
337 user->WriteNumeric(RPL_HELPTXT, "*", helptext[i]);
338 user->WriteNumeric(RPL_ENDOFHELP, "*", "End of DCCALLOW HELP");
340 LocalUser* localuser = IS_LOCAL(user);
342 localuser->CommandFloodPenalty += 4000;
345 void DisplayDCCAllowList(User* user)
347 // display current DCCALLOW list
348 user->WriteNumeric(RPL_DCCALLOWSTART, "Users on your DCCALLOW list:");
353 for (dccallowlist::const_iterator c = dl->begin(); c != dl->end(); ++c)
355 user->WriteNumeric(RPL_DCCALLOWLIST, user->nick, InspIRCd::Format("%s (%s)", c->nickname.c_str(), c->hostmask.c_str()));
359 user->WriteNumeric(RPL_DCCALLOWEND, "End of DCCALLOW list");
364 class ModuleDCCAllow : public Module
369 std::string defaultaction;
379 void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE
381 dccallowlist* udl = ext.get(user);
383 // remove their DCCALLOW list if they have one
385 stdalgo::erase(ul, user);
387 // remove them from any DCCALLOW lists
388 // they are currently on
392 void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
397 ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
400 return MOD_RES_PASSTHRU;
402 if (target.type == MessageTarget::TYPE_USER)
404 User* u = target.Get<User>();
406 /* Always allow a user to dcc themselves (although... why?) */
408 return MOD_RES_PASSTHRU;
410 if ((details.text.length()) && (details.text[0] == '\1'))
414 // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :DCC SEND m_dnsbl.cpp 3232235786 52650 9676
415 // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :VERSION
417 if (strncmp(details.text.c_str(), "\1DCC ", 5) == 0)
420 if (dl && dl->size())
422 for (dccallowlist::const_iterator iter = dl->begin(); iter != dl->end(); ++iter)
423 if (InspIRCd::Match(user->GetFullHost(), iter->hostmask))
424 return MOD_RES_PASSTHRU;
427 std::string buf = details.text.substr(5);
428 size_t s = buf.find(' ');
429 if (s == std::string::npos)
430 return MOD_RES_PASSTHRU;
432 const std::string type = buf.substr(0, s);
434 if (stdalgo::string::equalsci(type, "SEND"))
438 buf = buf.substr(s + 1);
440 if (!buf.empty() && buf[0] == '"')
442 s = buf.find('"', 1);
444 if (s == std::string::npos || s <= 1)
445 return MOD_RES_PASSTHRU;
456 if (s == std::string::npos)
457 return MOD_RES_PASSTHRU;
459 std::string filename = buf.substr(first, s);
462 for (unsigned int i = 0; i < bfl.size(); i++)
464 if (InspIRCd::Match(filename, bfl[i].filemask, ascii_case_insensitive_map))
466 /* We have a matching badfile entry, override whatever the default action is */
467 if (stdalgo::string::equalsci(bfl[i].action, "allow"))
468 return MOD_RES_PASSTHRU;
477 /* only follow the default action if no badfile matches were found above */
478 if ((!found) && (defaultaction == "allow"))
479 return MOD_RES_PASSTHRU;
481 user->WriteNotice("The user " + u->nick + " is not accepting DCC SENDs from you. Your file " + filename + " was not sent.");
482 u->WriteNotice(user->nick + " (" + user->ident + "@" + user->GetDisplayedHost() + ") attempted to send you a file named " + filename + ", which was blocked.");
483 u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
486 else if ((blockchat) && (stdalgo::string::equalsci(type, "CHAT")))
488 user->WriteNotice("The user " + u->nick + " is not accepting DCC CHAT requests from you.");
489 u->WriteNotice(user->nick + " (" + user->ident + "@" + user->GetDisplayedHost() + ") attempted to initiate a DCC CHAT session, which was blocked.");
490 u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
496 return MOD_RES_PASSTHRU;
501 for (userlist::iterator iter = ul.begin(); iter != ul.end();)
503 User* u = (User*)(*iter);
509 dccallowlist::iterator iter2 = dl->begin();
510 while (iter2 != dl->end())
512 time_t expires = iter2->set_on + iter2->length;
513 if (iter2->length != 0 && expires <= ServerInstance->Time())
515 u->WriteNumeric(RPL_DCCALLOWEXPIRED, u->nick, InspIRCd::Format("DCCALLOW entry for %s has expired", iter2->nickname.c_str()));
516 iter2 = dl->erase(iter2);
528 iter = ul.erase(iter);
533 void RemoveNick(User* user)
535 /* Iterate through all DCCALLOW lists and remove user */
536 for (userlist::iterator iter = ul.begin(); iter != ul.end();)
538 User *u = (User*)(*iter);
544 for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
546 if (i->nickname == user->nick)
549 u->WriteNotice(i->nickname + " left the network or changed their nickname and has been removed from your DCCALLOW list");
550 u->WriteNumeric(RPL_DCCALLOWREMOVED, u->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", i->nickname.c_str()));
560 iter = ul.erase(iter);
565 void RemoveFromUserlist(User *user)
567 // remove user from userlist
568 for (userlist::iterator j = ul.begin(); j != ul.end(); ++j)
570 User* u = (User*)(*j);
579 void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
581 bannedfilelist newbfl;
582 ConfigTagList tags = ServerInstance->Config->ConfTags("banfile");
583 for (ConfigIter i = tags.first; i != tags.second; ++i)
586 bf.filemask = i->second->getString("pattern");
587 bf.action = i->second->getString("action");
588 newbfl.push_back(bf);
592 ConfigTag* tag = ServerInstance->Config->ConfValue("dccallow");
593 cmd.ext.maxentries = tag->getUInt("maxentries", 20);
594 cmd.defaultlength = tag->getDuration("length", 0);
595 blockchat = tag->getBool("blockchat");
596 defaultaction = tag->getString("action");
599 Version GetVersion() CXX11_OVERRIDE
601 return Version("Provides the DCCALLOW command", VF_COMMON | VF_VENDOR);
605 MODULE_INIT(ModuleDCCAllow)