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