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