]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_callerid.cpp
Add support for blacklists and whitelists, just http password auth to go (the most...
[user/henk/code/inspircd.git] / src / modules / m_callerid.cpp
1 #include "inspircd.h"
2 #include "users.h"
3 #include "channels.h"
4 #include "modules.h"
5
6 #include <set>
7
8 /* $ModDesc: Implementation of callerid (umode +g & /accept, ala hybrid etc) */
9
10 class callerid_data : public classbase
11 {
12  public:
13         time_t lastnotify;
14         std::set<User*> accepting;
15
16         callerid_data() : lastnotify(0) { }
17 };
18
19 callerid_data* GetData(User* who, bool extend = true)
20 {
21         callerid_data* dat;
22         if (who->GetExt("callerid_data", dat))
23                 return dat;
24         else
25         {
26                 if (extend)
27                 {
28                         dat = new callerid_data;
29                         who->Extend("callerid_data", dat);
30                         return dat;
31                 }
32                 else
33                         return NULL;
34         }
35 }
36
37 void RemoveData(User* who)
38 {
39         callerid_data* dat;
40         who->GetExt("callerid_data", dat);
41
42         if (!dat)
43                 return;
44
45         who->Shrink("callerid_data");
46         delete dat;
47 }
48
49 void RemoveFromAllAccepts(InspIRCd* ServerInstance, User* who)
50 {
51         for (user_hash::iterator i = ServerInstance->Users->clientlist->begin(); i != ServerInstance->Users->clientlist->end(); ++i)
52         {
53                 callerid_data* dat = GetData(i->second, false);
54                 
55                 if (!dat)
56                         continue;
57
58                 std::set<User*>& accepting = dat->accepting;
59                 std::set<User*>::iterator iter = accepting.find(who);
60
61                 if (iter == accepting.end())
62                         continue;
63
64                 accepting.erase(iter);
65         }
66 }
67
68 class User_g : public SimpleUserModeHandler
69 {
70 public:
71         User_g(InspIRCd* Instance) : SimpleUserModeHandler(Instance, 'g') { }
72 };
73
74 class CommandAccept : public Command
75 {
76 private:
77         unsigned int& maxaccepts;
78 public:
79         CommandAccept(InspIRCd* Instance, unsigned int& max) : Command(Instance, "ACCEPT", 0, 1), maxaccepts(max)
80         {
81                 source = "m_callerid.so";
82                 syntax = "{[+|-]<nicks>}|*}";
83         }
84
85         /** Will take any number of nicks, which can be seperated by spaces, commas, or a mix.
86          * - in front of any nick removes, and an * lists. This effectively means you can do:
87          * /accept nick1,nick2,nick3,*
88          * to add 3 nicks and then show your list
89          */
90         CmdResult Handle(const std::vector<std::string> &parameters, User* user)
91         {
92                 /* Even if callerid mode is not set, we let them manage their ACCEPT list so that if they go +g they can
93                  * have a list already setup. */
94                 bool atleastonechange = false;
95                 for (unsigned int i = 0; i < parameters.size(); ++i)
96                 {
97                         const char* arg = parameters[i].c_str();
98                         irc::commasepstream css(arg);
99                         std::string tok;
100
101                         while (css.GetToken(tok))
102                         {
103                                 if (tok.empty())
104                                         continue;
105
106                                 if (tok == "*")
107                                 {
108                                         if (IS_LOCAL(user))
109                                                 continue;
110
111                                         ListAccept(user);
112                                 }
113                                 else if (tok[0] == '-')
114                                 {
115                                         User* whotoremove = ServerInstance->FindNick(tok.substr(1));
116                                         if (whotoremove)
117                                                 atleastonechange = RemoveAccept(user, whotoremove, false) || atleastonechange;
118                                 }
119                                 else
120                                 {
121                                         User* whotoadd = ServerInstance->FindNick(tok[0] == '+' ? tok.substr(1) : tok);
122                                         if (whotoadd)
123                                                 atleastonechange = AddAccept(user, whotoadd, false) || atleastonechange;
124                                         else
125                                                 user->WriteNumeric(401, "%s %s :No such nick/channel", user->nick, tok.c_str());
126                                 }
127                         }
128                 }
129                 return atleastonechange ? CMD_FAILURE : CMD_SUCCESS;
130         }
131
132         void ListAccept(User* user)
133         {
134                 callerid_data* dat = GetData(user, false);
135                 if (dat)
136                 {
137                         for (std::set<User*>::iterator i = dat->accepting.begin(); i != dat->accepting.end(); ++i)
138                                 user->WriteNumeric(281, "%s %s", user->nick, (*i)->nick);
139                 }
140                 user->WriteNumeric(282, "%s :End of ACCEPT list", user->nick);
141         }
142
143         bool AddAccept(User* user, User* whotoadd, bool quiet)
144         {
145                 callerid_data* dat = GetData(user, true);
146                 std::set<User*>& accepting = dat->accepting;
147                 if (accepting.size() >= maxaccepts)
148                 {
149                         if (!quiet)
150                                 user->WriteNumeric(456, "%s :Accept list is full (limit is %d)", user->nick, maxaccepts);
151
152                         return false;
153                 }
154                 if (!accepting.insert(whotoadd).second)
155                 {
156                         if (!quiet)
157                                 user->WriteNumeric(457, "%s %s :is already on your accept list", user->nick, whotoadd->nick);
158
159                         return false;
160                 }
161                 return true;
162         }
163
164         bool RemoveAccept(User* user, User* whotoremove, bool quiet)
165         {
166                 callerid_data* dat = GetData(user, false);
167                 if (!dat)
168                 {
169                         if (!quiet)
170                                 user->WriteNumeric(458, "%s %s :is not on your accept list", user->nick, whotoremove->nick);
171
172                         return false;
173                 }
174                 std::set<User*>& accepting = dat->accepting;
175                 std::set<User*>::iterator i = accepting.find(whotoremove);
176                 if (i == accepting.end())
177                 {
178                         if (!quiet)
179                                 user->WriteNumeric(458, "%s %s :is not on your accept list", user->nick, whotoremove->nick);
180
181                         return false;
182                 }
183                 accepting.erase(i);
184                 return true;
185         }
186 };
187
188 class ModuleCallerID : public Module
189 {
190 private:
191         CommandAccept *mycommand;
192         User_g* myumode;
193
194         // Configuration variables:
195         unsigned int maxaccepts; // Maximum ACCEPT entries.
196         bool operoverride; // Operators can override callerid.
197         bool tracknick; // Allow ACCEPT entries to update with nick changes.
198         unsigned int notify_cooldown; // Seconds between notifications.
199
200 public:
201         ModuleCallerID(InspIRCd* Me) : Module(Me)
202         {
203                 OnRehash(NULL, "");
204                 mycommand = new CommandAccept(ServerInstance, maxaccepts);
205                 myumode = new User_g(ServerInstance);
206
207                 try
208                 {
209                         ServerInstance->AddCommand(mycommand);
210                 }
211                 catch (const ModuleException& e)
212                 {
213                         delete mycommand;
214                         throw ModuleException("Could not add command!");
215                 }
216                 if (!ServerInstance->Modes->AddMode(myumode))
217                 {
218                         delete mycommand;
219                         delete myumode;
220                         throw ModuleException("Could not add usermode +g");
221                 }
222                 Implementation eventlist[] = { I_OnRehash, I_OnUserPreNick, I_OnUserQuit, I_On005Numeric, I_OnUserPreNotice, I_OnUserPreMessage, I_OnCleanup };
223                 ServerInstance->Modules->Attach(eventlist, this, 7);
224         }
225
226         ~ModuleCallerID()
227         {
228                 ServerInstance->Modes->DelMode(myumode);
229                 delete myumode;
230         }
231
232         Version GetVersion()
233         {
234                 return Version(1, 2, 0, 0, VF_COMMON | VF_VENDOR, API_VERSION);
235         }
236
237         void On005Numeric(std::string& output)
238         {
239                 output += " CALLERID=g";
240         }
241
242         int PreText(User* user, User* dest, std::string& text, bool notice)
243         {
244                 if (!dest->IsModeSet('g'))
245                         return 0;
246
247                 if (operoverride && IS_OPER(user))
248                         return 0;
249
250                 callerid_data* dat = GetData(dest, true);
251                 std::set<User*>& accepting = dat->accepting;
252                 time_t& lastnotify = dat->lastnotify;
253                 std::set<User*>::iterator i = accepting.find(dest);
254
255                 if (i == accepting.end())
256                 {
257                         time_t now = time(NULL);
258                         /* +g and *not* accepted */
259                         user->WriteNumeric(716, "%s %s :is in +g mode (server-side ignore).", user->nick, dest->nick);
260                         if (now > (lastnotify + (time_t)notify_cooldown))
261                         {
262                                 user->WriteNumeric(717, "%s %s :has been informed that you messaged them.", user->nick, dest->nick);
263                                 dest->WriteNumeric(718, "%s %s %s@%s :is messaging you, and you have umode +g", dest->nick, user->nick, user->ident, user->dhost);
264                                 lastnotify = now;
265                         }
266                         return 1;
267                 }
268                 return 0;
269         }
270
271         int OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList &exempt_list)
272         {
273                 if (IS_LOCAL(user) && target_type == TYPE_USER)
274                         return PreText(user, (User*)dest, text, true);
275
276                 return 0;
277         }
278
279         int OnUserPreNotice(User* user, void* dest, int target_type, std::string& text, char status, CUList &exempt_list)
280         {
281                 if (IS_LOCAL(user) && target_type == TYPE_USER)
282                         return PreText(user, (User*)dest, text, true);
283
284                 return 0;
285         }
286
287         void OnCleanup(int type, void* item)
288         {
289                 if (type != TYPE_USER)
290                         return;
291
292                 User* u = (User*)item;
293                 /* Cleanup only happens on unload (before dtor), so keep this O(n) instead of O(n^2) which deferring to OnUserQuit would do.  */
294                 RemoveData(u);
295         }
296
297         int OnUserPreNick(User* user, const std::string& newnick)
298         {
299                 if (!tracknick)
300                         RemoveFromAllAccepts(ServerInstance, user);
301                 return 0;
302         }
303
304         void OnUserQuit(User* user, const std::string& message, const std::string& oper_message)
305         {
306                 RemoveData(user);
307                 RemoveFromAllAccepts(ServerInstance, user);
308         }
309
310         void OnRehash(User* user, const std::string& parameter)
311         {
312                 ConfigReader Conf(ServerInstance);
313                 maxaccepts = Conf.ReadInteger("callerid", "maxaccepts", "16", 0, true);
314                 operoverride = Conf.ReadFlag("callerid", "operoverride", "0", 0);
315                 tracknick = Conf.ReadFlag("callerid", "tracknick", "0", 0);
316                 notify_cooldown = Conf.ReadInteger("callerid", "cooldown", "60", 0, true);
317         }
318 };
319
320 MODULE_INIT(ModuleCallerID)
321
322