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