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