]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/mode.cpp
Split inspircd_io.* insp inspsocket.* and configreader.* with a few odd bits going...
[user/henk/code/inspircd.git] / src / mode.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *            the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 using namespace std;
18
19 #include "inspircd_config.h"
20 #include "inspircd.h"
21 #include "configreader.h"
22 #include <unistd.h>
23 #include <sys/errno.h>
24 #include <time.h>
25 #include <string>
26 #ifdef GCC3
27 #include <ext/hash_map>
28 #else
29 #include <hash_map>
30 #endif
31 #include <map>
32 #include <sstream>
33 #include <vector>
34 #include <deque>
35 #include "connection.h"
36 #include "users.h"
37 #include "ctables.h"
38 #include "globals.h"
39 #include "modules.h"
40 #include "dynamic.h"
41 #include "wildcard.h"
42 #include "message.h"
43 #include "commands.h"
44 #include "xline.h"
45 #include "inspstring.h"
46 #include "helperfuncs.h"
47 #include "mode.h"
48
49 extern int MODCOUNT;
50 extern std::vector<Module*> modules;
51 extern std::vector<ircd_module*> factory;
52 extern InspIRCd* ServerInstance;
53 extern ServerConfig* Config;
54
55 extern time_t TIME;
56
57 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->modes[CM_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                                                         chan->modes[CM_KEY] = 1;
668                                                         k_set = true;
669                                                 }
670                                                 else param++;
671                                         }
672                                 }
673                                 else
674                                 {
675                                         /* checks on -k are case sensitive and only accurate to the
676                                                    first 32 characters */
677                                         if (previously_set_k)
678                                                 break;
679                                         previously_unset_k = true;
680
681                                         char key[MAXBUF];
682                                         MOD_RESULT = 0;
683                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'k', parameters[param], false, 1));
684                                         if (!MOD_RESULT)
685                                         {
686                                                 strlcpy(key,parameters[param++],32);
687                                                 /* only allow -k if correct key given */
688                                                 if (!strcmp(chan->key,key))
689                                                 {
690                                                         *outl++ = 'k';
691                                                         *chan->key = 0;
692                                                         chan->modes[CM_KEY] = 0;
693                                                         outpars[pc++] = key;
694                                                 }
695                                         }
696                                         else param++;
697                                 }
698                         break;
699                                 
700                         case 'l':
701                                 if (mdir == 0)
702                                 {
703                                         if (previously_set_l)
704                                                 break;
705                                         previously_unset_l = true;
706                                         MOD_RESULT = 0;
707                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'l', "", false, 0));
708                                         if (!MOD_RESULT)
709                                         {
710                                                 if (chan->modes[CM_LIMIT])
711                                                 {
712                                                         *outl++ = 'l';
713                                                         chan->limit = 0;
714                                                         chan->modes[CM_LIMIT] = 0;
715                                                 }
716                                         }
717                                 }
718                                         
719                                 if ((param >= pcnt)) break;
720                                 if (mdir == 1)
721                                 {
722                                         if (l_set)
723                                                 break;
724                                         if (previously_unset_l)
725                                                 break;
726                                         previously_set_l = true;
727                                         bool invalid = false;
728                                         for (char* f = parameters[param]; *f; f++)
729                                         {
730                                                 if ((*f < '0') || (*f > '9'))
731                                                 {
732                                                         invalid = true;
733                                                 }
734                                         }
735                                         /* If the limit is < 1, or the new limit is the current limit, dont allow */
736                                         if ((atoi(parameters[param]) < 1) || ((chan->limit > 0) && (atoi(parameters[param]) == chan->limit)))
737                                         {
738                                                 invalid = true;
739                                         }
740
741                                         if (invalid)
742                                                 break;
743
744                                         MOD_RESULT = 0;
745                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'l', parameters[param], true, 1));
746                                         if (!MOD_RESULT)
747                                         {
748         
749                                                 chan->limit = atoi(parameters[param]);
750                                                         
751                                                 // reported by mech: large values cause underflow
752                                                 if (chan->limit < 0)
753                                                         chan->limit = 0x7FFF;
754                                         }
755                                                 
756                                         if (chan->limit)
757                                         {
758                                                 *outl++ = 'l';
759                                                 chan->modes[CM_LIMIT] = 1;
760                                                 outpars[pc++] = parameters[param++];
761                                                 l_set = true;
762                                         }
763                                 }
764                         break;
765                                 
766                         case 'i':
767                                 MOD_RESULT = 0;
768                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'i', "", mdir, 0));
769                                 if (!MOD_RESULT)
770                                 {
771                                         if (mdir)
772                                         {
773                                                 if (!(chan->modes[CM_INVITEONLY])) *outl++ = 'i';
774                                                 chan->modes[CM_INVITEONLY] = 1;
775                                         }
776                                         else
777                                         {
778                                                 if (chan->modes[CM_INVITEONLY]) *outl++ = 'i';
779                                                 chan->modes[CM_INVITEONLY] = 0;
780                                         }
781                                 }
782                         break;
783                                 
784                         case 't':
785                                 MOD_RESULT = 0;
786                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 't', "", mdir, 0));
787                                 if (!MOD_RESULT)
788                                 {
789                                         if (mdir)
790                                         {
791                                                 if (!(chan->modes[CM_TOPICLOCK])) *outl++ = 't';
792                                                 chan->modes[CM_TOPICLOCK] = 1;
793                                         }
794                                         else
795                                         {
796                                                 if (chan->modes[CM_TOPICLOCK]) *outl++ = 't';
797                                                 chan->modes[CM_TOPICLOCK] = 0;
798                                         }
799                                 }
800                         break;
801                                 
802                         case 'n':
803                                 MOD_RESULT = 0;
804                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'n', "", mdir, 0));
805                                 if (!MOD_RESULT)
806                                 {
807                                         if (mdir)
808                                         {
809                                                 if (!(chan->modes[CM_NOEXTERNAL])) *outl++ = 'n';
810                                                 chan->modes[CM_NOEXTERNAL] = 1;
811                                         }
812                                         else
813                                         {
814                                                 if (chan->modes[CM_NOEXTERNAL]) *outl++ = 'n';
815                                                 chan->modes[CM_NOEXTERNAL] = 0;
816                                         }
817                                 }
818                         break;
819                                 
820                         case 'm':
821                                 MOD_RESULT = 0;
822                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'm', "", mdir, 0));
823                                 if (!MOD_RESULT)
824                                 {
825                                         if (mdir)
826                                         {
827                                                 if (!(chan->modes[CM_MODERATED])) *outl++ = 'm';
828                                                 chan->modes[CM_MODERATED] = 1;
829                                         }
830                                         else
831                                         {
832                                                 if (chan->modes[CM_MODERATED]) *outl++ = 'm';
833                                                 chan->modes[CM_MODERATED] = 0;
834                                         }
835                                 }
836                         break;
837                                 
838                         case 's':
839                                 MOD_RESULT = 0;
840                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 's', "", mdir, 0));
841                                 if (!MOD_RESULT)
842                                 {
843                                         if (mdir)
844                                         {
845                                                 if (!(chan->modes[CM_SECRET])) *outl++ = 's';
846                                                 chan->modes[CM_SECRET] = 1;
847                                                 if (chan->modes[CM_PRIVATE])
848                                                 {
849                                                         chan->modes[CM_PRIVATE] = 0;
850                                                         if (mdir)
851                                                         {
852                                                                 *outl++ = '-'; *outl++ = 'p'; *outl++ = '+';
853                                                         }
854                                                 }
855                                         }
856                                         else
857                                         {
858                                                 if (chan->modes[CM_SECRET]) *outl++ = 's';
859                                                 chan->modes[CM_SECRET] = 0;
860                                         }
861                                 }
862                         break;
863                                 
864                         case 'p':
865                                 MOD_RESULT = 0;
866                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'p', "", mdir, 0));
867                                 if(!MOD_RESULT)
868                                 {
869                                         if(mdir)
870                                         {
871                                                 if(!(chan->modes[CM_PRIVATE]))
872                                                         *outl++ = 'p';
873                                                 
874                                                 chan->modes[CM_PRIVATE] = 1;
875                                                 
876                                                 if(chan->modes[CM_SECRET])
877                                                 {
878                                                         chan->modes[CM_SECRET] = 0;
879
880                                                         *outl++ = '-';
881                                                         *outl++ = 's';
882                                                         *outl++ = '+';
883                                                 }
884                                         }
885                                         else
886                                         {
887                                                 if(chan->modes[CM_PRIVATE])
888                                                         *outl++ = 'p';
889                                                 
890                                                 chan->modes[CM_PRIVATE] = 0;
891                                         }
892                                 }
893                                 break;
894                                 
895                         default:
896                                 string_list p;
897                                 p.clear();
898                                 bool x = chan->modes[*modechar-65];
899                                 if ((!x && !mdir) || (x && mdir))
900                                 {
901                                         if (!ModeIsListMode(*modechar,MT_CHANNEL))
902                                         {
903                                                 log(DEBUG,"Mode %c isnt set on %s but trying to remove!",*modechar,chan->name);
904                                                 break;
905                                         }
906                                 }
907                                 if (ModeDefined(*modechar,MT_CHANNEL))
908                                 {
909                                         /* A module has claimed this mode */
910                                         if (param<pcnt)
911                                         {
912                                                 if ((ModeDefinedOn(*modechar,MT_CHANNEL)>0) && (mdir))
913                                                 {
914                                                         p.push_back(parameters[param]);
915                                                 }
916                                                 if ((ModeDefinedOff(*modechar,MT_CHANNEL)>0) && (!mdir))
917                                                 {
918                                                         p.push_back(parameters[param]);
919                                                 }
920                                         }
921                                         bool handled = false;
922                                         if (param>=pcnt)
923                                         {
924                                                 // we're supposed to have a parameter, but none was given... so dont handle the mode.
925                                                 if (((ModeDefinedOn(*modechar,MT_CHANNEL)>0) && (mdir)) || ((ModeDefinedOff(*modechar,MT_CHANNEL)>0) && (!mdir)))       
926                                                 {
927                                                         log(DEBUG,"Not enough parameters for module-mode %c",*modechar);
928                                                         handled = true;
929                                                         param++;
930                                                 }
931                                         }
932                                         // BIG ASS IDIOTIC CODER WARNING!
933                                         // Using OnRawMode on another modules mode's behavour 
934                                         // will confuse the crap out of admins! just because you CAN
935                                         // do it, doesnt mean you SHOULD!
936                                         MOD_RESULT = 0;
937                                         std::string para = "";
938                                         if (p.size())
939                                                 para = p[0];
940                                         
941                                         FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, *modechar, para, mdir, pcnt));
942                                         if(!MOD_RESULT)
943                                         {
944                                                 for (int i = 0; i <= MODCOUNT; i++)
945                                                 {
946                                                         if (!handled)
947                                                         {
948                                                                 int t = modules[i]->OnExtendedMode(user,chan,*modechar,MT_CHANNEL,mdir,p);
949                                                                 if (t != 0)
950                                                                 {
951                                                                         log(DEBUG,"OnExtendedMode returned nonzero for a module");
952                                                                         if (ModeIsListMode(*modechar,MT_CHANNEL))
953                                                                         {
954                                                                                 if (t == -1)
955                                                                                 {
956                                                                                         //pc++;
957                                                                                         param++;
958                                                                                 }
959                                                                                 else
960                                                                                 {
961                                                                                         if (param < pcnt)
962                                                                                         {
963                                                                                                 *outl++ = *modechar;
964                                                                                         }
965                                                                                         outpars[pc++] = parameters[param++];
966                                                                                 }
967                                                                         }
968                                                                         else
969                                                                         {
970                                                                                 *outl++ = *modechar;
971                                                                                 chan->SetCustomMode(*modechar,mdir);
972                                                                                 // include parameters in output if mode has them
973                                                                                 if ((ModeDefinedOn(*modechar,MT_CHANNEL)>0) && (mdir))
974                                                                                 {
975                                                                                         if (param < pcnt)
976                                                                                         {
977                                                                                                 chan->SetCustomModeParam(*modechar,parameters[param],mdir);
978                                                                                                 outpars[pc++] = parameters[param++];
979                                                                                         }
980                                                                                 }
981                                                                         }
982                                                                         // break, because only one module can handle the mode.
983                                                                         handled = true;
984                                                                 }
985                                                         }
986                                                 }
987                                         }
988                                 }
989                                 else
990                                 {
991                                         WriteServ(user->fd,"472 %s %c :is unknown mode char to me",user->nick,*modechar);
992                                 }
993                         break;
994                 }
995         }
996
997         /* Null terminate it now we're done */
998         *outl = 0;
999
1000
1001         /************ Fast, but confusing string tidying ************/
1002         outl = outlist;
1003         while (*outl && (*outl < 'A'))
1004                 outl++;
1005         /* outl now points to the first mode character after +'s and -'s */
1006         outl--;
1007         /* Now points at first mode-modifier + or - symbol */
1008         char* trim = outl;
1009         /* Now we tidy off any trailing -'s etc */
1010         while (*trim++);
1011         trim--;
1012         while ((*--trim == '+') || (*trim == '-'))
1013                 *trim = 0;
1014         /************ Done wih the string tidy functions ************/
1015
1016
1017         /* The mode change must be at least two characters long (+ or - and at least one mode) */
1018         if (((*outl == '+') || (*outl == '-')) && *(outl+1))
1019         {
1020                 for (ptr = 0; ptr < pc; ptr++)
1021                 {
1022                         charlcat(outl,' ',MAXBUF);
1023                         strlcat(outl,outpars[ptr],MAXBUF-1);
1024                 }
1025                 if (local)
1026                 {
1027                         log(DEBUG,"Local mode change");
1028                         WriteChannelLocal(chan, user, "MODE %s %s",chan->name,outl);
1029                         FOREACH_MOD(I_OnMode,OnMode(user, chan, TYPE_CHANNEL, outl));
1030                 }
1031                 else
1032                 {
1033                         if (servermode)
1034                         {
1035                                 if (!silent)
1036                                 {
1037                                         WriteChannelWithServ(Config->ServerName,chan,"MODE %s %s",chan->name,outl);
1038                                 }
1039                                         
1040                         }
1041                         else
1042                         {
1043                                 if (!silent)
1044                                 {
1045                                         WriteChannel(chan,user,"MODE %s %s",chan->name,outl);
1046                                         FOREACH_MOD(I_OnMode,OnMode(user, chan, TYPE_CHANNEL, outl));
1047                                 }
1048                         }
1049                 }
1050         }
1051 }
1052
1053 // based on sourcemodes, return true or false to determine if umode is a valid mode a user may set on themselves or others.
1054
1055 bool ModeParser::AllowedUmode(char umode, char* sourcemodes,bool adding,bool serveroverride)
1056 {
1057         bool sourceoper = (strchr(sourcemodes,'o') != NULL);
1058         log(DEBUG,"Allowed_umode: %c %s",umode,sourcemodes);
1059         // Servers can +o and -o arbitrarily
1060         if ((serveroverride == true) && (umode == 'o'))
1061         {
1062                 return true;
1063         }
1064         // RFC1459 specified modes
1065         if ((umode == 'w') || (umode == 's') || (umode == 'i'))
1066         {
1067                 /* umode allowed by RFC1459 scemantics */
1068                 return true;
1069         }
1070         
1071         /* user may not +o themselves or others, but an oper may de-oper other opers or themselves */
1072         if (sourceoper && !adding)
1073         {
1074                 return true;
1075         }
1076         else if (umode == 'o')
1077         {
1078                 /* Bad oper, bad bad! */
1079                 return false;
1080         }
1081         
1082         /* process any module-defined modes that need oper */
1083         if ((ModeDefinedOper(umode,MT_CLIENT)) && (sourceoper))
1084         {
1085                 log(DEBUG,"umode %c allowed by module handler (oper only mode)",umode);
1086                 return true;
1087         }
1088         else if (ModeDefined(umode,MT_CLIENT))
1089         {
1090                 // process any module-defined modes that don't need oper
1091                 log(DEBUG,"umode %c allowed by module handler (non-oper mode)",umode);
1092                 if ((ModeDefinedOper(umode,MT_CLIENT)) && (!sourceoper))
1093                 {
1094                         // no, this mode needs oper, and this user 'aint got what it takes!
1095                         return false;
1096                 }
1097                 return true;
1098         }
1099
1100         // anything else - return false.
1101         log(DEBUG,"umode %c not known by any ruleset",umode);
1102         return false;
1103 }
1104
1105 bool ModeParser::ProcessModuleUmode(char umode, userrec* source, void* dest, bool adding)
1106 {
1107         userrec* s2;
1108         bool faked = false;
1109         if (!source)
1110         {
1111                 s2 = new userrec;
1112                 strlcpy(s2->nick,Config->ServerName,NICKMAX-1);
1113                 *s2->modes = 'o';
1114                 *(s2->modes+1) = 0;
1115                 s2->fd = -1;
1116                 source = s2;
1117                 faked = true;
1118         }
1119         string_list p;
1120         p.clear();
1121         if (ModeDefined(umode,MT_CLIENT))
1122         {
1123                 for (int i = 0; i <= MODCOUNT; i++)
1124                 {
1125                         if (modules[i]->OnExtendedMode(source,(void*)dest,umode,MT_CLIENT,adding,p))
1126                         {
1127                                 log(DEBUG,"Module %s claims umode %c",Config->module_names[i].c_str(),umode);
1128                                 return true;
1129                         }
1130                 }
1131                 log(DEBUG,"No module claims umode %c",umode);
1132                 if (faked)
1133                 {
1134                         delete s2;
1135                         source = NULL;
1136                 }
1137                 return false;
1138         }
1139         else
1140         {
1141                 if (faked)
1142                 {
1143                         delete s2;
1144                         source = NULL;
1145                 }
1146                 return false;
1147         }
1148 }
1149
1150 void cmd_mode::Handle (char **parameters, int pcnt, userrec *user)
1151 {
1152         chanrec* chan;
1153         userrec* dest = Find(parameters[0]);
1154         int MOD_RESULT;
1155         int can_change;
1156         int direction = 1;
1157         char outpars[MAXBUF];
1158         bool next_ok = true;
1159
1160         if (!user)
1161                 return;
1162
1163         if ((dest) && (pcnt == 1))
1164         {
1165                 WriteServ(user->fd,"221 %s :+%s",dest->nick,dest->modes);
1166                 return;
1167         }
1168         else if ((dest) && (pcnt > 1))
1169         {
1170                 std::string tidied = ServerInstance->ModeGrok->CompressModes(parameters[1],false);
1171                 parameters[1] = (char*)tidied.c_str();
1172
1173                 char dmodes[MAXBUF];
1174                 strlcpy(dmodes,dest->modes,MAXMODES);
1175                 log(DEBUG,"pulled up dest user modes: %s",dmodes);
1176
1177                 can_change = 0;
1178                 if (user != dest)
1179                 {
1180                         if ((*user->oper) || (is_uline(user->server)))
1181                         {
1182                                 can_change = 1;
1183                         }
1184                 }
1185                 else
1186                 {
1187                         can_change = 1;
1188                 }
1189                 if (!can_change)
1190                 {
1191                         WriteServ(user->fd,"482 %s :Can't change mode for other users",user->nick);
1192                         return;
1193                 }
1194                 
1195                 outpars[0] = *parameters[1];
1196                 outpars[1] = 0;
1197                 direction = (*parameters[1] == '+');
1198
1199                 if ((*parameters[1] != '+') && (*parameters[1] != '-'))
1200                         return;
1201
1202                 for (char* i = parameters[1]; *i; i++)
1203                 {
1204                         if ((i != parameters[1]) && (*i != '+') && (*i != '-'))
1205                                 next_ok = true;
1206
1207                         switch (*i)
1208                         {
1209                                 case ' ':
1210                                 continue;
1211
1212                                 case '+':
1213                                         if ((direction != 1) && (next_ok))
1214                                         {
1215                                                 charlcat(outpars,'+',MAXBUF);
1216                                                 next_ok = false;
1217                                         }       
1218                                         direction = 1;
1219                                 break;
1220
1221                                 case '-':
1222                                         if ((direction != 0) && (next_ok))
1223                                         {
1224                                                 charlcat(outpars,'-',MAXBUF);
1225                                                 next_ok = false;
1226                                         }
1227                                         direction = 0;
1228                                 break;
1229
1230                                 default:
1231                                         can_change = 0;
1232                                         if (*user->oper)
1233                                         {
1234                                                 can_change = 1;
1235                                         }
1236                                         else
1237                                         {
1238                                                 if ((*i == 'i') || (*i == 'w') || (*i == 's') || (ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,direction,false)))
1239                                                 {
1240                                                         can_change = 1;
1241                                                 }
1242                                         }
1243                                         if (can_change)
1244                                         {
1245                                                 if (direction == 1)
1246                                                 {
1247                                                         if ((!strchr(dmodes,*i)) && (ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,true,false)))
1248                                                         {
1249                                                                 if ((ServerInstance->ModeGrok->ProcessModuleUmode(*i, user, dest, direction)) || (*i == 'i') || (*i == 's') || (*i == 'w') || (*i == 'o'))
1250                                                                 {
1251                                                                         charlcat(dmodes,*i,53);
1252                                                                         charlcat(outpars,*i,MAXMODES);
1253                                                                         switch (*i)
1254                                                                         {
1255                                                                                 case 'o':
1256                                                                                         FOREACH_MOD(I_OnGlobalOper,OnGlobalOper(dest));
1257                                                                                 break;
1258                                                                                 case 'i':
1259                                                                                         dest->modebits |= UM_INVISIBLE;
1260                                                                                 break;
1261                                                                                 case 's':
1262                                                                                         dest->modebits |= UM_SERVERNOTICE;
1263                                                                                 break;
1264                                                                                 case 'w':
1265                                                                                         dest->modebits |= UM_WALLOPS;
1266                                                                                 break;
1267                                                                                 default:
1268                                                                                 break;
1269                                                                         }
1270                                                                 }
1271                                                         }
1272                                                 }
1273                                                 else
1274                                                 {
1275                                                         if ((ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,false,false)) && (strchr(dmodes,*i)))
1276                                                         {
1277                                                                 if ((ServerInstance->ModeGrok->ProcessModuleUmode(*i, user, dest, direction)) || (*i == 'i') || (*i == 's') || (*i == 'w') || (*i == 'o'))
1278                                                                 {
1279                                                                         charlcat(outpars,*i,MAXMODES);
1280                                                                         charremove(dmodes,*i);
1281                                                                         switch (*i)
1282                                                                         {
1283                                                                                 case 'o':
1284                                                                                         *dest->oper = 0;
1285                                                                                         DeleteOper(dest);
1286                                                                                 break;
1287                                                                                 case 'i':
1288                                                                                         dest->modebits &= ~UM_INVISIBLE;
1289                                                                                 break;
1290                                                                                 case 's':
1291                                                                                         dest->modebits &= ~UM_SERVERNOTICE;
1292                                                                                 break;
1293                                                                                 case 'w':
1294                                                                                         dest->modebits &= ~UM_WALLOPS;
1295                                                                                 break;
1296                                                                                 default:
1297                                                                                 break;
1298                                                                         }
1299                                                                 }
1300                                                         }
1301                                                 }
1302                                         }
1303                                 break;
1304                         }
1305                 }
1306                 if (*outpars)
1307                 {
1308                         char b[MAXBUF];
1309                         char* z = b;
1310
1311                         for (char* i = outpars; *i;)
1312                         {
1313                                 *z++ = *i++;
1314                                 if (((*i == '-') || (*i == '+')) && ((*(i+1) == '-') || (*(i+1) == '+')))
1315                                 {
1316                                         // someones playing silly buggers and trying
1317                                         // to put a +- or -+ into the line...
1318                                         i++;
1319                                 }
1320                                 if (!*(i+1))
1321                                 {
1322                                         // Someone's trying to make the last character in
1323                                         // the line be a + or - symbol.
1324                                         if ((*i == '-') || (*i == '+'))
1325                                         {
1326                                                 i++;
1327                                         }
1328                                 }
1329                         }
1330                         *z = 0;
1331
1332                         if ((*b) && (!IS_SINGLE(b,'+')) && (!IS_SINGLE(b,'-')))
1333                         {
1334                                 WriteTo(user, dest, "MODE %s :%s", dest->nick, b);
1335                                 FOREACH_MOD(I_OnMode,OnMode(user, dest, TYPE_USER, b));
1336                         }
1337
1338                         log(DEBUG,"Stripped mode line");
1339                         log(DEBUG,"Line dest is now %s",dmodes);
1340                         strlcpy(dest->modes,dmodes,MAXMODES-1);
1341
1342                 }
1343
1344                 return;
1345         }
1346         else
1347         {
1348                 chan = FindChan(parameters[0]);
1349                 if(chan)
1350                 {
1351                         if (pcnt == 1)
1352                         {
1353                                 /* just /modes #channel */
1354                                 WriteServ(user->fd,"324 %s %s +%s",user->nick, chan->name, chanmodes(chan, chan->HasUser(user)));
1355                                 WriteServ(user->fd,"329 %s %s %d", user->nick, chan->name, chan->created);
1356                                 return;
1357                         }
1358                         else if (pcnt == 2)
1359                         {
1360                                 char* mode = parameters[1];
1361                                 
1362                                 MOD_RESULT = 0;
1363                                 
1364                                 if (*mode == '+')
1365                                         mode++;
1366
1367                                 FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, *mode, "", false, 0));
1368                                 if(!MOD_RESULT)
1369                                 {
1370                                         if (*mode == 'b')
1371                                         {
1372                                                 for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++)
1373                                                 {
1374                                                         WriteServ(user->fd,"367 %s %s %s %s %d",user->nick, chan->name, i->data, i->set_by, i->set_time);
1375                                                 }
1376                                                 WriteServ(user->fd,"368 %s %s :End of channel ban list",user->nick, chan->name);
1377                                                 return;
1378                                         }
1379                                         
1380                                         if ((ModeDefined(*mode,MT_CHANNEL)) && (ModeIsListMode(*mode,MT_CHANNEL)))
1381                                         {
1382                                                 // list of items for an extmode
1383                                                 log(DEBUG,"Calling OnSendList for all modules, list output for mode %c",*mode);
1384                                                 FOREACH_MOD(I_OnSendList,OnSendList(user,chan,*mode));
1385                                                 return;
1386                                         }
1387                                 }
1388                         }
1389
1390                         if ((IS_LOCAL(user)) && (!is_uline(user->server)) && (!chan->HasUser(user)))
1391                         {
1392                                 WriteServ(user->fd,"442 %s %s :You're not on that channel!",user->nick, chan->name);
1393                                 return;
1394                         }
1395         
1396                         MOD_RESULT = 0;
1397                         FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user, NULL, chan, AC_GENERAL_MODE));
1398                                 
1399                         if(MOD_RESULT == ACR_DENY)
1400                                 return;
1401
1402                         if(MOD_RESULT == ACR_DEFAULT)
1403                         {
1404                                 if ((IS_LOCAL(user)) && (cstatus(user,chan) < STATUS_HOP))
1405                                 {
1406                                         WriteServ(user->fd,"482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, chan->name);
1407                                         return;
1408                                 }
1409                         }
1410         
1411                         ServerInstance->ModeGrok->ProcessModes(parameters,user,chan,cstatus(user,chan),pcnt,false,false,false);
1412                 }
1413                 else
1414                 {
1415                         WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, parameters[0]);
1416                 }
1417         }
1418 }
1419
1420
1421
1422
1423 void ModeParser::ServerMode(char **parameters, int pcnt, userrec *user)
1424 {
1425         chanrec* Ptr;
1426         userrec* dest = Find(parameters[0]);
1427         int can_change;
1428         int direction = 1;
1429         char outpars[MAXBUF];
1430         bool next_ok = true;
1431
1432         if ((dest) && (pcnt > 1))
1433         {
1434                 std::string tidied = ServerInstance->ModeGrok->CompressModes(parameters[1],false);
1435                 parameters[1] = (char*)tidied.c_str();
1436
1437                 char dmodes[MAXBUF];
1438                 strlcpy(dmodes,dest->modes,MAXBUF);
1439
1440                 outpars[0] = *parameters[1];
1441                 outpars[1] = 0;
1442                 direction = (*parameters[1] == '+');
1443
1444                 if ((*parameters[1] != '+') && (*parameters[1] != '-'))
1445                         return;
1446
1447                 for (char* i = parameters[1]; *i; i++)
1448                 {
1449                         if ((i != parameters[1]) && (*i != '+') && (*i != '-'))
1450                                 next_ok = true;
1451
1452                         switch (*i)
1453                         {
1454                                 case ' ':
1455                                 continue;
1456
1457                                 case '+':
1458                                         if ((direction != 1) && (next_ok))
1459                                         {
1460                                                 next_ok = false;
1461                                                 charlcat(outpars,'+',MAXBUF);
1462                                         }
1463                                         direction = 1;
1464                                 break;
1465
1466                                 case '-':
1467                                         if ((direction != 0) && (next_ok))
1468                                         {
1469                                                 next_ok = false;
1470                                                 charlcat(outpars,'-',MAXBUF);
1471                                         }
1472                                         direction = 0;
1473                                 break;
1474
1475                                 default:
1476                                         log(DEBUG,"begin mode processing entry");
1477                                         can_change = 1;
1478                                         if (can_change)
1479                                         {
1480                                                 if (direction == 1)
1481                                                 {
1482                                                         log(DEBUG,"umode %c being added",*i);
1483                                                         if ((!strchr(dmodes,*i)) && (ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,true,true)))
1484                                                         {
1485                                                                 log(DEBUG,"umode %c is an allowed umode",*i);
1486                                                                 if ((*i == 'i') || (*i == 's') || (*i == 'w') || (*i == 'o') || (ServerInstance->ModeGrok->ProcessModuleUmode(*i, user, dest, direction)))
1487                                                                 {
1488                                                                         charlcat(dmodes,*i,MAXBUF);
1489                                                                         charlcat(outpars,*i,53);
1490                                                                         switch (*i)
1491                                                                         {
1492                                                                                 case 'i':
1493                                                                                         dest->modebits |= UM_INVISIBLE;
1494                                                                                 break;
1495                                                                                 case 's':
1496                                                                                         dest->modebits |= UM_SERVERNOTICE;
1497                                                                                 break;
1498                                                                                 case 'w':
1499                                                                                         dest->modebits |= UM_WALLOPS;
1500                                                                                 break;
1501                                                                                 default:
1502                                                                                 break;
1503                                                                         }
1504                                                                 }
1505                                                         }
1506                                                 }
1507                                                 else
1508                                                 {
1509                                                         // can only remove a mode they already have
1510                                                         log(DEBUG,"umode %c being removed",*i);
1511                                                         if ((ServerInstance->ModeGrok->AllowedUmode(*i,user->modes,false,true)) && (strchr(dmodes,*i)))
1512                                                         {
1513                                                                 log(DEBUG,"umode %c is an allowed umode",*i);
1514                                                                 if ((*i == 'i') || (*i == 's') || (*i == 'w') || (*i == 'o') || (ServerInstance->ModeGrok->ProcessModuleUmode(*i, user, dest, direction)))
1515                                                                 {
1516                                                                         charlcat(outpars,*i,MAXBUF);
1517                                                                         charremove(dmodes,*i);
1518                                                                         switch (*i)
1519                                                                         {
1520                                                                                 case 'i':
1521                                                                                         dest->modebits &= ~UM_INVISIBLE;
1522                                                                                 break;
1523                                                                                 case 's':
1524                                                                                         dest->modebits &= ~UM_SERVERNOTICE;
1525                                                                                 break;
1526                                                                                 case 'w':
1527                                                                                         dest->modebits &= ~UM_WALLOPS;
1528                                                                                 break;
1529                                                                                 default:
1530                                                                                 break;
1531                                                                         }
1532                                                                 }
1533                                                         }
1534                                                 }
1535                                         }
1536                                 break;
1537                         }
1538                 }
1539                 if (*outpars)
1540                 {
1541                         char b[MAXBUF];
1542                         char* z = b;
1543
1544                         for (char* i = outpars; *i;)
1545                         {
1546                                 *z++ = *i++;
1547                                 if (((*i == '-') || (*i == '+')) && ((*(i+1) == '-') || (*(i+1) == '+')))
1548                                 {
1549                                         // someones playing silly buggers and trying
1550                                         // to put a +- or -+ into the line...
1551                                         i++;
1552                                 }
1553                                 if (!*(i+1))
1554                                 {
1555                                         // Someone's trying to make the last character in
1556                                         // the line be a + or - symbol.
1557                                         if ((*i == '-') || (*i == '+'))
1558                                         {
1559                                                 i++;
1560                                         }
1561                                 }
1562                         }
1563                         *z = 0;
1564
1565                         if ((*b) && (!IS_SINGLE(b,'+')) && (!IS_SINGLE(b,'-')))
1566                         {
1567                                 WriteTo(user, dest, "MODE %s :%s", dest->nick, b);
1568                                 FOREACH_MOD(I_OnMode,OnMode(user, dest, TYPE_USER, b));
1569                         }
1570
1571                         log(DEBUG,"Stripped mode line");
1572                         log(DEBUG,"Line dest is now %s",dmodes);
1573                         strlcpy(dest->modes,dmodes,MAXMODES-1);
1574                                          
1575                 }
1576
1577                 return;
1578         }
1579         
1580         Ptr = FindChan(parameters[0]);
1581         if (Ptr)
1582         {
1583                 ServerInstance->ModeGrok->ProcessModes(parameters,user,Ptr,STATUS_OP,pcnt,true,false,false);
1584         }
1585         else
1586         {
1587                 WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, parameters[0]);
1588         }
1589 }