]>
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 /* Set up the nouser marker according to our own numeric */
47 nouser
=(mylongnum
<<18)|CU_NOUSERMASK
;
49 /* If we're connected to IRC, force a disconnect. This needs to be done
50 * before we register all our hooks which would otherwise get called
51 * during the disconnect. */
53 irc_send("%s SQ %s 0 :Resync [adding channel support]",mynumeric
->content
,myserver
->content
); irc_disconnected(0);
56 /* Set up our hooks */
57 registerhook(HOOK_NICK_NEWNICK
,&addordelnick
);
58 registerhook(HOOK_NICK_LOSTNICK
,&addordelnick
);
59 registerhook(HOOK_CORE_STATSREQUEST
,&channelstats
);
60 registerhook(HOOK_IRC_SENDBURSTBURSTS
,&sendchanburst
);
61 registerhook(HOOK_NICK_WHOISCHANNELS
,&handlewhoischannels
);
63 registerserverhandler("B",&handleburstmsg
,7);
64 registerserverhandler("J",&handlejoinmsg
,2);
65 registerserverhandler("C",&handlecreatemsg
,2);
66 registerserverhandler("L",&handlepartmsg
,1);
67 registerserverhandler("K",&handlekickmsg
,3);
68 registerserverhandler("T",&handletopicmsg
,3);
69 registerserverhandler("M",&handlemodemsg
,8);
70 registerserverhandler("OM",&handlemodemsg
,8); /* Treat OPMODE just like MODE */
71 registerserverhandler("CM",&handleclearmodemsg
,2);
77 struct chanindex
*cip
, *ncip
;
80 deregisterserverhandler("B",&handleburstmsg
);
81 deregisterserverhandler("J",&handlejoinmsg
);
82 deregisterserverhandler("C",&handlecreatemsg
);
83 deregisterserverhandler("L",&handlepartmsg
);
84 deregisterserverhandler("K",&handlekickmsg
);
85 deregisterserverhandler("T",&handletopicmsg
);
86 deregisterserverhandler("M",&handlemodemsg
);
87 deregisterserverhandler("OM",&handlemodemsg
);
88 deregisterserverhandler("CM",&handleclearmodemsg
);
90 deregisterhook(HOOK_NICK_NEWNICK
,&addordelnick
);
91 deregisterhook(HOOK_NICK_LOSTNICK
,&addordelnick
);
92 deregisterhook(HOOK_CORE_STATSREQUEST
,&channelstats
);
93 deregisterhook(HOOK_IRC_SENDBURSTBURSTS
,&sendchanburst
);
94 deregisterhook(HOOK_NICK_WHOISCHANNELS
,&handlewhoischannels
);
96 /* Free all the channels */
97 for(i
=0;i
<CHANNELHASHSIZE
;i
++) {
98 for (cip
=chantable
[i
];cip
;cip
=ncip
) {
100 if ((cp
=cip
->channel
))
105 /* We also need to remove the channels array from each user */
106 for (i
=0;i
<NICKHASHSIZE
;i
++) {
107 for (np
=nicktable
[i
];np
;np
=np
->next
) {
108 array_free(np
->channels
);
113 nsfreeall(POOL_CHANNEL
);
116 int addnicktochannel(channel
*cp
, long numeric
) {
122 /* Add the channel to the user first, since it might fail */
123 if ((np
=getnickbynumeric(numeric
&CU_NUMERICMASK
))==NULL
) {
124 Error("channel",ERR_ERROR
,"Non-existent numeric %ld joined channel %s",numeric
,cp
->index
->name
->content
);
128 if (getnumerichandlefromchanhash(cp
->users
,numeric
)) {
129 Error("channel",ERR_ERROR
,"User %s joined channel %s it was already on!",np
->nick
,cp
->index
->name
->content
);
133 i
=array_getfreeslot(np
->channels
);
134 ch
=(channel
**)(np
->channels
->content
);
137 /* Add the user to the channel.
138 * I don't expect this while loop to go round many times
139 * in the majority of cases */
140 while (addnumerictochanuserhash(cp
->users
,numeric
)) {
144 /* Trigger the hook */
147 triggerhook(HOOK_CHANNEL_NEWNICK
,args
);
152 void delnickfromchannel(channel
*cp
, long numeric
, int updateuser
) {
160 if ((np
=getnickbynumeric(numeric
&CU_NUMERICMASK
))==NULL
) {
161 Error("channel",ERR_ERROR
,"Trying to remove non-existent nick %lu from channel %s",numeric
,cp
->index
->name
->content
);
168 if ((lp
=getnumerichandlefromchanhash(cp
->users
,numeric
))==NULL
) {
169 /* User wasn't on the channel. It's perfectly legit that this can happen. */
172 triggerhook(HOOK_CHANNEL_LOSTNICK
,args
);
174 if (--cp
->users
->totalusers
==0) {
175 /* We're deleting the channel; flag it here */
176 triggerhook(HOOK_CHANNEL_LOSTCHANNEL
,cp
);
181 /* The updateuser part is optional; if the user is parting _all_ their channels
182 * at once we don't bother hunting each channel down we just blat the array */
185 ch
=(channel
**)(np
->channels
->content
);
186 for (i
=0;i
<np
->channels
->cursi
;i
++) {
188 array_delslot(np
->channels
,i
);
194 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
);
199 void delchannel(channel
*cp
) {
202 /* Remove entry from index */
205 releasechanindex(cip
);
207 freesstring(cp
->topic
);
208 freesstring(cp
->key
);
209 freechanuserhash(cp
->users
);
214 channel
*createchannel(char *name
) {
219 cip
=findorcreatechanindex(name
);
222 Error("channel",ERR_ERROR
,"Attempting to create existing channel %s (%s).",cip
->name
->content
,name
);
226 /* Check that chanindex record has the same capitalisation as actual channel */
227 if (strcmp(cip
->name
->content
,name
)) {
229 cip
->name
=getsstring(name
,CHANNELLEN
);
237 cp
->timestamp
=MAGIC_REMOTE_JOIN_TS
;
244 cp
->users
=newchanuserhash(1);
249 channel
*findchannel(char *name
) {
252 cip
=findchanindex(name
);
260 void channelstats(int hooknum
, void *arg
) {
261 long level
=(long)arg
;
262 int i
,curchain
,maxchain
=0,total
=0,buckets
=0,realchans
=0;
267 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
268 if (chantable
[i
]!=NULL
) {
271 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
274 if (cip
->channel
!=NULL
) {
276 users
+=cip
->channel
->users
->totalusers
;
277 slots
+=cip
->channel
->users
->hashsize
;
280 if (curchain
>maxchain
) {
288 sprintf(buf
,"Channel : %6d channels (HASH: %6d/%6d, chain %3d)",total
,buckets
,CHANNELHASHSIZE
,maxchain
);
289 triggerhook(HOOK_CORE_STATSREPLY
,buf
);
291 sprintf(buf
,"Channel :%7d channel users, %7d slots allocated, efficiency %.1f%%",users
,slots
,(float)(100*users
)/slots
);
292 triggerhook(HOOK_CORE_STATSREPLY
,buf
);
296 sprintf(buf
,"Channel : %6d channels formed.",realchans
);
297 triggerhook(HOOK_CORE_STATSREPLY
,buf
);
303 * Deal with users joining the network (create and initialise their channel array)
304 * or leaving the network (remove them from all channels and delete the array)
307 void addordelnick(int hooknum
, void *arg
) {
308 nick
*np
=(nick
*)arg
;
313 case HOOK_NICK_NEWNICK
:
314 np
->channels
=(array
*)malloc(sizeof(array
));
315 array_init(np
->channels
,sizeof(channel
**));
316 array_setlim1(np
->channels
,10);
317 array_setlim2(np
->channels
,15);
320 case HOOK_NICK_LOSTNICK
:
321 ch
=(channel
**)(np
->channels
->content
);
322 for(i
=0;i
<np
->channels
->cursi
;i
++) {
323 delnickfromchannel(ch
[i
],np
->numeric
,0);
325 array_free(np
->channels
);
332 * Spam our local burst on connect..
335 void sendchanburst(int hooknum
, void *arg
) {
347 long modeorder
[] = { 0, CUMODE_OP
, CUMODE_VOICE
, CUMODE_OP
|CUMODE_VOICE
};
350 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
351 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
356 /* Set up the burst */
357 sprintf(buf2
,"%d ",cp
->limit
);
358 bufpos
=sprintf(buf
,"%s B %s %lu %s %s%s%s",mynumeric
->content
,cip
->name
->content
,cp
->timestamp
,
359 printflags(cp
->flags
,cmodeflags
),IsLimit(cp
)?buf2
:"",
360 IsKey(cp
)?cp
->key
->content
:"",IsKey(cp
)?" ":"");
362 curmode
=modeorder
[j
];
364 for (k
=0;k
<cp
->users
->hashsize
;k
++) {
365 if (cp
->users
->content
[k
]!=nouser
&& ((cp
->users
->content
[k
]&(CU_MODEMASK
))==curmode
)) {
366 /* We found a user of the correct type for this pass */
367 if (BUFSIZE
-bufpos
<10) { /* Out of space.. wrap up the old line and send a new one */
370 bufpos
=sprintf(buf
,"%s B %s %lu ",mynumeric
->content
,cip
->name
->content
,cp
->timestamp
);
373 bufpos
+=sprintf(buf
+bufpos
,"%s%s%s%s%s",newline
?"":",",longtonumeric(cp
->users
->content
[k
]&CU_NUMERICMASK
,5),
374 (curmode
==0?"":":"),(curmode
&CUMODE_OP
)?"o":"",(curmode
&CUMODE_VOICE
)?"v":"");
376 bufpos
+=sprintf(buf
+bufpos
,",%s",longtonumeric(cp
->users
->content
[k
]&CU_NUMERICMASK
,5));
383 /* And now the bans */
385 for(ban
=cp
->bans
;ban
;ban
=ban
->next
) {
386 banstr
=bantostring(ban
);
387 if ((BUFSIZE
-bufpos
)<(strlen(banstr
)+10)) { /* Out of space.. wrap up the old line and send a new one */
390 bufpos
=sprintf(buf
,"%s B %s %lu",mynumeric
->content
,cip
->name
->content
,cp
->timestamp
);
392 bufpos
+=sprintf(buf
+bufpos
,"%s%s ",(newline
?" :%":""),banstr
);
402 * Uses the marker on all host records to count unique hosts
403 * on a channel in O(n) time (n is channel user hash size).
406 unsigned int countuniquehosts(channel
*cp
) {
412 marker
=nexthostmarker();
413 for (i
=0;i
<cp
->users
->hashsize
;i
++) {
414 if (cp
->users
->content
[i
]==nouser
)
417 if ((np
=getnickbynumeric(cp
->users
->content
[i
]))==NULL
) {
418 Error("channel",ERR_ERROR
,"Found unknown numeric %lu on channel %s",cp
->users
->content
[i
],cp
->index
->name
->content
);
422 if (np
->host
->marker
==marker
)
425 np
->host
->marker
=marker
;
433 * clean_key: returns a "cleaned" version of the key like ircu does.
435 * Note that s is a signed char, so we are basically allowing everything from 33-127 except : or ,
437 * Unlike ircu we don't check against KEYLEN here, this is done elsewhere.
439 void clean_key(char *key
) {
441 if (*key
<=32 || *key
==':' || *key
==',') {