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