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