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