]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_dccallow.cpp
Update the cloaks of connected users when their IP address changes.
[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 (!InspIRCd::IsValidDuration(parameters[1]))
217                                         {
218                                                 user->WriteNumeric(ERR_DCCALLOWINVALID, user->nick, InspIRCd::Format("%s is not a valid DCCALLOW duration", parameters[1].c_str()));
219                                                 return CMD_FAILURE;
220                                         }
221                                         else
222                                         {
223                                                 length = InspIRCd::Duration(parameters[1]);
224                                         }
225
226                                         if (!InspIRCd::IsValidMask(mask))
227                                         {
228                                                 return CMD_FAILURE;
229                                         }
230
231                                         dl->push_back(DCCAllow(target->nick, mask, ServerInstance->Time(), length));
232
233                                         if (length > 0)
234                                         {
235                                                 user->WriteNumeric(RPL_DCCALLOWTIMED, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for %ld seconds", target->nick.c_str(), length));
236                                         }
237                                         else
238                                         {
239                                                 user->WriteNumeric(RPL_DCCALLOWPERMANENT, user->nick, InspIRCd::Format("Added %s to DCCALLOW list for this session", target->nick.c_str()));
240                                         }
241
242                                         /* route it. */
243                                         return CMD_SUCCESS;
244                                 }
245                         }
246                         else
247                         {
248                                 // nick doesn't exist
249                                 user->WriteNumeric(Numerics::NoSuchNick(nick));
250                                 return CMD_FAILURE;
251                         }
252                 }
253                 return CMD_FAILURE;
254         }
255
256         RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
257         {
258                 return ROUTE_BROADCAST;
259         }
260
261         void DisplayHelp(User* user)
262         {
263                 user->WriteNumeric(RPL_HELPSTART, "*", "DCCALLOW [(+|-)<nick> [<time>]]|[LIST|HELP]");
264                 for (size_t i = 0; i < sizeof(helptext)/sizeof(helptext[0]); i++)
265                         user->WriteNumeric(RPL_HELPTXT, "*", helptext[i]);
266                 user->WriteNumeric(RPL_ENDOFHELP, "*", "End of DCCALLOW HELP");
267
268                 LocalUser* localuser = IS_LOCAL(user);
269                 if (localuser)
270                         localuser->CommandFloodPenalty += 4000;
271         }
272
273         void DisplayDCCAllowList(User* user)
274         {
275                  // display current DCCALLOW list
276                 user->WriteNumeric(RPL_DCCALLOWSTART, "Users on your DCCALLOW list:");
277
278                 dl = ext.get(user);
279                 if (dl)
280                 {
281                         for (dccallowlist::const_iterator c = dl->begin(); c != dl->end(); ++c)
282                         {
283                                 user->WriteNumeric(RPL_DCCALLOWLIST, user->nick, InspIRCd::Format("%s (%s)", c->nickname.c_str(), c->hostmask.c_str()));
284                         }
285                 }
286
287                 user->WriteNumeric(RPL_DCCALLOWEND, "End of DCCALLOW list");
288         }
289
290 };
291
292 class ModuleDCCAllow : public Module
293 {
294         DCCAllowExt ext;
295         CommandDccallow cmd;
296
297  public:
298         ModuleDCCAllow()
299                 : ext("dccallow", ExtensionItem::EXT_USER, this)
300                 , cmd(this, ext)
301         {
302         }
303
304         void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) CXX11_OVERRIDE
305         {
306                 dccallowlist* udl = ext.get(user);
307
308                 // remove their DCCALLOW list if they have one
309                 if (udl)
310                         stdalgo::erase(ul, user);
311
312                 // remove them from any DCCALLOW lists
313                 // they are currently on
314                 RemoveNick(user);
315         }
316
317         void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
318         {
319                 RemoveNick(user);
320         }
321
322         ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
323         {
324                 if (!IS_LOCAL(user))
325                         return MOD_RES_PASSTHRU;
326
327                 if (target.type == MessageTarget::TYPE_USER)
328                 {
329                         User* u = target.Get<User>();
330
331                         /* Always allow a user to dcc themselves (although... why?) */
332                         if (user == u)
333                                 return MOD_RES_PASSTHRU;
334
335                         if ((details.text.length()) && (details.text[0] == '\1'))
336                         {
337                                 Expire();
338
339                                 // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :DCC SEND m_dnsbl.cpp 3232235786 52650 9676
340                                 // :jamie!jamie@test-D4457903BA652E0F.silverdream.org PRIVMSG eimaj :VERSION
341
342                                 if (strncmp(details.text.c_str(), "\1DCC ", 5) == 0)
343                                 {
344                                         dl = ext.get(u);
345                                         if (dl && dl->size())
346                                         {
347                                                 for (dccallowlist::const_iterator iter = dl->begin(); iter != dl->end(); ++iter)
348                                                         if (InspIRCd::Match(user->GetFullHost(), iter->hostmask))
349                                                                 return MOD_RES_PASSTHRU;
350                                         }
351
352                                         std::string buf = details.text.substr(5);
353                                         size_t s = buf.find(' ');
354                                         if (s == std::string::npos)
355                                                 return MOD_RES_PASSTHRU;
356
357                                         const std::string type = buf.substr(0, s);
358
359                                         ConfigTag* conftag = ServerInstance->Config->ConfValue("dccallow");
360                                         bool blockchat = conftag->getBool("blockchat");
361
362                                         if (stdalgo::string::equalsci(type, "SEND"))
363                                         {
364                                                 size_t first;
365
366                                                 buf = buf.substr(s + 1);
367
368                                                 if (!buf.empty() && buf[0] == '"')
369                                                 {
370                                                         s = buf.find('"', 1);
371
372                                                         if (s == std::string::npos || s <= 1)
373                                                                 return MOD_RES_PASSTHRU;
374
375                                                         --s;
376                                                         first = 1;
377                                                 }
378                                                 else
379                                                 {
380                                                         s = buf.find(' ');
381                                                         first = 0;
382                                                 }
383
384                                                 if (s == std::string::npos)
385                                                         return MOD_RES_PASSTHRU;
386
387                                                 std::string defaultaction = conftag->getString("action");
388                                                 std::string filename = buf.substr(first, s);
389
390                                                 bool found = false;
391                                                 for (unsigned int i = 0; i < bfl.size(); i++)
392                                                 {
393                                                         if (InspIRCd::Match(filename, bfl[i].filemask, ascii_case_insensitive_map))
394                                                         {
395                                                                 /* We have a matching badfile entry, override whatever the default action is */
396                                                                 if (stdalgo::string::equalsci(bfl[i].action, "allow"))
397                                                                         return MOD_RES_PASSTHRU;
398                                                                 else
399                                                                 {
400                                                                         found = true;
401                                                                         break;
402                                                                 }
403                                                         }
404                                                 }
405
406                                                 /* only follow the default action if no badfile matches were found above */
407                                                 if ((!found) && (defaultaction == "allow"))
408                                                         return MOD_RES_PASSTHRU;
409
410                                                 user->WriteNotice("The user " + u->nick + " is not accepting DCC SENDs from you. Your file " + filename + " was not sent.");
411                                                 u->WriteNotice(user->nick + " (" + user->ident + "@" + user->GetDisplayedHost() + ") attempted to send you a file named " + filename + ", which was blocked.");
412                                                 u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
413                                                 return MOD_RES_DENY;
414                                         }
415                                         else if ((blockchat) && (stdalgo::string::equalsci(type, "CHAT")))
416                                         {
417                                                 user->WriteNotice("The user " + u->nick + " is not accepting DCC CHAT requests from you.");
418                                                 u->WriteNotice(user->nick + " (" + user->ident + "@" + user->GetDisplayedHost() + ") attempted to initiate a DCC CHAT session, which was blocked.");
419                                                 u->WriteNotice("If you trust " + user->nick + " and were expecting this, you can type /DCCALLOW HELP for information on the DCCALLOW system.");
420                                                 return MOD_RES_DENY;
421                                         }
422                                 }
423                         }
424                 }
425                 return MOD_RES_PASSTHRU;
426         }
427
428         void Expire()
429         {
430                 for (userlist::iterator iter = ul.begin(); iter != ul.end();)
431                 {
432                         User* u = (User*)(*iter);
433                         dl = ext.get(u);
434                         if (dl)
435                         {
436                                 if (dl->size())
437                                 {
438                                         dccallowlist::iterator iter2 = dl->begin();
439                                         while (iter2 != dl->end())
440                                         {
441                                                 time_t expires = iter2->set_on + iter2->length;
442                                                 if (iter2->length != 0 && expires <= ServerInstance->Time())
443                                                 {
444                                                         u->WriteNumeric(RPL_DCCALLOWEXPIRED, u->nick, InspIRCd::Format("DCCALLOW entry for %s has expired", iter2->nickname.c_str()));
445                                                         iter2 = dl->erase(iter2);
446                                                 }
447                                                 else
448                                                 {
449                                                         ++iter2;
450                                                 }
451                                         }
452                                 }
453                                 ++iter;
454                         }
455                         else
456                         {
457                                 iter = ul.erase(iter);
458                         }
459                 }
460         }
461
462         void RemoveNick(User* user)
463         {
464                 /* Iterate through all DCCALLOW lists and remove user */
465                 for (userlist::iterator iter = ul.begin(); iter != ul.end();)
466                 {
467                         User *u = (User*)(*iter);
468                         dl = ext.get(u);
469                         if (dl)
470                         {
471                                 if (dl->size())
472                                 {
473                                         for (dccallowlist::iterator i = dl->begin(); i != dl->end(); ++i)
474                                         {
475                                                 if (i->nickname == user->nick)
476                                                 {
477
478                                                         u->WriteNotice(i->nickname + " left the network or changed their nickname and has been removed from your DCCALLOW list");
479                                                         u->WriteNumeric(RPL_DCCALLOWREMOVED, u->nick, InspIRCd::Format("Removed %s from your DCCALLOW list", i->nickname.c_str()));
480                                                         dl->erase(i);
481                                                         break;
482                                                 }
483                                         }
484                                 }
485                                 ++iter;
486                         }
487                         else
488                         {
489                                 iter = ul.erase(iter);
490                         }
491                 }
492         }
493
494         void RemoveFromUserlist(User *user)
495         {
496                 // remove user from userlist
497                 for (userlist::iterator j = ul.begin(); j != ul.end(); ++j)
498                 {
499                         User* u = (User*)(*j);
500                         if (u == user)
501                         {
502                                 ul.erase(j);
503                                 break;
504                         }
505                 }
506         }
507
508         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
509         {
510                 ConfigTag* tag = ServerInstance->Config->ConfValue("dccallow");
511                 cmd.maxentries = tag->getUInt("maxentries", 20);
512
513                 bfl.clear();
514                 ConfigTagList tags = ServerInstance->Config->ConfTags("banfile");
515                 for (ConfigIter i = tags.first; i != tags.second; ++i)
516                 {
517                         BannedFileList bf;
518                         bf.filemask = i->second->getString("pattern");
519                         bf.action = i->second->getString("action");
520                         bfl.push_back(bf);
521                 }
522         }
523
524         Version GetVersion() CXX11_OVERRIDE
525         {
526                 return Version("Provides support for the /DCCALLOW command", VF_COMMON | VF_VENDOR);
527         }
528 };
529
530 MODULE_INIT(ModuleDCCAllow)