]>
jfr.im git - irc/quakenet/newserv.git/blob - channel/channel.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"
11 #include "../lib/version.h"
12 #include "../core/nsmalloc.h"
19 #define channelhash(x) (crc32i(x)%CHANNELHASHSIZE)
23 const flag cmodeflags
[] = {
24 { 'n', CHANMODE_NOEXTMSG
},
25 { 't', CHANMODE_TOPICLIMIT
},
26 { 's', CHANMODE_SECRET
},
27 { 'p', CHANMODE_PRIVATE
},
28 { 'i', CHANMODE_INVITEONLY
},
29 { 'l', CHANMODE_LIMIT
},
30 { 'k', CHANMODE_KEY
},
31 { 'm', CHANMODE_MODERATE
},
32 { 'c', CHANMODE_NOCOLOUR
},
33 { 'C', CHANMODE_NOCTCP
},
34 { 'r', CHANMODE_REGONLY
},
35 { 'D', CHANMODE_DELJOINS
},
36 { 'u', CHANMODE_NOQUITMSG
},
37 { 'N', CHANMODE_NONOTICE
},
38 { 'M', CHANMODE_MODNOAUTH
},
39 { 'T', CHANMODE_SINGLETARG
},
42 void channelstats(int hooknum
, void *arg
);
43 void sendchanburst(int hooknum
, void *arg
);
46 /* Initialise internal structures */
49 /* Set up the nouser marker according to our own numeric */
50 nouser
=(mylongnum
<<18)|CU_NOUSERMASK
;
52 /* If we're connected to IRC, force a disconnect. This needs to be done
53 * before we register all our hooks which would otherwise get called
54 * during the disconnect. */
56 irc_send("%s SQ %s 0 :Resync [adding channel support]",mynumeric
->content
,myserver
->content
); irc_disconnected();
59 /* Set up our hooks */
60 registerhook(HOOK_NICK_NEWNICK
,&addordelnick
);
61 registerhook(HOOK_NICK_LOSTNICK
,&addordelnick
);
62 registerhook(HOOK_CORE_STATSREQUEST
,&channelstats
);
63 registerhook(HOOK_IRC_SENDBURSTBURSTS
,&sendchanburst
);
64 registerhook(HOOK_NICK_WHOISCHANNELS
,&handlewhoischannels
);
66 registerserverhandler("B",&handleburstmsg
,7);
67 registerserverhandler("J",&handlejoinmsg
,2);
68 registerserverhandler("C",&handlecreatemsg
,2);
69 registerserverhandler("L",&handlepartmsg
,1);
70 registerserverhandler("K",&handlekickmsg
,3);
71 registerserverhandler("T",&handletopicmsg
,3);
72 registerserverhandler("M",&handlemodemsg
,8);
73 registerserverhandler("OM",&handlemodemsg
,8); /* Treat OPMODE just like MODE */
74 registerserverhandler("CM",&handleclearmodemsg
,2);
80 struct chanindex
*cip
, *ncip
;
83 deregisterserverhandler("B",&handleburstmsg
);
84 deregisterserverhandler("J",&handlejoinmsg
);
85 deregisterserverhandler("C",&handlecreatemsg
);
86 deregisterserverhandler("L",&handlepartmsg
);
87 deregisterserverhandler("K",&handlekickmsg
);
88 deregisterserverhandler("T",&handletopicmsg
);
89 deregisterserverhandler("M",&handlemodemsg
);
90 deregisterserverhandler("OM",&handlemodemsg
);
91 deregisterserverhandler("CM",&handleclearmodemsg
);
93 deregisterhook(HOOK_NICK_NEWNICK
,&addordelnick
);
94 deregisterhook(HOOK_NICK_LOSTNICK
,&addordelnick
);
95 deregisterhook(HOOK_CORE_STATSREQUEST
,&channelstats
);
96 deregisterhook(HOOK_IRC_SENDBURSTBURSTS
,&sendchanburst
);
97 deregisterhook(HOOK_NICK_WHOISCHANNELS
,&handlewhoischannels
);
99 /* Free all the channels */
100 for(i
=0;i
<CHANNELHASHSIZE
;i
++) {
101 for (cip
=chantable
[i
];cip
;cip
=ncip
) {
103 if ((cp
=cip
->channel
))
108 /* We also need to remove the channels array from each user */
109 for (i
=0;i
<NICKHASHSIZE
;i
++) {
110 for (np
=nicktable
[i
];np
;np
=np
->next
) {
111 array_free(np
->channels
);
116 nsfreeall(POOL_CHANNEL
);
119 int addnicktochannel(channel
*cp
, long numeric
) {
125 /* Add the channel to the user first, since it might fail */
126 if ((np
=getnickbynumeric(numeric
&CU_NUMERICMASK
))==NULL
) {
127 Error("channel",ERR_ERROR
,"Non-existent numeric %ld joined channel %s",numeric
,cp
->index
->name
->content
);
131 if (getnumerichandlefromchanhash(cp
->users
,numeric
)) {
132 Error("channel",ERR_ERROR
,"User %s joined channel %s it was already on!",np
->nick
,cp
->index
->name
->content
);
136 i
=array_getfreeslot(np
->channels
);
137 ch
=(channel
**)(np
->channels
->content
);
140 /* Add the user to the channel.
141 * I don't expect this while loop to go round many times
142 * in the majority of cases */
143 while (addnumerictochanuserhash(cp
->users
,numeric
)) {
147 /* Trigger the hook */
150 triggerhook(HOOK_CHANNEL_NEWNICK
,args
);
155 void delnickfromchannel(channel
*cp
, long numeric
, int updateuser
) {
163 if ((np
=getnickbynumeric(numeric
&CU_NUMERICMASK
))==NULL
) {
164 Error("channel",ERR_ERROR
,"Trying to remove non-existent nick %d from channel %s",numeric
,cp
->index
->name
);
171 if ((lp
=getnumerichandlefromchanhash(cp
->users
,numeric
))==NULL
) {
172 /* User wasn't on the channel. It's perfectly legit that this can happen. */
175 triggerhook(HOOK_CHANNEL_LOSTNICK
,args
);
177 if (--cp
->users
->totalusers
==0) {
178 /* We're deleting the channel; flag it here */
179 triggerhook(HOOK_CHANNEL_LOSTCHANNEL
,cp
);
184 /* The updateuser part is optional; if the user is parting _all_ their channels
185 * at once we don't bother hunting each channel down we just blat the array */
188 ch
=(channel
**)(np
->channels
->content
);
189 for (i
=0;i
<np
->channels
->cursi
;i
++) {
191 array_delslot(np
->channels
,i
);
197 Error("channel",ERR_ERROR
,"Trying to remove nick %s from channel %s it was not on (chan not on nick)",np
->nick
,cp
->index
->name
->content
);
202 void delchannel(channel
*cp
) {
205 /* Remove entry from index */
208 releasechanindex(cip
);
210 freesstring(cp
->topic
);
211 freesstring(cp
->key
);
212 freechanuserhash(cp
->users
);
217 channel
*createchannel(char *name
) {
222 cip
=findorcreatechanindex(name
);
225 Error("channel",ERR_ERROR
,"Attempting to create existing channel %s (%s).",cip
->name
->content
,name
);
229 /* Check that chanindex record has the same capitalisation as actual channel */
230 if (strcmp(cip
->name
->content
,name
)) {
232 cip
->name
=getsstring(name
,CHANNELLEN
);
240 cp
->timestamp
=MAGIC_REMOTE_JOIN_TS
;
247 cp
->users
=newchanuserhash(1);
252 channel
*findchannel(char *name
) {
255 cip
=findchanindex(name
);
263 void channelstats(int hooknum
, void *arg
) {
264 long level
=(long)arg
;
265 int i
,curchain
,maxchain
=0,total
=0,buckets
=0,realchans
=0;
270 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
271 if (chantable
[i
]!=NULL
) {
274 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
277 if (cip
->channel
!=NULL
) {
279 users
+=cip
->channel
->users
->totalusers
;
280 slots
+=cip
->channel
->users
->hashsize
;
283 if (curchain
>maxchain
) {
291 sprintf(buf
,"Channel : %6d channels (HASH: %6d/%6d, chain %3d)",total
,buckets
,CHANNELHASHSIZE
,maxchain
);
292 triggerhook(HOOK_CORE_STATSREPLY
,buf
);
294 sprintf(buf
,"Channel :%7d channel users, %7d slots allocated, efficiency %.1f%%",users
,slots
,(float)(100*users
)/slots
);
295 triggerhook(HOOK_CORE_STATSREPLY
,buf
);
299 sprintf(buf
,"Channel : %6d channels formed.",realchans
);
300 triggerhook(HOOK_CORE_STATSREPLY
,buf
);
306 * Deal with users joining the network (create and initialise their channel array)
307 * or leaving the network (remove them from all channels and delete the array)
310 void addordelnick(int hooknum
, void *arg
) {
311 nick
*np
=(nick
*)arg
;
316 case HOOK_NICK_NEWNICK
:
317 np
->channels
=(array
*)malloc(sizeof(array
));
318 array_init(np
->channels
,sizeof(channel
**));
319 array_setlim1(np
->channels
,10);
320 array_setlim2(np
->channels
,15);
323 case HOOK_NICK_LOSTNICK
:
324 ch
=(channel
**)(np
->channels
->content
);
325 for(i
=0;i
<np
->channels
->cursi
;i
++) {
326 delnickfromchannel(ch
[i
],np
->numeric
,0);
328 array_free(np
->channels
);
335 * Spam our local burst on connect..
338 void sendchanburst(int hooknum
, void *arg
) {
350 long modeorder
[] = { 0, CUMODE_OP
, CUMODE_VOICE
, CUMODE_OP
|CUMODE_VOICE
};
353 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
354 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
359 /* Set up the burst */
360 sprintf(buf2
,"%d ",cp
->limit
);
361 bufpos
=sprintf(buf
,"%s B %s %lu %s %s%s%s",mynumeric
->content
,cip
->name
->content
,cp
->timestamp
,
362 printflags(cp
->flags
,cmodeflags
),IsLimit(cp
)?buf2
:"",
363 IsKey(cp
)?cp
->key
->content
:"",IsKey(cp
)?" ":"");
365 curmode
=modeorder
[j
];
367 for (k
=0;k
<cp
->users
->hashsize
;k
++) {
368 if (cp
->users
->content
[k
]!=nouser
&& ((cp
->users
->content
[k
]&(CU_MODEMASK
))==curmode
)) {
369 /* We found a user of the correct type for this pass */
370 if (BUFSIZE
-bufpos
<10) { /* Out of space.. wrap up the old line and send a new one */
373 bufpos
=sprintf(buf
,"%s B %s %lu ",mynumeric
->content
,cip
->name
->content
,cp
->timestamp
);
376 bufpos
+=sprintf(buf
+bufpos
,"%s%s%s%s%s",newline
?"":",",longtonumeric(cp
->users
->content
[k
]&CU_NUMERICMASK
,5),
377 (curmode
==0?"":":"),(curmode
&CUMODE_OP
)?"o":"",(curmode
&CUMODE_VOICE
)?"v":"");
379 bufpos
+=sprintf(buf
+bufpos
,",%s",longtonumeric(cp
->users
->content
[k
]&CU_NUMERICMASK
,5));
386 /* And now the bans */
388 for(ban
=cp
->bans
;ban
;ban
=ban
->next
) {
389 banstr
=bantostring(ban
);
390 if ((BUFSIZE
-bufpos
)<(strlen(banstr
)+10)) { /* Out of space.. wrap up the old line and send a new one */
393 bufpos
=sprintf(buf
,"%s B %s %lu",mynumeric
->content
,cip
->name
->content
,cp
->timestamp
);
395 bufpos
+=sprintf(buf
+bufpos
,"%s%s ",(newline
?" :%":""),banstr
);
405 * Uses the marker on all host records to count unique hosts
406 * on a channel in O(n) time (n is channel user hash size).
409 unsigned int countuniquehosts(channel
*cp
) {
415 marker
=nexthostmarker();
416 for (i
=0;i
<cp
->users
->hashsize
;i
++) {
417 if (cp
->users
->content
[i
]==nouser
)
420 if ((np
=getnickbynumeric(cp
->users
->content
[i
]))==NULL
) {
421 Error("channel",ERR_ERROR
,"Found unknown numeric %u on channel %s",cp
->users
->content
[i
],cp
->index
->name
->content
);
425 if (np
->host
->marker
==marker
)
428 np
->host
->marker
=marker
;
436 * clean_key: returns a "cleaned" version of the key like ircu does.
438 * Note that s is a signed char, so we are basically allowing everything from 33-127 except : or ,
440 * Unlike ircu we don't check against KEYLEN here, this is done elsewhere.
442 void clean_key(char *key
) {
444 if (*key
<=32 || *key
==':' || *key
==',') {