]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/mode.cpp
b81b26babaacfb2c93b57b59a9012a4fa9357b69
[user/henk/code/inspircd.git] / src / mode.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *            the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 using namespace std;
18
19 #include "inspircd_config.h"
20 #include "inspircd.h"
21 #include "configreader.h"
22 #include <unistd.h>
23 #include <sys/errno.h>
24 #include <time.h>
25 #include <string>
26 #ifdef GCC3
27 #include <ext/hash_map>
28 #else
29 #include <hash_map>
30 #endif
31 #include <map>
32 #include <sstream>
33 #include <vector>
34 #include <deque>
35 #include "connection.h"
36 #include "users.h"
37 #include "ctables.h"
38 #include "globals.h"
39 #include "modules.h"
40 #include "dynamic.h"
41 #include "wildcard.h"
42 #include "message.h"
43 #include "commands.h"
44 #include "xline.h"
45 #include "inspstring.h"
46 #include "helperfuncs.h"
47 #include "mode.h"
48
49 #include "modes/cmode_s.h"
50 #include "modes/cmode_p.h"
51
52 extern int MODCOUNT;
53 extern std::vector<Module*> modules;
54 extern std::vector<ircd_module*> factory;
55 extern InspIRCd* ServerInstance;
56 extern ServerConfig* Config;
57
58 extern time_t TIME;
59
60 ModeHandler::ModeHandler(char modeletter, int parameters_on, int parameters_off, bool listmode, ModeType type, bool operonly) : mode(modeletter), n_params_on(parameters_on), n_params_off(parameters_off), list(listmode), m_type(type), oper(operonly)
61 {
62 }
63
64 ModeHandler::~ModeHandler()
65 {
66 }
67
68 bool ModeHandler::IsListMode()
69 {
70         return list;
71 }
72
73 ModeType ModeHandler::GetModeType()
74 {
75         return m_type;
76 }
77
78 bool ModeHandler::NeedsOper()
79 {
80         return oper;
81 }
82
83 int ModeHandler::GetNumParams(bool adding)
84 {
85         return adding ? n_params_on : n_params_off;
86 }
87
88 char ModeHandler::GetModeChar()
89 {
90         return mode;
91 }
92
93 ModeAction ModeHandler::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
94 {
95         return MODEACTION_DENY;
96 }
97
98 void ModeHandler::DisplayList(userrec* user, chanrec* channel)
99 {
100 }
101
102 bool ModeHandler::CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel)
103 {
104         return (ours < theirs);
105 }
106
107 ModeWatcher::ModeWatcher(char modeletter, ModeType type) : mode(modeletter), m_type(type)
108 {
109 }
110
111 ModeWatcher::~ModeWatcher()
112 {
113 }
114
115 char ModeWatcher::GetModeChar()
116 {
117         return mode;
118 }
119
120 ModeType ModeWatcher::GetModeType()
121 {
122         return m_type;
123 }
124
125 bool ModeWatcher::BeforeMode(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding, ModeType type)
126 {
127         return true;
128 }
129
130 void ModeWatcher::AfterMode(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter, bool adding, ModeType type)
131 {
132 }
133
134 userrec* ModeParser::SanityChecks(userrec *user,char *dest,chanrec *chan,int status)
135 {
136         userrec *d;
137         if ((!user) || (!dest) || (!chan) || (!*dest))
138         {
139                 return NULL;
140         }
141         d = Find(dest);
142         if (!d)
143         {
144                 WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, dest);
145                 return NULL;
146         }
147         return d;
148 }
149
150 char* ModeParser::Grant(userrec *d,chanrec *chan,int MASK)
151 {
152         if (!chan)
153                 return NULL;
154
155         for (std::vector<ucrec*>::const_iterator i = d->chans.begin(); i != d->chans.end(); i++)
156         {
157                 if (((ucrec*)(*i))->channel == chan)
158                 {
159                         if (((ucrec*)(*i))->uc_modes & MASK)
160                         {
161                                 return NULL;
162                         }
163                         ((ucrec*)(*i))->uc_modes = ((ucrec*)(*i))->uc_modes | MASK;
164                         switch (MASK)
165                         {
166                                 case UCMODE_OP:
167                                         ((ucrec*)(*i))->channel->AddOppedUser(d);
168                                 break;
169                                 case UCMODE_HOP:
170                                         ((ucrec*)(*i))->channel->AddHalfoppedUser(d);
171                                 break;
172                                 case UCMODE_VOICE:
173                                         ((ucrec*)(*i))->channel->AddVoicedUser(d);
174                                 break;
175                         }
176                         log(DEBUG,"grant: %s %s",((ucrec*)(*i))->channel->name,d->nick);
177                         return d->nick;
178                 }
179         }
180         return NULL;
181 }
182
183 char* ModeParser::Revoke(userrec *d,chanrec *chan,int MASK)
184 {
185         if (!chan)
186                 return NULL;
187
188         for (std::vector<ucrec*>::const_iterator i = d->chans.begin(); i != d->chans.end(); i++)
189         {
190                 if (((ucrec*)(*i))->channel == chan)
191                 {
192                         if ((((ucrec*)(*i))->uc_modes & MASK) == 0)
193                         {
194                                 return NULL;
195                         }
196                         ((ucrec*)(*i))->uc_modes ^= MASK;
197                         switch (MASK)
198                         {
199                                 case UCMODE_OP:
200                                         ((ucrec*)(*i))->channel->DelOppedUser(d);
201                                 break;
202                                 case UCMODE_HOP:
203                                         ((ucrec*)(*i))->channel->DelHalfoppedUser(d);
204                                 break;
205                                 case UCMODE_VOICE:
206                                         ((ucrec*)(*i))->channel->DelVoicedUser(d);
207                                 break;
208                         }
209                         log(DEBUG,"revoke: %s %s",((ucrec*)(*i))->channel->name,d->nick);
210                         return d->nick;
211                 }
212         }
213         return NULL;
214 }
215
216 char* ModeParser::GiveOps(userrec *user,char *dest,chanrec *chan,int status)
217 {
218         userrec *d = this->SanityChecks(user,dest,chan,status);
219         
220         if (d)
221         {
222                 if (IS_LOCAL(user))
223                 {
224                         int MOD_RESULT = 0;
225                         FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_OP));
226                         
227                         if (MOD_RESULT == ACR_DENY)
228                                 return NULL;
229                         if (MOD_RESULT == ACR_DEFAULT)
230                         {
231                                 if ((status < STATUS_OP) && (!is_uline(user->server)))
232                                 {
233                                         WriteServ(user->fd,"482 %s %s :You're not a channel operator",user->nick, chan->name);
234                                         return NULL;
235                                 }
236                         }
237                 }
238
239                 return this->Grant(d,chan,UCMODE_OP);
240         }
241         return NULL;
242 }
243
244 char* ModeParser::GiveHops(userrec *user,char *dest,chanrec *chan,int status)
245 {
246         userrec *d = this->SanityChecks(user,dest,chan,status);
247         
248         if (d)
249         {
250                 if (IS_LOCAL(user))
251                 {
252                         int MOD_RESULT = 0;
253                         FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_HALFOP));
254                 
255                         if (MOD_RESULT == ACR_DENY)
256                                 return NULL;
257                         if (MOD_RESULT == ACR_DEFAULT)
258                         {
259                                 if ((status < STATUS_OP) && (!is_uline(user->server)))
260                                 {
261                                         WriteServ(user->fd,"482 %s %s :You're not a channel operator",user->nick, chan->name);
262                                         return NULL;
263                                 }
264                         }
265                 }
266
267                 return this->Grant(d,chan,UCMODE_HOP);
268         }
269         return NULL;
270 }
271
272 char* ModeParser::GiveVoice(userrec *user,char *dest,chanrec *chan,int status)
273 {
274         userrec *d = this->SanityChecks(user,dest,chan,status);
275         
276         if (d)
277         {
278                 if (IS_LOCAL(user))
279                 {
280                         int MOD_RESULT = 0;
281                         FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_VOICE));
282                         
283                         if (MOD_RESULT == ACR_DENY)
284                                 return NULL;
285                         if (MOD_RESULT == ACR_DEFAULT)
286                         {
287                                 if ((status < STATUS_HOP) && (!is_uline(user->server)))
288                                 {
289                                         WriteServ(user->fd,"482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, chan->name);
290                                         return NULL;
291                                 }
292                         }
293                 }
294
295                 return this->Grant(d,chan,UCMODE_VOICE);
296         }
297         return NULL;
298 }
299
300 char* ModeParser::TakeOps(userrec *user,char *dest,chanrec *chan,int status)
301 {
302         userrec *d = this->SanityChecks(user,dest,chan,status);
303         
304         if (d)
305         {
306                 if (IS_LOCAL(user))
307                 {
308                         int MOD_RESULT = 0;
309                         FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_DEOP));
310                         
311                         if (MOD_RESULT == ACR_DENY)
312                                 return NULL;
313                         if (MOD_RESULT == ACR_DEFAULT)
314                         {
315                                 if ((status < STATUS_OP) && (!is_uline(user->server)) && (IS_LOCAL(user)))
316                                 {
317                                         WriteServ(user->fd,"482 %s %s :You are not a channel operator",user->nick, chan->name);
318                                         return NULL;
319                                 }
320                         }
321                 }
322
323                 return this->Revoke(d,chan,UCMODE_OP);
324         }
325         return NULL;
326 }
327
328 char* ModeParser::TakeHops(userrec *user,char *dest,chanrec *chan,int status)
329 {
330         userrec *d = this->SanityChecks(user,dest,chan,status);
331         
332         if (d)
333         {
334                 if (IS_LOCAL(user))
335                 {
336                         int MOD_RESULT = 0;
337                         FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_DEHALFOP));
338                         
339                         if (MOD_RESULT == ACR_DENY)
340                                 return NULL;
341                         if (MOD_RESULT == ACR_DEFAULT)
342                         {
343                                 /* Tweak by Brain suggested by w00t, allow a halfop to dehalfop themselves */
344                                 if ((user != d) && ((status < STATUS_OP) && (!is_uline(user->server))))
345                                 {
346                                         WriteServ(user->fd,"482 %s %s :You are not a channel operator",user->nick, chan->name);
347                                         return NULL;
348                                 }
349                         }
350                 }
351
352                 return this->Revoke(d,chan,UCMODE_HOP);
353         }
354         return NULL;
355 }
356
357 char* ModeParser::TakeVoice(userrec *user,char *dest,chanrec *chan,int status)
358 {
359         userrec *d = this->SanityChecks(user,dest,chan,status);
360
361         if (d)  
362         {
363                 if (IS_LOCAL(user))
364                 {
365                         int MOD_RESULT = 0;
366                         FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_DEVOICE));
367                         
368                         if (MOD_RESULT == ACR_DENY)
369                                 return NULL;
370                         if (MOD_RESULT == ACR_DEFAULT)
371                         {
372                                 if ((status < STATUS_HOP) && (!is_uline(user->server)))
373                                 {
374                                         WriteServ(user->fd,"482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, chan->name);
375                                         return NULL;
376                                 }
377                         }
378                 }
379
380                 return this->Revoke(d,chan,UCMODE_VOICE);
381         }
382         return NULL;
383 }
384
385 void ModeParser::Process(char **parameters, int pcnt, userrec *user, bool servermode)
386 {
387         std::string target = parameters[0];
388         ModeType type = MODETYPE_USER;
389         unsigned char mask = 0;
390         chanrec* targetchannel = FindChan(parameters[0]);
391         userrec* targetuser  = Find(parameters[0]);
392
393         log(DEBUG,"ModeParser::Process start");
394
395         if (pcnt > 1)
396         {
397                 if (targetchannel)
398                 {
399                         log(DEBUG,"Target type is CHANNEL");
400                         type = MODETYPE_CHANNEL;
401                         mask = MASK_CHANNEL;
402                 }
403                 else if (targetuser)
404                 {
405                         log(DEBUG,"Target type is USER");
406                         type = MODETYPE_USER;
407                         mask = MASK_USER;
408                 }
409                 else
410                 {
411                         /* No such nick/channel */
412                         log(DEBUG,"Target type is UNKNOWN, bailing");
413                         return;
414                 }
415                 std::string mode_sequence = parameters[1];
416                 std::string parameter = "";
417                 std::ostringstream parameter_list;
418                 std::string output_sequence = "";
419                 bool adding = true, state_change = false;
420                 int handler_id = 0;
421                 int parameter_counter = 2; /* Index of first parameter */
422
423                 for (std::string::const_iterator modeletter = mode_sequence.begin(); modeletter != mode_sequence.end(); modeletter++)
424                 {
425                         switch (*modeletter)
426                         {
427
428                                 log(DEBUG,"Iterate mode letter %c",*modeletter);
429
430                                 /* NB:
431                                  * For + and - mode characters, we don't just stick the character into the output sequence.
432                                  * This is because the user may do something dumb, like: +-+ooo or +oo-+. To prevent this
433                                  * appearing in the output sequence, we store a flag which says there was a state change,
434                                  * which is set on any + or -, however, the + or - that we finish on is only appended to
435                                  * the output stream in the event it is followed by a non "+ or -" character, such as o or v.
436                                  */
437                                 case '+':
438                                         /* The following expression prevents: +o+o nick nick, compressing it to +oo nick nick,
439                                          * however, will allow the + if it is the first item in the sequence, regardless.
440                                          */
441                                         if ((!adding) || (!output_sequence.length()))
442                                                 state_change = true;
443                                         adding = true;
444                                         continue;
445                                 break;
446                                 case '-':
447                                         if ((adding) || (!output_sequence.length()))
448                                                 state_change = true;
449                                         adding = false;
450                                         continue;
451                                 break;
452                                 default:
453
454                                         /**
455                                          * Watch carefully for the sleight of hand trick.
456                                          * 65 is the ascii value of 'A'. We take this from
457                                          * the char we're looking at to get a number between
458                                          * 1 and 127. We then logic-or it to get the hashed
459                                          * position, dependent on wether its a channel or
460                                          * a user mode. This is a little stranger, but a lot
461                                          * faster, than using a map of pairs.
462                                          */
463                                         handler_id = (*modeletter - 65) | mask;
464
465                                         if (modehandlers[handler_id])
466                                         {
467                                                 bool abort = false;
468
469                                                 log(DEBUG,"Found a ModeHandler* for mode %c",*modeletter);
470
471                                                 for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
472                                                 {
473                                                         log(DEBUG,"Call a ModeWatcher*");
474                                                         if ((*watchers)->BeforeMode(user, targetuser, targetchannel, parameter, adding, type) == MODEACTION_DENY)
475                                                                 abort = true;
476                                                 }
477                                                 if ((modehandlers[handler_id]->GetModeType() == type) && (!abort))
478                                                 {
479                                                         log(DEBUG,"Modetype match, calling handler");
480
481                                                         if (modehandlers[handler_id]->GetNumParams(adding))
482                                                         {
483                                                                 log(DEBUG,"ModeHandler* for this mode says it has parameters");
484
485                                                                 if (pcnt < parameter_counter)
486                                                                 {
487                                                                         parameter = parameters[parameter_counter++];
488                                                                 }
489                                                                 else
490                                                                 {
491                                                                         parameter = "";
492                                                                 }
493                                                         }
494                                                         ModeAction ma = modehandlers[handler_id]->OnModeChange(user, targetuser, targetchannel, parameter, adding);
495                                                         if (ma == MODEACTION_ALLOW)
496                                                         {
497                                                                 log(DEBUG,"ModeAction was allow");
498
499                                                                 /* We're about to output a valid mode letter - was there previously a pending state-change? */
500                                                                 if (state_change)
501                                                                 {
502                                                                         log(DEBUG,"Appending state change");
503                                                                         output_sequence.append(adding ? "+" : "-");
504                                                                 }
505                                                                 
506                                                                 /* Add the mode letter */
507                                                                 output_sequence = output_sequence + *modeletter;
508                                                                 log(DEBUG,"Added mode letter to output sequence, sequence now: '%s'",output_sequence.c_str());
509
510                                                                 /* Is there a valid parameter for this mode? If so add it to the parameter list */
511                                                                 if ((modehandlers[handler_id]->GetNumParams(adding)) && (parameter != ""))
512                                                                 {
513                                                                         log(DEBUG,"Added parameter to parameter_list, list now: '%s'",parameter_list.str().c_str());
514                                                                         parameter_list << " " << parameter;
515                                                                 }
516
517                                                                 /* Call all the AfterMode events in the mode watchers for this mode */
518                                                                 for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
519                                                                 {
520                                                                         log(DEBUG,"Called a ModeWatcher* after event");
521                                                                         (*watchers)->AfterMode(user, targetuser, targetchannel, parameter, adding, type);
522                                                                 }
523
524                                                                 /* Reset the state change flag */
525                                                                 state_change = false;
526                                                         }
527                                                 }
528                                         }
529                                 break;
530                         }
531                 }
532                 /* Was there at least one valid mode in the sequence? */
533                 if (output_sequence != "")
534                 {
535                         if (servermode)
536                         {
537                                 if (type == MODETYPE_CHANNEL)
538                                 {
539                                         WriteChannelWithServ(Config->ServerName,targetchannel,"MODE %s %s%s",targetchannel->name,output_sequence.c_str(),parameter_list.str().c_str());
540                                 }
541                         }
542                         else
543                         {
544                                 if (type == MODETYPE_CHANNEL)
545                                 {
546                                         log(DEBUG,"Write output sequence and parameters to channel: %s %s%s",targetchannel->name,output_sequence.c_str(),parameter_list.str().c_str());
547                                         WriteChannel(targetchannel,user,"MODE %s %s%s",targetchannel->name,output_sequence.c_str(),parameter_list.str().c_str());
548                                         FOREACH_MOD(I_OnMode,OnMode(user, targetchannel, TYPE_CHANNEL, output_sequence + parameter_list.str()));
549                                 }
550                         }
551                 }
552         }
553 }
554
555
556 void cmd_mode::Handle (char **parameters, int pcnt, userrec *user)
557 {
558         if (!user)
559                 return;
560
561         ServerInstance->ModeGrok->Process(parameters, pcnt, user, false);
562
563         return;
564 }
565
566 bool ModeParser::AddMode(ModeHandler* mh, unsigned const char modeletter)
567 {
568         unsigned char mask = 0;
569         unsigned char pos = 0;
570
571         /* Yes, i know, this might let people declare modes like '_' or '^'.
572          * If they do that, thats their problem, and if i ever EVER see an
573          * official InspIRCd developer do that, i'll beat them with a paddle!
574          */
575         if ((modeletter < 'A') || (modeletter > 'z'))
576                 return false;
577
578         mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
579         pos = (modeletter-65) | mask;
580
581         if (modehandlers[pos])
582                 return false;
583
584         modehandlers[pos] = mh;
585         return true;
586 }
587
588 ModeParser::ModeParser()
589 {
590         /* Clear mode list */
591         memset(modehandlers, 0, sizeof(modehandlers));
592         memset(modewatchers, 0, sizeof(modewatchers));
593
594         /* Initialise the RFC mode letters */
595         this->AddMode(new ModeChannelSecret, 's');
596         this->AddMode(new ModeChannelPrivate, 'p');
597 }
598