]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/mode.cpp
Log client output, so we can actually see what we're sending out, and make more sense...
[user/henk/code/inspircd.git] / src / mode.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include "users.h"
16 #include "modules.h"
17 #include "inspstring.h"
18 #include "mode.h"
19
20 /* +s (secret) */
21 #include "modes/cmode_s.h"
22 /* +p (private) */
23 #include "modes/cmode_p.h"
24 /* +b (bans) */
25 #include "modes/cmode_b.h"
26 /* +m (moderated) */
27 #include "modes/cmode_m.h"
28 /* +t (only (half) ops can change topic) */
29 #include "modes/cmode_t.h"
30 /* +n (no external messages) */
31 #include "modes/cmode_n.h"
32 /* +i (invite only) */
33 #include "modes/cmode_i.h"
34 /* +k (keyed channel) */
35 #include "modes/cmode_k.h"
36 /* +l (channel user limit) */
37 #include "modes/cmode_l.h"
38 /* +o (channel op) */
39 #include "modes/cmode_o.h"
40 /* +h (channel halfop) */
41 #include "modes/cmode_h.h"
42 /* +v (channel voice) */
43 #include "modes/cmode_v.h"
44 /* +s (server notices) */
45 #include "modes/umode_s.h"
46 /* +w (see wallops) */
47 #include "modes/umode_w.h"
48 /* +i (invisible) */
49 #include "modes/umode_i.h"
50 /* +o (operator) */
51 #include "modes/umode_o.h"
52 /* +n (notice mask - our implementation of snomasks) */
53 #include "modes/umode_n.h"
54
55 ModeHandler::ModeHandler(InspIRCd* Instance, char modeletter, int parameters_on, int parameters_off, bool listmode, ModeType type, bool operonly, char mprefix)
56         : ServerInstance(Instance), mode(modeletter), n_params_on(parameters_on), n_params_off(parameters_off), list(listmode), m_type(type), oper(operonly), prefix(mprefix), count(0)
57 {
58 }
59
60 ModeHandler::~ModeHandler()
61 {
62 }
63
64 bool ModeHandler::IsListMode()
65 {
66         return list;
67 }
68
69 unsigned int ModeHandler::GetPrefixRank()
70 {
71         return 0;
72 }
73
74 unsigned int ModeHandler::GetCount()
75 {
76         return 0;
77 }
78
79 void ModeHandler::ChangeCount(int modifier)
80 {
81         count += modifier;
82 }
83
84 ModeType ModeHandler::GetModeType()
85 {
86         return m_type;
87 }
88
89 bool ModeHandler::NeedsOper()
90 {
91         return oper;
92 }
93
94 char ModeHandler::GetPrefix()
95 {
96         return prefix;
97 }
98
99 int ModeHandler::GetNumParams(bool adding)
100 {
101         return adding ? n_params_on : n_params_off;
102 }
103
104 char ModeHandler::GetModeChar()
105 {
106         return mode;
107 }
108
109 ModeAction ModeHandler::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
110 {
111         return MODEACTION_DENY;
112 }
113
114 ModePair ModeHandler::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
115 {
116         if (dest)
117         {
118                 return std::make_pair(dest->IsModeSet(this->mode), "");
119         }
120         else
121         {
122                 return std::make_pair(channel->IsModeSet(this->mode), "");
123         }
124 }
125
126 void ModeHandler::DisplayList(userrec* user, chanrec* channel)
127 {
128 }
129
130 bool ModeHandler::CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel)
131 {
132         return (ours < theirs);
133 }
134
135 ModeWatcher::ModeWatcher(InspIRCd* Instance, char modeletter, ModeType type) : ServerInstance(Instance), mode(modeletter), m_type(type)
136 {
137 }
138
139 ModeWatcher::~ModeWatcher()
140 {
141 }
142
143 char ModeWatcher::GetModeChar()
144 {
145         return mode;
146 }
147
148 ModeType ModeWatcher::GetModeType()
149 {
150         return m_type;
151 }
152
153 bool ModeWatcher::BeforeMode(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding, ModeType type)
154 {
155         return true;
156 }
157
158 void ModeWatcher::AfterMode(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter, bool adding, ModeType type)
159 {
160 }
161
162 userrec* ModeParser::SanityChecks(userrec *user,const char *dest,chanrec *chan,int status)
163 {
164         userrec *d;
165         if ((!user) || (!dest) || (!chan) || (!*dest))
166         {
167                 return NULL;
168         }
169         d = ServerInstance->FindNick(dest);
170         if (!d)
171         {
172                 user->WriteServ("401 %s %s :No such nick/channel",user->nick, dest);
173                 return NULL;
174         }
175         return d;
176 }
177
178 const char* ModeParser::Grant(userrec *d,chanrec *chan,int MASK)
179 {
180         if (!chan)
181                 return "";
182
183         UCListIter n = d->chans.find(chan);
184         if (n != d->chans.end())
185         {
186                 if (n->second & MASK)
187                 {
188                         return "";
189                 }
190                 n->second = n->second | MASK;
191                 switch (MASK)
192                 {
193                         case UCMODE_OP:
194                                 n->first->AddOppedUser(d);
195                         break;
196                         case UCMODE_HOP:
197                                 n->first->AddHalfoppedUser(d);
198                         break;
199                         case UCMODE_VOICE:
200                                 n->first->AddVoicedUser(d);
201                         break;
202                 }
203                 return d->nick;
204         }
205         return "";
206 }
207
208 const char* ModeParser::Revoke(userrec *d,chanrec *chan,int MASK)
209 {
210         if (!chan)
211                 return "";
212
213         UCListIter n = d->chans.find(chan);
214         if (n != d->chans.end())
215         {
216                 if ((n->second & MASK) == 0)
217                 {
218                         return "";
219                 }
220                 n->second ^= MASK;
221                 switch (MASK)
222                 {
223                         case UCMODE_OP:
224                                 n->first->DelOppedUser(d);
225                         break;
226                         case UCMODE_HOP:
227                                 n->first->DelHalfoppedUser(d);
228                         break;
229                         case UCMODE_VOICE:
230                                 n->first->DelVoicedUser(d);
231                         break;
232                 }
233                 return d->nick;
234         }
235         return "";
236 }
237
238 void ModeParser::DisplayCurrentModes(userrec *user, userrec* targetuser, chanrec* targetchannel, const char* text)
239 {
240         if (targetchannel)
241         {
242                 /* Display channel's current mode string */
243                 user->WriteServ("324 %s %s +%s",user->nick, targetchannel->name, targetchannel->ChanModes(targetchannel->HasUser(user)));
244                 user->WriteServ("329 %s %s %lu", user->nick, targetchannel->name, (unsigned long)targetchannel->age);
245                 return;
246         }
247         else if (targetuser)
248         {
249                 if ((targetuser == user) || (*user->oper))
250                 {
251                         /* Display user's current mode string */
252                         user->WriteServ("221 %s :+%s",targetuser->nick,targetuser->FormatModes());
253                         if (*targetuser->oper)
254                                 user->WriteServ("008 %s +%s :Server notice mask", targetuser->nick, targetuser->FormatNoticeMasks());
255                         return;
256                 }
257                 else
258                 {
259                         user->WriteServ("502 %s :Can't change mode for other users", user->nick);
260                         return;
261                 }
262         }
263
264         /* No such nick/channel */
265         user->WriteServ("401 %s %s :No such nick/channel",user->nick, text);
266         return;
267 }
268
269 void ModeParser::Process(const char** parameters, int pcnt, userrec *user, bool servermode)
270 {
271         std::string target = parameters[0];
272         ModeType type = MODETYPE_USER;
273         unsigned char mask = 0;
274         chanrec* targetchannel = ServerInstance->FindChan(parameters[0]);
275         userrec* targetuser  = ServerInstance->FindNick(parameters[0]);
276
277         LastParse = "";
278
279         /* Special case for displaying the list for listmodes,
280          * e.g. MODE #chan b, or MODE #chan +b without a parameter
281          */
282         if ((targetchannel) && (pcnt == 2))
283         {
284                 const char* mode = parameters[1];
285
286                 mask = MASK_CHANNEL;
287                 
288                 while (mode && *mode)
289                 {
290                         if (*mode == '+')
291                         {
292                                 mode++;
293                                 continue;
294                         }
295
296                         ModeHandler *mh = this->FindMode(*mode, MODETYPE_CHANNEL);
297                         bool display = true;
298
299                         if ((mh) && (mh->IsListMode()))
300                         {
301                                 /** See below for a description of what craq this is :D
302                                  */
303                                 unsigned char handler_id = (*mode - 65) | mask;
304
305                                 for(ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
306                                 {
307                                         std::string dummyparam;
308                                         
309                                         if (!((*watchers)->BeforeMode(user, NULL, targetchannel, dummyparam, true, MODETYPE_CHANNEL)))
310                                                 display = false;
311                                 }
312
313                                 if (display)
314                                         mh->DisplayList(user, targetchannel);
315                         }
316
317                         mode++;
318                 }
319         }
320
321         if (pcnt == 1)
322         {
323                 this->DisplayCurrentModes(user, targetuser, targetchannel, parameters[0]);
324         }
325         else if (pcnt > 1)
326         {
327                 if (targetchannel)
328                 {
329                         type = MODETYPE_CHANNEL;
330                         mask = MASK_CHANNEL;
331
332                         /* Extra security checks on channel modes
333                          * (e.g. are they a (half)op?
334                          */
335
336                         if ((IS_LOCAL(user)) && (targetchannel->GetStatus(user) < STATUS_HOP))
337                         {
338                                 /* We don't have halfop */
339                                 int MOD_RESULT = 0;
340                                 FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user, NULL, targetchannel, AC_GENERAL_MODE));
341                                 if (MOD_RESULT == ACR_DENY)
342                                         return;
343
344                                 if (MOD_RESULT == ACR_DEFAULT)
345                                 {
346                                         /* Are we a uline or is it a servermode? */
347                                         if ((!ServerInstance->ULine(user->server)) && (!servermode))
348                                         {
349                                                 /* Not enough permission:
350                                                  * NOT a uline and NOT a servermode,
351                                                  * OR, NOT halfop or above.
352                                                  */
353                                                 user->WriteServ("482 %s %s :You're not a channel (half)operator",user->nick, targetchannel->name);
354                                                 return;
355                                         }
356                                 }
357                         }
358                 }
359                 else if (targetuser)
360                 {
361                         type = MODETYPE_USER;
362                         mask = MASK_USER;
363                         if ((user != targetuser) && (!ServerInstance->ULine(user->server)))
364                         {
365                                 user->WriteServ("502 %s :Can't change mode for other users", user->nick);
366                                 return;
367                         }
368                 }
369                 else
370                 {
371                         /* No such nick/channel */
372                         user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
373                         return;
374                 }
375
376                 std::string mode_sequence = parameters[1];
377                 std::string parameter = "";
378                 std::ostringstream parameter_list;
379                 std::string output_sequence = "";
380                 bool adding = true, state_change = false;
381                 unsigned char handler_id = 0;
382                 int parameter_counter = 2; /* Index of first parameter */
383                 int parameter_count = 0;
384                 bool last_successful_state_change = false;
385
386                 /* A mode sequence that doesnt start with + or -. Assume +. - Thanks for the suggestion spike (bug#132) */
387                 if ((*mode_sequence.begin() != '+') && (*mode_sequence.begin() != '-'))
388                         mode_sequence.insert(0, "+");
389
390                 for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++)
391                 {
392                         unsigned char modechar = *letter;
393
394                         switch (modechar)
395                         {
396                                 /* NB:
397                                  * For + and - mode characters, we don't just stick the character into the output sequence.
398                                  * This is because the user may do something dumb, like: +-+ooo or +oo-+. To prevent this
399                                  * appearing in the output sequence, we store a flag which says there was a state change,
400                                  * which is set on any + or -, however, the + or - that we finish on is only appended to
401                                  * the output stream in the event it is followed by a non "+ or -" character, such as o or v.
402                                  */
403                                 case '+':
404                                         /* The following expression prevents: +o+o nick nick, compressing it to +oo nick nick,
405                                          * however, will allow the + if it is the first item in the sequence, regardless.
406                                          */
407                                         if ((!adding) || (!output_sequence.length()))
408                                                 state_change = true;
409                                         adding = true;
410                                         if (!output_sequence.length())
411                                                 last_successful_state_change = false;
412                                         continue;
413                                 break;
414                                 case '-':
415                                         if ((adding) || (!output_sequence.length()))
416                                                 state_change = true;
417                                         adding = false;
418                                         if (!output_sequence.length())
419                                                 last_successful_state_change = true;
420                                         continue;
421                                 break;
422                                 default:
423
424                                         /**
425                                          * Watch carefully for the sleight of hand trick.
426                                          * 65 is the ascii value of 'A'. We take this from
427                                          * the char we're looking at to get a number between
428                                          * 1 and 127. We then logic-or it to get the hashed
429                                          * position, dependent on wether its a channel or
430                                          * a user mode. This is a little stranger, but a lot
431                                          * faster, than using a map of pairs.
432                                          */
433                                         handler_id = (modechar - 65) | mask;
434
435                                         if (modehandlers[handler_id])
436                                         {
437                                                 bool abort = false;
438
439                                                 if (modehandlers[handler_id]->GetModeType() == type)
440                                                 {
441                                                         if (modehandlers[handler_id]->GetNumParams(adding))
442                                                         {
443                                                                 /* This mode expects a parameter, do we have any parameters left in our list to use? */
444                                                                 if (parameter_counter < pcnt)
445                                                                 {
446                                                                         parameter = parameters[parameter_counter++];
447
448                                                                         /* Yerk, invalid! */
449                                                                         if ((parameter.find(':') == 0) || (parameter.rfind(' ') != std::string::npos))
450                                                                                 parameter = "";
451                                                                 }
452                                                                 else
453                                                                 {
454                                                                         /* No parameter, continue to the next mode */
455                                                                         continue;
456                                                                 }
457
458                                                                 bool had_parameter = !parameter.empty();
459                                                                 
460                                                                 for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
461                                                                 {
462                                                                         if ((*watchers)->BeforeMode(user, targetuser, targetchannel, parameter, adding, type) == false)
463                                                                         {
464                                                                                 abort = true;
465                                                                                 break;
466                                                                         }
467                                                                         /* A module whacked the parameter completely, and there was one. abort. */
468                                                                         if ((had_parameter) && (parameter.empty()))
469                                                                         {
470                                                                                 abort = true;
471                                                                                 break;
472                                                                         }
473                                                                 }
474
475                                                                 if (abort)
476                                                                         continue;
477                                                         }
478
479                                                         /* It's an oper only mode, check if theyre an oper. If they arent,
480                                                          * eat any parameter that  came with the mode, and continue to next
481                                                          */
482                                                         if ((IS_LOCAL(user)) && (modehandlers[handler_id]->NeedsOper()) && (!*user->oper))
483                                                         {
484                                                                 user->WriteServ("481 %s :Permission Denied- Only IRC operators may %sset %s mode %c", user->nick,
485                                                                                 adding ? "" : "un", type == MODETYPE_CHANNEL ? "channel" : "user",
486                                                                                 modehandlers[handler_id]->GetModeChar());
487                                                                 continue;
488                                                         }
489
490                                                         /* Call the handler for the mode */
491                                                         ModeAction ma = modehandlers[handler_id]->OnModeChange(user, targetuser, targetchannel, parameter, adding);
492
493                                                         if ((modehandlers[handler_id]->GetNumParams(adding)) && (parameter == ""))
494                                                         {
495                                                                 /* The handler nuked the parameter and they are supposed to have one.
496                                                                  * We CANT continue now, even if they actually returned MODEACTION_ALLOW,
497                                                                  * so we bail to the next mode character.
498                                                                  */
499                                                                 continue;
500                                                         }
501
502                                                         if (ma == MODEACTION_ALLOW)
503                                                         {
504                                                                 /* We're about to output a valid mode letter - was there previously a pending state-change? */
505                                                                 if (state_change)
506                                                                 {
507                                                                         if (adding != last_successful_state_change)
508                                                                                 output_sequence.append(adding ? "+" : "-");
509                                                                         last_successful_state_change = adding;
510                                                                 }
511                                                                 
512                                                                 /* Add the mode letter */
513                                                                 output_sequence.push_back(modechar);
514
515                                                                 /* Is there a valid parameter for this mode? If so add it to the parameter list */
516                                                                 if ((modehandlers[handler_id]->GetNumParams(adding)) && (parameter != ""))
517                                                                 {
518                                                                         parameter_list << " " << parameter;
519                                                                         parameter_count++;
520                                                                         /* Does this mode have a prefix? */
521                                                                         if (modehandlers[handler_id]->GetPrefix() && targetchannel)
522                                                                         {
523                                                                                 userrec* user_to_prefix = ServerInstance->FindNick(parameter);
524                                                                                 if (user_to_prefix)
525                                                                                         targetchannel->SetPrefix(user_to_prefix, modehandlers[handler_id]->GetPrefix(),
526                                                                                                         modehandlers[handler_id]->GetPrefixRank(), adding);
527                                                                         }
528                                                                 }
529
530                                                                 /* Call all the AfterMode events in the mode watchers for this mode */
531                                                                 for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
532                                                                         (*watchers)->AfterMode(user, targetuser, targetchannel, parameter, adding, type);
533
534                                                                 /* Reset the state change flag */
535                                                                 state_change = false;
536
537                                                                 if ((output_sequence.length() + parameter_list.str().length() > 450) || (output_sequence.length() > 100)
538                                                                                 || (parameter_count > MAXMODES))
539                                                                 {
540                                                                         /* We cant have a mode sequence this long */
541                                                                         letter = mode_sequence.end() - 1;
542                                                                         continue;
543                                                                 }
544                                                         }
545                                                 }
546                                         }
547                                         else
548                                         {
549                                                 /* No mode handler? Unknown mode character then. */
550                                                 user->WriteServ("472 %s %c :is unknown mode char to me",user->nick, modechar);
551                                         }
552                                 break;
553                         }
554                 }
555                 /* Was there at least one valid mode in the sequence? */
556                 if (output_sequence != "")
557                 {
558                         if (servermode)
559                         {
560                                 if (type == MODETYPE_CHANNEL)
561                                 {
562                                         targetchannel->WriteChannelWithServ(ServerInstance->Config->ServerName, "MODE %s %s%s", targetchannel->name, output_sequence.c_str(), parameter_list.str().c_str());
563                                         this->LastParse = targetchannel->name;
564                                 }
565                                 else
566                                 {
567                                         targetuser->WriteServ("MODE %s %s%s",targetuser->nick,output_sequence.c_str(), parameter_list.str().c_str());
568                                         this->LastParse = targetuser->nick;
569                                 }
570                         }
571                         else
572                         {
573                                 if (type == MODETYPE_CHANNEL)
574                                 {
575                                         targetchannel->WriteChannel(user,"MODE %s %s%s",targetchannel->name,output_sequence.c_str(),parameter_list.str().c_str());
576                                         FOREACH_MOD(I_OnMode,OnMode(user, targetchannel, TYPE_CHANNEL, output_sequence + parameter_list.str()));
577                                         this->LastParse = targetchannel->name;
578                                 }
579                                 else
580                                 {
581                                         user->WriteTo(targetuser,"MODE %s %s%s",targetuser->nick,output_sequence.c_str(), parameter_list.str().c_str());
582                                         FOREACH_MOD(I_OnMode,OnMode(user, targetuser, TYPE_USER, output_sequence + parameter_list.str()));
583                                         this->LastParse = targetuser->nick;
584                                 }
585                         }
586
587                         LastParse.append(" ");
588                         LastParse.append(output_sequence);
589                         LastParse.append(parameter_list.str());
590                 }
591         }
592 }
593
594 const std::string& ModeParser::GetLastParse()
595 {
596         return LastParse;
597 }
598
599 void ModeParser::CleanMask(std::string &mask)
600 {
601         std::string::size_type pos_of_pling = mask.find_first_of('!');
602         std::string::size_type pos_of_at = mask.find_first_of('@');
603         std::string::size_type pos_of_dot = mask.find_first_of('.');
604         std::string::size_type pos_of_colon = mask.find_first_of(':'); /* Because ipv6 addresses are colon delimited */
605
606         if ((pos_of_pling == std::string::npos) && (pos_of_at == std::string::npos))
607         {
608                 /* Just a nick, or just a host */
609                 if ((pos_of_dot == std::string::npos) && (pos_of_colon == std::string::npos))
610                 {
611                         /* It has no '.' in it, it must be a nick. */
612                         mask.append("!*@*");
613                 }
614                 else
615                 {
616                         /* Got a dot in it? Has to be a host */
617                         mask = "*!*@" + mask;
618                 }
619         }
620         else if ((pos_of_pling == std::string::npos) && (pos_of_at != std::string::npos))
621         {
622                 /* Has an @ but no !, its a user@host */
623                  mask = "*!" + mask;
624         }
625         else if ((pos_of_pling != std::string::npos) && (pos_of_at == std::string::npos))
626         {
627                 /* Has a ! but no @, it must be a nick!ident */
628                 mask.append("@*");
629         }
630 }
631
632 bool ModeParser::AddMode(ModeHandler* mh, unsigned const char modeletter)
633 {
634         unsigned char mask = 0;
635         unsigned char pos = 0;
636
637         /* Yes, i know, this might let people declare modes like '_' or '^'.
638          * If they do that, thats their problem, and if i ever EVER see an
639          * official InspIRCd developer do that, i'll beat them with a paddle!
640          */
641         if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z'))
642                 return false;
643
644         /* A mode prefix of ',' is not acceptable, it would fuck up server to server.
645          * A mode prefix of ':' will fuck up both server to server, and client to server.
646          * A mode prefix of '#' will mess up /whois and /privmsg
647          */
648         if ((mh->GetPrefix() == ',') || (mh->GetPrefix() == ':') || (mh->GetPrefix() == '#'))
649                 return false;
650
651         mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
652         pos = (mh->GetModeChar()-65) | mask;
653
654         if (modehandlers[pos])
655                 return false;
656
657         modehandlers[pos] = mh;
658         return true;
659 }
660
661 bool ModeParser::DelMode(ModeHandler* mh)
662 {
663         unsigned char mask = 0;
664         unsigned char pos = 0;
665
666         if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z'))
667                 return false;
668
669         mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
670         pos = (mh->GetModeChar()-65) | mask;
671
672         if (!modehandlers[pos])
673                 return false;
674
675         switch (mh->GetModeType())
676         {
677                 case MODETYPE_USER:
678                         for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++)
679                         {
680                                 mh->RemoveMode(i->second);
681                         }
682                 break;
683                 case MODETYPE_CHANNEL:
684                         for (chan_hash::iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++)
685                         {
686                                 mh->RemoveMode(i->second);
687                         }
688                 break;
689         }
690
691         modehandlers[pos] = NULL;
692
693         return true;
694 }
695
696 ModeHandler* ModeParser::FindMode(unsigned const char modeletter, ModeType mt)
697 {
698         unsigned char mask = 0;
699         unsigned char pos = 0;
700
701         if ((modeletter < 'A') || (modeletter > 'z'))
702                 return NULL;
703
704         mt == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
705         pos = (modeletter-65) | mask;
706
707         return modehandlers[pos];
708 }
709
710 std::string ModeParser::UserModeList()
711 {
712         char modestr[256];
713         int pointer = 0;
714
715         for (unsigned char mode = 'A'; mode <= 'z'; mode++)
716         {
717                 unsigned char pos = (mode-65) | MASK_USER;
718
719                 if (modehandlers[pos])
720                         modestr[pointer++] = mode;
721         }
722         modestr[pointer++] = 0;
723         return modestr;
724 }
725
726 std::string ModeParser::ChannelModeList()
727 {
728         char modestr[256];
729         int pointer = 0;
730
731         for (unsigned char mode = 'A'; mode <= 'z'; mode++)
732         {
733                 unsigned char pos = (mode-65) | MASK_CHANNEL;
734
735                 if (modehandlers[pos])
736                         modestr[pointer++] = mode;
737         }
738         modestr[pointer++] = 0;
739         return modestr;
740 }
741
742 std::string ModeParser::ParaModeList()
743 {
744         char modestr[256];
745         int pointer = 0;
746
747         for (unsigned char mode = 'A'; mode <= 'z'; mode++)
748         {
749                 unsigned char pos = (mode-65) | MASK_CHANNEL;
750
751                 if ((modehandlers[pos]) && (modehandlers[pos]->GetNumParams(true)))
752                         modestr[pointer++] = mode;
753         }
754         modestr[pointer++] = 0;
755         return modestr;
756 }
757
758 ModeHandler* ModeParser::FindPrefix(unsigned const char pfxletter)
759 {
760         for (unsigned char mode = 'A'; mode <= 'z'; mode++)
761         {
762                 unsigned char pos = (mode-65) | MASK_CHANNEL;
763
764                 if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix() == pfxletter))
765                 {
766                         return modehandlers[pos];
767                 }
768         }
769         return NULL;
770 }
771
772 std::string ModeParser::ModeString(userrec* user, chanrec* channel)
773 {
774         std::string types;
775         std::string pars;
776
777         if (!channel || !user)
778                 return "";
779
780         for (unsigned char mode = 'A'; mode <= 'z'; mode++)
781         {
782                 unsigned char pos = (mode-65) | MASK_CHANNEL;
783                 ModeHandler* mh = modehandlers[pos];
784                 if ((mh) && (mh->GetNumParams(true)) && (mh->GetNumParams(false)))
785                 {
786                         ModePair ret;
787                         ret = mh->ModeSet(NULL, user, channel, user->nick);
788                         if ((ret.first) && (ret.second == user->nick))
789                         {
790                                 pars.append(" ");
791                                 pars.append(user->nick);
792                                 types.push_back(mh->GetModeChar());
793                         }
794                 }
795         }
796
797         return types+pars;
798 }
799
800 std::string ModeParser::ChanModes()
801 {
802         std::string type1;      /* Listmodes EXCEPT those with a prefix */
803         std::string type2;      /* Modes that take a param when adding or removing */
804         std::string type3;      /* Modes that only take a param when adding */
805         std::string type4;      /* Modes that dont take a param */
806
807         for (unsigned char mode = 'A'; mode <= 'z'; mode++)
808         {
809                 unsigned char pos = (mode-65) | MASK_CHANNEL;
810                  /* One parameter when adding */
811                 if (modehandlers[pos])
812                 {       
813                         if (modehandlers[pos]->GetNumParams(true))
814                         {
815                                 if ((modehandlers[pos]->IsListMode()) && (!modehandlers[pos]->GetPrefix()))
816                                 {
817                                         type1 += modehandlers[pos]->GetModeChar();
818                                 }
819                                 else
820                                 {
821                                         /* ... and one parameter when removing */
822                                         if (modehandlers[pos]->GetNumParams(false))
823                                         {
824                                                 /* But not a list mode */
825                                                 if (!modehandlers[pos]->GetPrefix())
826                                                 {
827                                                         type2 += modehandlers[pos]->GetModeChar();
828                                                 }
829                                         }
830                                         else
831                                         {
832                                                 /* No parameters when removing */
833                                                 type3 += modehandlers[pos]->GetModeChar();
834                                         }
835                                 }
836                         }
837                         else
838                         {
839                                 type4 += modehandlers[pos]->GetModeChar();
840                         }
841                 }
842                          
843         }
844
845         return type1 + "," + type2 + "," + type3 + "," + type4;
846 }
847
848 bool ModeParser::PrefixComparison(const prefixtype one, const prefixtype two)
849 {       
850         return one.second > two.second;
851 }
852
853 std::string ModeParser::BuildPrefixes()
854 {
855         std::string mletters = "";
856         std::string mprefixes = "";
857         pfxcontainer pfx;
858         std::map<char,char> prefix_to_mode;
859
860         for (unsigned char mode = 'A'; mode <= 'z'; mode++)
861         {
862                 unsigned char pos = (mode-65) | MASK_CHANNEL;
863
864                 if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix()))
865                 {
866                         pfx.push_back(std::make_pair<char,unsigned int>(modehandlers[pos]->GetPrefix(), modehandlers[pos]->GetPrefixRank()));
867                         prefix_to_mode[modehandlers[pos]->GetPrefix()] = modehandlers[pos]->GetModeChar();
868                 }
869         }
870
871         sort(pfx.begin(), pfx.end(), ModeParser::PrefixComparison);
872
873         for (pfxcontainer::iterator n = pfx.begin(); n != pfx.end(); n++)
874         {
875                 mletters = mletters + n->first;
876                 mprefixes = mprefixes + prefix_to_mode.find(n->first)->second;
877         }
878
879         return "(" + mprefixes + ")" + mletters;
880 }
881
882 bool ModeParser::AddModeWatcher(ModeWatcher* mw)
883 {
884         unsigned char mask = 0;
885         unsigned char pos = 0;
886
887         if (!mw)
888                 return false;
889
890         if ((mw->GetModeChar() < 'A') || (mw->GetModeChar() > 'z'))
891                 return false;
892
893         mw->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
894         pos = (mw->GetModeChar()-65) | mask;
895
896         modewatchers[pos].push_back(mw);
897         return true;
898 }
899
900 bool ModeParser::DelModeWatcher(ModeWatcher* mw)
901 {
902         unsigned char mask = 0;
903         unsigned char pos = 0;
904
905         if (!mw)
906                 return false;
907
908         if ((mw->GetModeChar() < 'A') || (mw->GetModeChar() > 'z'))
909                 return false;
910
911         mw->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
912         pos = (mw->GetModeChar()-65) | mask;
913
914         ModeWatchIter a = find(modewatchers[pos].begin(),modewatchers[pos].end(),mw);
915
916         if (a == modewatchers[pos].end())
917         {
918                 return false;
919         }
920
921         modewatchers[pos].erase(a);
922
923         return true;
924 }
925
926 /** This default implementation can remove simple user modes
927  */
928 void ModeHandler::RemoveMode(userrec* user)
929 {
930         char moderemove[MAXBUF];
931         const char* parameters[] = { user->nick, moderemove };
932
933         if (user->IsModeSet(this->GetModeChar()))
934         {
935                 sprintf(moderemove,"-%c",this->GetModeChar());
936                 ServerInstance->Parser->CallHandler("MODE", parameters, 2, user);
937         }
938 }
939
940 /** This default implementation can remove simple channel modes
941  * (no parameters)
942  */
943 void ModeHandler::RemoveMode(chanrec* channel)
944 {
945         char moderemove[MAXBUF];
946         const char* parameters[] = { channel->name, moderemove };
947
948         if (channel->IsModeSet(this->GetModeChar()))
949         {
950                 userrec* n = new userrec(ServerInstance);
951
952                 sprintf(moderemove,"-%c",this->GetModeChar());
953                 n->SetFd(FD_MAGIC_NUMBER);
954
955                 ServerInstance->SendMode(parameters, 2, n);
956
957                 delete n;
958         }
959 }
960
961 ModeParser::ModeParser(InspIRCd* Instance) : ServerInstance(Instance)
962 {
963         /* Clear mode list */
964         memset(modehandlers, 0, sizeof(modehandlers));
965         memset(modewatchers, 0, sizeof(modewatchers));
966
967         /* Last parse string */
968         LastParse = "";
969
970         /* Initialise the RFC mode letters */
971
972         /* Start with channel simple modes, no params */
973         this->AddMode(new ModeChannelSecret(Instance), 's');
974         this->AddMode(new ModeChannelPrivate(Instance), 'p');
975         this->AddMode(new ModeChannelModerated(Instance), 'm');
976         this->AddMode(new ModeChannelTopicOps(Instance), 't');
977         this->AddMode(new ModeChannelNoExternal(Instance), 'n');
978         this->AddMode(new ModeChannelInviteOnly(Instance), 'i');
979
980         /* Cannel modes with params */
981         this->AddMode(new ModeChannelKey(Instance), 'k');
982         this->AddMode(new ModeChannelLimit(Instance), 'l');
983
984         /* Channel listmodes */
985         this->AddMode(new ModeChannelBan(Instance), 'b');
986         this->AddMode(new ModeChannelOp(Instance), 'o');
987         this->AddMode(new ModeChannelHalfOp(Instance), 'h');
988         this->AddMode(new ModeChannelVoice(Instance), 'v');
989
990         /* Now for usermodes */
991         this->AddMode(new ModeUserServerNotice(Instance), 's');
992         this->AddMode(new ModeUserWallops(Instance), 'w');
993         this->AddMode(new ModeUserInvisible(Instance), 'i');
994         this->AddMode(new ModeUserOperator(Instance), 'o');
995         this->AddMode(new ModeUserServerNoticeMask(Instance), 'n');
996 }