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