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