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