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