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