]>
jfr.im git - irc/quakenet/newserv.git/blob - channel/channelhandlers.c
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"
12 int handleburstmsg(void *source
, int cargc
, char **cargv
) {
20 int waslimit
,waskeyed
;
22 unsigned long currentmode
;
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 */
29 Error("channel",ERR_WARNING
,"Burst message with only %d parameters",cargc
);
33 timestamp
=strtol(cargv
[1],NULL
,10);
35 if ((cp
=findchannel(cargv
[0]))==NULL
) {
36 /* We don't have this channel already */
37 cp
=createchannel(cargv
[0]);
38 cp
->timestamp
=timestamp
;
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
);
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
;
59 } else if (timestamp
>cp
->timestamp
) {
60 /* The incoming timestamp is greater. Ignore any incoming modes they may happen to set */
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]=='+') {
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') {
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 */
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 */
92 newlimit
=strtol(cargv
[arg
],NULL
,10);
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
) {
100 /* No limit before -- we just have the new one */
103 } else if (waslimit
) {
104 SetLimit(cp
); /* We had a limit before, but the burst didn't set one. Restore flag. */
107 if (IsKey(cp
)) { /* A key was SET by the burst */
109 /* Ran out of args -- oopsie! */
110 Error("channel",ERR_WARNING
,"Burst +k with no argument");
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
);
121 /* No key before -- just the new one */
122 cp
->key
=getsstring(cargv
[arg
],KEYLEN
);
124 } else if (waskeyed
) {
125 SetKey(cp
); /* We had a key before, but the burst didn't set one. Restore flag. */
128 } else if (cargv
[arg
][0]=='%') {
129 /* We have one or more bans here */
130 nextnum
=cargv
[arg
]+1;
132 /* Split off the next ban */
133 for (charp
=nextnum
;*charp
;charp
++) {
144 /* List of numerics */
145 nextnum
=charp
=cargv
[arg
];
147 while (*nextnum
!='\0') {
148 /* Step over the next numeric */
159 } else if (*charp
==':') {
164 for (;*charp
;charp
++) {
166 currentmode
|=CUMODE_VOICE
;
167 } else if (*charp
=='o') {
168 currentmode
|=CUMODE_OP
;
169 } else if (*charp
==',') {
174 /* If we're ignore incoming modes, zap it to zero again */
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
));
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.. */
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
);
197 /* If this is a new channel, we do the NEWCHANNEL hook also */
199 triggerhook(HOOK_CHANNEL_NEWCHANNEL
,cp
);
201 /* Just one hook to say "something happened to this channel" */
202 triggerhook(HOOK_CHANNEL_BURST
,cp
);
208 int handlejoinmsg(void *source
, int cargc
, char **cargv
) {
222 /* We must have received a timestamp too */
223 timestamp
=strtol(cargv
[1],NULL
,10);
226 /* Find out who we are talking about here */
227 np
=getnickbynumericstr(source
);
229 Error("channel",ERR_WARNING
,"Channel join from non existent user %s",source
);
233 nextchan
=pos
=cargv
[0];
234 while (*nextchan
!='\0') {
235 /* Find the next chan position */
236 for (;*pos
!='\0' && *pos
!=',';pos
++)
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 */
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
++) {
256 triggerhook(HOOK_CHANNEL_PART
,harg
);
257 delnickfromchannel(ch
[i
],np
->numeric
,0);
259 array_free(np
->channels
);
260 array_init(np
->channels
,sizeof(channel
*));
262 /* It's an actual channel join */
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
);
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
;
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 */
283 /* If we just created a channel, flag it */
285 triggerhook(HOOK_CHANNEL_NEWCHANNEL
,cp
);
291 triggerhook(HOOK_CHANNEL_JOIN
,harg
);
299 int handlecreatemsg(void *source
, int cargc
, char **cargv
) {
311 timestamp
=strtol(cargv
[1],NULL
,10);
313 /* Find out who we are talking about here */
314 np
=getnickbynumericstr(source
);
316 Error("channel",ERR_WARNING
,"Channel create from non existent user %s",source
);
320 nextchan
=pos
=cargv
[0];
321 while (*nextchan
!='\0') {
322 /* Find the next chan position */
323 for (;*pos
!='\0' && *pos
!=',';pos
++)
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 */
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
;
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
;
348 /* Add the user to the channel, preopped */
349 if (addnicktochannel(cp
,(np
->numeric
)|CUMODE_OP
)) {
354 /* Flag the channel as new if necessary */
356 triggerhook(HOOK_CHANNEL_NEWCHANNEL
,cp
);
362 triggerhook(HOOK_CHANNEL_CREATE
,harg
);
370 int handlepartmsg(void *source
, int cargc
, char **cargv
) {
381 Error("channel",ERR_WARNING
,"PART with too many parameters (%d)",cargc
);
385 harg
[2]=(void *)cargv
[1];
390 /* Find out who we are talking about here */
391 np
=getnickbynumericstr(source
);
393 Error("channel",ERR_WARNING
,"PART from non existent numeric %s",source
);
399 nextchan
=pos
=cargv
[0];
400 while (*nextchan
!='\0') {
401 /* Find the next chan position */
402 for (;*pos
!='\0' && *pos
!=',';pos
++)
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 */
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
);
417 /* Trigger hook *FIRST* */
419 triggerhook(HOOK_CHANNEL_PART
,harg
);
421 delnickfromchannel(cp
,np
->numeric
,1);
429 int handlekickmsg(void *source
, int cargc
, char **cargv
) {
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]);
444 /* And who did the kicking */
445 if (((char *)source
)[2]=='\0') {
446 /* 'Twas a server.. */
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
);
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]);
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
);
463 /* Trigger hook *FIRST* */
468 triggerhook(HOOK_CHANNEL_KICK
,harg
);
470 /* We let delnickfromchannel() worry about whether this nick is actually on this channel */
471 delnickfromchannel(cp
,np
->numeric
,1);
477 int handletopicmsg(void *source
, int cargc
, char **cargv
) {
481 time_t topictime
=0, timestamp
=0;
488 topictime
=strtol(cargv
[cargc
-2], NULL
, 10);
491 timestamp
=strtol(cargv
[cargc
-3], NULL
, 10);
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
);
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.. */
503 if (timestamp
&& (cp
->timestamp
< timestamp
)) {
504 /* Ignore topic change for younger channel
505 * (note that topic change for OLDER channel should be impossible!) */
508 if (topictime
&& (cp
->topictime
> topictime
)) {
509 /* Ignore topic change with older topic */
512 if (cp
->topic
!=NULL
) {
513 freesstring(cp
->topic
);
515 if (cargv
[cargc
-1][0]=='\0') {
519 cp
->topic
=getsstring(cargv
[cargc
-1],TOPICLEN
);
520 cp
->topictime
=topictime
?topictime
:getnettime();
525 triggerhook(HOOK_CHANNEL_TOPIC
,harg
);
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)
537 int handlemodemsg(void *source
, int cargc
, char **cargv
) {
552 if (cargv
[0][0]!='#' && cargv
[0][0]!='+') {
553 /* Not a channel, ignore */
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]);
563 if (((char *)source
)[2]=='\0') {
564 /* Server mode change, treat as divine intervention */
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
);
571 /* Set up the hook data */
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
++) {
579 /* Set whether we are adding or removing modes */
589 /* Simple modes: just set or clear based on value of dir */
592 if (dir
) { SetNoExtMsg(cp
); } else { ClearNoExtMsg(cp
); }
593 changes
|= MODECHANGE_MODES
;
597 if (dir
) { SetTopicLimit(cp
); } else { ClearTopicLimit(cp
); }
598 changes
|= MODECHANGE_MODES
;
602 if (dir
) { SetSecret(cp
); ClearPrivate(cp
); } else { ClearSecret(cp
); }
603 changes
|= MODECHANGE_MODES
;
607 if (dir
) { SetPrivate(cp
); ClearSecret(cp
); } else { ClearPrivate(cp
); }
608 changes
|= MODECHANGE_MODES
;
612 if (dir
) { SetInviteOnly(cp
); } else { ClearInviteOnly(cp
); }
613 changes
|= MODECHANGE_MODES
;
617 if (dir
) { SetModerated(cp
); } else { ClearModerated(cp
); }
618 changes
|= MODECHANGE_MODES
;
622 if (dir
) { SetNoColour(cp
); } else { ClearNoColour(cp
); }
623 changes
|= MODECHANGE_MODES
;
627 if (dir
) { SetNoCTCP(cp
); } else { ClearNoCTCP(cp
); }
628 changes
|= MODECHANGE_MODES
;
632 if (dir
) { SetRegOnly(cp
); } else { ClearRegOnly(cp
); }
633 changes
|= MODECHANGE_MODES
;
637 if (dir
) { SetDelJoins(cp
); } else { ClearDelJoins(cp
); }
638 changes
|= MODECHANGE_MODES
;
642 if (dir
) { SetNoQuitMsg(cp
); } else { ClearNoQuitMsg(cp
); }
643 changes
|= MODECHANGE_MODES
;
647 if (dir
) { SetNoNotice(cp
); } else { ClearNoNotice(cp
); }
648 changes
|= MODECHANGE_MODES
;
651 /* Parameter modes: advance parameter and possibly read it in */
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 ;) */
660 cp
->limit
=strtol(cargv
[arg
++],NULL
,10);
667 changes
|= MODECHANGE_MODES
;
672 /* +k uses a parameter in both directions */
674 freesstring(cp
->key
); /* It's probably NULL, but be safe */
675 cp
->key
=getsstring(cargv
[arg
++],KEYLEN
);
679 freesstring(cp
->key
);
682 arg
++; /* Eat the arg without looking at it, even if it's not there */
684 changes
|= MODECHANGE_MODES
;
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
);
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 */
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
);
710 changes
|= MODECHANGE_USERS
;
716 setban(cp
,cargv
[arg
++]);
717 triggerhook(HOOK_CHANNEL_BANSET
,harg
);
719 clearban(cp
,cargv
[arg
++],0);
720 triggerhook(HOOK_CHANNEL_BANCLEAR
,harg
);
723 changes
|= MODECHANGE_BANS
;
727 Error("channel",ERR_DEBUG
,"Unknown mode char '%c' %s on %s",*modestr
,dir
?"set":"cleared",cp
->index
->name
->content
);
732 harg
[2]=(void *)changes
;
733 triggerhook(HOOK_CHANNEL_MODECHANGE
,(void *)harg
);
738 * Deal with 2.10.11ism: CLEARMODE
740 * [hAAA CM #twilightzone ovpsmikbl
743 int handleclearmodemsg(void *source
, int cargc
, char **cargv
) {
748 unsigned long usermask
=0;
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]);
762 if (((char *)source
)[2]=='\0') {
763 /* Server mode change? (I don't think servers are allowed to do CLEARMODE) */
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
);
773 for (mcp
=cargv
[1];*mcp
;mcp
++) {
776 usermask
|= CUMODE_OP
;
777 changes
|= MODECHANGE_USERS
;
781 usermask
|= CUMODE_VOICE
;
782 changes
|= MODECHANGE_USERS
;
787 changes
|= MODECHANGE_MODES
;
792 changes
|= MODECHANGE_MODES
;
797 changes
|= MODECHANGE_MODES
;
802 changes
|= MODECHANGE_MODES
;
807 changes
|= MODECHANGE_MODES
;
812 changes
|= MODECHANGE_MODES
;
817 changes
|= MODECHANGE_MODES
;
822 changes
|= MODECHANGE_MODES
;
827 changes
|= MODECHANGE_MODES
;
832 changes
|= MODECHANGE_MODES
;
837 changes
|= MODECHANGE_MODES
;
842 changes
|= MODECHANGE_MODES
;
847 changes
|= MODECHANGE_BANS
;
851 /* This is all safe even if there is no key atm */
852 freesstring(cp
->key
);
855 changes
|= MODECHANGE_MODES
;
861 changes
|= MODECHANGE_MODES
;
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?");
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
;
888 harg
[2]=(void *)changes
;
889 triggerhook(HOOK_CHANNEL_MODECHANGE
, harg
);