]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_dccallow.cpp
Make kicknorejoin respect /INVITE (#1514).
[user/henk/code/inspircd.git] / src / modules / m_dccallow.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
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 ??? <???@???>
11  *
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.
15  *
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
19  * details.
20  *
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/>.
23  */
24
25
26 #include "inspircd.h"
27
28 enum
29 {
30         // From ircd-ratbox.
31         RPL_HELPSTART = 704,
32         RPL_HELPTXT = 705,
33         RPL_ENDOFHELP = 706,
34
35         // InspIRCd-specific?
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
45 };
46
47 static const char* const helptext[] =
48 {
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",
52         "you would type:",
53         "/DCCALLOW +Brain",
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:",
59         "/DCCALLOW +Brain 0",
60         "To remove the user from your DCCALLOW list, type:",
61         "/DCCALLOW -Brain",
62         "To see the users in your DCCALLOW list, type:",
63         "/DCCALLOW LIST",
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."
67 };
68
69 class BannedFileList
70 {
71  public:
72         std::string filemask;
73         std::string action;
74 };
75
76 class DCCAllow
77 {
78  public:
79         std::string nickname;
80         std::string hostmask;
81         time_t set_on;
82         unsigned long length;
83
84         DCCAllow() { }
85
86         DCCAllow(const std::string& nick, const std::string& hm, time_t so, unsigned long ln)
87                 : nickname(nick)
88                 , hostmask(hm)
89                 , set_on(so)
90                 , length(ln)
91         {
92         }
93 };
94
95 typedef std::vector<User *> userlist;
96 userlist ul;
97 typedef std::vector<DCCAllow> dccallowlist;
98 dccallowlist* dl;
99 typedef std::vector<BannedFileList> bannedfilelist;
100 bannedfilelist bfl;
101 typedef SimpleExtItem<dccallowlist> DCCAllowExt;
102
103 class CommandDccallow : public Command
104 {
105         DCCAllowExt& ext;
106
107  public:
108         unsigned int maxentries;
109         CommandDccallow(Module* parent, DCCAllowExt& Ext)
110                 : Command(parent, "DCCALLOW", 0)
111                 , ext(Ext)
112         {
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 */
115         }
116
117         CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
118         {
119                 /* syntax: DCCALLOW [+|-]<nick> (<time>) */
120                 if (!parameters.size())
121                 {
122                         // display current DCCALLOW list
123                         DisplayDCCAllowList(user);
124                         return CMD_FAILURE;
125                 }
126                 else if (parameters.size() > 0)
127                 {
128                         char action = *parameters[0].c_str();
129
130                         // if they didn't specify an action, this is probably a command
131                         if (action != '+' && action != '-')
132                         {
133                                 if (!strcasecmp(parameters[0].c_str(), "LIST"))
134                                 {
135                                         // list current DCCALLOW list
136                                         DisplayDCCAllowList(user);
137                                         return CMD_FAILURE;
138                                 }
139                                 else if (!strcasecmp(parameters[0].c_str(), "HELP"))
140                                 {
141                                         // display help
142                                         DisplayHelp(user);
143                                         return CMD_FAILURE;
144                                 }
145                                 else
146                                 {
147                                         user->WriteNumeric(ERR_UNKNOWNDCCALLOWCMD, "DCCALLOW command not understood. For help on DCCALLOW, type /DCCALLOW HELP");
148                                         return CMD_FAILURE;
149                                 }
150                         }
151
152                         std::string nick(parameters[0], 1);
153                         User *target = ServerInstance->FindNickOnly(nick);
154
155                         if ((target) && (!target->quitting) && (target->registered == REG_ALL))
156                         {
157
158                                 if (action == '-')
159                                 {
160                                         // check if it contains any entries
161                                         dl = ext.get(user);
162                                         if (dl)
163                                         {
164                                                 for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
165                                                 {
166                                                         // search through list
167                                                         if (i->nickname == target->nick)
168                                                         {
169                                                                 dl->erase(i);
170                                                                 user->WriteNumeric(RPL_DCCALLOWREMOVED, user->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", target->nick.c_str()));
171                                                                 break;
172                                                         }
173                                                 }
174                                         }
175                                 }
176                                 else if (action == '+')
177                                 {
178                                         if (target == user)
179                                         {
180                                                 user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, "You cannot add yourself to your own DCCALLOW list!");
181                                                 return CMD_FAILURE;
182                                         }
183
184                                         dl = ext.get(user);
185                                         if (!dl)
186                                         {
187                                                 dl = new dccallowlist;
188                                                 ext.set(user, dl);
189                                                 // add this user to the userlist
190                                                 ul.push_back(user);
191                                         }
192
193                                         if (dl->size() >= maxentries)
194                                         {
195                                                 user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, "Too many nicks on DCCALLOW list");
196                                                 return CMD_FAILURE;
197                                         }
198
199                                         for (dccallowlist::const_iterator k = dl->begin(); k != dl->end(); ++k)
200                                         {
201                                                 if (k->nickname == target->nick)
202                                                 {
203                                                         user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, InspIRCd::Format("%s is already on your DCCALLOW list", target->nick.c_str()));
204                                                         return CMD_FAILURE;
205                                                 }
206                                         }
207
208                                         std::string mask = target->nick+"!"+target->ident+"@"+target->GetDisplayedHost();
209                                         std::string default_length = ServerInstance->Config->ConfValue("dccallow")->getString("length");
210
211                                         unsigned long length;
212                                         if (parameters.size() < 2)
213                                         {
214                                                 length = InspIRCd::Duration(default_length);
215                                         }
216                                         else if (!atoi(parameters[1].c_str()))
217                                         {
218                                                 length = 0;
219                                         }
220                                         else
221                                         {
222                                                 length = InspIRCd::Duration(parameters[1]);
223                                         }
224
225                                         if (!InspIRCd::IsValidMask(mask))
226                                         {
227                                                 return CMD_FAILURE;
228                                         }
229
230                                         dl->push_back(DCCAllow(target->nick, mask, ServerInstance->Time(), length));
231
232                                         if (length > 0)
233                                         {
234                                                 user->WriteNumeric(RPL_DCCALLOWTIMED, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for %ld seconds", target->nick.c_str(), length));
235                                         }
236                                         else
237                                         {
238                                                 user->WriteNumeric(RPL_DCCALLOWPERMANENT, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for this session", target->nick.c_str()));
239                                         }
240
241                                         /* route it. */
242                                         return CMD_SUCCESS;
243                                 }
244                         }
245                         else
246                         {
247                                 // nick doesn't exist
248                                 user->WriteNumeric(Numerics::NoSuchNick(nick));
249                                 return CMD_FAILURE;
250                         }
251                 }
252                 return CMD_FAILURE;
253         }
254
255         RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
256         {
257                 return ROUTE_BROADCAST;
258         }
259
260         void DisplayHelp(User* user)
261         {
262                 user->WriteNumeric(RPL_HELPSTART, "*", "DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP]");
263                 for (size_t i = 0; i < sizeof(helptext)/sizeof(helptext[0]); i++)
264                         user->WriteNumeric(RPL_HELPTXT, "*", helptext[i]);
265                 user->WriteNumeric(RPL_ENDOFHELP, "*", "End of DCCALLOW HELP");
266
267                 LocalUser* localuser = IS_LOCAL(user);
268                 if (localuser)
269                         localuser->CommandFloodPenalty += 4000;
270         }
271
272         void DisplayDCCAllowList(User* user)
273         {
274                  // display current DCCALLOW list
275                 user->WriteNumeric(RPL_DCCALLOWSTART, "Users on your DCCALLOW list:");
276
277                 dl = ext.get(user);
278                 if (dl)
279                 {
280                         for (dccallowlist::const_iterator c = dl->begin(); c != dl->end(); ++c)
281                         {
282                                 user->WriteNumeric(RPL_DCCALLOWLIST, user->nick, InspIRCd::Format("%s (%s)", c->nickname.c_str(), c->hostmask.c_str()));
283                         }
284                 }
285
286                 user->WriteNumeric(RPL_DCCALLOWEND, "End of DCCALLOW list");
287         }
288
289 };
290
291 class ModuleDCCAllow : public Module
292 {
293         DCCAllowExt ext;
294         CommandDccallow cmd;
295
296  public:
297         ModuleDCCAllow()
298                 : ext("dccallow", ExtensionItem::EXT_USER, this)
299                 , cmd(this, ext)
300         {
301         }
302
303         void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE
304         {
305                 dccallowlist* udl = ext.get(user);
306
307                 // remove their DCCALLOW list if they have one
308                 if (udl)
309                         stdalgo::erase(ul, user);
310
311                 // remove them from any DCCALLOW lists
312                 // they are currently on
313                 RemoveNick(user);
314         }
315
316         void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
317         {
318                 RemoveNick(user);
319         }
320
321         ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
322         {
323                 if (!IS_LOCAL(user))
324                         return MOD_RES_PASSTHRU;
325
326                 if (target.type == MessageTarget::TYPE_USER)
327                 {
328                         User* u = target.Get<User>();
329
330                         /* Always allow a user to dcc themselves (although... why?) */
331                         if (user == u)
332                                 return MOD_RES_PASSTHRU;
333
334                         if ((details.text.length()) && (details.text[0] == '\1'))
335                         {
336                                 Expire();
337
338                                 // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :DCC SEND m_dnsbl.cpp 3232235786 52650 9676
339                                 // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :VERSION
340
341                                 if (strncmp(details.text.c_str(), "\1DCC ", 5) == 0)
342                                 {
343                                         dl = ext.get(u);
344                                         if (dl && dl->size())
345                                         {
346                                                 for (dccallowlist::const_iterator iter = dl->begin(); iter != dl->end(); ++iter)
347                                                         if (InspIRCd::Match(user->GetFullHost(), iter->hostmask))
348                                                                 return MOD_RES_PASSTHRU;
349                                         }
350
351                                         std::string buf = details.text.substr(5);
352                                         size_t s = buf.find(' ');
353                                         if (s == std::string::npos)
354                                                 return MOD_RES_PASSTHRU;
355
356                                         const std::string type = buf.substr(0, s);
357
358                                         ConfigTag* conftag = ServerInstance->Config->ConfValue("dccallow");
359                                         bool blockchat = conftag->getBool("blockchat");
360
361                                         if (stdalgo::string::equalsci(type, "SEND"))
362                                         {
363                                                 size_t first;
364
365                                                 buf = buf.substr(s + 1);
366
367                                                 if (!buf.empty() && buf[0] == '"')
368                                                 {
369                                                         s = buf.find('"', 1);
370
371                                                         if (s == std::string::npos || s <= 1)
372                                                                 return MOD_RES_PASSTHRU;
373
374                                                         --s;
375                                                         first = 1;
376                                                 }
377                                                 else
378                                                 {
379                                                         s = buf.find(' ');
380                                                         first = 0;
381                                                 }
382
383                                                 if (s == std::string::npos)
384                                                         return MOD_RES_PASSTHRU;
385
386                                                 std::string defaultaction = conftag->getString("action");
387                                                 std::string filename = buf.substr(first, s);
388
389                                                 bool found = false;
390                                                 for (unsigned int i = 0; i < bfl.size(); i++)
391                                                 {
392                                                         if (InspIRCd::Match(filename, bfl[i].filemask, ascii_case_insensitive_map))
393                                                         {
394                                                                 /* We have a matching badfile entry, override whatever the default action is */
395                                                                 if (stdalgo::string::equalsci(bfl[i].action, "allow"))
396                                                                         return MOD_RES_PASSTHRU;
397                                                                 else
398                                                                 {
399                                                                         found = true;
400                                                                         break;
401                                                                 }
402                                                         }
403                                                 }
404
405                                                 /* only follow the default action if no badfile matches were found above */
406                                                 if ((!found) && (defaultaction == "allow"))
407                                                         return MOD_RES_PASSTHRU;
408
409                                                 user->WriteNotice("The user " + u->nick + " is not accepting DCC SENDs from you. Your file " + filename + " was not sent.");
410                                                 u->WriteNotice(user->nick + " (" + user->ident + "@" + user->GetDisplayedHost() + ") attempted to send you a file named " + filename + ", which was blocked.");
411                                                 u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
412                                                 return MOD_RES_DENY;
413                                         }
414                                         else if ((blockchat) && (stdalgo::string::equalsci(type, "CHAT")))
415                                         {
416                                                 user->WriteNotice("The user " + u->nick + " is not accepting DCC CHAT requests from you.");
417                                                 u->WriteNotice(user->nick + " (" + user->ident + "@" + user->GetDisplayedHost() + ") attempted to initiate a DCC CHAT session, which was blocked.");
418                                                 u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
419                                                 return MOD_RES_DENY;
420                                         }
421                                 }
422                         }
423                 }
424                 return MOD_RES_PASSTHRU;
425         }
426
427         void Expire()
428         {
429                 for (userlist::iterator iter = ul.begin(); iter != ul.end();)
430                 {
431                         User* u = (User*)(*iter);
432                         dl = ext.get(u);
433                         if (dl)
434                         {
435                                 if (dl->size())
436                                 {
437                                         dccallowlist::iterator iter2 = dl->begin();
438                                         while (iter2 != dl->end())
439                                         {
440                                                 time_t expires = iter2->set_on + iter2->length;
441                                                 if (iter2->length != 0 && expires <= ServerInstance->Time())
442                                                 {
443                                                         u->WriteNumeric(RPL_DCCALLOWEXPIRED, u->nick, InspIRCd::Format("DCCALLOW entry for %s has expired", iter2->nickname.c_str()));
444                                                         iter2 = dl->erase(iter2);
445                                                 }
446                                                 else
447                                                 {
448                                                         ++iter2;
449                                                 }
450                                         }
451                                 }
452                                 ++iter;
453                         }
454                         else
455                         {
456                                 iter = ul.erase(iter);
457                         }
458                 }
459         }
460
461         void RemoveNick(User* user)
462         {
463                 /* Iterate through all DCCALLOW lists and remove user */
464                 for (userlist::iterator iter = ul.begin(); iter != ul.end();)
465                 {
466                         User *u = (User*)(*iter);
467                         dl = ext.get(u);
468                         if (dl)
469                         {
470                                 if (dl->size())
471                                 {
472                                         for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
473                                         {
474                                                 if (i->nickname == user->nick)
475                                                 {
476
477                                                         u->WriteNotice(i->nickname + " left the network or changed their nickname and has been removed from your DCCALLOW list");
478                                                         u->WriteNumeric(RPL_DCCALLOWREMOVED, u->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", i->nickname.c_str()));
479                                                         dl->erase(i);
480                                                         break;
481                                                 }
482                                         }
483                                 }
484                                 ++iter;
485                         }
486                         else
487                         {
488                                 iter = ul.erase(iter);
489                         }
490                 }
491         }
492
493         void RemoveFromUserlist(User *user)
494         {
495                 // remove user from userlist
496                 for (userlist::iterator j = ul.begin(); j != ul.end(); ++j)
497                 {
498                         User* u = (User*)(*j);
499                         if (u == user)
500                         {
501                                 ul.erase(j);
502                                 break;
503                         }
504                 }
505         }
506
507         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
508         {
509                 ConfigTag* tag = ServerInstance->Config->ConfValue("dccallow");
510                 cmd.maxentries = tag->getUInt("maxentries", 20);
511
512                 bfl.clear();
513                 ConfigTagList tags = ServerInstance->Config->ConfTags("banfile");
514                 for (ConfigIter i = tags.first; i != tags.second; ++i)
515                 {
516                         BannedFileList bf;
517                         bf.filemask = i->second->getString("pattern");
518                         bf.action = i->second->getString("action");
519                         bfl.push_back(bf);
520                 }
521         }
522
523         Version GetVersion() CXX11_OVERRIDE
524         {
525                 return Version("Provides support for the /DCCALLOW command", VF_COMMON | VF_VENDOR);
526         }
527 };
528
529 MODULE_INIT(ModuleDCCAllow)