]> jfr.im git - irc/quakenet/newserv.git/blob - channel/channelhandlers.c
Initial Import
[irc/quakenet/newserv.git] / channel / channelhandlers.c
1 /* channel.c */
2
3 #include "channel.h"
4 #include "../server/server.h"
5 #include "../nick/nick.h"
6 #include "../lib/irc_string.h"
7 #include "../irc/irc_config.h"
8 #include "../parser/parser.h"
9 #include "../irc/irc.h"
10 #include "../lib/base64.h"
11
12 int handleburstmsg(void *source, int cargc, char **cargv) {
13 channel *cp;
14 time_t timestamp;
15 int wipeout=0;
16 int i;
17 int arg=0;
18 char *charp;
19 int newlimit;
20 int waslimit,waskeyed;
21 char *nextnum;
22 unsigned long currentmode;
23 int isnewchan;
24
25 /* (we don't see the first 2 params in cargc) */
26 /* AK B #+lod+ 1017561154 +tnk eits ATJWu:o,AiW1a,Ag3lV,AiWnl,AE6oI :%*!@D577A90D.kabel.telenet.be */
27
28 if (cargc<2) {
29 Error("channel",ERR_WARNING,"Burst message with only %d parameters",cargc);
30 return CMD_OK;
31 }
32
33 timestamp=strtol(cargv[1],NULL,10);
34
35 if ((cp=findchannel(cargv[0]))==NULL) {
36 /* We don't have this channel already */
37 cp=createchannel(cargv[0]);
38 cp->timestamp=timestamp;
39 isnewchan=1;
40 } else {
41 isnewchan=0;
42 if (timestamp<cp->timestamp) {
43 /* The incoming timestamp is older. Erase all our current channel modes, and the topic. */
44 cp->timestamp=timestamp;
45 freesstring(cp->topic);
46 cp->topic=NULL;
47 cp->topictime=0;
48 freesstring(cp->key);
49 cp->key=NULL;
50 cp->limit=0;
51 cp->flags=0;
52 clearallbans(cp);
53 /* Remove all +v, +o we currently have */
54 for(i=0;i<cp->users->hashsize;i++) {
55 if (cp->users->content[i]!=nouser) {
56 cp->users->content[i]&=CU_NUMERICMASK;
57 }
58 }
59 } else if (timestamp>cp->timestamp) {
60 /* The incoming timestamp is greater. Ignore any incoming modes they may happen to set */
61 wipeout=1;
62 }
63 }
64
65 /* OK, dealt with the name and timestamp.
66 * Loop over the remaining args */
67 for (arg=2;arg<cargc;arg++) {
68 if (cargv[arg][0]=='+') {
69 /* Channel modes */
70 if (wipeout) {
71 /* We ignore the modes, but we need to see if they include +l or +k
72 * so that we can ignore their corresponding values */
73 for (charp=cargv[arg];*charp;charp++) {
74 if (*charp=='k' || *charp=='l') {
75 arg++;
76 }
77 }
78 } else {
79 /* Clear off the limit and key flags before calling setflags so we can see if the burst tried to set them */
80 /* If the burst doesn't set them, we restore them afterwards */
81 waslimit=IsLimit(cp); ClearLimit(cp);
82 waskeyed=IsKey(cp); ClearKey(cp);
83 /* We can then use the flag function for these modes */
84 setflags(&(cp->flags),CHANMODE_ALL,cargv[arg],cmodeflags,REJECT_NONE);
85 /* Pick up the limit and key, if they were set. Note that the limit comes first */
86 if (IsLimit(cp)) { /* A limit was SET by the burst */
87 if (++arg>=cargc) {
88 /* Ran out of args -- damn ircd is spewing out crap again */
89 Error("channel",ERR_WARNING,"Burst +l with no argument");
90 break; /* "break" being the operative word */
91 } else {
92 newlimit=strtol(cargv[arg],NULL,10);
93 }
94 if (cp->limit>0 && waslimit) {
95 /* We had a limit before -- we now have the lowest one of the two */
96 if (newlimit<cp->limit) {
97 cp->limit=newlimit;
98 }
99 } else {
100 /* No limit before -- we just have the new one */
101 cp->limit=newlimit;
102 }
103 } else if (waslimit) {
104 SetLimit(cp); /* We had a limit before, but the burst didn't set one. Restore flag. */
105 }
106
107 if (IsKey(cp)) { /* A key was SET by the burst */
108 if (++arg>=cargc) {
109 /* Ran out of args -- oopsie! */
110 Error("channel",ERR_WARNING,"Burst +k with no argument");
111 break;
112 }
113 if (waskeyed) {
114 /* We had a key before -- alphabetically first wins */
115 if (ircd_strcmp(cargv[arg],cp->key->content)<0) {
116 /* Replace our key */
117 freesstring(cp->key);
118 cp->key=getsstring(cargv[arg],KEYLEN);
119 }
120 } else {
121 /* No key before -- just the new one */
122 cp->key=getsstring(cargv[arg],KEYLEN);
123 }
124 } else if (waskeyed) {
125 SetKey(cp); /* We had a key before, but the burst didn't set one. Restore flag. */
126 }
127 }
128 } else if (cargv[arg][0]=='%') {
129 /* We have one or more bans here */
130 nextnum=cargv[arg]+1;
131 while (*nextnum) {
132 /* Split off the next ban */
133 for (charp=nextnum;*charp;charp++) {
134 if (*charp==' ') {
135 *charp='\0';
136 charp++;
137 break;
138 }
139 }
140 setban(cp,nextnum);
141 nextnum=charp;
142 }
143 } else {
144 /* List of numerics */
145 nextnum=charp=cargv[arg];
146 currentmode=0;
147 while (*nextnum!='\0') {
148 /* Step over the next numeric */
149 for (i=0;i<5;i++) {
150 if (*charp++=='\0')
151 break;
152 }
153 if (i<5) {
154 break;
155 }
156 if (*charp==',') {
157 *charp='\0';
158 charp++;
159 } else if (*charp==':') {
160 *charp='\0';
161 charp++;
162 currentmode=0;
163 /* Look for modes */
164 for (;*charp;charp++) {
165 if (*charp=='v') {
166 currentmode|=CUMODE_VOICE;
167 } else if (*charp=='o') {
168 currentmode|=CUMODE_OP;
169 } else if (*charp==',') {
170 charp++;
171 break;
172 }
173 }
174 /* If we're ignore incoming modes, zap it to zero again */
175 if (wipeout) {
176 currentmode=0;
177 }
178 }
179 /* OK. At this point charp points to either '\0' if we're at the end,
180 * or the start of the next numeric otherwise. nextnum points at a valid numeric
181 * we need to add, and currentmode reflects the correct mode */
182 addnicktochannel(cp,(numerictolong(nextnum,5)|currentmode));
183 nextnum=charp;
184 }
185 }
186 }
187 if (cp->users->totalusers==0) {
188 /* Oh dear, the channel is now empty. Perhaps one of those
189 * charming empty burst messages you get sometimes.. */
190 if (!isnewchan) {
191 /* I really don't think this can happen, can it..? */
192 /* Only send the LOSTCHANNEL if the channel existed before */
193 triggerhook(HOOK_CHANNEL_LOSTCHANNEL,cp);
194 }
195 delchannel(cp);
196 } else {
197 /* If this is a new channel, we do the NEWCHANNEL hook also */
198 if (isnewchan) {
199 triggerhook(HOOK_CHANNEL_NEWCHANNEL,cp);
200 }
201 /* Just one hook to say "something happened to this channel" */
202 triggerhook(HOOK_CHANNEL_BURST,cp);
203 }
204
205 return CMD_OK;
206 }
207
208 int handlejoinmsg(void *source, int cargc, char **cargv) {
209 char *pos,*nextchan;
210 nick *np;
211 void *harg[2];
212 channel *cp,**ch;
213 long timestamp=0;
214 int i;
215 int newchan=0;
216
217 if (cargc<1) {
218 return CMD_OK;
219 }
220
221 if (cargc>1) {
222 /* We must have received a timestamp too */
223 timestamp=strtol(cargv[1],NULL,10);
224 }
225
226 /* Find out who we are talking about here */
227 np=getnickbynumericstr(source);
228 if (np==NULL) {
229 Error("channel",ERR_WARNING,"Channel join from non existent user %s",source);
230 return CMD_OK;
231 }
232
233 nextchan=pos=cargv[0];
234 while (*nextchan!='\0') {
235 /* Find the next chan position */
236 for (;*pos!='\0' && *pos!=',';pos++)
237 ; /* Empty loop */
238
239 if (*pos==',') {
240 *pos='\0';
241 pos++;
242 }
243
244 /* OK, pos now points at either null or the next chan
245 * and nextchan now points at the channel name we want to parse next */
246
247 if(nextchan[0]=='0' && nextchan[1]=='\0') {
248 /* Leave all channels
249 * We do this as if they were leaving the network,
250 * then vape their channels array and start again */
251 ch=(channel **)(np->channels->content);
252 for(i=0;i<np->channels->cursi;i++) {
253 /* Send hook */
254 harg[0]=ch[i];
255 harg[1]=np;
256 triggerhook(HOOK_CHANNEL_PART,harg);
257 delnickfromchannel(ch[i],np->numeric,0);
258 }
259 array_free(np->channels);
260 array_init(np->channels,sizeof(channel *));
261 } else {
262 /* It's an actual channel join */
263 newchan=0;
264 if ((cp=findchannel(nextchan))==NULL) {
265 /* User joined non-existent channel - create it. Note that createchannel automatically
266 * puts the right magic timestamp in for us */
267 Error("channel",ERR_DEBUG,"User %s joined non existent channel %s.",np->nick,nextchan);
268 cp=createchannel(nextchan);
269 newchan=1;
270 }
271 if (cp->timestamp==MAGIC_REMOTE_JOIN_TS && timestamp) {
272 /* No valid timestamp on the chan and we received one -- set */
273 cp->timestamp=timestamp;
274 }
275 /* OK, this is slightly inefficient since we turn the nick * into a numeric,
276 * and addnicktochannel then converts it back again. BUT it's fewer lines of code :) */
277 if (addnicktochannel(cp,np->numeric)) {
278 /* The user wasn't added */
279 if (newchan) {
280 delchannel(cp);
281 }
282 } else {
283 /* If we just created a channel, flag it */
284 if (newchan) {
285 triggerhook(HOOK_CHANNEL_NEWCHANNEL,cp);
286 }
287
288 /* send hook */
289 harg[0]=cp;
290 harg[1]=np;
291 triggerhook(HOOK_CHANNEL_JOIN,harg);
292 }
293 }
294 nextchan=pos;
295 }
296 return CMD_OK;
297 }
298
299 int handlecreatemsg(void *source, int cargc, char **cargv) {
300 char *pos,*nextchan;
301 nick *np;
302 channel *cp;
303 long timestamp=0;
304 int newchan=1;
305 void *harg[2];
306
307 if (cargc<2) {
308 return CMD_OK;
309 }
310
311 timestamp=strtol(cargv[1],NULL,10);
312
313 /* Find out who we are talking about here */
314 np=getnickbynumericstr(source);
315 if (np==NULL) {
316 Error("channel",ERR_WARNING,"Channel create from non existent user %s",source);
317 return CMD_OK;
318 }
319
320 nextchan=pos=cargv[0];
321 while (*nextchan!='\0') {
322 /* Find the next chan position */
323 for (;*pos!='\0' && *pos!=',';pos++)
324 ; /* Empty loop */
325
326 if (*pos==',') {
327 *pos='\0';
328 pos++;
329 }
330
331 /* OK, pos now points at either null or the next chan
332 * and nextchan now points at the channel name we want to parse next */
333
334 /* It's a channel create */
335 if ((cp=findchannel(nextchan))==NULL) {
336 /* This is the expected case -- the channel didn't exist before */
337 cp=createchannel(nextchan);
338 cp->timestamp=timestamp;
339 newchan=1;
340 } else {
341 Error("channel",ERR_DEBUG,"Received CREATE for already existing channel %s",cp->index->name->content);
342 if (cp->timestamp==MAGIC_REMOTE_JOIN_TS && timestamp) {
343 /* No valid timestamp on the chan and we received one -- set */
344 cp->timestamp=timestamp;
345 }
346 newchan=0;
347 }
348 /* Add the user to the channel, preopped */
349 if (addnicktochannel(cp,(np->numeric)|CUMODE_OP)) {
350 if (newchan) {
351 delchannel(cp);
352 }
353 } else {
354 /* Flag the channel as new if necessary */
355 if (newchan) {
356 triggerhook(HOOK_CHANNEL_NEWCHANNEL,cp);
357 }
358
359 /* Trigger hook */
360 harg[0]=cp;
361 harg[1]=np;
362 triggerhook(HOOK_CHANNEL_CREATE,harg);
363 }
364 nextchan=pos;
365 }
366
367 return CMD_OK;
368 }
369
370 int handlepartmsg(void *source, int cargc, char **cargv) {
371 char *pos,*nextchan;
372 nick *np;
373 channel *cp;
374 void *harg[3];
375
376 if (cargc<1) {
377 return CMD_OK;
378 }
379
380 if (cargc>2) {
381 Error("channel",ERR_WARNING,"PART with too many parameters (%d)",cargc);
382 }
383
384 if (cargc>1) {
385 harg[2]=(void *)cargv[1];
386 } else {
387 harg[2]=(void *)"";
388 }
389
390 /* Find out who we are talking about here */
391 np=getnickbynumericstr(source);
392 if (np==NULL) {
393 Error("channel",ERR_WARNING,"PART from non existent numeric %s",source);
394 return CMD_OK;
395 }
396
397 harg[1]=np;
398
399 nextchan=pos=cargv[0];
400 while (*nextchan!='\0') {
401 /* Find the next chan position */
402 for (;*pos!='\0' && *pos!=',';pos++)
403 ; /* Empty loop */
404
405 if (*pos==',') {
406 *pos='\0';
407 pos++;
408 }
409
410 /* OK, pos now points at either null or the next chan
411 * and nextchan now points at the channel name we want to parse next */
412
413 if ((cp=findchannel(nextchan))==NULL) {
414 /* Erm, parting a channel that's not there?? */
415 Error("channel",ERR_WARNING,"Nick %s left non-existent channel %s",np->nick,nextchan);
416 } else {
417 /* Trigger hook *FIRST* */
418 harg[0]=cp;
419 triggerhook(HOOK_CHANNEL_PART,harg);
420
421 delnickfromchannel(cp,np->numeric,1);
422 }
423 nextchan=pos;
424 }
425
426 return CMD_OK;
427 }
428
429 int handlekickmsg(void *source, int cargc, char **cargv) {
430 nick *np,*kicker;
431 channel *cp;
432 void *harg[4];
433
434 if (cargc<3) {
435 return CMD_OK;
436 }
437
438 /* Find out who we are talking about here */
439 if ((np=getnickbynumericstr(cargv[1]))==NULL) {
440 Error("channel",ERR_DEBUG,"Non-existant numeric %s kicked from channel %s",source,cargv[0]);
441 return CMD_OK;
442 }
443
444 /* And who did the kicking */
445 if (((char *)source)[2]=='\0') {
446 /* 'Twas a server.. */
447 kicker=NULL;
448 } else if ((kicker=getnickbynumericstr((char *)source))==NULL) {
449 /* It looks strange, but we let the kick go through anyway */
450 Error("channel",ERR_DEBUG,"Kick from non-existant nick %s",(char *)source);
451 }
452
453 /* And find out which channel */
454 if ((cp=findchannel(cargv[0]))==NULL) {
455 /* OK, not a channel that actually exists then.. */
456 Error("channel",ERR_DEBUG,"Nick %s kicked from non-existent channel %s",np->nick,cargv[0]);
457 } else {
458 /* Before we do anything else, we have to acknowledge the kick to the network */
459 if (homeserver(np->numeric)==mylongnum) {
460 irc_send("%s L %s",longtonumeric(np->numeric,5),cp->index->name->content);
461 }
462
463 /* Trigger hook *FIRST* */
464 harg[0]=cp;
465 harg[1]=np;
466 harg[2]=kicker;
467 harg[3]=cargv[2];
468 triggerhook(HOOK_CHANNEL_KICK,harg);
469
470 /* We let delnickfromchannel() worry about whether this nick is actually on this channel */
471 delnickfromchannel(cp,np->numeric,1);
472 }
473
474 return CMD_OK;
475 }
476
477 int handletopicmsg(void *source, int cargc, char **cargv) {
478 channel *cp;
479 nick *np;
480 void *harg[2];
481 time_t topictime=0, timestamp=0;
482
483 if (cargc<2) {
484 return CMD_OK;
485 }
486
487 if (cargc>2)
488 topictime=strtol(cargv[cargc-2], NULL, 10);
489
490 if (cargc>3)
491 timestamp=strtol(cargv[cargc-3], NULL, 10);
492
493 if ((np=getnickbynumericstr((char *)source))==NULL) {
494 /* We should check the sender exists, but we still change the topic even if it doesn't */
495 Error("channel",ERR_WARNING,"Topic change by non-existent user %s",(char *)source);
496 }
497
498 /* Grab channel pointer */
499 if ((cp=findchannel(cargv[0]))==NULL) {
500 /* We're not going to create a channel for the sake of a topic.. */
501 return CMD_OK;
502 } else {
503 if (timestamp && (cp->timestamp < timestamp)) {
504 /* Ignore topic change for younger channel
505 * (note that topic change for OLDER channel should be impossible!) */
506 return CMD_OK;
507 }
508 if (topictime && (cp->topictime > topictime)) {
509 /* Ignore topic change with older topic */
510 return CMD_OK;
511 }
512 if (cp->topic!=NULL) {
513 freesstring(cp->topic);
514 }
515 if (cargv[cargc-1][0]=='\0') {
516 cp->topic=NULL;
517 cp->topictime=0;
518 } else {
519 cp->topic=getsstring(cargv[cargc-1],TOPICLEN);
520 cp->topictime=topictime?topictime:getnettime();
521 }
522 /* Trigger hook */
523 harg[0]=cp;
524 harg[1]=np;
525 triggerhook(HOOK_CHANNEL_TOPIC,harg);
526 }
527
528 return CMD_OK;
529 }
530
531 /*
532 * Note that this function is also used for processing OPMODE messages.
533 * There is no checking on the source etc. anyway, so this should be OK.
534 * (we are trusting ircd not to feed us bogus modes)
535 */
536
537 int handlemodemsg(void *source, int cargc, char **cargv) {
538 channel *cp;
539 int dir=1;
540 int arg=2;
541 char *modestr;
542 unsigned long *lp;
543 void *harg[3];
544 nick *np, *target;
545 int hooknum;
546 int changes=0;
547
548 if (cargc<2) {
549 return CMD_OK;
550 }
551
552 if (cargv[0][0]!='#' && cargv[0][0]!='+') {
553 /* Not a channel, ignore */
554 return CMD_OK;
555 }
556
557 if ((cp=findchannel(cargv[0]))==NULL) {
558 /* No channel, abort */
559 Error("channel",ERR_WARNING,"Mode change on non-existent channel %s",cargv[0]);
560 return CMD_OK;
561 }
562
563 if (((char *)source)[2]=='\0') {
564 /* Server mode change, treat as divine intervention */
565 np=NULL;
566 } else if ((np=getnickbynumericstr((char *)source))==NULL) {
567 /* No sender, continue but moan */
568 Error("channel",ERR_WARNING,"Mode change by non-existent user %s on channel %s",(char *)source,cp->index->name->content);
569 }
570
571 /* Set up the hook data */
572 harg[0]=cp;
573 harg[1]=np;
574
575 /* Process the mode string one character at a time */
576 /* Maybe I'll write this more intelligently one day if I can comprehend the ircu code that does this */
577 for (modestr=cargv[1];*modestr;modestr++) {
578 switch(*modestr) {
579 /* Set whether we are adding or removing modes */
580
581 case '+':
582 dir=1;
583 break;
584
585 case '-':
586 dir=0;
587 break;
588
589 /* Simple modes: just set or clear based on value of dir */
590
591 case 'n':
592 if (dir) { SetNoExtMsg(cp); } else { ClearNoExtMsg(cp); }
593 changes |= MODECHANGE_MODES;
594 break;
595
596 case 't':
597 if (dir) { SetTopicLimit(cp); } else { ClearTopicLimit(cp); }
598 changes |= MODECHANGE_MODES;
599 break;
600
601 case 's':
602 if (dir) { SetSecret(cp); ClearPrivate(cp); } else { ClearSecret(cp); }
603 changes |= MODECHANGE_MODES;
604 break;
605
606 case 'p':
607 if (dir) { SetPrivate(cp); ClearSecret(cp); } else { ClearPrivate(cp); }
608 changes |= MODECHANGE_MODES;
609 break;
610
611 case 'i':
612 if (dir) { SetInviteOnly(cp); } else { ClearInviteOnly(cp); }
613 changes |= MODECHANGE_MODES;
614 break;
615
616 case 'm':
617 if (dir) { SetModerated(cp); } else { ClearModerated(cp); }
618 changes |= MODECHANGE_MODES;
619 break;
620
621 case 'c':
622 if (dir) { SetNoColour(cp); } else { ClearNoColour(cp); }
623 changes |= MODECHANGE_MODES;
624 break;
625
626 case 'C':
627 if (dir) { SetNoCTCP(cp); } else { ClearNoCTCP(cp); }
628 changes |= MODECHANGE_MODES;
629 break;
630
631 case 'r':
632 if (dir) { SetRegOnly(cp); } else { ClearRegOnly(cp); }
633 changes |= MODECHANGE_MODES;
634 break;
635
636 case 'D':
637 if (dir) { SetDelJoins(cp); } else { ClearDelJoins(cp); }
638 changes |= MODECHANGE_MODES;
639 break;
640
641 case 'u':
642 if (dir) { SetNoQuitMsg(cp); } else { ClearNoQuitMsg(cp); }
643 changes |= MODECHANGE_MODES;
644 break;
645
646 case 'N':
647 if (dir) { SetNoNotice(cp); } else { ClearNoNotice(cp); }
648 changes |= MODECHANGE_MODES;
649 break;
650
651 /* Parameter modes: advance parameter and possibly read it in */
652
653 case 'l':
654 if (dir) {
655 /* +l uses a parameter, but -l does not.
656 * If there is no parameter, don't set the mode.
657 * I guess we should moan too in that case, but
658 * they might be even nastier to us if we do ;) */
659 if (arg<cargc) {
660 cp->limit=strtol(cargv[arg++],NULL,10);
661 SetLimit(cp);
662 }
663 } else {
664 ClearLimit(cp);
665 cp->limit=0;
666 }
667 changes |= MODECHANGE_MODES;
668 break;
669
670 case 'k':
671 if (dir) {
672 /* +k uses a parameter in both directions */
673 if (arg<cargc) {
674 freesstring(cp->key); /* It's probably NULL, but be safe */
675 cp->key=getsstring(cargv[arg++],KEYLEN);
676 SetKey(cp);
677 }
678 } else {
679 freesstring(cp->key);
680 cp->key=NULL;
681 ClearKey(cp);
682 arg++; /* Eat the arg without looking at it, even if it's not there */
683 }
684 changes |= MODECHANGE_MODES;
685 break;
686
687 /* Op/Voice */
688
689 case 'o':
690 case 'v':
691 if (arg<cargc) {
692 if((lp=getnumerichandlefromchanhash(cp->users,numerictolong(cargv[arg++],5)))==NULL) {
693 /* They're not on the channel; MODE crossed with part/kill/kick/blah */
694 Error("channel",ERR_DEBUG,"Mode change for user %s not on channel %s",cargv[arg-1],cp->index->name->content);
695 } else {
696 if ((target=getnickbynumeric(*lp))==NULL) {
697 /* This really is a fuckup, we found them on the channel but there isn't a user with that numeric */
698 /* This means there's a serious bug in the nick/channel tracking code */
699 Error("channel",ERR_ERROR,"Mode change for user %s on channel %s who doesn't exist",cargv[arg-1],cp->index->name->content);
700 } else { /* Do the mode change whilst admiring the beautiful code layout */
701 harg[2]=target;
702 if (*modestr=='o') { if (dir) { *lp |= CUMODE_OP; hooknum=HOOK_CHANNEL_OPPED; } else
703 { *lp &= ~CUMODE_OP; hooknum=HOOK_CHANNEL_DEOPPED; } }
704 else { if (dir) { *lp |= CUMODE_VOICE; hooknum=HOOK_CHANNEL_VOICED; } else
705 { *lp &= ~CUMODE_VOICE; hooknum=HOOK_CHANNEL_DEVOICED; } }
706 triggerhook(hooknum,harg);
707 }
708 }
709 }
710 changes |= MODECHANGE_USERS;
711 break;
712
713 case 'b':
714 if (arg<cargc) {
715 if (dir) {
716 setban(cp,cargv[arg++]);
717 triggerhook(HOOK_CHANNEL_BANSET,harg);
718 } else {
719 clearban(cp,cargv[arg++],0);
720 triggerhook(HOOK_CHANNEL_BANCLEAR,harg);
721 }
722 }
723 changes |= MODECHANGE_BANS;
724 break;
725
726 default:
727 Error("channel",ERR_DEBUG,"Unknown mode char '%c' %s on %s",*modestr,dir?"set":"cleared",cp->index->name->content);
728 break;
729 }
730 }
731
732 harg[2]=(void *)changes;
733 triggerhook(HOOK_CHANNEL_MODECHANGE,(void *)harg);
734 return CMD_OK;
735 }
736
737 /*
738 * Deal with 2.10.11ism: CLEARMODE
739 *
740 * [hAAA CM #twilightzone ovpsmikbl
741 */
742
743 int handleclearmodemsg(void *source, int cargc, char **cargv) {
744 channel *cp;
745 void *harg[3];
746 nick *np, *target;
747 char *mcp;
748 unsigned long usermask=0;
749 int i;
750 int changes=0;
751
752 if (cargc<2) {
753 return CMD_OK;
754 }
755
756 if ((cp=findchannel(cargv[0]))==NULL) {
757 /* No channel, abort */
758 Error("channel",ERR_WARNING,"Mode change on non-existent channel %s",cargv[0]);
759 return CMD_OK;
760 }
761
762 if (((char *)source)[2]=='\0') {
763 /* Server mode change? (I don't think servers are allowed to do CLEARMODE) */
764 np=NULL;
765 } else if ((np=getnickbynumericstr((char *)source))==NULL) {
766 /* No sender, continue but moan */
767 Error("channel",ERR_WARNING,"Mode change by non-existent user %s on channel %s",(char *)source,cp->index->name->content);
768 }
769
770 harg[0]=cp;
771 harg[1]=np;
772
773 for (mcp=cargv[1];*mcp;mcp++) {
774 switch (*mcp) {
775 case 'o':
776 usermask |= CUMODE_OP;
777 changes |= MODECHANGE_USERS;
778 break;
779
780 case 'v':
781 usermask |= CUMODE_VOICE;
782 changes |= MODECHANGE_USERS;
783 break;
784
785 case 'n':
786 ClearNoExtMsg(cp);
787 changes |= MODECHANGE_MODES;
788 break;
789
790 case 't':
791 ClearTopicLimit(cp);
792 changes |= MODECHANGE_MODES;
793 break;
794
795 case 's':
796 ClearSecret(cp);
797 changes |= MODECHANGE_MODES;
798 break;
799
800 case 'p':
801 ClearPrivate(cp);
802 changes |= MODECHANGE_MODES;
803 break;
804
805 case 'i':
806 ClearInviteOnly(cp);
807 changes |= MODECHANGE_MODES;
808 break;
809
810 case 'm':
811 ClearModerated(cp);
812 changes |= MODECHANGE_MODES;
813 break;
814
815 case 'c':
816 ClearNoColour(cp);
817 changes |= MODECHANGE_MODES;
818 break;
819
820 case 'C':
821 ClearNoCTCP(cp);
822 changes |= MODECHANGE_MODES;
823 break;
824
825 case 'r':
826 ClearRegOnly(cp);
827 changes |= MODECHANGE_MODES;
828 break;
829
830 case 'D':
831 ClearDelJoins(cp);
832 changes |= MODECHANGE_MODES;
833 break;
834
835 case 'u':
836 ClearNoQuitMsg(cp);
837 changes |= MODECHANGE_MODES;
838 break;
839
840 case 'N':
841 ClearNoNotice(cp);
842 changes |= MODECHANGE_MODES;
843 break;
844
845 case 'b':
846 clearallbans(cp);
847 changes |= MODECHANGE_BANS;
848 break;
849
850 case 'k':
851 /* This is all safe even if there is no key atm */
852 freesstring(cp->key);
853 cp->key=NULL;
854 ClearKey(cp);
855 changes |= MODECHANGE_MODES;
856 break;
857
858 case 'l':
859 cp->limit=0;
860 ClearLimit(cp);
861 changes |= MODECHANGE_MODES;
862 break;
863 }
864 }
865
866 if (usermask) {
867 /* We have to strip something off each user */
868 for (i=0;i<cp->users->hashsize;i++) {
869 if (cp->users->content[i]!=nouser && (cp->users->content[i] & usermask)) {
870 /* This user exists and has at least one of the modes we're clearing */
871 if ((target=getnickbynumeric(cp->users->content[i]))==NULL) {
872 /* This really is a fuckup, we found them on the channel but there isn't a user with that numeric */
873 /* This means there's a serious bug in the nick/channel tracking code */
874 Error("channel",ERR_ERROR,"CLEARMODE failed: user on channel who doesn't exist?");
875 } else {
876 harg[2]=target;
877 /* Yes, these are deliberate three way bitwise ANDs.. */
878 if (cp->users->content[i] & usermask & CUMODE_OP)
879 triggerhook(HOOK_CHANNEL_DEOPPED, harg);
880 if (cp->users->content[i] & usermask & CUMODE_VOICE)
881 triggerhook(HOOK_CHANNEL_DEVOICED, harg);
882 cp->users->content[i] &= ~usermask;
883 }
884 }
885 }
886 }
887
888 harg[2]=(void *)changes;
889 triggerhook(HOOK_CHANNEL_MODECHANGE, harg);
890 return CMD_OK;
891 }