]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/mode.cpp
Om's way is better and lets watchers change mode parameters for the modes theyre...
[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, bool listmode, ModeType type, bool operonly) : mode(modeletter), n_params(parameters), 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()
81 {
82         return n_params;
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)
123 {
124         return true;
125 }
126
127 void ModeWatcher::AfterMode(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter, bool adding)
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
479 /** ModeParser::CompressModes()
480  * Tidies up redundant modes,
481  * e.g. +nt-nt+i becomes +-+i
482  * A section further down the chain tidies up the +-+- crap.
483  */
484 std::string ModeParser::CompressModes(std::string modes,bool channelmodes)
485 {
486         /*
487          * OK, iterate over the mode string and count how many times a certain mode appears in it.
488          * Then, erase all instances of any character that appears more than once.
489          * This only operates on modes with no parameters, you can still +v-v+v-v+v-v to your heart's content.
490          */
491         
492         /* Do we really need an int here? Can you fit enough modes in a line to overflow a short? */
493         short counts[127];
494         bool active[127];
495         memset(counts, 0, sizeof(counts));
496         memset(active, 0, sizeof(active));
497         
498         for(unsigned char* i = (unsigned char*)modes.c_str(); *i; i++)
499         {
500                 if((*i == '+') || (*i == '-'))
501                         continue;
502
503                 if(!channelmodes || (channelmodes && (strchr("itnmsp", *i) || (ModeDefined(*i, MT_CHANNEL) && !ModeDefinedOn(*i,MT_CHANNEL) && !ModeDefinedOff(*i,MT_CHANNEL)))))
504                 {
505                         log(DEBUG,"Tidy mode %c", *i);
506                         counts[*i]++;
507                         active[*i] = true;
508                 }
509         }
510         
511         for(unsigned char j = 65; j < 127; j++)
512         {
513                 if ((counts[j] > 1) && (active[j] == true))
514                 {
515                         std::string::size_type pos;
516
517                         while((pos = modes.find(j)) != std::string::npos)
518                         {
519                                 log(DEBUG, "Deleting occurence of mode %c...", j);
520                                 modes.erase(pos, 1);
521                                 log(DEBUG,"New mode line: %s", modes.c_str());
522                         }
523                 }
524         }
525         
526         return modes;
527 }
528
529 void ModeParser::ProcessModes(char **parameters,userrec* user,chanrec *chan,int status, int pcnt, bool servermode, bool silent, bool local)
530 {
531         if ((!parameters) || (pcnt < 2)) {
532                 return;
533         }
534
535         char outlist[MAXBUF];
536         char mlist[MAXBUF];
537         char *outpars[32];
538         int param = 2;
539         int pc = 0;
540         int ptr = 0;
541         int mdir = 1;
542         char* r = NULL;
543         bool k_set = false, l_set = false, previously_set_l = false, previously_unset_l = false, previously_set_k = false, previously_unset_k = false;
544
545         int MOD_RESULT = 0;
546         
547         if (IS_LOCAL(user))
548         {
549                 FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,NULL,chan,AC_GENERAL_MODE));  
550                 if (MOD_RESULT == ACR_DENY)
551                         return;
552         }
553
554         std::string tidied = this->CompressModes(parameters[1],true);
555         strlcpy(mlist,tidied.c_str(),MAXBUF);
556         char* modelist = mlist;
557
558         *outlist = *modelist;
559         char* outl = outlist+1;
560
561         mdir = (*modelist == '+');
562
563         log(DEBUG,"process_modes: modelist: %s",modelist);
564
565         int len = tidied.length();
566         while (modelist[len-1] == ' ')
567                 modelist[--len] = '\0';
568
569         for (char* modechar = (modelist + 1); *modechar; ptr++, modechar++)
570         {
571                 r = NULL;
572
573                 /* If we have more than MAXMODES changes in one line,
574                  * drop all after the MAXMODES
575                  */
576                 if (pc > MAXMODES-1)
577                         break;
578
579
580                 
581                 switch (*modechar)
582                 {
583                         case '-':
584                                 *outl++ = '-';
585                                 mdir = 0;
586                         break;                  
587
588                         case '+':
589                                 *outl++ = '+';
590                                 mdir = 1;
591                         break;
592
593                         case 'o':
594                                 log(DEBUG,"Ops");
595                                 if ((param >= pcnt)) break;
596                                 log(DEBUG,"Enough parameters left");
597                                 r = NULL;
598                                 if (mdir == 1)
599                                 {
600                                         MOD_RESULT = 0;
601                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'o', parameters[param], true, 1));
602                                         if (!MOD_RESULT)
603                                         {
604                                                 r = GiveOps(user,parameters[param++],chan,status);
605                                         }
606                                         else param++;
607                                 }
608                                 else
609                                 {
610                                         MOD_RESULT = 0;
611                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'o', parameters[param], false, 1));
612                                         if (!MOD_RESULT)
613                                         {
614                                                 r = TakeOps(user,parameters[param++],chan,status);
615                                         }
616                                         else param++;
617                                 }
618                                 if (r)
619                                 {
620                                         *outl++ = 'o';
621                                         outpars[pc++] = r;
622                                 }
623                         break;
624                         
625                         case 'h':
626                                 if (((param >= pcnt)) || (!Config->AllowHalfop)) break;
627                                 r = NULL;
628                                 if (mdir == 1)
629                                 {
630                                         MOD_RESULT = 0;
631                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'h', parameters[param], true, 1));
632                                         if (!MOD_RESULT)
633                                         {
634                                                 r = GiveHops(user,parameters[param++],chan,status);
635                                         }
636                                         else param++;
637                                 }
638                                 else
639                                 {
640                                         MOD_RESULT = 0;
641                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'h', parameters[param], false, 1));
642                                         if (!MOD_RESULT)
643                                         {
644                                                 r = TakeHops(user,parameters[param++],chan,status);
645                                         }
646                                         else param++;
647                                 }
648                                 if (r)
649                                 {
650                                         *outl++ = 'h';
651                                         outpars[pc++] = r;
652                                 }
653                         break;
654                         
655                                 
656                         case 'v':
657                                         if ((param >= pcnt)) break;
658                                         r = NULL;
659                                         if (mdir == 1)
660                                         {
661                                                 MOD_RESULT = 0;
662                                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'v', parameters[param], true, 1));
663                                                 if (!MOD_RESULT)
664                                                 {
665                                                         r = GiveVoice(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, 'v', parameters[param], false, 1));
673                                                 if (!MOD_RESULT)
674                                                 {
675                                                         r = TakeVoice(user,parameters[param++],chan,status);
676                                                 }
677                                                 else param++;
678                                         }
679                                         if (r)
680                                         {
681                                                 *outl++ = 'v';
682                                                 outpars[pc++] = r;
683                                         }
684                         break;
685                                 
686                         case 'b':
687                                 if ((param >= pcnt)) break;
688                                 r = NULL;
689                                 if (mdir == 1)
690                                 {
691                                         MOD_RESULT = 0;
692                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'b', parameters[param], true, 1));
693                                         if (!MOD_RESULT)
694                                         {
695                                                 r = AddBan(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, 'b', parameters[param], false, 1));
703                                         if (!MOD_RESULT)
704                                         {
705                                                 r = TakeBan(user,parameters[param++],chan,status);
706                                         }
707                                         else param++;
708                                 }
709                                 if (r)
710                                 {
711                                         *outl++ = 'b';
712                                         outpars[pc++] = parameters[param-1];
713                                 }
714                         break;
715
716
717                         case 'k':
718                                 if ((param >= pcnt))
719                                         break;
720
721                                 if (mdir == 1)
722                                 {
723                                         if (k_set)
724                                                 break;
725
726                                         if (previously_unset_k)
727                                                 break;
728                                         previously_set_k = true;
729                                                 
730                                         if (!chan->modes[CM_KEY])
731                                         {
732                                                 MOD_RESULT = 0;
733                                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'k', parameters[param], true, 1));
734                                                 if (!MOD_RESULT)
735                                                 {
736                                                         *outl++ = 'k';
737                                                         char key[MAXBUF];
738                                                         strlcpy(key,parameters[param++],32);
739                                                         outpars[pc++] = key;
740                                                         strlcpy(chan->key,key,MAXBUF);
741                                                         chan->modes[CM_KEY] = 1;
742                                                         k_set = true;
743                                                 }
744                                                 else param++;
745                                         }
746                                 }
747                                 else
748                                 {
749                                         /* checks on -k are case sensitive and only accurate to the
750                                                    first 32 characters */
751                                         if (previously_set_k)
752                                                 break;
753                                         previously_unset_k = true;
754
755                                         char key[MAXBUF];
756                                         MOD_RESULT = 0;
757                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'k', parameters[param], false, 1));
758                                         if (!MOD_RESULT)
759                                         {
760                                                 strlcpy(key,parameters[param++],32);
761                                                 /* only allow -k if correct key given */
762                                                 if (!strcmp(chan->key,key))
763                                                 {
764                                                         *outl++ = 'k';
765                                                         *chan->key = 0;
766                                                         chan->modes[CM_KEY] = 0;
767                                                         outpars[pc++] = key;
768                                                 }
769                                         }
770                                         else param++;
771                                 }
772                         break;
773                                 
774                         case 'l':
775                                 if (mdir == 0)
776                                 {
777                                         if (previously_set_l)
778                                                 break;
779                                         previously_unset_l = true;
780                                         MOD_RESULT = 0;
781                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'l', "", false, 0));
782                                         if (!MOD_RESULT)
783                                         {
784                                                 if (chan->modes[CM_LIMIT])
785                                                 {
786                                                         *outl++ = 'l';
787                                                         chan->limit = 0;
788                                                         chan->modes[CM_LIMIT] = 0;
789                                                 }
790                                         }
791                                 }
792                                         
793                                 if ((param >= pcnt)) break;
794                                 if (mdir == 1)
795                                 {
796                                         if (l_set)
797                                                 break;
798                                         if (previously_unset_l)
799                                                 break;
800                                         previously_set_l = true;
801                                         bool invalid = false;
802                                         for (char* f = parameters[param]; *f; f++)
803                                         {
804                                                 if ((*f < '0') || (*f > '9'))
805                                                 {
806                                                         invalid = true;
807                                                 }
808                                         }
809                                         /* If the limit is < 1, or the new limit is the current limit, dont allow */
810                                         if ((atoi(parameters[param]) < 1) || ((chan->limit > 0) && (atoi(parameters[param]) == chan->limit)))
811                                         {
812                                                 invalid = true;
813                                         }
814
815                                         if (invalid)
816                                                 break;
817
818                                         MOD_RESULT = 0;
819                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'l', parameters[param], true, 1));
820                                         if (!MOD_RESULT)
821                                         {
822         
823                                                 chan->limit = atoi(parameters[param]);
824                                                         
825                                                 // reported by mech: large values cause underflow
826                                                 if (chan->limit < 0)
827                                                         chan->limit = 0x7FFF;
828                                         }
829                                                 
830                                         if (chan->limit)
831                                         {
832                                                 *outl++ = 'l';
833                                                 chan->modes[CM_LIMIT] = 1;
834                                                 outpars[pc++] = parameters[param++];
835                                                 l_set = true;
836                                         }
837                                 }
838                         break;
839                                 
840                         case 'i':
841                                 MOD_RESULT = 0;
842                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'i', "", mdir, 0));
843                                 if (!MOD_RESULT)
844                                 {
845                                         if (mdir)
846                                         {
847                                                 if (!(chan->modes[CM_INVITEONLY])) *outl++ = 'i';
848                                                 chan->modes[CM_INVITEONLY] = 1;
849                                         }
850                                         else
851                                         {
852                                                 if (chan->modes[CM_INVITEONLY]) *outl++ = 'i';
853                                                 chan->modes[CM_INVITEONLY] = 0;
854                                         }
855                                 }
856                         break;
857                                 
858                         case 't':
859                                 MOD_RESULT = 0;
860                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 't', "", mdir, 0));
861                                 if (!MOD_RESULT)
862                                 {
863                                         if (mdir)
864                                         {
865                                                 if (!(chan->modes[CM_TOPICLOCK])) *outl++ = 't';
866                                                 chan->modes[CM_TOPICLOCK] = 1;
867                                         }
868                                         else
869                                         {
870                                                 if (chan->modes[CM_TOPICLOCK]) *outl++ = 't';
871                                                 chan->modes[CM_TOPICLOCK] = 0;
872                                         }
873                                 }
874                         break;
875                                 
876                         case 'n':
877                                 MOD_RESULT = 0;
878                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'n', "", mdir, 0));
879                                 if (!MOD_RESULT)
880                                 {
881                                         if (mdir)
882                                         {
883                                                 if (!(chan->modes[CM_NOEXTERNAL])) *outl++ = 'n';
884                                                 chan->modes[CM_NOEXTERNAL] = 1;
885                                         }
886                                         else
887                                         {
888                                                 if (chan->modes[CM_NOEXTERNAL]) *outl++ = 'n';
889                                                 chan->modes[CM_NOEXTERNAL] = 0;
890                                         }
891                                 }
892                         break;
893                                 
894                         case 'm':
895                                 MOD_RESULT = 0;
896                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'm', "", mdir, 0));
897                                 if (!MOD_RESULT)
898                                 {
899                                         if (mdir)
900                                         {
901                                                 if (!(chan->modes[CM_MODERATED])) *outl++ = 'm';
902                                                 chan->modes[CM_MODERATED] = 1;
903                                         }
904                                         else
905                                         {
906                                                 if (chan->modes[CM_MODERATED]) *outl++ = 'm';
907                                                 chan->modes[CM_MODERATED] = 0;
908                                         }
909                                 }
910                         break;
911                                 
912                         case 's':
913                                 MOD_RESULT = 0;
914                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 's', "", mdir, 0));
915                                 if (!MOD_RESULT)
916                                 {
917                                         if (mdir)
918                                         {
919                                                 if (!(chan->modes[CM_SECRET])) *outl++ = 's';
920                                                 chan->modes[CM_SECRET] = 1;
921                                                 if (chan->modes[CM_PRIVATE])
922                                                 {
923                                                         chan->modes[CM_PRIVATE] = 0;
924                                                         if (mdir)
925                                                         {
926                                                                 *outl++ = '-'; *outl++ = 'p'; *outl++ = '+';
927                                                         }
928                                                 }
929                                         }
930                                         else
931                                         {
932                                                 if (chan->modes[CM_SECRET]) *outl++ = 's';
933                                                 chan->modes[CM_SECRET] = 0;
934                                         }
935                                 }
936                         break;
937                                 
938                         case 'p':
939                                 MOD_RESULT = 0;
940                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'p', "", mdir, 0));
941                                 if(!MOD_RESULT)
942                                 {
943                                         if(mdir)
944                                         {
945                                                 if(!(chan->modes[CM_PRIVATE]))
946                                                         *outl++ = 'p';
947                                                 
948                                                 chan->modes[CM_PRIVATE] = 1;
949                                                 
950                                                 if(chan->modes[CM_SECRET])
951                                                 {
952                                                         chan->modes[CM_SECRET] = 0;
953
954                                                         *outl++ = '-';
955                                                         *outl++ = 's';
956                                                         *outl++ = '+';
957                                                 }
958                                         }
959                                         else
960                                         {
961                                                 if(chan->modes[CM_PRIVATE])
962                                                         *outl++ = 'p';
963                                                 
964                                                 chan->modes[CM_PRIVATE] = 0;
965                                         }
966                                 }
967                                 break;
968                                 
969                         default:
970                                 string_list p;
971                                 p.clear();
972                                 bool x = chan->modes[*modechar-65];
973                                 if ((!x && !mdir) || (x && mdir))
974                                 {
975                                         if (!ModeIsListMode(*modechar,MT_CHANNEL))
976                                         {
977                                                 log(DEBUG,"Mode %c isnt set on %s but trying to remove!",*modechar,chan->name);
978                                                 break;
979                                         }
980                                 }
981                                 if (ModeDefined(*modechar,MT_CHANNEL))
982                                 {
983                                         /* A module has claimed this mode */
984                                         if (param<pcnt)
985                                         {
986                                                 if ((ModeDefinedOn(*modechar,MT_CHANNEL)>0) && (mdir))
987                                                 {
988                                                         p.push_back(parameters[param]);
989                                                 }
990                                                 if ((ModeDefinedOff(*modechar,MT_CHANNEL)>0) && (!mdir))
991                                                 {
992                                                         p.push_back(parameters[param]);
993                                                 }
994                                         }
995                                         bool handled = false;
996                                         if (param>=pcnt)
997                                         {
998                                                 // we're supposed to have a parameter, but none was given... so dont handle the mode.
999                                                 if (((ModeDefinedOn(*modechar,MT_CHANNEL)>0) && (mdir)) || ((ModeDefinedOff(*modechar,MT_CHANNEL)>0) && (!mdir)))       
1000                                                 {
1001                                                         log(DEBUG,"Not enough parameters for module-mode %c",*modechar);
1002                                                         handled = true;
1003                                                         param++;
1004                                                 }
1005                                         }
1006                                         // BIG ASS IDIOTIC CODER WARNING!
1007                                         // Using OnRawMode on another modules mode's behavour 
1008                                         // will confuse the crap out of admins! just because you CAN
1009                                         // do it, doesnt mean you SHOULD!
1010                                         MOD_RESULT = 0;
1011                                         std::string para = "";
1012                                         if (p.size())
1013                                                 para = p[0];
1014                                         
1015                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, *modechar, para, mdir, pcnt));
1016                                         if(!MOD_RESULT)
1017                                         {
1018                                                 for (int i = 0; i <= MODCOUNT; i++)
1019                                                 {
1020                                                         if (!handled)
1021                                                         {
1022                                                                 int t = modules[i]->OnExtendedMode(user,chan,*modechar,MT_CHANNEL,mdir,p);
1023                                                                 if (t != 0)
1024                                                                 {
1025                                                                         log(DEBUG,"OnExtendedMode returned nonzero for a module");
1026                                                                         if (ModeIsListMode(*modechar,MT_CHANNEL))
1027                                                                         {
1028                                                                                 if (t == -1)
1029                                                                                 {
1030                                                                                         //pc++;
1031                                                                                         param++;
1032                                                                                 }
1033                                                                                 else
1034                                                                                 {
1035                                                                                         if (param < pcnt)
1036                                                                                         {
1037                                                                                                 *outl++ = *modechar;
1038                                                                                         }
1039                                                                                         outpars[pc++] = parameters[param++];
1040                                                                                 }
1041                                                                         }
1042                                                                         else
1043                                                                         {
1044                                                                                 *outl++ = *modechar;
1045                                                                                 chan->SetCustomMode(*modechar,mdir);
1046                                                                                 // include parameters in output if mode has them
1047                                                                                 if ((ModeDefinedOn(*modechar,MT_CHANNEL)>0) && (mdir))
1048                                                                                 {
1049                                                                                         if (param < pcnt)
1050                                                                                         {
1051                                                                                                 chan->SetCustomModeParam(*modechar,parameters[param],mdir);
1052                                                                                                 outpars[pc++] = parameters[param++];
1053                                                                                         }
1054                                                                                 }
1055                                                                         }
1056                                                                         // break, because only one module can handle the mode.
1057                                                                         handled = true;
1058                                                                 }
1059                                                         }
1060                                                 }
1061                                         }
1062                                 }
1063                                 else
1064                                 {
1065                                         WriteServ(user->fd,"472 %s %c :is unknown mode char to me",user->nick,*modechar);
1066                                 }
1067                         break;
1068                 }
1069         }
1070
1071         /* Null terminate it now we're done */
1072         *outl = 0;
1073
1074
1075         /************ Fast, but confusing string tidying ************/
1076         outl = outlist;
1077         while (*outl && (*outl < 'A'))
1078                 outl++;
1079         /* outl now points to the first mode character after +'s and -'s */
1080         outl--;
1081         /* Now points at first mode-modifier + or - symbol */
1082         char* trim = outl;
1083         /* Now we tidy off any trailing -'s etc */
1084         while (*trim++);
1085         trim--;
1086         while ((*--trim == '+') || (*trim == '-'))
1087                 *trim = 0;
1088         /************ Done wih the string tidy functions ************/
1089
1090
1091         /* The mode change must be at least two characters long (+ or - and at least one mode) */
1092         if (((*outl == '+') || (*outl == '-')) && *(outl+1))
1093         {
1094                 for (ptr = 0; ptr < pc; ptr++)
1095                 {
1096                         charlcat(outl,' ',MAXBUF);
1097                         strlcat(outl,outpars[ptr],MAXBUF-1);
1098                 }
1099                 if (local)
1100                 {
1101                         log(DEBUG,"Local mode change");
1102                         WriteChannelLocal(chan, user, "MODE %s %s",chan->name,outl);
1103                         FOREACH_MOD(I_OnMode,OnMode(user, chan, TYPE_CHANNEL, outl));
1104                 }
1105                 else
1106                 {
1107                         if (servermode)
1108                         {
1109                                 if (!silent)
1110                                 {
1111                                         WriteChannelWithServ(Config->ServerName,chan,"MODE %s %s",chan->name,outl);
1112                                 }
1113                                         
1114                         }
1115                         else
1116                         {
1117                                 if (!silent)
1118                                 {
1119                                         WriteChannel(chan,user,"MODE %s %s",chan->name,outl);
1120                                         FOREACH_MOD(I_OnMode,OnMode(user, chan, TYPE_CHANNEL, outl));
1121                                 }
1122                         }
1123                 }
1124         }
1125 }
1126
1127 // based on sourcemodes, return true or false to determine if umode is a valid mode a user may set on themselves or others.
1128
1129 bool ModeParser::AllowedUmode(char umode, char* sourcemodes,bool adding,bool serveroverride)
1130 {
1131         bool sourceoper = (strchr(sourcemodes,'o') != NULL);
1132         log(DEBUG,"Allowed_umode: %c %s",umode,sourcemodes);
1133         // Servers can +o and -o arbitrarily
1134         if ((serveroverride == true) && (umode == 'o'))
1135         {
1136                 return true;
1137         }
1138         // RFC1459 specified modes
1139         if ((umode == 'w') || (umode == 's') || (umode == 'i'))
1140         {
1141                 /* umode allowed by RFC1459 scemantics */
1142                 return true;
1143         }
1144         
1145         /* user may not +o themselves or others, but an oper may de-oper other opers or themselves */
1146         if (sourceoper && !adding)
1147         {
1148                 return true;
1149         }
1150         else if (umode == 'o')
1151         {
1152                 /* Bad oper, bad bad! */
1153                 return false;
1154         }
1155         
1156         /* process any module-defined modes that need oper */
1157         if ((ModeDefinedOper(umode,MT_CLIENT)) && (sourceoper))
1158         {
1159                 log(DEBUG,"umode %c allowed by module handler (oper only mode)",umode);
1160                 return true;
1161         }
1162         else if (ModeDefined(umode,MT_CLIENT))
1163         {
1164                 // process any module-defined modes that don't need oper
1165                 log(DEBUG,"umode %c allowed by module handler (non-oper mode)",umode);
1166                 if ((ModeDefinedOper(umode,MT_CLIENT)) && (!sourceoper))
1167                 {
1168                         // no, this mode needs oper, and this user 'aint got what it takes!
1169                         return false;
1170                 }
1171                 return true;
1172         }
1173
1174         // anything else - return false.
1175         log(DEBUG,"umode %c not known by any ruleset",umode);
1176         return false;
1177 }
1178
1179 bool ModeParser::ProcessModuleUmode(char umode, userrec* source, void* dest, bool adding)
1180 {
1181         userrec* s2;
1182         bool faked = false;
1183         if (!source)
1184         {
1185                 s2 = new userrec;
1186                 strlcpy(s2->nick,Config->ServerName,NICKMAX-1);
1187                 *s2->modes = 'o';
1188                 *(s2->modes+1) = 0;
1189                 s2->fd = -1;
1190                 source = s2;
1191                 faked = true;
1192         }
1193         string_list p;
1194         p.clear();
1195         if (ModeDefined(umode,MT_CLIENT))
1196         {
1197                 for (int i = 0; i <= MODCOUNT; i++)
1198                 {
1199                         if (modules[i]->OnExtendedMode(source,(void*)dest,umode,MT_CLIENT,adding,p))
1200                         {
1201                                 log(DEBUG,"Module %s claims umode %c",Config->module_names[i].c_str(),umode);
1202                                 return true;
1203                         }
1204                 }
1205                 log(DEBUG,"No module claims umode %c",umode);
1206                 if (faked)
1207                 {
1208                         delete s2;
1209                         source = NULL;
1210                 }
1211                 return false;
1212         }
1213         else
1214         {
1215                 if (faked)
1216                 {
1217                         delete s2;
1218                         source = NULL;
1219                 }
1220                 return false;
1221         }
1222 }
1223
1224 void cmd_mode::Handle (char **parameters, int pcnt, userrec *user)
1225 {
1226         chanrec* chan;
1227         userrec* dest = Find(parameters[0]);
1228         int MOD_RESULT;
1229         int can_change;
1230         int direction = 1;
1231         char outpars[MAXBUF];
1232         bool next_ok = true;
1233
1234         if (!user)
1235                 return;
1236
1237         if ((dest) && (pcnt == 1))
1238         {
1239                 WriteServ(user->fd,"221 %s :+%s",dest->nick,dest->modes);
1240                 return;
1241         }
1242         else if ((dest) && (pcnt > 1))
1243         {
1244                 std::string tidied = ServerInstance->ModeGrok->CompressModes(parameters[1],false);
1245                 parameters[1] = (char*)tidied.c_str();
1246
1247                 char dmodes[MAXBUF];
1248                 strlcpy(dmodes,dest->modes,MAXMODES);
1249                 log(DEBUG,"pulled up dest user modes: %s",dmodes);
1250
1251                 can_change = 0;
1252                 if (user != dest)
1253                 {
1254                         if ((*user->oper) || (is_uline(user->server)))
1255                         {
1256                                 can_change = 1;
1257                         }
1258                 }
1259                 else
1260                 {
1261                         can_change = 1;
1262                 }
1263                 if (!can_change)
1264                 {
1265                         WriteServ(user->fd,"482 %s :Can't change mode for other users",user->nick);
1266                         return;
1267                 }
1268                 
1269                 outpars[0] = *parameters[1];
1270                 outpars[1] = 0;
1271                 direction = (*parameters[1] == '+');
1272
1273                 if ((*parameters[1] != '+') && (*parameters[1] != '-'))
1274                         return;
1275
1276                 for (char* i = parameters[1]; *i; i++)
1277                 {
1278                         if ((i != parameters[1]) && (*i != '+') && (*i != '-'))
1279                                 next_ok = true;
1280
1281                         switch (*i)
1282                         {
1283                                 case ' ':
1284                                 continue;
1285
1286                                 case '+':
1287                                         if ((direction != 1) && (next_ok))
1288                                         {
1289                                                 charlcat(outpars,'+',MAXBUF);
1290                                                 next_ok = false;
1291                                         }       
1292                                         direction = 1;
1293                                 break;
1294
1295                                 case '-':
1296                                         if ((direction != 0) && (next_ok))
1297                                         {
1298                                                 charlcat(outpars,'-',MAXBUF);
1299                                                 next_ok = false;
1300                                         }
1301                                         direction = 0;
1302                                 break;
1303
1304                                 default:
1305                                         can_change = 0;
1306                                         if (*user->oper)
1307                                         {
1308                                                 can_change = 1;
1309                                         }
1310                                         else
1311                                         {
1312                                                 if ((*i == 'i') || (*i == 'w') || (*i == 's') || (ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,direction,false)))
1313                                                 {
1314                                                         can_change = 1;
1315                                                 }
1316                                         }
1317                                         if (can_change)
1318                                         {
1319                                                 if (direction == 1)
1320                                                 {
1321                                                         if ((!strchr(dmodes,*i)) && (ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,true,false)))
1322                                                         {
1323                                                                 if ((ServerInstance->ModeGrok->ProcessModuleUmode(*i, user, dest, direction)) || (*i == 'i') || (*i == 's') || (*i == 'w') || (*i == 'o'))
1324                                                                 {
1325                                                                         charlcat(dmodes,*i,53);
1326                                                                         charlcat(outpars,*i,MAXMODES);
1327                                                                         switch (*i)
1328                                                                         {
1329                                                                                 case 'o':
1330                                                                                         FOREACH_MOD(I_OnGlobalOper,OnGlobalOper(dest));
1331                                                                                 break;
1332                                                                                 case 'i':
1333                                                                                         dest->modebits |= UM_INVISIBLE;
1334                                                                                 break;
1335                                                                                 case 's':
1336                                                                                         dest->modebits |= UM_SERVERNOTICE;
1337                                                                                 break;
1338                                                                                 case 'w':
1339                                                                                         dest->modebits |= UM_WALLOPS;
1340                                                                                 break;
1341                                                                                 default:
1342                                                                                 break;
1343                                                                         }
1344                                                                 }
1345                                                         }
1346                                                 }
1347                                                 else
1348                                                 {
1349                                                         if ((ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,false,false)) && (strchr(dmodes,*i)))
1350                                                         {
1351                                                                 if ((ServerInstance->ModeGrok->ProcessModuleUmode(*i, user, dest, direction)) || (*i == 'i') || (*i == 's') || (*i == 'w') || (*i == 'o'))
1352                                                                 {
1353                                                                         charlcat(outpars,*i,MAXMODES);
1354                                                                         charremove(dmodes,*i);
1355                                                                         switch (*i)
1356                                                                         {
1357                                                                                 case 'o':
1358                                                                                         *dest->oper = 0;
1359                                                                                         DeleteOper(dest);
1360                                                                                 break;
1361                                                                                 case 'i':
1362                                                                                         dest->modebits &= ~UM_INVISIBLE;
1363                                                                                 break;
1364                                                                                 case 's':
1365                                                                                         dest->modebits &= ~UM_SERVERNOTICE;
1366                                                                                 break;
1367                                                                                 case 'w':
1368                                                                                         dest->modebits &= ~UM_WALLOPS;
1369                                                                                 break;
1370                                                                                 default:
1371                                                                                 break;
1372                                                                         }
1373                                                                 }
1374                                                         }
1375                                                 }
1376                                         }
1377                                 break;
1378                         }
1379                 }
1380                 if (*outpars)
1381                 {
1382                         char b[MAXBUF];
1383                         char* z = b;
1384
1385                         for (char* i = outpars; *i;)
1386                         {
1387                                 *z++ = *i++;
1388                                 if (((*i == '-') || (*i == '+')) && ((*(i+1) == '-') || (*(i+1) == '+')))
1389                                 {
1390                                         // someones playing silly buggers and trying
1391                                         // to put a +- or -+ into the line...
1392                                         i++;
1393                                 }
1394                                 if (!*(i+1))
1395                                 {
1396                                         // Someone's trying to make the last character in
1397                                         // the line be a + or - symbol.
1398                                         if ((*i == '-') || (*i == '+'))
1399                                         {
1400                                                 i++;
1401                                         }
1402                                 }
1403                         }
1404                         *z = 0;
1405
1406                         if ((*b) && (!IS_SINGLE(b,'+')) && (!IS_SINGLE(b,'-')))
1407                         {
1408                                 WriteTo(user, dest, "MODE %s :%s", dest->nick, b);
1409                                 FOREACH_MOD(I_OnMode,OnMode(user, dest, TYPE_USER, b));
1410                         }
1411
1412                         log(DEBUG,"Stripped mode line");
1413                         log(DEBUG,"Line dest is now %s",dmodes);
1414                         strlcpy(dest->modes,dmodes,MAXMODES-1);
1415
1416                 }
1417
1418                 return;
1419         }
1420         else
1421         {
1422                 chan = FindChan(parameters[0]);
1423                 if(chan)
1424                 {
1425                         if (pcnt == 1)
1426                         {
1427                                 /* just /modes #channel */
1428                                 WriteServ(user->fd,"324 %s %s +%s",user->nick, chan->name, chanmodes(chan, chan->HasUser(user)));
1429                                 WriteServ(user->fd,"329 %s %s %d", user->nick, chan->name, chan->created);
1430                                 return;
1431                         }
1432                         else if (pcnt == 2)
1433                         {
1434                                 char* mode = parameters[1];
1435                                 
1436                                 MOD_RESULT = 0;
1437                                 
1438                                 if (*mode == '+')
1439                                         mode++;
1440
1441                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, *mode, "", false, 0));
1442                                 if(!MOD_RESULT)
1443                                 {
1444                                         if (*mode == 'b')
1445                                         {
1446                                                 for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++)
1447                                                 {
1448                                                         WriteServ(user->fd,"367 %s %s %s %s %d",user->nick, chan->name, i->data, i->set_by, i->set_time);
1449                                                 }
1450                                                 WriteServ(user->fd,"368 %s %s :End of channel ban list",user->nick, chan->name);
1451                                                 return;
1452                                         }
1453                                         
1454                                         if ((ModeDefined(*mode,MT_CHANNEL)) && (ModeIsListMode(*mode,MT_CHANNEL)))
1455                                         {
1456                                                 // list of items for an extmode
1457                                                 log(DEBUG,"Calling OnSendList for all modules, list output for mode %c",*mode);
1458                                                 FOREACH_MOD(I_OnSendList,OnSendList(user,chan,*mode));
1459                                                 return;
1460                                         }
1461                                 }
1462                         }
1463
1464                         if ((IS_LOCAL(user)) && (!is_uline(user->server)) && (!chan->HasUser(user)))
1465                         {
1466                                 WriteServ(user->fd,"442 %s %s :You're not on that channel!",user->nick, chan->name);
1467                                 return;
1468                         }
1469         
1470                         MOD_RESULT = 0;
1471                         FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user, NULL, chan, AC_GENERAL_MODE));
1472                                 
1473                         if(MOD_RESULT == ACR_DENY)
1474                                 return;
1475
1476                         if(MOD_RESULT == ACR_DEFAULT)
1477                         {
1478                                 if ((IS_LOCAL(user)) && (cstatus(user,chan) < STATUS_HOP))
1479                                 {
1480                                         WriteServ(user->fd,"482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, chan->name);
1481                                         return;
1482                                 }
1483                         }
1484         
1485                         ServerInstance->ModeGrok->ProcessModes(parameters,user,chan,cstatus(user,chan),pcnt,false,false,false);
1486                 }
1487                 else
1488                 {
1489                         WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, parameters[0]);
1490                 }
1491         }
1492 }
1493
1494
1495
1496
1497 void ModeParser::ServerMode(char **parameters, int pcnt, userrec *user)
1498 {
1499         chanrec* Ptr;
1500         userrec* dest = Find(parameters[0]);
1501         int can_change;
1502         int direction = 1;
1503         char outpars[MAXBUF];
1504         bool next_ok = true;
1505
1506         if ((dest) && (pcnt > 1))
1507         {
1508                 std::string tidied = ServerInstance->ModeGrok->CompressModes(parameters[1],false);
1509                 parameters[1] = (char*)tidied.c_str();
1510
1511                 char dmodes[MAXBUF];
1512                 strlcpy(dmodes,dest->modes,MAXBUF);
1513
1514                 outpars[0] = *parameters[1];
1515                 outpars[1] = 0;
1516                 direction = (*parameters[1] == '+');
1517
1518                 if ((*parameters[1] != '+') && (*parameters[1] != '-'))
1519                         return;
1520
1521                 for (char* i = parameters[1]; *i; i++)
1522                 {
1523                         if ((i != parameters[1]) && (*i != '+') && (*i != '-'))
1524                                 next_ok = true;
1525
1526                         switch (*i)
1527                         {
1528                                 case ' ':
1529                                 continue;
1530
1531                                 case '+':
1532                                         if ((direction != 1) && (next_ok))
1533                                         {
1534                                                 next_ok = false;
1535                                                 charlcat(outpars,'+',MAXBUF);
1536                                         }
1537                                         direction = 1;
1538                                 break;
1539
1540                                 case '-':
1541                                         if ((direction != 0) && (next_ok))
1542                                         {
1543                                                 next_ok = false;
1544                                                 charlcat(outpars,'-',MAXBUF);
1545                                         }
1546                                         direction = 0;
1547                                 break;
1548
1549                                 default:
1550                                         log(DEBUG,"begin mode processing entry");
1551                                         can_change = 1;
1552                                         if (can_change)
1553                                         {
1554                                                 if (direction == 1)
1555                                                 {
1556                                                         log(DEBUG,"umode %c being added",*i);
1557                                                         if ((!strchr(dmodes,*i)) && (ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,true,true)))
1558                                                         {
1559                                                                 log(DEBUG,"umode %c is an allowed umode",*i);
1560                                                                 if ((*i == 'i') || (*i == 's') || (*i == 'w') || (*i == 'o') || (ServerInstance->ModeGrok->ProcessModuleUmode(*i, user, dest, direction)))
1561                                                                 {
1562                                                                         charlcat(dmodes,*i,MAXBUF);
1563                                                                         charlcat(outpars,*i,53);
1564                                                                         switch (*i)
1565                                                                         {
1566                                                                                 case 'i':
1567                                                                                         dest->modebits |= UM_INVISIBLE;
1568                                                                                 break;
1569                                                                                 case 's':
1570                                                                                         dest->modebits |= UM_SERVERNOTICE;
1571                                                                                 break;
1572                                                                                 case 'w':
1573                                                                                         dest->modebits |= UM_WALLOPS;
1574                                                                                 break;
1575                                                                                 default:
1576                                                                                 break;
1577                                                                         }
1578                                                                 }
1579                                                         }
1580                                                 }
1581                                                 else
1582                                                 {
1583                                                         // can only remove a mode they already have
1584                                                         log(DEBUG,"umode %c being removed",*i);
1585                                                         if ((ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,false,true)) && (strchr(dmodes,*i)))
1586                                                         {
1587                                                                 log(DEBUG,"umode %c is an allowed umode",*i);
1588                                                                 if ((*i == 'i') || (*i == 's') || (*i == 'w') || (*i == 'o') || (ServerInstance->ModeGrok->ProcessModuleUmode(*i, user, dest, direction)))
1589                                                                 {
1590                                                                         charlcat(outpars,*i,MAXBUF);
1591                                                                         charremove(dmodes,*i);
1592                                                                         switch (*i)
1593                                                                         {
1594                                                                                 case 'i':
1595                                                                                         dest->modebits &= ~UM_INVISIBLE;
1596                                                                                 break;
1597                                                                                 case 's':
1598                                                                                         dest->modebits &= ~UM_SERVERNOTICE;
1599                                                                                 break;
1600                                                                                 case 'w':
1601                                                                                         dest->modebits &= ~UM_WALLOPS;
1602                                                                                 break;
1603                                                                                 default:
1604                                                                                 break;
1605                                                                         }
1606                                                                 }
1607                                                         }
1608                                                 }
1609                                         }
1610                                 break;
1611                         }
1612                 }
1613                 if (*outpars)
1614                 {
1615                         char b[MAXBUF];
1616                         char* z = b;
1617
1618                         for (char* i = outpars; *i;)
1619                         {
1620                                 *z++ = *i++;
1621                                 if (((*i == '-') || (*i == '+')) && ((*(i+1) == '-') || (*(i+1) == '+')))
1622                                 {
1623                                         // someones playing silly buggers and trying
1624                                         // to put a +- or -+ into the line...
1625                                         i++;
1626                                 }
1627                                 if (!*(i+1))
1628                                 {
1629                                         // Someone's trying to make the last character in
1630                                         // the line be a + or - symbol.
1631                                         if ((*i == '-') || (*i == '+'))
1632                                         {
1633                                                 i++;
1634                                         }
1635                                 }
1636                         }
1637                         *z = 0;
1638
1639                         if ((*b) && (!IS_SINGLE(b,'+')) && (!IS_SINGLE(b,'-')))
1640                         {
1641                                 WriteTo(user, dest, "MODE %s :%s", dest->nick, b);
1642                                 FOREACH_MOD(I_OnMode,OnMode(user, dest, TYPE_USER, b));
1643                         }
1644
1645                         log(DEBUG,"Stripped mode line");
1646                         log(DEBUG,"Line dest is now %s",dmodes);
1647                         strlcpy(dest->modes,dmodes,MAXMODES-1);
1648                                          
1649                 }
1650
1651                 return;
1652         }
1653         
1654         Ptr = FindChan(parameters[0]);
1655         if (Ptr)
1656         {
1657                 ServerInstance->ModeGrok->ProcessModes(parameters,user,Ptr,STATUS_OP,pcnt,true,false,false);
1658         }
1659         else
1660         {
1661                 WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, parameters[0]);
1662         }
1663 }