]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/mode.cpp
UM_WALLOPS had value 3 which was a combination of UM_SERVERNOTICE and UM_INVISIBLE :/
[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)) *outl++ = 'p';
868                                                 chan->binarymodes |= CM_PRIVATE;
869                                                 if (chan->binarymodes & CM_SECRET)
870                                                 {
871                                                         chan->binarymodes &= ~CM_SECRET;
872                                                         if (mdir)
873                                                         {
874                                                                 *outl++ = '-'; *outl++ = 's'; *outl++ = '+';
875                                                         }
876                                                 }
877                                         }
878                                         else
879                                         {
880                                                 if (chan->binarymodes & CM_PRIVATE) *outl++ = 'p';
881                                                 chan->binarymodes &= ~CM_PRIVATE;
882                                         }
883                                 }
884                         break;
885                                 
886                         default:
887                                 string_list p;
888                                 p.clear();
889                                 bool x = chan->custom_modes[*modechar-65];
890                                 if ((!x && !mdir) || (x && mdir))
891                                 {
892                                         if (!ModeIsListMode(*modechar,MT_CHANNEL))
893                                         {
894                                                 log(DEBUG,"Mode %c isnt set on %s but trying to remove!",*modechar,chan->name);
895                                                 break;
896                                         }
897                                 }
898                                 if (ModeDefined(*modechar,MT_CHANNEL))
899                                 {
900                                         /* A module has claimed this mode */
901                                         if (param<pcnt)
902                                         {
903                                                 if ((ModeDefinedOn(*modechar,MT_CHANNEL)>0) && (mdir))
904                                                 {
905                                                         p.push_back(parameters[param]);
906                                                 }
907                                                 if ((ModeDefinedOff(*modechar,MT_CHANNEL)>0) && (!mdir))
908                                                 {
909                                                         p.push_back(parameters[param]);
910                                                 }
911                                         }
912                                         bool handled = false;
913                                         if (param>=pcnt)
914                                         {
915                                                 // we're supposed to have a parameter, but none was given... so dont handle the mode.
916                                                 if (((ModeDefinedOn(*modechar,MT_CHANNEL)>0) && (mdir)) || ((ModeDefinedOff(*modechar,MT_CHANNEL)>0) && (!mdir)))       
917                                                 {
918                                                         log(DEBUG,"Not enough parameters for module-mode %c",*modechar);
919                                                         handled = true;
920                                                         param++;
921                                                 }
922                                         }
923                                         // BIG ASS IDIOTIC CODER WARNING!
924                                         // Using OnRawMode on another modules mode's behavour 
925                                         // will confuse the crap out of admins! just because you CAN
926                                         // do it, doesnt mean you SHOULD!
927                                         MOD_RESULT = 0;
928                                         std::string para = "";
929                                         if (p.size())
930                                                 para = p[0];
931                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, *modechar, para, mdir, pcnt));
932                                         if (!MOD_RESULT)
933                                         {
934                                                         for (int i = 0; i <= MODCOUNT; i++)
935                                                 {
936                                                         if (!handled)
937                                                         {
938                                                                 int t = modules[i]->OnExtendedMode(user,chan,*modechar,MT_CHANNEL,mdir,p);
939                                                                 if (t != 0)
940                                                                 {
941                                                                         log(DEBUG,"OnExtendedMode returned nonzero for a module");
942                                                                         if (ModeIsListMode(*modechar,MT_CHANNEL))
943                                                                         {
944                                                                                 if (t == -1)
945                                                                                 {
946                                                                                         //pc++;
947                                                                                         param++;
948                                                                                 }
949                                                                                 else
950                                                                                 {
951                                                                                         if (param < pcnt)
952                                                                                         {
953                                                                                                 *outl++ = *modechar;
954                                                                                         }
955                                                                                         outpars[pc++] = parameters[param++];
956                                                                                 }
957                                                                         }
958                                                                         else
959                                                                         {
960                                                                                 *outl++ = *modechar;
961                                                                                 chan->SetCustomMode(*modechar,mdir);
962                                                                                 // include parameters in output if mode has them
963                                                                                 if ((ModeDefinedOn(*modechar,MT_CHANNEL)>0) && (mdir))
964                                                                                 {
965                                                                                         if (param < pcnt)
966                                                                                         {
967                                                                                                 chan->SetCustomModeParam(*modechar,parameters[param],mdir);
968                                                                                                 outpars[pc++] = parameters[param++];
969                                                                                         }
970                                                                                 }
971                                                                         }
972                                                                         // break, because only one module can handle the mode.
973                                                                         handled = true;
974                                                                 }
975                                                         }
976                                                 }
977                                         }
978                                 }
979                                 else
980                                 {
981                                         WriteServ(user->fd,"472 %s %c :is unknown mode char to me",user->nick,*modechar);
982                                 }
983                         break;
984                 }
985         }
986
987         /* Null terminate it now we're done */
988         *outl = 0;
989
990
991         /************ Fast, but confusing string tidying ************/
992         outl = outlist;
993         while (*outl && (*outl < 'A'))
994                 outl++;
995         /* outl now points to the first mode character after +'s and -'s */
996         outl--;
997         /* Now points at first mode-modifier + or - symbol */
998         char* trim = outl;
999         /* Now we tidy off any trailing -'s etc */
1000         while (*trim++);
1001         trim--;
1002         while ((*--trim == '+') || (*trim == '-'))
1003                 *trim = 0;
1004         /************ Done wih the string tidy functions ************/
1005
1006
1007         /* The mode change must be at least two characters long (+ or - and at least one mode) */
1008         if (((*outl == '+') || (*outl == '-')) && *(outl+1))
1009         {
1010                 for (ptr = 0; ptr < pc; ptr++)
1011                 {
1012                         charlcat(outl,' ',MAXBUF);
1013                         strlcat(outl,outpars[ptr],MAXBUF-1);
1014                 }
1015                 if (local)
1016                 {
1017                         log(DEBUG,"Local mode change");
1018                         WriteChannelLocal(chan, user, "MODE %s %s",chan->name,outl);
1019                         FOREACH_MOD(I_OnMode,OnMode(user, chan, TYPE_CHANNEL, outl));
1020                 }
1021                 else
1022                 {
1023                         if (servermode)
1024                         {
1025                                 if (!silent)
1026                                 {
1027                                         WriteChannelWithServ(Config->ServerName,chan,"MODE %s %s",chan->name,outl);
1028                                 }
1029                                         
1030                         }
1031                         else
1032                         {
1033                                 if (!silent)
1034                                 {
1035                                         WriteChannel(chan,user,"MODE %s %s",chan->name,outl);
1036                                         FOREACH_MOD(I_OnMode,OnMode(user, chan, TYPE_CHANNEL, outl));
1037                                 }
1038                         }
1039                 }
1040         }
1041 }
1042
1043 // based on sourcemodes, return true or false to determine if umode is a valid mode a user may set on themselves or others.
1044
1045 bool ModeParser::AllowedUmode(char umode, char* sourcemodes,bool adding,bool serveroverride)
1046 {
1047         bool sourceoper = (strchr(sourcemodes,'o') != NULL);
1048         log(DEBUG,"Allowed_umode: %c %s",umode,sourcemodes);
1049         // Servers can +o and -o arbitrarily
1050         if ((serveroverride == true) && (umode == 'o'))
1051         {
1052                 return true;
1053         }
1054         // RFC1459 specified modes
1055         if ((umode == 'w') || (umode == 's') || (umode == 'i'))
1056         {
1057                 /* umode allowed by RFC1459 scemantics */
1058                 return true;
1059         }
1060         
1061         /* user may not +o themselves or others, but an oper may de-oper other opers or themselves */
1062         if (sourceoper && !adding)
1063         {
1064                 return true;
1065         }
1066         else if (umode == 'o')
1067         {
1068                 /* Bad oper, bad bad! */
1069                 return false;
1070         }
1071         
1072         /* process any module-defined modes that need oper */
1073         if ((ModeDefinedOper(umode,MT_CLIENT)) && (sourceoper))
1074         {
1075                 log(DEBUG,"umode %c allowed by module handler (oper only mode)",umode);
1076                 return true;
1077         }
1078         else if (ModeDefined(umode,MT_CLIENT))
1079         {
1080                 // process any module-defined modes that don't need oper
1081                 log(DEBUG,"umode %c allowed by module handler (non-oper mode)",umode);
1082                 if ((ModeDefinedOper(umode,MT_CLIENT)) && (!sourceoper))
1083                 {
1084                         // no, this mode needs oper, and this user 'aint got what it takes!
1085                         return false;
1086                 }
1087                 return true;
1088         }
1089
1090         // anything else - return false.
1091         log(DEBUG,"umode %c not known by any ruleset",umode);
1092         return false;
1093 }
1094
1095 bool ModeParser::ProcessModuleUmode(char umode, userrec* source, void* dest, bool adding)
1096 {
1097         userrec* s2;
1098         bool faked = false;
1099         if (!source)
1100         {
1101                 s2 = new userrec;
1102                 strlcpy(s2->nick,Config->ServerName,NICKMAX-1);
1103                 *s2->modes = 'o';
1104                 *(s2->modes+1) = 0;
1105                 s2->fd = -1;
1106                 source = s2;
1107                 faked = true;
1108         }
1109         string_list p;
1110         p.clear();
1111         if (ModeDefined(umode,MT_CLIENT))
1112         {
1113                 for (int i = 0; i <= MODCOUNT; i++)
1114                 {
1115                         if (modules[i]->OnExtendedMode(source,(void*)dest,umode,MT_CLIENT,adding,p))
1116                         {
1117                                 log(DEBUG,"Module %s claims umode %c",Config->module_names[i].c_str(),umode);
1118                                 return true;
1119                         }
1120                 }
1121                 log(DEBUG,"No module claims umode %c",umode);
1122                 if (faked)
1123                 {
1124                         delete s2;
1125                         source = NULL;
1126                 }
1127                 return false;
1128         }
1129         else
1130         {
1131                 if (faked)
1132                 {
1133                         delete s2;
1134                         source = NULL;
1135                 }
1136                 return false;
1137         }
1138 }
1139
1140 void cmd_mode::Handle (char **parameters, int pcnt, userrec *user)
1141 {
1142         chanrec* Ptr;
1143         userrec* dest = Find(parameters[0]);
1144         int can_change;
1145         int direction = 1;
1146         char outpars[MAXBUF];
1147         bool next_ok = true;
1148
1149         if (!user)
1150                 return;
1151
1152         if ((dest) && (pcnt == 1))
1153         {
1154                 WriteServ(user->fd,"221 %s :+%s",dest->nick,dest->modes);
1155                 return;
1156         }
1157         else if ((dest) && (pcnt > 1))
1158         {
1159                 std::string tidied = ServerInstance->ModeGrok->CompressModes(parameters[1],false);
1160                 parameters[1] = (char*)tidied.c_str();
1161
1162                 char dmodes[MAXBUF];
1163                 strlcpy(dmodes,dest->modes,MAXMODES);
1164                 log(DEBUG,"pulled up dest user modes: %s",dmodes);
1165
1166                 can_change = 0;
1167                 if (user != dest)
1168                 {
1169                         if ((*user->oper) || (is_uline(user->server)))
1170                         {
1171                                 can_change = 1;
1172                         }
1173                 }
1174                 else
1175                 {
1176                         can_change = 1;
1177                 }
1178                 if (!can_change)
1179                 {
1180                         WriteServ(user->fd,"482 %s :Can't change mode for other users",user->nick);
1181                         return;
1182                 }
1183                 
1184                 outpars[0] = *parameters[1];
1185                 outpars[1] = 0;
1186                 direction = (*parameters[1] == '+');
1187
1188                 if ((*parameters[1] != '+') && (*parameters[1] != '-'))
1189                         return;
1190
1191                 for (char* i = parameters[1]; *i; i++)
1192                 {
1193                         if ((i != parameters[1]) && (*i != '+') && (*i != '-'))
1194                                 next_ok = true;
1195
1196                         switch (*i)
1197                         {
1198                                 case ' ':
1199                                 continue;
1200
1201                                 case '+':
1202                                         if ((direction != 1) && (next_ok))
1203                                         {
1204                                                 charlcat(outpars,'+',MAXBUF);
1205                                                 next_ok = false;
1206                                         }       
1207                                         direction = 1;
1208                                 break;
1209
1210                                 case '-':
1211                                         if ((direction != 0) && (next_ok))
1212                                         {
1213                                                 charlcat(outpars,'-',MAXBUF);
1214                                                 next_ok = false;
1215                                         }
1216                                         direction = 0;
1217                                 break;
1218
1219                                 default:
1220                                         can_change = 0;
1221                                         if (*user->oper)
1222                                         {
1223                                                 can_change = 1;
1224                                         }
1225                                         else
1226                                         {
1227                                                 if ((*i == 'i') || (*i == 'w') || (*i == 's') || (ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,direction,false)))
1228                                                 {
1229                                                         can_change = 1;
1230                                                 }
1231                                         }
1232                                         if (can_change)
1233                                         {
1234                                                 if (direction == 1)
1235                                                 {
1236                                                         if ((!strchr(dmodes,*i)) && (ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,true,false)))
1237                                                         {
1238                                                                 if ((ServerInstance->ModeGrok->ProcessModuleUmode(*i, user, dest, direction)) || (*i == 'i') || (*i == 's') || (*i == 'w') || (*i == 'o'))
1239                                                                 {
1240                                                                         charlcat(dmodes,*i,53);
1241                                                                         charlcat(outpars,*i,MAXMODES);
1242                                                                         switch (*i)
1243                                                                         {
1244                                                                                 case 'o':
1245                                                                                         FOREACH_MOD(I_OnGlobalOper,OnGlobalOper(dest));
1246                                                                                 break;
1247                                                                                 case 'i':
1248                                                                                         dest->modebits |= UM_INVISIBLE;
1249                                                                                 break;
1250                                                                                 case 's':
1251                                                                                         dest->modebits |= UM_SERVERNOTICE;
1252                                                                                 break;
1253                                                                                 case 'w':
1254                                                                                         dest->modebits |= UM_WALLOPS;
1255                                                                                 break;
1256                                                                                 default:
1257                                                                                 break;
1258                                                                         }
1259                                                                 }
1260                                                         }
1261                                                 }
1262                                                 else
1263                                                 {
1264                                                         if ((ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,false,false)) && (strchr(dmodes,*i)))
1265                                                         {
1266                                                                 if ((ServerInstance->ModeGrok->ProcessModuleUmode(*i, user, dest, direction)) || (*i == 'i') || (*i == 's') || (*i == 'w') || (*i == 'o'))
1267                                                                 {
1268                                                                         charlcat(outpars,*i,MAXMODES);
1269                                                                         charremove(dmodes,*i);
1270                                                                         switch (*i)
1271                                                                         {
1272                                                                                 case 'o':
1273                                                                                         *dest->oper = 0;
1274                                                                                         DeleteOper(dest);
1275                                                                                 break;
1276                                                                                 case 'i':
1277                                                                                         dest->modebits &= ~UM_INVISIBLE;
1278                                                                                 break;
1279                                                                                 case 's':
1280                                                                                         dest->modebits &= ~UM_SERVERNOTICE;
1281                                                                                 break;
1282                                                                                 case 'w':
1283                                                                                         dest->modebits &= ~UM_WALLOPS;
1284                                                                                 break;
1285                                                                                 default:
1286                                                                                 break;
1287                                                                         }
1288                                                                 }
1289                                                         }
1290                                                 }
1291                                         }
1292                                 break;
1293                         }
1294                 }
1295                 if (*outpars)
1296                 {
1297                         char b[MAXBUF];
1298                         char* z = b;
1299
1300                         for (char* i = outpars; *i;)
1301                         {
1302                                 *z++ = *i++;
1303                                 if (((*i == '-') || (*i == '+')) && ((*(i+1) == '-') || (*(i+1) == '+')))
1304                                 {
1305                                         // someones playing silly buggers and trying
1306                                         // to put a +- or -+ into the line...
1307                                         i++;
1308                                 }
1309                                 if (!*(i+1))
1310                                 {
1311                                         // Someone's trying to make the last character in
1312                                         // the line be a + or - symbol.
1313                                         if ((*i == '-') || (*i == '+'))
1314                                         {
1315                                                 i++;
1316                                         }
1317                                 }
1318                         }
1319                         *z = 0;
1320
1321                         if ((*b) && (!IS_SINGLE(b,'+')) && (!IS_SINGLE(b,'-')))
1322                         {
1323                                 WriteTo(user, dest, "MODE %s :%s", dest->nick, b);
1324                                 FOREACH_MOD(I_OnMode,OnMode(user, dest, TYPE_USER, b));
1325                         }
1326
1327                         log(DEBUG,"Stripped mode line");
1328                         log(DEBUG,"Line dest is now %s",dmodes);
1329                         strlcpy(dest->modes,dmodes,MAXMODES-1);
1330
1331                 }
1332
1333                 return;
1334         }
1335         else
1336         {
1337                 Ptr = FindChan(parameters[0]);
1338                 if (Ptr)
1339                 {
1340                         if (pcnt == 1)
1341                         {
1342                                 /* just /modes #channel */
1343                                 WriteServ(user->fd,"324 %s %s +%s",user->nick, Ptr->name, chanmodes(Ptr,Ptr->HasUser(user)));
1344                                 WriteServ(user->fd,"329 %s %s %d", user->nick, Ptr->name, Ptr->created);
1345                                 return;
1346                         }
1347                         else if (pcnt == 2)
1348                         {
1349                                 char* mode = parameters[1];
1350                                 if (*mode == '+')
1351                                         mode++;
1352                                 int MOD_RESULT = 0;
1353                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, Ptr, *mode, "", false, 0));
1354                                 if (!MOD_RESULT)
1355                                 {
1356                                         if (*mode == 'b')
1357                                         {
1358                                                 for (BanList::iterator i = Ptr->bans.begin(); i != Ptr->bans.end(); i++)
1359                                                 {
1360                                                         WriteServ(user->fd,"367 %s %s %s %s %d",user->nick, Ptr->name, i->data, i->set_by, i->set_time);
1361                                                 }
1362                                                 WriteServ(user->fd,"368 %s %s :End of channel ban list",user->nick, Ptr->name);
1363                                                 return;
1364                                         }
1365                                         if ((ModeDefined(*mode,MT_CHANNEL)) && (ModeIsListMode(*mode,MT_CHANNEL)))
1366                                         {
1367                                                 // list of items for an extmode
1368                                                 log(DEBUG,"Calling OnSendList for all modules, list output for mode %c",*mode);
1369                                                 FOREACH_MOD(I_OnSendList,OnSendList(user,Ptr,*mode));
1370                                                 return;
1371                                         }
1372                                 }
1373                         }
1374
1375                         if ((IS_LOCAL(user)) && (!is_uline(user->server)) && (!Ptr->HasUser(user)))
1376                         {
1377                                 WriteServ(user->fd,"442 %s %s :You're not on that channel!",user->nick, Ptr->name);
1378                                 return;
1379                         }
1380         
1381                         if (Ptr)
1382                         {
1383                                 int MOD_RESULT = 0;
1384                                 FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user,NULL,Ptr,AC_GENERAL_MODE));
1385                                 
1386                                 if (MOD_RESULT == ACR_DENY)
1387                                         return;
1388                                 if (MOD_RESULT == ACR_DEFAULT)
1389                                 {
1390                                         if ((IS_LOCAL(user)) && (cstatus(user,Ptr) < STATUS_HOP))
1391                                         {
1392                                                 WriteServ(user->fd,"482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, Ptr->name);
1393                                                 return;
1394                                         }
1395                                 }
1396         
1397                                 ServerInstance->ModeGrok->ProcessModes(parameters,user,Ptr,cstatus(user,Ptr),pcnt,false,false,false);
1398                         }
1399                 }
1400                 else
1401                 {
1402                         WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, parameters[0]);
1403                 }
1404         }
1405 }
1406
1407
1408
1409
1410 void ModeParser::ServerMode(char **parameters, int pcnt, userrec *user)
1411 {
1412         chanrec* Ptr;
1413         userrec* dest = Find(parameters[0]);
1414         int can_change;
1415         int direction = 1;
1416         char outpars[MAXBUF];
1417         bool next_ok = true;
1418
1419         if ((dest) && (pcnt > 1))
1420         {
1421                 std::string tidied = ServerInstance->ModeGrok->CompressModes(parameters[1],false);
1422                 parameters[1] = (char*)tidied.c_str();
1423
1424                 char dmodes[MAXBUF];
1425                 strlcpy(dmodes,dest->modes,MAXBUF);
1426
1427                 outpars[0] = *parameters[1];
1428                 outpars[1] = 0;
1429                 direction = (*parameters[1] == '+');
1430
1431                 if ((*parameters[1] != '+') && (*parameters[1] != '-'))
1432                         return;
1433
1434                 for (char* i = parameters[1]; *i; i++)
1435                 {
1436                         if ((i != parameters[1]) && (*i != '+') && (*i != '-'))
1437                                 next_ok = true;
1438
1439                         switch (*i)
1440                         {
1441                                 case ' ':
1442                                 continue;
1443
1444                                 case '+':
1445                                         if ((direction != 1) && (next_ok))
1446                                         {
1447                                                 next_ok = false;
1448                                                 charlcat(outpars,'+',MAXBUF);
1449                                         }
1450                                         direction = 1;
1451                                 break;
1452
1453                                 case '-':
1454                                         if ((direction != 0) && (next_ok))
1455                                         {
1456                                                 next_ok = false;
1457                                                 charlcat(outpars,'-',MAXBUF);
1458                                         }
1459                                         direction = 0;
1460                                 break;
1461
1462                                 default:
1463                                         log(DEBUG,"begin mode processing entry");
1464                                         can_change = 1;
1465                                         if (can_change)
1466                                         {
1467                                                 if (direction == 1)
1468                                                 {
1469                                                         log(DEBUG,"umode %c being added",*i);
1470                                                         if ((!strchr(dmodes,*i)) && (ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,true,true)))
1471                                                         {
1472                                                                 log(DEBUG,"umode %c is an allowed umode",*i);
1473                                                                 if ((*i == 'i') || (*i == 's') || (*i == 'w') || (*i == 'o') || (ServerInstance->ModeGrok->ProcessModuleUmode(*i, user, dest, direction)))
1474                                                                 {
1475                                                                         charlcat(dmodes,*i,MAXBUF);
1476                                                                         charlcat(outpars,*i,53);
1477                                                                 }
1478                                                         }
1479                                                 }
1480                                                 else
1481                                                 {
1482                                                         // can only remove a mode they already have
1483                                                         log(DEBUG,"umode %c being removed",*i);
1484                                                         if ((ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,false,true)) && (strchr(dmodes,*i)))
1485                                                         {
1486                                                                 log(DEBUG,"umode %c is an allowed umode",*i);
1487                                                                 if ((*i == 'i') || (*i == 's') || (*i == 'w') || (*i == 'o') || (ServerInstance->ModeGrok->ProcessModuleUmode(*i, user, dest, direction)))
1488                                                                 {
1489                                                                         charlcat(outpars,*i,MAXBUF);
1490                                                                         charremove(dmodes,*i);
1491                                                                 }
1492                                                         }
1493                                                 }
1494                                         }
1495                                 break;
1496                         }
1497                 }
1498                 if (*outpars)
1499                 {
1500                         char b[MAXBUF];
1501                         char* z = b;
1502
1503                         for (char* i = outpars; *i;)
1504                         {
1505                                 *z++ = *i++;
1506                                 if (((*i == '-') || (*i == '+')) && ((*(i+1) == '-') || (*(i+1) == '+')))
1507                                 {
1508                                         // someones playing silly buggers and trying
1509                                         // to put a +- or -+ into the line...
1510                                         i++;
1511                                 }
1512                                 if (!*(i+1))
1513                                 {
1514                                         // Someone's trying to make the last character in
1515                                         // the line be a + or - symbol.
1516                                         if ((*i == '-') || (*i == '+'))
1517                                         {
1518                                                 i++;
1519                                         }
1520                                 }
1521                         }
1522                         *z = 0;
1523
1524                         if ((*b) && (!IS_SINGLE(b,'+')) && (!IS_SINGLE(b,'-')))
1525                         {
1526                                 WriteTo(user, dest, "MODE %s :%s", dest->nick, b);
1527                                 FOREACH_MOD(I_OnMode,OnMode(user, dest, TYPE_USER, b));
1528                         }
1529
1530                         log(DEBUG,"Stripped mode line");
1531                         log(DEBUG,"Line dest is now %s",dmodes);
1532                         strlcpy(dest->modes,dmodes,MAXMODES-1);
1533                                          
1534                 }
1535
1536                 return;
1537         }
1538         
1539         Ptr = FindChan(parameters[0]);
1540         if (Ptr)
1541         {
1542                 ServerInstance->ModeGrok->ProcessModes(parameters,user,Ptr,STATUS_OP,pcnt,true,false,false);
1543         }
1544         else
1545         {
1546                 WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, parameters[0]);
1547         }
1548 }