]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/mode.cpp
Dummy framework to insert the class for channelmode +s
[user/henk/code/inspircd.git] / src / mode.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *            the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 using namespace std;
18
19 #include "inspircd_config.h"
20 #include "inspircd.h"
21 #include "configreader.h"
22 #include <unistd.h>
23 #include <sys/errno.h>
24 #include <time.h>
25 #include <string>
26 #ifdef GCC3
27 #include <ext/hash_map>
28 #else
29 #include <hash_map>
30 #endif
31 #include <map>
32 #include <sstream>
33 #include <vector>
34 #include <deque>
35 #include "connection.h"
36 #include "users.h"
37 #include "ctables.h"
38 #include "globals.h"
39 #include "modules.h"
40 #include "dynamic.h"
41 #include "wildcard.h"
42 #include "message.h"
43 #include "commands.h"
44 #include "xline.h"
45 #include "inspstring.h"
46 #include "helperfuncs.h"
47 #include "mode.h"
48
49 #include "modes/cmode_s.h"
50
51 extern int MODCOUNT;
52 extern std::vector<Module*> modules;
53 extern std::vector<ircd_module*> factory;
54 extern InspIRCd* ServerInstance;
55 extern ServerConfig* Config;
56
57 extern time_t TIME;
58
59 ModeHandler::ModeHandler(char modeletter, int parameters_on, int parameters_off, bool listmode, ModeType type, bool operonly) : mode(modeletter), n_params_on(parameters_on), n_params_off(parameters_off), list(listmode), m_type(type), oper(operonly)
60 {
61 }
62
63 ModeHandler::~ModeHandler()
64 {
65 }
66
67 bool ModeHandler::IsListMode()
68 {
69         return list;
70 }
71
72 ModeType ModeHandler::GetModeType()
73 {
74         return m_type;
75 }
76
77 bool ModeHandler::NeedsOper()
78 {
79         return oper;
80 }
81
82 int ModeHandler::GetNumParams(bool adding)
83 {
84         return adding ? n_params_on : n_params_off;
85 }
86
87 char ModeHandler::GetModeChar()
88 {
89         return mode;
90 }
91
92 ModeAction ModeHandler::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
93 {
94         return MODEACTION_DENY;
95 }
96
97 void ModeHandler::DisplayList(userrec* user, chanrec* channel)
98 {
99 }
100
101 bool ModeHandler::CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel)
102 {
103         return (ours < theirs);
104 }
105
106 ModeWatcher::ModeWatcher(char modeletter, ModeType type) : mode(modeletter), m_type(type)
107 {
108 }
109
110 ModeWatcher::~ModeWatcher()
111 {
112 }
113
114 char ModeWatcher::GetModeChar()
115 {
116         return mode;
117 }
118
119 ModeType ModeWatcher::GetModeType()
120 {
121         return m_type;
122 }
123
124 bool ModeWatcher::BeforeMode(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding, ModeType type)
125 {
126         return true;
127 }
128
129 void ModeWatcher::AfterMode(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter, bool adding, ModeType type)
130 {
131 }
132
133 userrec* ModeParser::SanityChecks(userrec *user,char *dest,chanrec *chan,int status)
134 {
135         userrec *d;
136         if ((!user) || (!dest) || (!chan) || (!*dest))
137         {
138                 return NULL;
139         }
140         d = Find(dest);
141         if (!d)
142         {
143                 WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, dest);
144                 return NULL;
145         }
146         return d;
147 }
148
149 char* ModeParser::Grant(userrec *d,chanrec *chan,int MASK)
150 {
151         if (!chan)
152                 return NULL;
153
154         for (std::vector<ucrec*>::const_iterator i = d->chans.begin(); i != d->chans.end(); i++)
155         {
156                 if (((ucrec*)(*i))->channel == chan)
157                 {
158                         if (((ucrec*)(*i))->uc_modes & MASK)
159                         {
160                                 return NULL;
161                         }
162                         ((ucrec*)(*i))->uc_modes = ((ucrec*)(*i))->uc_modes | MASK;
163                         switch (MASK)
164                         {
165                                 case UCMODE_OP:
166                                         ((ucrec*)(*i))->channel->AddOppedUser(d);
167                                 break;
168                                 case UCMODE_HOP:
169                                         ((ucrec*)(*i))->channel->AddHalfoppedUser(d);
170                                 break;
171                                 case UCMODE_VOICE:
172                                         ((ucrec*)(*i))->channel->AddVoicedUser(d);
173                                 break;
174                         }
175                         log(DEBUG,"grant: %s %s",((ucrec*)(*i))->channel->name,d->nick);
176                         return d->nick;
177                 }
178         }
179         return NULL;
180 }
181
182 char* ModeParser::Revoke(userrec *d,chanrec *chan,int MASK)
183 {
184         if (!chan)
185                 return NULL;
186
187         for (std::vector<ucrec*>::const_iterator i = d->chans.begin(); i != d->chans.end(); i++)
188         {
189                 if (((ucrec*)(*i))->channel == chan)
190                 {
191                         if ((((ucrec*)(*i))->uc_modes & MASK) == 0)
192                         {
193                                 return NULL;
194                         }
195                         ((ucrec*)(*i))->uc_modes ^= MASK;
196                         switch (MASK)
197                         {
198                                 case UCMODE_OP:
199                                         ((ucrec*)(*i))->channel->DelOppedUser(d);
200                                 break;
201                                 case UCMODE_HOP:
202                                         ((ucrec*)(*i))->channel->DelHalfoppedUser(d);
203                                 break;
204                                 case UCMODE_VOICE:
205                                         ((ucrec*)(*i))->channel->DelVoicedUser(d);
206                                 break;
207                         }
208                         log(DEBUG,"revoke: %s %s",((ucrec*)(*i))->channel->name,d->nick);
209                         return d->nick;
210                 }
211         }
212         return NULL;
213 }
214
215 char* ModeParser::GiveOps(userrec *user,char *dest,chanrec *chan,int status)
216 {
217         userrec *d = this->SanityChecks(user,dest,chan,status);
218         
219         if (d)
220         {
221                 if (IS_LOCAL(user))
222                 {
223                         int MOD_RESULT = 0;
224                         FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_OP));
225                         
226                         if (MOD_RESULT == ACR_DENY)
227                                 return NULL;
228                         if (MOD_RESULT == ACR_DEFAULT)
229                         {
230                                 if ((status < STATUS_OP) && (!is_uline(user->server)))
231                                 {
232                                         WriteServ(user->fd,"482 %s %s :You're not a channel operator",user->nick, chan->name);
233                                         return NULL;
234                                 }
235                         }
236                 }
237
238                 return this->Grant(d,chan,UCMODE_OP);
239         }
240         return NULL;
241 }
242
243 char* ModeParser::GiveHops(userrec *user,char *dest,chanrec *chan,int status)
244 {
245         userrec *d = this->SanityChecks(user,dest,chan,status);
246         
247         if (d)
248         {
249                 if (IS_LOCAL(user))
250                 {
251                         int MOD_RESULT = 0;
252                         FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_HALFOP));
253                 
254                         if (MOD_RESULT == ACR_DENY)
255                                 return NULL;
256                         if (MOD_RESULT == ACR_DEFAULT)
257                         {
258                                 if ((status < STATUS_OP) && (!is_uline(user->server)))
259                                 {
260                                         WriteServ(user->fd,"482 %s %s :You're not a channel operator",user->nick, chan->name);
261                                         return NULL;
262                                 }
263                         }
264                 }
265
266                 return this->Grant(d,chan,UCMODE_HOP);
267         }
268         return NULL;
269 }
270
271 char* ModeParser::GiveVoice(userrec *user,char *dest,chanrec *chan,int status)
272 {
273         userrec *d = this->SanityChecks(user,dest,chan,status);
274         
275         if (d)
276         {
277                 if (IS_LOCAL(user))
278                 {
279                         int MOD_RESULT = 0;
280                         FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_VOICE));
281                         
282                         if (MOD_RESULT == ACR_DENY)
283                                 return NULL;
284                         if (MOD_RESULT == ACR_DEFAULT)
285                         {
286                                 if ((status < STATUS_HOP) && (!is_uline(user->server)))
287                                 {
288                                         WriteServ(user->fd,"482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, chan->name);
289                                         return NULL;
290                                 }
291                         }
292                 }
293
294                 return this->Grant(d,chan,UCMODE_VOICE);
295         }
296         return NULL;
297 }
298
299 char* ModeParser::TakeOps(userrec *user,char *dest,chanrec *chan,int status)
300 {
301         userrec *d = this->SanityChecks(user,dest,chan,status);
302         
303         if (d)
304         {
305                 if (IS_LOCAL(user))
306                 {
307                         int MOD_RESULT = 0;
308                         FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_DEOP));
309                         
310                         if (MOD_RESULT == ACR_DENY)
311                                 return NULL;
312                         if (MOD_RESULT == ACR_DEFAULT)
313                         {
314                                 if ((status < STATUS_OP) && (!is_uline(user->server)) && (IS_LOCAL(user)))
315                                 {
316                                         WriteServ(user->fd,"482 %s %s :You are not a channel operator",user->nick, chan->name);
317                                         return NULL;
318                                 }
319                         }
320                 }
321
322                 return this->Revoke(d,chan,UCMODE_OP);
323         }
324         return NULL;
325 }
326
327 char* ModeParser::TakeHops(userrec *user,char *dest,chanrec *chan,int status)
328 {
329         userrec *d = this->SanityChecks(user,dest,chan,status);
330         
331         if (d)
332         {
333                 if (IS_LOCAL(user))
334                 {
335                         int MOD_RESULT = 0;
336                         FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_DEHALFOP));
337                         
338                         if (MOD_RESULT == ACR_DENY)
339                                 return NULL;
340                         if (MOD_RESULT == ACR_DEFAULT)
341                         {
342                                 /* Tweak by Brain suggested by w00t, allow a halfop to dehalfop themselves */
343                                 if ((user != d) && ((status < STATUS_OP) && (!is_uline(user->server))))
344                                 {
345                                         WriteServ(user->fd,"482 %s %s :You are not a channel operator",user->nick, chan->name);
346                                         return NULL;
347                                 }
348                         }
349                 }
350
351                 return this->Revoke(d,chan,UCMODE_HOP);
352         }
353         return NULL;
354 }
355
356 char* ModeParser::TakeVoice(userrec *user,char *dest,chanrec *chan,int status)
357 {
358         userrec *d = this->SanityChecks(user,dest,chan,status);
359
360         if (d)  
361         {
362                 if (IS_LOCAL(user))
363                 {
364                         int MOD_RESULT = 0;
365                         FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,d,chan,AC_DEVOICE));
366                         
367                         if (MOD_RESULT == ACR_DENY)
368                                 return NULL;
369                         if (MOD_RESULT == ACR_DEFAULT)
370                         {
371                                 if ((status < STATUS_HOP) && (!is_uline(user->server)))
372                                 {
373                                         WriteServ(user->fd,"482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, chan->name);
374                                         return NULL;
375                                 }
376                         }
377                 }
378
379                 return this->Revoke(d,chan,UCMODE_VOICE);
380         }
381         return NULL;
382 }
383
384 char* ModeParser::AddBan(userrec *user,char *dest,chanrec *chan,int status)
385 {
386         BanItem b;
387         int toomanyexclamation = 0;
388         int toomanyat = 0;
389
390         if ((!user) || (!dest) || (!chan) || (!*dest))
391         {
392                 log(DEFAULT,"*** BUG *** AddBan was given an invalid parameter");
393                 return NULL;
394         }
395
396         for (char* i = dest; *i; i++)
397         {
398                 if ((*i < 32) || (*i > 126))
399                 {
400                         return NULL;
401                 }
402                 else if (*i == '!')
403                 {
404                         toomanyexclamation++;
405                 }
406                 else if (*i == '@')
407                 {
408                         toomanyat++;
409                 }
410         }
411
412         if (toomanyexclamation != 1 || toomanyat != 1)
413                 /*
414                  * this stops sillyness like n!u!u!u@h, though note that most
415                  * ircds don't actually verify banmask validity. --w00t
416                  */
417                 return NULL;
418
419         long maxbans = GetMaxBans(chan->name);
420         if ((unsigned)chan->bans.size() > (unsigned)maxbans)
421         {
422                 WriteServ(user->fd,"478 %s %s :Channel ban list for %s is full (maximum entries for this channel is %d)",user->nick, chan->name,chan->name,maxbans);
423                 return NULL;
424         }
425
426         log(DEBUG,"AddBan: %s %s",chan->name,user->nick);
427
428         int MOD_RESULT = 0;
429         FOREACH_RESULT(I_OnAddBan,OnAddBan(user,chan,dest));
430         if (MOD_RESULT)
431                 return NULL;
432
433         TidyBan(dest);
434         for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++)
435         {
436                 if (!strcasecmp(i->data,dest))
437                 {
438                         // dont allow a user to set the same ban twice
439                         return NULL;
440                 }
441         }
442
443         b.set_time = TIME;
444         strlcpy(b.data,dest,MAXBUF);
445         if (*user->nick)
446         {
447                 strlcpy(b.set_by,user->nick,NICKMAX-1);
448         }
449         else
450         {
451                 strlcpy(b.set_by,Config->ServerName,NICKMAX-1);
452         }
453         chan->bans.push_back(b);
454         return dest;
455 }
456
457 char* ModeParser::TakeBan(userrec *user,char *dest,chanrec *chan,int status)
458 {
459         if ((!user) || (!dest) || (!chan) || (!*dest)) {
460                 log(DEFAULT,"*** BUG *** TakeBan was given an invalid parameter");
461                 return 0;
462         }
463
464         log(DEBUG,"del_ban: %s %s",chan->name,user->nick);
465         for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++)
466         {
467                 if (!strcasecmp(i->data,dest))
468                 {
469                         int MOD_RESULT = 0;
470                         FOREACH_RESULT(I_OnDelBan,OnDelBan(user,chan,dest));
471                         if (MOD_RESULT)
472                                 return NULL;
473                         chan->bans.erase(i);
474                         return dest;
475                 }
476         }
477         return NULL;
478 }
479
480 void ModeParser::Process(char **parameters, int pcnt, userrec *user, bool servermode)
481 {
482         std::string target = parameters[0];
483         ModeType type = MODETYPE_USER;
484         chanrec* targetchannel = FindChan(parameters[0]);
485         userrec* targetuser  = Find(parameters[0]);
486
487         if (pcnt > 1)
488         {
489                 if (targetchannel)
490                 {
491                         type = MODETYPE_CHANNEL;
492                 }
493                 else if (targetuser)
494                 {
495                         type = MODETYPE_USER;
496                 }
497                 else
498                 {
499                         /* No such nick/channel */
500                         return;
501                 }
502                 std::string mode_sequence = parameters[1];
503                 std::string parameter = "";
504                 std::ostringstream parameter_list;
505                 std::string output_sequence = "";
506                 bool adding = true, state_change = false;
507                 int handler_id = 0;
508                 int parameter_counter = 2; /* Index of first parameter */
509
510                 for (std::string::const_iterator modeletter = mode_sequence.begin(); modeletter != mode_sequence.end(); modeletter++)
511                 {
512                         switch (*modeletter)
513                         {
514                                 /* NB:
515                                  * For + and - mode characters, we don't just stick the character into the output sequence.
516                                  * This is because the user may do something dumb, like: +-+ooo or +oo-+. To prevent this
517                                  * appearing in the output sequence, we store a flag which says there was a state change,
518                                  * which is set on any + or -, however, the + or - that we finish on is only appended to
519                                  * the output stream in the event it is followed by a non "+ or -" character, such as o or v.
520                                  */
521                                 case '+':
522                                         /* The following expression prevents: +o+o nick nick, compressing it to +oo nick nick,
523                                          * however, will allow the + if it is the first item in the sequence, regardless.
524                                          */
525                                         if ((!adding) || (!output_sequence.length()))
526                                                 state_change = true;
527                                         adding = true;
528                                         continue;
529                                 break;
530                                 case '-':
531                                         if ((adding) || (!output_sequence.length()))
532                                                 state_change = true;
533                                         adding = false;
534                                         continue;
535                                 break;
536                                 default:
537
538                                         /* 65 is the ascii value of 'A' */
539                                         handler_id = *modeletter - 65;
540
541                                         if (modehandlers[handler_id])
542                                         {
543                                                 bool abort = false;
544                                                 for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
545                                                 {
546                                                         if ((*watchers)->BeforeMode(user, targetuser, targetchannel, parameter, adding, type) == MODEACTION_DENY)
547                                                                 abort = true;
548                                                 }
549                                                 if ((modehandlers[handler_id]->GetModeType() == type) && (!abort))
550                                                 {
551                                                         if (modehandlers[handler_id]->GetNumParams(adding))
552                                                         {
553                                                                 if (pcnt < parameter_counter)
554                                                                 {
555                                                                         parameter = parameters[parameter_counter++];
556                                                                 }
557                                                                 else
558                                                                 {
559                                                                         parameter = "";
560                                                                 }
561                                                         }
562                                                         ModeAction ma = modehandlers[handler_id]->OnModeChange(user, targetuser, targetchannel, parameter, adding);
563                                                         if (ma == MODEACTION_ALLOW)
564                                                         {
565                                                                 /* We're about to output a valid mode letter - was there previously a pending state-change? */
566                                                                 if (state_change)
567                                                                         output_sequence.append(adding ? "+" : "-");
568                                                                 
569                                                                 /* Add the mode letter */
570                                                                 output_sequence = output_sequence + *modeletter;
571
572                                                                 /* Is there a valid parameter for this mode? If so add it to the parameter list */
573                                                                 if ((modehandlers[handler_id]->GetNumParams(adding)) && (parameter != ""))
574                                                                 {
575                                                                         parameter_list << " " << parameter;
576                                                                 }
577
578                                                                 /* Call all the AfterMode events in the mode watchers for this mode */
579                                                                 for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)
580                                                                 {
581                                                                         (*watchers)->AfterMode(user, targetuser, targetchannel, parameter, adding, type);
582                                                                 }
583
584                                                                 /* Reset the state change flag */
585                                                                 state_change = false;
586                                                         }
587                                                 }
588                                         }
589                                 break;
590                         }
591                 }
592                 /* Was there at least one valid mode in the sequence? */
593                 if (output_sequence != "")
594                 {
595                         if (servermode)
596                         {
597                                 if (type == MODETYPE_CHANNEL)
598                                 {
599                                         WriteChannelWithServ(Config->ServerName,targetchannel,"MODE %s %s%s",targetchannel->name,output_sequence.c_str(),parameter_list.str().c_str());
600                                 }
601                         }
602                         else
603                         {
604                                 if (type == MODETYPE_CHANNEL)
605                                 {
606                                         WriteChannel(targetchannel,user,"MODE %s %s%s",targetchannel->name,output_sequence.c_str(),parameter_list.str().c_str());
607                                         FOREACH_MOD(I_OnMode,OnMode(user, targetchannel, TYPE_CHANNEL, output_sequence + parameter_list.str()));
608                                 }
609                         }
610                 }
611         }
612 }
613
614 std::string ModeParser::CompressModes(std::string modes,bool channelmodes)
615 {
616         return "";
617 }
618
619 void ModeParser::ProcessModes(char **parameters,userrec* user,chanrec *chan,int status, int pcnt, bool servermode, bool silent, bool local)
620 {
621         if ((!parameters) || (pcnt < 2)) {
622                 return;
623         }
624
625         char outlist[MAXBUF];
626         char mlist[MAXBUF];
627         char *outpars[32];
628         int param = 2;
629         int pc = 0;
630         int ptr = 0;
631         int mdir = 1;
632         char* r = NULL;
633         bool k_set = false, l_set = false, previously_set_l = false, previously_unset_l = false, previously_set_k = false, previously_unset_k = false;
634
635         int MOD_RESULT = 0;
636         
637         if (IS_LOCAL(user))
638         {
639                 FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,NULL,chan,AC_GENERAL_MODE));  
640                 if (MOD_RESULT == ACR_DENY)
641                         return;
642         }
643
644         std::string tidied = this->CompressModes(parameters[1],true);
645         strlcpy(mlist,tidied.c_str(),MAXBUF);
646         char* modelist = mlist;
647
648         *outlist = *modelist;
649         char* outl = outlist+1;
650
651         mdir = (*modelist == '+');
652
653         log(DEBUG,"process_modes: modelist: %s",modelist);
654
655         int len = tidied.length();
656         while (modelist[len-1] == ' ')
657                 modelist[--len] = '\0';
658
659         for (char* modechar = (modelist + 1); *modechar; ptr++, modechar++)
660         {
661                 r = NULL;
662
663                 /* If we have more than MAXMODES changes in one line,
664                  * drop all after the MAXMODES
665                  */
666                 if (pc > MAXMODES-1)
667                         break;
668
669
670                 
671                 switch (*modechar)
672                 {
673                         case '-':
674                                 *outl++ = '-';
675                                 mdir = 0;
676                         break;                  
677
678                         case '+':
679                                 *outl++ = '+';
680                                 mdir = 1;
681                         break;
682
683                         case 'o':
684                                 log(DEBUG,"Ops");
685                                 if ((param >= pcnt)) break;
686                                 log(DEBUG,"Enough parameters left");
687                                 r = NULL;
688                                 if (mdir == 1)
689                                 {
690                                         MOD_RESULT = 0;
691                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'o', parameters[param], true, 1));
692                                         if (!MOD_RESULT)
693                                         {
694                                                 r = GiveOps(user,parameters[param++],chan,status);
695                                         }
696                                         else param++;
697                                 }
698                                 else
699                                 {
700                                         MOD_RESULT = 0;
701                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'o', parameters[param], false, 1));
702                                         if (!MOD_RESULT)
703                                         {
704                                                 r = TakeOps(user,parameters[param++],chan,status);
705                                         }
706                                         else param++;
707                                 }
708                                 if (r)
709                                 {
710                                         *outl++ = 'o';
711                                         outpars[pc++] = r;
712                                 }
713                         break;
714                         
715                         case 'h':
716                                 if (((param >= pcnt)) || (!Config->AllowHalfop)) break;
717                                 r = NULL;
718                                 if (mdir == 1)
719                                 {
720                                         MOD_RESULT = 0;
721                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'h', parameters[param], true, 1));
722                                         if (!MOD_RESULT)
723                                         {
724                                                 r = GiveHops(user,parameters[param++],chan,status);
725                                         }
726                                         else param++;
727                                 }
728                                 else
729                                 {
730                                         MOD_RESULT = 0;
731                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'h', parameters[param], false, 1));
732                                         if (!MOD_RESULT)
733                                         {
734                                                 r = TakeHops(user,parameters[param++],chan,status);
735                                         }
736                                         else param++;
737                                 }
738                                 if (r)
739                                 {
740                                         *outl++ = 'h';
741                                         outpars[pc++] = r;
742                                 }
743                         break;
744                         
745                                 
746                         case 'v':
747                                         if ((param >= pcnt)) break;
748                                         r = NULL;
749                                         if (mdir == 1)
750                                         {
751                                                 MOD_RESULT = 0;
752                                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'v', parameters[param], true, 1));
753                                                 if (!MOD_RESULT)
754                                                 {
755                                                         r = GiveVoice(user,parameters[param++],chan,status);
756                                                 }
757                                                 else param++;
758                                         }
759                                         else
760                                         {
761                                                 MOD_RESULT = 0;
762                                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'v', parameters[param], false, 1));
763                                                 if (!MOD_RESULT)
764                                                 {
765                                                         r = TakeVoice(user,parameters[param++],chan,status);
766                                                 }
767                                                 else param++;
768                                         }
769                                         if (r)
770                                         {
771                                                 *outl++ = 'v';
772                                                 outpars[pc++] = r;
773                                         }
774                         break;
775                                 
776                         case 'b':
777                                 if ((param >= pcnt)) break;
778                                 r = NULL;
779                                 if (mdir == 1)
780                                 {
781                                         MOD_RESULT = 0;
782                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'b', parameters[param], true, 1));
783                                         if (!MOD_RESULT)
784                                         {
785                                                 r = AddBan(user,parameters[param++],chan,status);
786                                         }
787                                         else param++;
788                                 }
789                                 else
790                                 {
791                                         MOD_RESULT = 0;
792                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'b', parameters[param], false, 1));
793                                         if (!MOD_RESULT)
794                                         {
795                                                 r = TakeBan(user,parameters[param++],chan,status);
796                                         }
797                                         else param++;
798                                 }
799                                 if (r)
800                                 {
801                                         *outl++ = 'b';
802                                         outpars[pc++] = parameters[param-1];
803                                 }
804                         break;
805
806
807                         case 'k':
808                                 if ((param >= pcnt))
809                                         break;
810
811                                 if (mdir == 1)
812                                 {
813                                         if (k_set)
814                                                 break;
815
816                                         if (previously_unset_k)
817                                                 break;
818                                         previously_set_k = true;
819                                                 
820                                         if (!chan->modes[CM_KEY])
821                                         {
822                                                 MOD_RESULT = 0;
823                                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'k', parameters[param], true, 1));
824                                                 if (!MOD_RESULT)
825                                                 {
826                                                         *outl++ = 'k';
827                                                         char key[MAXBUF];
828                                                         strlcpy(key,parameters[param++],32);
829                                                         outpars[pc++] = key;
830                                                         strlcpy(chan->key,key,MAXBUF);
831                                                         chan->modes[CM_KEY] = 1;
832                                                         k_set = true;
833                                                 }
834                                                 else param++;
835                                         }
836                                 }
837                                 else
838                                 {
839                                         /* checks on -k are case sensitive and only accurate to the
840                                                    first 32 characters */
841                                         if (previously_set_k)
842                                                 break;
843                                         previously_unset_k = true;
844
845                                         char key[MAXBUF];
846                                         MOD_RESULT = 0;
847                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'k', parameters[param], false, 1));
848                                         if (!MOD_RESULT)
849                                         {
850                                                 strlcpy(key,parameters[param++],32);
851                                                 /* only allow -k if correct key given */
852                                                 if (!strcmp(chan->key,key))
853                                                 {
854                                                         *outl++ = 'k';
855                                                         *chan->key = 0;
856                                                         chan->modes[CM_KEY] = 0;
857                                                         outpars[pc++] = key;
858                                                 }
859                                         }
860                                         else param++;
861                                 }
862                         break;
863                                 
864                         case 'l':
865                                 if (mdir == 0)
866                                 {
867                                         if (previously_set_l)
868                                                 break;
869                                         previously_unset_l = true;
870                                         MOD_RESULT = 0;
871                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'l', "", false, 0));
872                                         if (!MOD_RESULT)
873                                         {
874                                                 if (chan->modes[CM_LIMIT])
875                                                 {
876                                                         *outl++ = 'l';
877                                                         chan->limit = 0;
878                                                         chan->modes[CM_LIMIT] = 0;
879                                                 }
880                                         }
881                                 }
882                                         
883                                 if ((param >= pcnt)) break;
884                                 if (mdir == 1)
885                                 {
886                                         if (l_set)
887                                                 break;
888                                         if (previously_unset_l)
889                                                 break;
890                                         previously_set_l = true;
891                                         bool invalid = false;
892                                         for (char* f = parameters[param]; *f; f++)
893                                         {
894                                                 if ((*f < '0') || (*f > '9'))
895                                                 {
896                                                         invalid = true;
897                                                 }
898                                         }
899                                         /* If the limit is < 1, or the new limit is the current limit, dont allow */
900                                         if ((atoi(parameters[param]) < 1) || ((chan->limit > 0) && (atoi(parameters[param]) == chan->limit)))
901                                         {
902                                                 invalid = true;
903                                         }
904
905                                         if (invalid)
906                                                 break;
907
908                                         MOD_RESULT = 0;
909                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'l', parameters[param], true, 1));
910                                         if (!MOD_RESULT)
911                                         {
912         
913                                                 chan->limit = atoi(parameters[param]);
914                                                         
915                                                 // reported by mech: large values cause underflow
916                                                 if (chan->limit < 0)
917                                                         chan->limit = 0x7FFF;
918                                         }
919                                                 
920                                         if (chan->limit)
921                                         {
922                                                 *outl++ = 'l';
923                                                 chan->modes[CM_LIMIT] = 1;
924                                                 outpars[pc++] = parameters[param++];
925                                                 l_set = true;
926                                         }
927                                 }
928                         break;
929                                 
930                         case 'i':
931                                 MOD_RESULT = 0;
932                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'i', "", mdir, 0));
933                                 if (!MOD_RESULT)
934                                 {
935                                         if (mdir)
936                                         {
937                                                 if (!(chan->modes[CM_INVITEONLY])) *outl++ = 'i';
938                                                 chan->modes[CM_INVITEONLY] = 1;
939                                         }
940                                         else
941                                         {
942                                                 if (chan->modes[CM_INVITEONLY]) *outl++ = 'i';
943                                                 chan->modes[CM_INVITEONLY] = 0;
944                                         }
945                                 }
946                         break;
947                                 
948                         case 't':
949                                 MOD_RESULT = 0;
950                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 't', "", mdir, 0));
951                                 if (!MOD_RESULT)
952                                 {
953                                         if (mdir)
954                                         {
955                                                 if (!(chan->modes[CM_TOPICLOCK])) *outl++ = 't';
956                                                 chan->modes[CM_TOPICLOCK] = 1;
957                                         }
958                                         else
959                                         {
960                                                 if (chan->modes[CM_TOPICLOCK]) *outl++ = 't';
961                                                 chan->modes[CM_TOPICLOCK] = 0;
962                                         }
963                                 }
964                         break;
965                                 
966                         case 'n':
967                                 MOD_RESULT = 0;
968                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'n', "", mdir, 0));
969                                 if (!MOD_RESULT)
970                                 {
971                                         if (mdir)
972                                         {
973                                                 if (!(chan->modes[CM_NOEXTERNAL])) *outl++ = 'n';
974                                                 chan->modes[CM_NOEXTERNAL] = 1;
975                                         }
976                                         else
977                                         {
978                                                 if (chan->modes[CM_NOEXTERNAL]) *outl++ = 'n';
979                                                 chan->modes[CM_NOEXTERNAL] = 0;
980                                         }
981                                 }
982                         break;
983                                 
984                         case 'm':
985                                 MOD_RESULT = 0;
986                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'm', "", mdir, 0));
987                                 if (!MOD_RESULT)
988                                 {
989                                         if (mdir)
990                                         {
991                                                 if (!(chan->modes[CM_MODERATED])) *outl++ = 'm';
992                                                 chan->modes[CM_MODERATED] = 1;
993                                         }
994                                         else
995                                         {
996                                                 if (chan->modes[CM_MODERATED]) *outl++ = 'm';
997                                                 chan->modes[CM_MODERATED] = 0;
998                                         }
999                                 }
1000                         break;
1001                                 
1002                         case 's':
1003                                 MOD_RESULT = 0;
1004                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 's', "", mdir, 0));
1005                                 if (!MOD_RESULT)
1006                                 {
1007                                         if (mdir)
1008                                         {
1009                                                 if (!(chan->modes[CM_SECRET])) *outl++ = 's';
1010                                                 chan->modes[CM_SECRET] = 1;
1011                                                 if (chan->modes[CM_PRIVATE])
1012                                                 {
1013                                                         chan->modes[CM_PRIVATE] = 0;
1014                                                         if (mdir)
1015                                                         {
1016                                                                 *outl++ = '-'; *outl++ = 'p'; *outl++ = '+';
1017                                                         }
1018                                                 }
1019                                         }
1020                                         else
1021                                         {
1022                                                 if (chan->modes[CM_SECRET]) *outl++ = 's';
1023                                                 chan->modes[CM_SECRET] = 0;
1024                                         }
1025                                 }
1026                         break;
1027                                 
1028                         case 'p':
1029                                 MOD_RESULT = 0;
1030                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'p', "", mdir, 0));
1031                                 if(!MOD_RESULT)
1032                                 {
1033                                         if(mdir)
1034                                         {
1035                                                 if(!(chan->modes[CM_PRIVATE]))
1036                                                         *outl++ = 'p';
1037                                                 
1038                                                 chan->modes[CM_PRIVATE] = 1;
1039                                                 
1040                                                 if(chan->modes[CM_SECRET])
1041                                                 {
1042                                                         chan->modes[CM_SECRET] = 0;
1043
1044                                                         *outl++ = '-';
1045                                                         *outl++ = 's';
1046                                                         *outl++ = '+';
1047                                                 }
1048                                         }
1049                                         else
1050                                         {
1051                                                 if(chan->modes[CM_PRIVATE])
1052                                                         *outl++ = 'p';
1053                                                 
1054                                                 chan->modes[CM_PRIVATE] = 0;
1055                                         }
1056                                 }
1057                                 break;
1058                                 
1059                         default:
1060                                 string_list p;
1061                                 p.clear();
1062                                 bool x = chan->modes[*modechar-65];
1063                                 if ((!x && !mdir) || (x && mdir))
1064                                 {
1065                                         if (!ModeIsListMode(*modechar,MT_CHANNEL))
1066                                         {
1067                                                 log(DEBUG,"Mode %c isnt set on %s but trying to remove!",*modechar,chan->name);
1068                                                 break;
1069                                         }
1070                                 }
1071                                 if (ModeDefined(*modechar,MT_CHANNEL))
1072                                 {
1073                                         /* A module has claimed this mode */
1074                                         if (param<pcnt)
1075                                         {
1076                                                 if ((ModeDefinedOn(*modechar,MT_CHANNEL)>0) && (mdir))
1077                                                 {
1078                                                         p.push_back(parameters[param]);
1079                                                 }
1080                                                 if ((ModeDefinedOff(*modechar,MT_CHANNEL)>0) && (!mdir))
1081                                                 {
1082                                                         p.push_back(parameters[param]);
1083                                                 }
1084                                         }
1085                                         bool handled = false;
1086                                         if (param>=pcnt)
1087                                         {
1088                                                 // we're supposed to have a parameter, but none was given... so dont handle the mode.
1089                                                 if (((ModeDefinedOn(*modechar,MT_CHANNEL)>0) && (mdir)) || ((ModeDefinedOff(*modechar,MT_CHANNEL)>0) && (!mdir)))       
1090                                                 {
1091                                                         log(DEBUG,"Not enough parameters for module-mode %c",*modechar);
1092                                                         handled = true;
1093                                                         param++;
1094                                                 }
1095                                         }
1096                                         // BIG ASS IDIOTIC CODER WARNING!
1097                                         // Using OnRawMode on another modules mode's behavour 
1098                                         // will confuse the crap out of admins! just because you CAN
1099                                         // do it, doesnt mean you SHOULD!
1100                                         MOD_RESULT = 0;
1101                                         std::string para = "";
1102                                         if (p.size())
1103                                                 para = p[0];
1104                                         
1105                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, *modechar, para, mdir, pcnt));
1106                                         if(!MOD_RESULT)
1107                                         {
1108                                                 for (int i = 0; i <= MODCOUNT; i++)
1109                                                 {
1110                                                         if (!handled)
1111                                                         {
1112                                                                 int t = modules[i]->OnExtendedMode(user,chan,*modechar,MT_CHANNEL,mdir,p);
1113                                                                 if (t != 0)
1114                                                                 {
1115                                                                         log(DEBUG,"OnExtendedMode returned nonzero for a module");
1116                                                                         if (ModeIsListMode(*modechar,MT_CHANNEL))
1117                                                                         {
1118                                                                                 if (t == -1)
1119                                                                                 {
1120                                                                                         //pc++;
1121                                                                                         param++;
1122                                                                                 }
1123                                                                                 else
1124                                                                                 {
1125                                                                                         if (param < pcnt)
1126                                                                                         {
1127                                                                                                 *outl++ = *modechar;
1128                                                                                         }
1129                                                                                         outpars[pc++] = parameters[param++];
1130                                                                                 }
1131                                                                         }
1132                                                                         else
1133                                                                         {
1134                                                                                 *outl++ = *modechar;
1135                                                                                 chan->SetCustomMode(*modechar,mdir);
1136                                                                                 // include parameters in output if mode has them
1137                                                                                 if ((ModeDefinedOn(*modechar,MT_CHANNEL)>0) && (mdir))
1138                                                                                 {
1139                                                                                         if (param < pcnt)
1140                                                                                         {
1141                                                                                                 chan->SetCustomModeParam(*modechar,parameters[param],mdir);
1142                                                                                                 outpars[pc++] = parameters[param++];
1143                                                                                         }
1144                                                                                 }
1145                                                                         }
1146                                                                         // break, because only one module can handle the mode.
1147                                                                         handled = true;
1148                                                                 }
1149                                                         }
1150                                                 }
1151                                         }
1152                                 }
1153                                 else
1154                                 {
1155                                         WriteServ(user->fd,"472 %s %c :is unknown mode char to me",user->nick,*modechar);
1156                                 }
1157                         break;
1158                 }
1159         }
1160
1161         /* Null terminate it now we're done */
1162         *outl = 0;
1163
1164
1165         /************ Fast, but confusing string tidying ************/
1166         outl = outlist;
1167         while (*outl && (*outl < 'A'))
1168                 outl++;
1169         /* outl now points to the first mode character after +'s and -'s */
1170         outl--;
1171         /* Now points at first mode-modifier + or - symbol */
1172         char* trim = outl;
1173         /* Now we tidy off any trailing -'s etc */
1174         while (*trim++);
1175         trim--;
1176         while ((*--trim == '+') || (*trim == '-'))
1177                 *trim = 0;
1178         /************ Done wih the string tidy functions ************/
1179
1180
1181         /* The mode change must be at least two characters long (+ or - and at least one mode) */
1182         if (((*outl == '+') || (*outl == '-')) && *(outl+1))
1183         {
1184                 for (ptr = 0; ptr < pc; ptr++)
1185                 {
1186                         charlcat(outl,' ',MAXBUF);
1187                         strlcat(outl,outpars[ptr],MAXBUF-1);
1188                 }
1189                 if (local)
1190                 {
1191                         log(DEBUG,"Local mode change");
1192                         WriteChannelLocal(chan, user, "MODE %s %s",chan->name,outl);
1193                         FOREACH_MOD(I_OnMode,OnMode(user, chan, TYPE_CHANNEL, outl));
1194                 }
1195                 else
1196                 {
1197                         if (servermode)
1198                         {
1199                                 if (!silent)
1200                                 {
1201                                         WriteChannelWithServ(Config->ServerName,chan,"MODE %s %s",chan->name,outl);
1202                                 }
1203                                         
1204                         }
1205                         else
1206                         {
1207                                 if (!silent)
1208                                 {
1209                                         WriteChannel(chan,user,"MODE %s %s",chan->name,outl);
1210                                         FOREACH_MOD(I_OnMode,OnMode(user, chan, TYPE_CHANNEL, outl));
1211                                 }
1212                         }
1213                 }
1214         }
1215 }
1216
1217 // based on sourcemodes, return true or false to determine if umode is a valid mode a user may set on themselves or others.
1218
1219 bool ModeParser::AllowedUmode(char umode, char* sourcemodes,bool adding,bool serveroverride)
1220 {
1221         bool sourceoper = (strchr(sourcemodes,'o') != NULL);
1222         log(DEBUG,"Allowed_umode: %c %s",umode,sourcemodes);
1223         // Servers can +o and -o arbitrarily
1224         if ((serveroverride == true) && (umode == 'o'))
1225         {
1226                 return true;
1227         }
1228         // RFC1459 specified modes
1229         if ((umode == 'w') || (umode == 's') || (umode == 'i'))
1230         {
1231                 /* umode allowed by RFC1459 scemantics */
1232                 return true;
1233         }
1234         
1235         /* user may not +o themselves or others, but an oper may de-oper other opers or themselves */
1236         if (sourceoper && !adding)
1237         {
1238                 return true;
1239         }
1240         else if (umode == 'o')
1241         {
1242                 /* Bad oper, bad bad! */
1243                 return false;
1244         }
1245         
1246         /* process any module-defined modes that need oper */
1247         if ((ModeDefinedOper(umode,MT_CLIENT)) && (sourceoper))
1248         {
1249                 log(DEBUG,"umode %c allowed by module handler (oper only mode)",umode);
1250                 return true;
1251         }
1252         else if (ModeDefined(umode,MT_CLIENT))
1253         {
1254                 // process any module-defined modes that don't need oper
1255                 log(DEBUG,"umode %c allowed by module handler (non-oper mode)",umode);
1256                 if ((ModeDefinedOper(umode,MT_CLIENT)) && (!sourceoper))
1257                 {
1258                         // no, this mode needs oper, and this user 'aint got what it takes!
1259                         return false;
1260                 }
1261                 return true;
1262         }
1263
1264         // anything else - return false.
1265         log(DEBUG,"umode %c not known by any ruleset",umode);
1266         return false;
1267 }
1268
1269 bool ModeParser::ProcessModuleUmode(char umode, userrec* source, void* dest, bool adding)
1270 {
1271         userrec* s2;
1272         bool faked = false;
1273         if (!source)
1274         {
1275                 s2 = new userrec;
1276                 strlcpy(s2->nick,Config->ServerName,NICKMAX-1);
1277                 *s2->modes = 'o';
1278                 *(s2->modes+1) = 0;
1279                 s2->fd = -1;
1280                 source = s2;
1281                 faked = true;
1282         }
1283         string_list p;
1284         p.clear();
1285         if (ModeDefined(umode,MT_CLIENT))
1286         {
1287                 for (int i = 0; i <= MODCOUNT; i++)
1288                 {
1289                         if (modules[i]->OnExtendedMode(source,(void*)dest,umode,MT_CLIENT,adding,p))
1290                         {
1291                                 log(DEBUG,"Module %s claims umode %c",Config->module_names[i].c_str(),umode);
1292                                 return true;
1293                         }
1294                 }
1295                 log(DEBUG,"No module claims umode %c",umode);
1296                 if (faked)
1297                 {
1298                         DELETE(s2);
1299                         source = NULL;
1300                 }
1301                 return false;
1302         }
1303         else
1304         {
1305                 if (faked)
1306                 {
1307                         DELETE(s2);
1308                         source = NULL;
1309                 }
1310                 return false;
1311         }
1312 }
1313
1314 void cmd_mode::Handle (char **parameters, int pcnt, userrec *user)
1315 {
1316         chanrec* chan;
1317         userrec* dest = Find(parameters[0]);
1318         int MOD_RESULT;
1319         int can_change;
1320         int direction = 1;
1321         char outpars[MAXBUF];
1322         bool next_ok = true;
1323
1324         if (!user)
1325                 return;
1326
1327         if ((dest) && (pcnt == 1))
1328         {
1329                 WriteServ(user->fd,"221 %s :+%s",dest->nick,dest->modes);
1330                 return;
1331         }
1332         else if ((dest) && (pcnt > 1))
1333         {
1334                 std::string tidied = ServerInstance->ModeGrok->CompressModes(parameters[1],false);
1335                 parameters[1] = (char*)tidied.c_str();
1336
1337                 char dmodes[MAXBUF];
1338                 strlcpy(dmodes,dest->modes,MAXMODES);
1339                 log(DEBUG,"pulled up dest user modes: %s",dmodes);
1340
1341                 can_change = 0;
1342                 if (user != dest)
1343                 {
1344                         if ((*user->oper) || (is_uline(user->server)))
1345                         {
1346                                 can_change = 1;
1347                         }
1348                 }
1349                 else
1350                 {
1351                         can_change = 1;
1352                 }
1353                 if (!can_change)
1354                 {
1355                         WriteServ(user->fd,"482 %s :Can't change mode for other users",user->nick);
1356                         return;
1357                 }
1358                 
1359                 outpars[0] = *parameters[1];
1360                 outpars[1] = 0;
1361                 direction = (*parameters[1] == '+');
1362
1363                 if ((*parameters[1] != '+') && (*parameters[1] != '-'))
1364                         return;
1365
1366                 for (char* i = parameters[1]; *i; i++)
1367                 {
1368                         if ((i != parameters[1]) && (*i != '+') && (*i != '-'))
1369                                 next_ok = true;
1370
1371                         switch (*i)
1372                         {
1373                                 case ' ':
1374                                 continue;
1375
1376                                 case '+':
1377                                         if ((direction != 1) && (next_ok))
1378                                         {
1379                                                 charlcat(outpars,'+',MAXBUF);
1380                                                 next_ok = false;
1381                                         }       
1382                                         direction = 1;
1383                                 break;
1384
1385                                 case '-':
1386                                         if ((direction != 0) && (next_ok))
1387                                         {
1388                                                 charlcat(outpars,'-',MAXBUF);
1389                                                 next_ok = false;
1390                                         }
1391                                         direction = 0;
1392                                 break;
1393
1394                                 default:
1395                                         can_change = 0;
1396                                         if (*user->oper)
1397                                         {
1398                                                 can_change = 1;
1399                                         }
1400                                         else
1401                                         {
1402                                                 if ((*i == 'i') || (*i == 'w') || (*i == 's') || (ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,direction,false)))
1403                                                 {
1404                                                         can_change = 1;
1405                                                 }
1406                                         }
1407                                         if (can_change)
1408                                         {
1409                                                 if (direction == 1)
1410                                                 {
1411                                                         if ((!strchr(dmodes,*i)) && (ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,true,false)))
1412                                                         {
1413                                                                 if ((ServerInstance->ModeGrok->ProcessModuleUmode(*i, user, dest, direction)) || (*i == 'i') || (*i == 's') || (*i == 'w') || (*i == 'o'))
1414                                                                 {
1415                                                                         charlcat(dmodes,*i,53);
1416                                                                         charlcat(outpars,*i,MAXMODES);
1417                                                                         switch (*i)
1418                                                                         {
1419                                                                                 case 'o':
1420                                                                                         FOREACH_MOD(I_OnGlobalOper,OnGlobalOper(dest));
1421                                                                                 break;
1422                                                                                 case 'i':
1423                                                                                         dest->modebits |= UM_INVISIBLE;
1424                                                                                 break;
1425                                                                                 case 's':
1426                                                                                         dest->modebits |= UM_SERVERNOTICE;
1427                                                                                 break;
1428                                                                                 case 'w':
1429                                                                                         dest->modebits |= UM_WALLOPS;
1430                                                                                 break;
1431                                                                                 default:
1432                                                                                 break;
1433                                                                         }
1434                                                                 }
1435                                                         }
1436                                                 }
1437                                                 else
1438                                                 {
1439                                                         if ((ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,false,false)) && (strchr(dmodes,*i)))
1440                                                         {
1441                                                                 if ((ServerInstance->ModeGrok->ProcessModuleUmode(*i, user, dest, direction)) || (*i == 'i') || (*i == 's') || (*i == 'w') || (*i == 'o'))
1442                                                                 {
1443                                                                         charlcat(outpars,*i,MAXMODES);
1444                                                                         charremove(dmodes,*i);
1445                                                                         switch (*i)
1446                                                                         {
1447                                                                                 case 'o':
1448                                                                                         *dest->oper = 0;
1449                                                                                         DeleteOper(dest);
1450                                                                                 break;
1451                                                                                 case 'i':
1452                                                                                         dest->modebits &= ~UM_INVISIBLE;
1453                                                                                 break;
1454                                                                                 case 's':
1455                                                                                         dest->modebits &= ~UM_SERVERNOTICE;
1456                                                                                 break;
1457                                                                                 case 'w':
1458                                                                                         dest->modebits &= ~UM_WALLOPS;
1459                                                                                 break;
1460                                                                                 default:
1461                                                                                 break;
1462                                                                         }
1463                                                                 }
1464                                                         }
1465                                                 }
1466                                         }
1467                                 break;
1468                         }
1469                 }
1470                 if (*outpars)
1471                 {
1472                         char b[MAXBUF];
1473                         char* z = b;
1474
1475                         for (char* i = outpars; *i;)
1476                         {
1477                                 *z++ = *i++;
1478                                 if (((*i == '-') || (*i == '+')) && ((*(i+1) == '-') || (*(i+1) == '+')))
1479                                 {
1480                                         // someones playing silly buggers and trying
1481                                         // to put a +- or -+ into the line...
1482                                         i++;
1483                                 }
1484                                 if (!*(i+1))
1485                                 {
1486                                         // Someone's trying to make the last character in
1487                                         // the line be a + or - symbol.
1488                                         if ((*i == '-') || (*i == '+'))
1489                                         {
1490                                                 i++;
1491                                         }
1492                                 }
1493                         }
1494                         *z = 0;
1495
1496                         if ((*b) && (!IS_SINGLE(b,'+')) && (!IS_SINGLE(b,'-')))
1497                         {
1498                                 WriteTo(user, dest, "MODE %s :%s", dest->nick, b);
1499                                 FOREACH_MOD(I_OnMode,OnMode(user, dest, TYPE_USER, b));
1500                         }
1501
1502                         log(DEBUG,"Stripped mode line");
1503                         log(DEBUG,"Line dest is now %s",dmodes);
1504                         strlcpy(dest->modes,dmodes,MAXMODES-1);
1505
1506                 }
1507
1508                 return;
1509         }
1510         else
1511         {
1512                 chan = FindChan(parameters[0]);
1513                 if(chan)
1514                 {
1515                         if (pcnt == 1)
1516                         {
1517                                 /* just /modes #channel */
1518                                 WriteServ(user->fd,"324 %s %s +%s",user->nick, chan->name, chanmodes(chan, chan->HasUser(user)));
1519                                 WriteServ(user->fd,"329 %s %s %d", user->nick, chan->name, chan->created);
1520                                 return;
1521                         }
1522                         else if (pcnt == 2)
1523                         {
1524                                 char* mode = parameters[1];
1525                                 
1526                                 MOD_RESULT = 0;
1527                                 
1528                                 if (*mode == '+')
1529                                         mode++;
1530
1531                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, *mode, "", false, 0));
1532                                 if(!MOD_RESULT)
1533                                 {
1534                                         if (*mode == 'b')
1535                                         {
1536                                                 for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++)
1537                                                 {
1538                                                         WriteServ(user->fd,"367 %s %s %s %s %d",user->nick, chan->name, i->data, i->set_by, i->set_time);
1539                                                 }
1540                                                 WriteServ(user->fd,"368 %s %s :End of channel ban list",user->nick, chan->name);
1541                                                 return;
1542                                         }
1543                                         
1544                                         if ((ModeDefined(*mode,MT_CHANNEL)) && (ModeIsListMode(*mode,MT_CHANNEL)))
1545                                         {
1546                                                 // list of items for an extmode
1547                                                 log(DEBUG,"Calling OnSendList for all modules, list output for mode %c",*mode);
1548                                                 FOREACH_MOD(I_OnSendList,OnSendList(user,chan,*mode));
1549                                                 return;
1550                                         }
1551                                 }
1552                         }
1553
1554                         if ((IS_LOCAL(user)) && (!is_uline(user->server)) && (!chan->HasUser(user)))
1555                         {
1556                                 WriteServ(user->fd,"442 %s %s :You're not on that channel!",user->nick, chan->name);
1557                                 return;
1558                         }
1559         
1560                         MOD_RESULT = 0;
1561                         FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user, NULL, chan, AC_GENERAL_MODE));
1562                                 
1563                         if(MOD_RESULT == ACR_DENY)
1564                                 return;
1565
1566                         if(MOD_RESULT == ACR_DEFAULT)
1567                         {
1568                                 if ((IS_LOCAL(user)) && (cstatus(user,chan) < STATUS_HOP))
1569                                 {
1570                                         WriteServ(user->fd,"482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, chan->name);
1571                                         return;
1572                                 }
1573                         }
1574         
1575                         ServerInstance->ModeGrok->ProcessModes(parameters,user,chan,cstatus(user,chan),pcnt,false,false,false);
1576                 }
1577                 else
1578                 {
1579                         WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, parameters[0]);
1580                 }
1581         }
1582 }
1583
1584
1585
1586
1587 void ModeParser::ServerMode(char **parameters, int pcnt, userrec *user)
1588 {
1589         chanrec* Ptr;
1590         userrec* dest = Find(parameters[0]);
1591         int can_change;
1592         int direction = 1;
1593         char outpars[MAXBUF];
1594         bool next_ok = true;
1595
1596         if ((dest) && (pcnt > 1))
1597         {
1598                 std::string tidied = ServerInstance->ModeGrok->CompressModes(parameters[1],false);
1599                 parameters[1] = (char*)tidied.c_str();
1600
1601                 char dmodes[MAXBUF];
1602                 strlcpy(dmodes,dest->modes,MAXBUF);
1603
1604                 outpars[0] = *parameters[1];
1605                 outpars[1] = 0;
1606                 direction = (*parameters[1] == '+');
1607
1608                 if ((*parameters[1] != '+') && (*parameters[1] != '-'))
1609                         return;
1610
1611                 for (char* i = parameters[1]; *i; i++)
1612                 {
1613                         if ((i != parameters[1]) && (*i != '+') && (*i != '-'))
1614                                 next_ok = true;
1615
1616                         switch (*i)
1617                         {
1618                                 case ' ':
1619                                 continue;
1620
1621                                 case '+':
1622                                         if ((direction != 1) && (next_ok))
1623                                         {
1624                                                 next_ok = false;
1625                                                 charlcat(outpars,'+',MAXBUF);
1626                                         }
1627                                         direction = 1;
1628                                 break;
1629
1630                                 case '-':
1631                                         if ((direction != 0) && (next_ok))
1632                                         {
1633                                                 next_ok = false;
1634                                                 charlcat(outpars,'-',MAXBUF);
1635                                         }
1636                                         direction = 0;
1637                                 break;
1638
1639                                 default:
1640                                         log(DEBUG,"begin mode processing entry");
1641                                         can_change = 1;
1642                                         if (can_change)
1643                                         {
1644                                                 if (direction == 1)
1645                                                 {
1646                                                         log(DEBUG,"umode %c being added",*i);
1647                                                         if ((!strchr(dmodes,*i)) && (ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,true,true)))
1648                                                         {
1649                                                                 log(DEBUG,"umode %c is an allowed umode",*i);
1650                                                                 if ((*i == 'i') || (*i == 's') || (*i == 'w') || (*i == 'o') || (ServerInstance->ModeGrok->ProcessModuleUmode(*i, user, dest, direction)))
1651                                                                 {
1652                                                                         charlcat(dmodes,*i,MAXBUF);
1653                                                                         charlcat(outpars,*i,53);
1654                                                                         switch (*i)
1655                                                                         {
1656                                                                                 case 'i':
1657                                                                                         dest->modebits |= UM_INVISIBLE;
1658                                                                                 break;
1659                                                                                 case 's':
1660                                                                                         dest->modebits |= UM_SERVERNOTICE;
1661                                                                                 break;
1662                                                                                 case 'w':
1663                                                                                         dest->modebits |= UM_WALLOPS;
1664                                                                                 break;
1665                                                                                 default:
1666                                                                                 break;
1667                                                                         }
1668                                                                 }
1669                                                         }
1670                                                 }
1671                                                 else
1672                                                 {
1673                                                         // can only remove a mode they already have
1674                                                         log(DEBUG,"umode %c being removed",*i);
1675                                                         if ((ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,false,true)) && (strchr(dmodes,*i)))
1676                                                         {
1677                                                                 log(DEBUG,"umode %c is an allowed umode",*i);
1678                                                                 if ((*i == 'i') || (*i == 's') || (*i == 'w') || (*i == 'o') || (ServerInstance->ModeGrok->ProcessModuleUmode(*i, user, dest, direction)))
1679                                                                 {
1680                                                                         charlcat(outpars,*i,MAXBUF);
1681                                                                         charremove(dmodes,*i);
1682                                                                         switch (*i)
1683                                                                         {
1684                                                                                 case 'i':
1685                                                                                         dest->modebits &= ~UM_INVISIBLE;
1686                                                                                 break;
1687                                                                                 case 's':
1688                                                                                         dest->modebits &= ~UM_SERVERNOTICE;
1689                                                                                 break;
1690                                                                                 case 'w':
1691                                                                                         dest->modebits &= ~UM_WALLOPS;
1692                                                                                 break;
1693                                                                                 default:
1694                                                                                 break;
1695                                                                         }
1696                                                                 }
1697                                                         }
1698                                                 }
1699                                         }
1700                                 break;
1701                         }
1702                 }
1703                 if (*outpars)
1704                 {
1705                         char b[MAXBUF];
1706                         char* z = b;
1707
1708                         for (char* i = outpars; *i;)
1709                         {
1710                                 *z++ = *i++;
1711                                 if (((*i == '-') || (*i == '+')) && ((*(i+1) == '-') || (*(i+1) == '+')))
1712                                 {
1713                                         // someones playing silly buggers and trying
1714                                         // to put a +- or -+ into the line...
1715                                         i++;
1716                                 }
1717                                 if (!*(i+1))
1718                                 {
1719                                         // Someone's trying to make the last character in
1720                                         // the line be a + or - symbol.
1721                                         if ((*i == '-') || (*i == '+'))
1722                                         {
1723                                                 i++;
1724                                         }
1725                                 }
1726                         }
1727                         *z = 0;
1728
1729                         if ((*b) && (!IS_SINGLE(b,'+')) && (!IS_SINGLE(b,'-')))
1730                         {
1731                                 WriteTo(user, dest, "MODE %s :%s", dest->nick, b);
1732                                 FOREACH_MOD(I_OnMode,OnMode(user, dest, TYPE_USER, b));
1733                         }
1734
1735                         log(DEBUG,"Stripped mode line");
1736                         log(DEBUG,"Line dest is now %s",dmodes);
1737                         strlcpy(dest->modes,dmodes,MAXMODES-1);
1738                                          
1739                 }
1740
1741                 return;
1742         }
1743         
1744         Ptr = FindChan(parameters[0]);
1745         if (Ptr)
1746         {
1747                 ServerInstance->ModeGrok->ProcessModes(parameters,user,Ptr,STATUS_OP,pcnt,true,false,false);
1748         }
1749         else
1750         {
1751                 WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, parameters[0]);
1752         }
1753 }
1754
1755 ModeParser::ModeParser()
1756 {
1757         /* Dummy framework, XXX tidyme */
1758         ModeChannelSecret* cmode_s = new ModeChannelSecret();
1759         modehandlers[(unsigned char)'s'] = cmode_s;
1760 }