]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/mode.cpp
Add a Flash Policy Daemon module
[user/henk/code/inspircd.git] / src / mode.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2012 Shawn Smith <shawn@inspircd.org>
5  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
6  *   Copyright (C) 2007, 2009 Dennis Friis <peavey@inspircd.org>
7  *   Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
8  *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
9  *   Copyright (C) 2004-2008 Craig Edwards <craigedwards@brainbox.cc>
10  *   Copyright (C) 2006 Oliver Lupton <oliverlupton@gmail.com>
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 #include "builtinmodes.h"
28
29 ModeHandler::ModeHandler(Module* Creator, const std::string& Name, char modeletter, ParamSpec Params, ModeType type, Class mclass)
30         : ServiceProvider(Creator, Name, SERVICE_MODE), m_paramtype(TR_TEXT),
31         parameters_taken(Params), mode(modeletter), oper(false),
32         list(false), m_type(type), type_id(mclass), levelrequired(HALFOP_VALUE)
33 {
34 }
35
36 CullResult ModeHandler::cull()
37 {
38         if (ServerInstance->Modes)
39                 ServerInstance->Modes->DelMode(this);
40         return classbase::cull();
41 }
42
43 ModeHandler::~ModeHandler()
44 {
45 }
46
47 int ModeHandler::GetNumParams(bool adding)
48 {
49         switch (parameters_taken)
50         {
51                 case PARAM_ALWAYS:
52                         return 1;
53                 case PARAM_SETONLY:
54                         return adding ? 1 : 0;
55                 case PARAM_NONE:
56                         break;
57         }
58         return 0;
59 }
60
61 std::string ModeHandler::GetUserParameter(User* user)
62 {
63         return "";
64 }
65
66 ModResult ModeHandler::AccessCheck(User*, Channel*, std::string &, bool)
67 {
68         return MOD_RES_PASSTHRU;
69 }
70
71 ModeAction ModeHandler::OnModeChange(User*, User*, Channel*, std::string&, bool)
72 {
73         return MODEACTION_DENY;
74 }
75
76 void ModeHandler::DisplayList(User*, Channel*)
77 {
78 }
79
80 void ModeHandler::DisplayEmptyList(User*, Channel*)
81 {
82 }
83
84 void ModeHandler::OnParameterMissing(User* user, User* dest, Channel* channel)
85 {
86 }
87
88 bool ModeHandler::ResolveModeConflict(std::string& theirs, const std::string& ours, Channel*)
89 {
90         return (theirs < ours);
91 }
92
93 ModeAction SimpleUserModeHandler::OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
94 {
95         /* We're either trying to add a mode we already have or
96                 remove a mode we don't have, deny. */
97         if (dest->IsModeSet(this) == adding)
98                 return MODEACTION_DENY;
99
100         /* adding will be either true or false, depending on if we
101                 are adding or removing the mode, since we already checked
102                 to make sure we aren't adding a mode we have or that we
103                 aren't removing a mode we don't have, we don't have to do any
104                 other checks here to see if it's true or false, just add or
105                 remove the mode */
106         dest->SetMode(this, adding);
107
108         return MODEACTION_ALLOW;
109 }
110
111
112 ModeAction SimpleChannelModeHandler::OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
113 {
114         /* We're either trying to add a mode we already have or
115                 remove a mode we don't have, deny. */
116         if (channel->IsModeSet(this) == adding)
117                 return MODEACTION_DENY;
118
119         /* adding will be either true or false, depending on if we
120                 are adding or removing the mode, since we already checked
121                 to make sure we aren't adding a mode we have or that we
122                 aren't removing a mode we don't have, we don't have to do any
123                 other checks here to see if it's true or false, just add or
124                 remove the mode */
125         channel->SetMode(this, adding);
126
127         return MODEACTION_ALLOW;
128 }
129
130 ModeAction ParamChannelModeHandler::OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
131 {
132         if (adding && !ParamValidate(parameter))
133                 return MODEACTION_DENY;
134         std::string now = channel->GetModeParameter(this);
135         if (parameter == now)
136                 return MODEACTION_DENY;
137         return MODEACTION_ALLOW;
138 }
139
140 bool ParamChannelModeHandler::ParamValidate(std::string& parameter)
141 {
142         return true;
143 }
144
145 ModeWatcher::ModeWatcher(Module* Creator, const std::string& modename, ModeType type)
146         : mode(modename), m_type(type), creator(Creator)
147 {
148 }
149
150 ModeWatcher::~ModeWatcher()
151 {
152 }
153
154 ModeType ModeWatcher::GetModeType()
155 {
156         return m_type;
157 }
158
159 bool ModeWatcher::BeforeMode(User*, User*, Channel*, std::string&, bool)
160 {
161         return true;
162 }
163
164 void ModeWatcher::AfterMode(User*, User*, Channel*, const std::string&, bool)
165 {
166 }
167
168 void ModeParser::DisplayCurrentModes(User *user, User* targetuser, Channel* targetchannel, const char* text)
169 {
170         if (targetchannel)
171         {
172                 /* Display channel's current mode string */
173                 user->WriteNumeric(RPL_CHANNELMODEIS, "%s +%s", targetchannel->name.c_str(), targetchannel->ChanModes(targetchannel->HasUser(user)));
174                 user->WriteNumeric(RPL_CHANNELCREATED, "%s %lu", targetchannel->name.c_str(), (unsigned long)targetchannel->age);
175                 return;
176         }
177         else
178         {
179                 if (targetuser == user || user->HasPrivPermission("users/auspex"))
180                 {
181                         /* Display user's current mode string */
182                         user->WriteNumeric(RPL_UMODEIS, ":+%s", targetuser->FormatModes());
183                         if ((targetuser->IsOper()))
184                         {
185                                 ModeHandler* snomask = FindMode('s', MODETYPE_USER);
186                                 user->WriteNumeric(RPL_SNOMASKIS, "%s :Server notice mask", snomask->GetUserParameter(user).c_str());
187                         }
188                         return;
189                 }
190                 else
191                 {
192                         user->WriteNumeric(ERR_USERSDONTMATCH, ":Can't view modes for other users");
193                         return;
194                 }
195         }
196 }
197
198 PrefixMode::PrefixMode(Module* Creator, const std::string& Name, char ModeLetter)
199         : ModeHandler(Creator, Name, ModeLetter, PARAM_ALWAYS, MODETYPE_CHANNEL, MC_PREFIX)
200         , prefix(0), prefixrank(0)
201 {
202         list = true;
203         m_paramtype = TR_NICK;
204 }
205
206 ModeAction PrefixMode::OnModeChange(User* source, User*, Channel* chan, std::string& parameter, bool adding)
207 {
208         User* target = ServerInstance->FindNick(parameter);
209         if (!target)
210         {
211                 source->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameter.c_str());
212                 return MODEACTION_DENY;
213         }
214
215         Membership* memb = chan->GetUser(target);
216         if (!memb)
217                 return MODEACTION_DENY;
218
219         parameter = target->nick;
220         return (memb->SetPrefix(this, adding) ? MODEACTION_ALLOW : MODEACTION_DENY);
221 }
222
223 ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool adding, const unsigned char modechar,
224                 std::string &parameter, bool SkipACL)
225 {
226         ModeType type = chan ? MODETYPE_CHANNEL : MODETYPE_USER;
227
228         ModeHandler *mh = FindMode(modechar, type);
229         int pcnt = mh->GetNumParams(adding);
230
231         // crop mode parameter size to 250 characters
232         if (parameter.length() > 250 && adding)
233                 parameter = parameter.substr(0, 250);
234
235         ModResult MOD_RESULT;
236         FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, modechar, parameter, adding, pcnt));
237
238         if (IS_LOCAL(user) && (MOD_RESULT == MOD_RES_DENY))
239                 return MODEACTION_DENY;
240
241         if (chan && !SkipACL && (MOD_RESULT != MOD_RES_ALLOW))
242         {
243                 MOD_RESULT = mh->AccessCheck(user, chan, parameter, adding);
244
245                 if (MOD_RESULT == MOD_RES_DENY)
246                         return MODEACTION_DENY;
247                 if (MOD_RESULT == MOD_RES_PASSTHRU)
248                 {
249                         unsigned int neededrank = mh->GetLevelRequired();
250                         /* Compare our rank on the channel against the rank of the required prefix,
251                          * allow if >= ours. Because mIRC and xchat throw a tizz if the modes shown
252                          * in NAMES(X) are not in rank order, we know the most powerful mode is listed
253                          * first, so we don't need to iterate, we just look up the first instead.
254                          */
255                         unsigned int ourrank = chan->GetPrefixValue(user);
256                         if (ourrank < neededrank)
257                         {
258                                 PrefixMode* neededmh = NULL;
259                                 for(char c='A'; c <= 'z'; c++)
260                                 {
261                                         PrefixMode* privmh = FindPrefixMode(c);
262                                         if (privmh && privmh->GetPrefixRank() >= neededrank)
263                                         {
264                                                 // this mode is sufficient to allow this action
265                                                 if (!neededmh || privmh->GetPrefixRank() < neededmh->GetPrefixRank())
266                                                         neededmh = privmh;
267                                         }
268                                 }
269                                 if (neededmh)
270                                         user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You must have channel %s access or above to %sset channel mode %c",
271                                                 chan->name.c_str(), neededmh->name.c_str(), adding ? "" : "un", modechar);
272                                 else
273                                         user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You cannot %sset channel mode %c",
274                                                 chan->name.c_str(), adding ? "" : "un", modechar);
275                                 return MODEACTION_DENY;
276                         }
277                 }
278         }
279
280         // Ask mode watchers whether this mode change is OK
281         std::pair<ModeWatchIter, ModeWatchIter> itpair = modewatchermap.equal_range(mh->name);
282         for (ModeWatchIter i = itpair.first; i != itpair.second; ++i)
283         {
284                 ModeWatcher* mw = i->second;
285                 if (mw->GetModeType() == type)
286                 {
287                         if (!mw->BeforeMode(user, targetuser, chan, parameter, adding))
288                                 return MODEACTION_DENY;
289
290                         // A module whacked the parameter completely, and there was one. Abort.
291                         if (pcnt && parameter.empty())
292                                 return MODEACTION_DENY;
293                 }
294         }
295
296         if (IS_LOCAL(user) && !user->IsOper())
297         {
298                 char* disabled = (type == MODETYPE_CHANNEL) ? ServerInstance->Config->DisabledCModes : ServerInstance->Config->DisabledUModes;
299                 if (disabled[modechar - 'A'])
300                 {
301                         user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - %s mode %c has been locked by the administrator",
302                                 type == MODETYPE_CHANNEL ? "channel" : "user", modechar);
303                         return MODEACTION_DENY;
304                 }
305         }
306
307         if (adding && IS_LOCAL(user) && mh->NeedsOper() && !user->HasModePermission(modechar, type))
308         {
309                 /* It's an oper only mode, and they don't have access to it. */
310                 if (user->IsOper())
311                 {
312                         user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - Oper type %s does not have access to set %s mode %c",
313                                         user->oper->name.c_str(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar);
314                 }
315                 else
316                 {
317                         user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - Only operators may set %s mode %c",
318                                         type == MODETYPE_CHANNEL ? "channel" : "user", modechar);
319                 }
320                 return MODEACTION_DENY;
321         }
322
323         /* Call the handler for the mode */
324         ModeAction ma = mh->OnModeChange(user, targetuser, chan, parameter, adding);
325
326         if (pcnt && parameter.empty())
327                 return MODEACTION_DENY;
328
329         if (ma != MODEACTION_ALLOW)
330                 return ma;
331
332         if ((!mh->IsListMode()) && (mh->GetNumParams(true)) && (chan))
333                 chan->SetModeParam(mh, (adding ? parameter : ""));
334
335         itpair = modewatchermap.equal_range(mh->name);
336         for (ModeWatchIter i = itpair.first; i != itpair.second; ++i)
337         {
338                 ModeWatcher* mw = i->second;
339                 if (mw->GetModeType() == type)
340                         mw->AfterMode(user, targetuser, chan, parameter, adding);
341         }
342
343         return MODEACTION_ALLOW;
344 }
345
346 void ModeParser::Process(const std::vector<std::string>& parameters, User* user, ModeProcessFlag flags)
347 {
348         std::string target = parameters[0];
349         Channel* targetchannel = ServerInstance->FindChan(target);
350         User* targetuser  = ServerInstance->FindNick(target);
351         ModeType type = targetchannel ? MODETYPE_CHANNEL : MODETYPE_USER;
352
353         LastParse.clear();
354         LastParseParams.clear();
355         LastParseTranslate.clear();
356
357         if ((!targetchannel) && ((!targetuser) || (IS_SERVER(targetuser))))
358         {
359                 user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", target.c_str());
360                 return;
361         }
362         if (parameters.size() == 1)
363         {
364                 this->DisplayCurrentModes(user, targetuser, targetchannel, target.c_str());
365                 return;
366         }
367
368         ModResult MOD_RESULT;
369         FIRST_MOD_RESULT(OnPreMode, MOD_RESULT, (user, targetuser, targetchannel, parameters));
370
371         bool SkipAccessChecks = false;
372
373         if (!IS_LOCAL(user) || ServerInstance->ULine(user->server) || MOD_RESULT == MOD_RES_ALLOW)
374                 SkipAccessChecks = true;
375         else if (MOD_RESULT == MOD_RES_DENY)
376                 return;
377
378         if (targetuser && !SkipAccessChecks && user != targetuser)
379         {
380                 user->WriteNumeric(ERR_USERSDONTMATCH, ":Can't change mode for other users");
381                 return;
382         }
383
384         std::string mode_sequence = parameters[1];
385
386         std::string output_mode;
387         std::ostringstream output_parameters;
388         LastParseParams.push_back(output_mode);
389         LastParseTranslate.push_back(TR_TEXT);
390
391         bool adding = true;
392         char output_pm = '\0'; // current output state, '+' or '-'
393         unsigned int param_at = 2;
394
395         for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++)
396         {
397                 unsigned char modechar = *letter;
398                 if (modechar == '+' || modechar == '-')
399                 {
400                         adding = (modechar == '+');
401                         continue;
402                 }
403
404                 ModeHandler *mh = this->FindMode(modechar, type);
405                 if (!mh)
406                 {
407                         /* No mode handler? Unknown mode character then. */
408                         user->WriteNumeric(type == MODETYPE_CHANNEL ? ERR_UNKNOWNMODE : ERR_UNKNOWNSNOMASK, "%c :is unknown mode char to me", modechar);
409                         continue;
410                 }
411
412                 std::string parameter;
413                 int pcnt = mh->GetNumParams(adding);
414                 if (pcnt && param_at == parameters.size())
415                 {
416                         /* No parameter, continue to the next mode */
417                         mh->OnParameterMissing(user, targetuser, targetchannel);
418                         continue;
419                 }
420                 else if (pcnt)
421                 {
422                         parameter = parameters[param_at++];
423                         /* Make sure the user isn't trying to slip in an invalid parameter */
424                         if ((parameter.find(':') == 0) || (parameter.rfind(' ') != std::string::npos))
425                                 continue;
426                         if ((flags & MODE_MERGE) && targetchannel && targetchannel->IsModeSet(mh) && !mh->IsListMode())
427                         {
428                                 std::string ours = targetchannel->GetModeParameter(mh);
429                                 if (!mh->ResolveModeConflict(parameter, ours, targetchannel))
430                                         /* we won the mode merge, don't apply this mode */
431                                         continue;
432                         }
433                 }
434
435                 ModeAction ma = TryMode(user, targetuser, targetchannel, adding, modechar, parameter, SkipAccessChecks);
436
437                 if (ma != MODEACTION_ALLOW)
438                         continue;
439
440                 char needed_pm = adding ? '+' : '-';
441                 if (needed_pm != output_pm)
442                 {
443                         output_pm = needed_pm;
444                         output_mode.append(1, output_pm);
445                 }
446                 output_mode.append(1, modechar);
447
448                 if (pcnt)
449                 {
450                         output_parameters << " " << parameter;
451                         LastParseParams.push_back(parameter);
452                         LastParseTranslate.push_back(mh->GetTranslateType());
453                 }
454
455                 if ( (output_mode.length() + output_parameters.str().length() > 450)
456                                 || (output_mode.length() > 100)
457                                 || (LastParseParams.size() > ServerInstance->Config->Limits.MaxModes))
458                 {
459                         /* mode sequence is getting too long */
460                         break;
461                 }
462         }
463
464         LastParseParams[0] = output_mode;
465
466         if (!output_mode.empty())
467         {
468                 LastParse = targetchannel ? targetchannel->name : targetuser->nick;
469                 LastParse.append(" ");
470                 LastParse.append(output_mode);
471                 LastParse.append(output_parameters.str());
472
473                 if (!(flags & MODE_LOCALONLY))
474                         ServerInstance->PI->SendMode(user, targetuser, targetchannel, LastParseParams, LastParseTranslate);
475
476                 if (targetchannel)
477                         targetchannel->WriteChannel(user, "MODE " + LastParse);
478                 else
479                         targetuser->WriteFrom(user, "MODE " + LastParse);
480
481                 FOREACH_MOD(OnMode, (user, targetuser, targetchannel, LastParseParams, LastParseTranslate));
482         }
483         else if (targetchannel && parameters.size() == 2)
484         {
485                 /* Special case for displaying the list for listmodes,
486                  * e.g. MODE #chan b, or MODE #chan +b without a parameter
487                  */
488                 this->DisplayListModes(user, targetchannel, mode_sequence);
489         }
490 }
491
492 void ModeParser::DisplayListModes(User* user, Channel* chan, std::string &mode_sequence)
493 {
494         seq++;
495
496         for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++)
497         {
498                 unsigned char mletter = *letter;
499                 if (mletter == '+')
500                         continue;
501
502                 /* Ensure the user doesnt request the same mode twice,
503                  * so they cant flood themselves off out of idiocy.
504                  */
505                 if (sent[mletter] == seq)
506                         continue;
507
508                 sent[mletter] = seq;
509
510                 ModeHandler *mh = this->FindMode(mletter, MODETYPE_CHANNEL);
511
512                 if (!mh || !mh->IsListMode())
513                         return;
514
515                 ModResult MOD_RESULT;
516                 FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, mletter, "", true, 0));
517                 if (MOD_RESULT == MOD_RES_DENY)
518                         continue;
519
520                 bool display = true;
521                 if (!user->HasPrivPermission("channels/auspex") && ServerInstance->Config->HideModeLists[mletter] && (chan->GetPrefixValue(user) < HALFOP_VALUE))
522                 {
523                         user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You do not have access to view the +%c list",
524                                 chan->name.c_str(), mletter);
525                         display = false;
526                 }
527
528                 // Ask mode watchers whether it's OK to show the list
529                 std::pair<ModeWatchIter, ModeWatchIter> itpair = modewatchermap.equal_range(mh->name);
530                 for (ModeWatchIter i = itpair.first; i != itpair.second; ++i)
531                 {
532                         ModeWatcher* mw = i->second;
533                         if (mw->GetModeType() == MODETYPE_CHANNEL)
534                         {
535                                 std::string dummyparam;
536
537                                 if (!mw->BeforeMode(user, NULL, chan, dummyparam, true))
538                                 {
539                                         // A mode watcher doesn't want us to show the list
540                                         display = false;
541                                         break;
542                                 }
543                         }
544                 }
545
546                 if (display)
547                         mh->DisplayList(user, chan);
548                 else
549                         mh->DisplayEmptyList(user, chan);
550         }
551 }
552
553 void ModeParser::CleanMask(std::string &mask)
554 {
555         std::string::size_type pos_of_pling = mask.find_first_of('!');
556         std::string::size_type pos_of_at = mask.find_first_of('@');
557         std::string::size_type pos_of_dot = mask.find_first_of('.');
558         std::string::size_type pos_of_colons = mask.find("::"); /* Because ipv6 addresses are colon delimited -- double so it treats extban as nick */
559
560         if (mask.length() >= 2 && mask[1] == ':')
561                 return; // if it's an extban, don't even try guess how it needs to be formed.
562
563         if ((pos_of_pling == std::string::npos) && (pos_of_at == std::string::npos))
564         {
565                 /* Just a nick, or just a host - or clearly ipv6 (starting with :) */
566                 if ((pos_of_dot == std::string::npos) && (pos_of_colons == std::string::npos) && mask[0] != ':')
567                 {
568                         /* It has no '.' in it, it must be a nick. */
569                         mask.append("!*@*");
570                 }
571                 else
572                 {
573                         /* Got a dot in it? Has to be a host */
574                         mask = "*!*@" + mask;
575                 }
576         }
577         else if ((pos_of_pling == std::string::npos) && (pos_of_at != std::string::npos))
578         {
579                 /* Has an @ but no !, its a user@host */
580                  mask = "*!" + mask;
581         }
582         else if ((pos_of_pling != std::string::npos) && (pos_of_at == std::string::npos))
583         {
584                 /* Has a ! but no @, it must be a nick!ident */
585                 mask.append("@*");
586         }
587 }
588
589 bool ModeParser::AddMode(ModeHandler* mh)
590 {
591         unsigned char mask = 0;
592         unsigned char pos = 0;
593
594         /* Yes, i know, this might let people declare modes like '_' or '^'.
595          * If they do that, thats their problem, and if i ever EVER see an
596          * official InspIRCd developer do that, i'll beat them with a paddle!
597          */
598         if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z'))
599                 return false;
600
601         /* A mode prefix of ',' is not acceptable, it would fuck up server to server.
602          * A mode prefix of ':' will fuck up both server to server, and client to server.
603          * A mode prefix of '#' will mess up /whois and /privmsg
604          */
605         PrefixMode* pm = mh->IsPrefixMode();
606         if (pm)
607         {
608                 if ((pm->GetPrefix() > 126) || (pm->GetPrefix() == ',') || (pm->GetPrefix() == ':') || (pm->GetPrefix() == '#'))
609                         return false;
610
611                 if (FindPrefix(pm->GetPrefix()))
612                         return false;
613         }
614
615         mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
616         pos = (mh->GetModeChar()-65) | mask;
617
618         if (modehandlers[pos])
619                 return false;
620
621         // Everything is fine, add the mode
622         modehandlers[pos] = mh;
623         if (pm)
624                 mhlist.prefix.push_back(pm);
625         else if (mh->IsListModeBase())
626                 mhlist.list.push_back(mh->IsListModeBase());
627
628         RecreateModeListFor004Numeric();
629         return true;
630 }
631
632 bool ModeParser::DelMode(ModeHandler* mh)
633 {
634         unsigned char mask = 0;
635         unsigned char pos = 0;
636
637         if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z'))
638                 return false;
639
640         mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
641         pos = (mh->GetModeChar()-65) | mask;
642
643         if (modehandlers[pos] != mh)
644                 return false;
645
646         /* Note: We can't stack here, as we have modes potentially being removed across many different channels.
647          * To stack here we have to make the algorithm slower. Discuss.
648          */
649         switch (mh->GetModeType())
650         {
651                 case MODETYPE_USER:
652                         for (user_hash::iterator i = ServerInstance->Users->clientlist->begin(); i != ServerInstance->Users->clientlist->end(); )
653                         {
654                                 User* user = i->second;
655                                 ++i;
656                                 mh->RemoveMode(user);
657                         }
658                 break;
659                 case MODETYPE_CHANNEL:
660                         for (chan_hash::iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); )
661                         {
662                                 // The channel may not be in the hash after RemoveMode(), see m_permchannels
663                                 Channel* chan = i->second;
664                                 ++i;
665
666                                 irc::modestacker stack(false);
667                                 mh->RemoveMode(chan, stack);
668
669                                 std::vector<std::string> stackresult;
670                                 stackresult.push_back(chan->name);
671                                 while (stack.GetStackedLine(stackresult))
672                                 {
673                                         this->Process(stackresult, ServerInstance->FakeClient, MODE_LOCALONLY);
674                                         stackresult.erase(stackresult.begin() + 1, stackresult.end());
675                                 }
676                         }
677                 break;
678         }
679
680         modehandlers[pos] = NULL;
681         if (mh->IsPrefixMode())
682                 mhlist.prefix.erase(std::find(mhlist.prefix.begin(), mhlist.prefix.end(), mh->IsPrefixMode()));
683         else if (mh->IsListModeBase())
684                 mhlist.list.erase(std::find(mhlist.list.begin(), mhlist.list.end(), mh->IsListModeBase()));
685
686         RecreateModeListFor004Numeric();
687         return true;
688 }
689
690 ModeHandler* ModeParser::FindMode(unsigned const char modeletter, ModeType mt)
691 {
692         unsigned char mask = 0;
693         unsigned char pos = 0;
694
695         if ((modeletter < 'A') || (modeletter > 'z'))
696                 return NULL;
697
698         mt == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;
699         pos = (modeletter-65) | mask;
700
701         return modehandlers[pos];
702 }
703
704 PrefixMode* ModeParser::FindPrefixMode(unsigned char modeletter)
705 {
706         ModeHandler* mh = FindMode(modeletter, MODETYPE_CHANNEL);
707         if (!mh)
708                 return NULL;
709         return mh->IsPrefixMode();
710 }
711
712 std::string ModeParser::CreateModeList(ModeType mt, bool needparam)
713 {
714         std::string modestr;
715         unsigned char mask = ((mt == MODETYPE_CHANNEL) ? MASK_CHANNEL : MASK_USER);
716
717         for (unsigned char mode = 'A'; mode <= 'z'; mode++)
718         {
719                 unsigned char pos = (mode-65) | mask;
720
721                 if ((modehandlers[pos]) && ((!needparam) || (modehandlers[pos]->GetNumParams(true))))
722                         modestr.push_back(mode);
723         }
724
725         return modestr;
726 }
727
728 void ModeParser::RecreateModeListFor004Numeric()
729 {
730         Cached004ModeList = CreateModeList(MODETYPE_USER) + " " + CreateModeList(MODETYPE_CHANNEL) + " " + CreateModeList(MODETYPE_CHANNEL, true);
731 }
732
733 PrefixMode* ModeParser::FindPrefix(unsigned const char pfxletter)
734 {
735         const PrefixModeList& list = GetPrefixModes();
736         for (PrefixModeList::const_iterator i = list.begin(); i != list.end(); ++i)
737         {
738                 PrefixMode* pm = *i;
739                 if (pm->GetPrefix() == pfxletter)
740                         return pm;
741         }
742         return NULL;
743 }
744
745 std::string ModeParser::GiveModeList(ModeMasks m)
746 {
747         std::string type1;      /* Listmodes EXCEPT those with a prefix */
748         std::string type2;      /* Modes that take a param when adding or removing */
749         std::string type3;      /* Modes that only take a param when adding */
750         std::string type4;      /* Modes that dont take a param */
751
752         for (unsigned char mode = 'A'; mode <= 'z'; mode++)
753         {
754                 unsigned char pos = (mode-65) | m;
755                  /* One parameter when adding */
756                 if (modehandlers[pos])
757                 {
758                         if (modehandlers[pos]->GetNumParams(true))
759                         {
760                                 PrefixMode* pm = modehandlers[pos]->IsPrefixMode();
761                                 if ((modehandlers[pos]->IsListMode()) && ((!pm) || (pm->GetPrefix() == 0)))
762                                 {
763                                         type1 += modehandlers[pos]->GetModeChar();
764                                 }
765                                 else
766                                 {
767                                         /* ... and one parameter when removing */
768                                         if (modehandlers[pos]->GetNumParams(false))
769                                         {
770                                                 /* But not a list mode */
771                                                 if (!pm)
772                                                 {
773                                                         type2 += modehandlers[pos]->GetModeChar();
774                                                 }
775                                         }
776                                         else
777                                         {
778                                                 /* No parameters when removing */
779                                                 type3 += modehandlers[pos]->GetModeChar();
780                                         }
781                                 }
782                         }
783                         else
784                         {
785                                 type4 += modehandlers[pos]->GetModeChar();
786                         }
787                 }
788         }
789
790         return type1 + "," + type2 + "," + type3 + "," + type4;
791 }
792
793 std::string ModeParser::BuildPrefixes(bool lettersAndModes)
794 {
795         std::string mletters;
796         std::string mprefixes;
797         std::map<int,std::pair<char,char> > prefixes;
798
799         const PrefixModeList& list = GetPrefixModes();
800         for (PrefixModeList::const_iterator i = list.begin(); i != list.end(); ++i)
801         {
802                 PrefixMode* pm = *i;
803                 if (pm->GetPrefix())
804                         prefixes[pm->GetPrefixRank()] = std::make_pair(pm->GetPrefix(), pm->GetModeChar());
805         }
806
807         for(std::map<int,std::pair<char,char> >::reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); n++)
808         {
809                 mletters = mletters + n->second.first;
810                 mprefixes = mprefixes + n->second.second;
811         }
812
813         return lettersAndModes ? "(" + mprefixes + ")" + mletters : mletters;
814 }
815
816 void ModeParser::AddModeWatcher(ModeWatcher* mw)
817 {
818         modewatchermap.insert(std::make_pair(mw->GetModeName(), mw));
819 }
820
821 bool ModeParser::DelModeWatcher(ModeWatcher* mw)
822 {
823         std::pair<ModeWatchIter, ModeWatchIter> itpair = modewatchermap.equal_range(mw->GetModeName());
824         for (ModeWatchIter i = itpair.first; i != itpair.second; ++i)
825         {
826                 if (i->second == mw)
827                 {
828                         modewatchermap.erase(i);
829                         return true;
830                 }
831         }
832
833         return false;
834 }
835
836 void ModeHandler::RemoveMode(User* user)
837 {
838         // Remove the mode if it's set on the user
839         if (user->IsModeSet(this->GetModeChar()))
840         {
841                 std::vector<std::string> parameters;
842                 parameters.push_back(user->nick);
843                 parameters.push_back("-");
844                 parameters[1].push_back(this->GetModeChar());
845                 ServerInstance->Modes->Process(parameters, ServerInstance->FakeClient, ModeParser::MODE_LOCALONLY);
846         }
847 }
848
849 void ModeHandler::RemoveMode(Channel* channel, irc::modestacker& stack)
850 {
851         if (channel->IsModeSet(this))
852         {
853                 if (this->GetNumParams(false))
854                         // Removing this mode requires a parameter
855                         stack.Push(this->GetModeChar(), channel->GetModeParameter(this));
856                 else
857                         stack.Push(this->GetModeChar());
858         }
859 }
860
861 void PrefixMode::RemoveMode(Channel* chan, irc::modestacker& stack)
862 {
863         const UserMembList* userlist = chan->GetUsers();
864         for (UserMembCIter i = userlist->begin(); i != userlist->end(); ++i)
865         {
866                 if (i->second->hasMode(this->GetModeChar()))
867                         stack.Push(this->GetModeChar(), i->first->nick);
868         }
869 }
870
871 struct builtin_modes
872 {
873         ModeChannelSecret s;
874         ModeChannelPrivate p;
875         ModeChannelModerated m;
876         ModeChannelTopicOps t;
877
878         ModeChannelNoExternal n;
879         ModeChannelInviteOnly i;
880         ModeChannelKey k;
881         ModeChannelLimit l;
882
883         ModeChannelBan b;
884         ModeChannelOp o;
885         ModeChannelVoice v;
886
887         ModeUserWallops uw;
888         ModeUserInvisible ui;
889         ModeUserOperator uo;
890         ModeUserServerNoticeMask us;
891
892         void init()
893         {
894                 ServiceProvider* modes[] = { &s, &p, &m, &t, &n, &i, &k, &l, &b, &o, &v,
895                                                                          &uw, &ui, &uo, &us };
896                 ServerInstance->Modules->AddServices(modes, sizeof(modes)/sizeof(ServiceProvider*));
897         }
898 };
899
900 static builtin_modes static_modes;
901
902 void ModeParser::InitBuiltinModes()
903 {
904         static_modes.init();
905         static_modes.b.DoRehash();
906 }
907
908 ModeParser::ModeParser()
909 {
910         /* Clear mode handler list */
911         memset(modehandlers, 0, sizeof(modehandlers));
912
913         seq = 0;
914         memset(&sent, 0, sizeof(sent));
915 }
916
917 ModeParser::~ModeParser()
918 {
919 }