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