]>
jfr.im git - irc/quakenet/newserv.git/blob - channel/channel.c
bd1377655f517ecff13614a46ed65a626074836f
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"
18 #define channelhash(x) (crc32i(x)%CHANNELHASHSIZE)
21 chanindex
*chantable
[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
},
40 void channelstats(int hooknum
, void *arg
);
41 void sendchanburst(int hooknum
, void *arg
);
44 /* Initialise internal structures */
47 memset(chantable
,0,sizeof(chantable
));
49 /* Set up the nouser marker according to our own numeric */
50 nouser
=(mylongnum
<<18)|CU_NOUSERMASK
;
52 /* Set up our hooks */
53 registerhook(HOOK_NICK_NEWNICK
,&addordelnick
);
54 registerhook(HOOK_NICK_LOSTNICK
,&addordelnick
);
55 registerhook(HOOK_CORE_STATSREQUEST
,&channelstats
);
56 registerhook(HOOK_IRC_SENDBURSTBURSTS
,&sendchanburst
);
57 registerhook(HOOK_NICK_WHOISCHANNELS
,&handlewhoischannels
);
59 registerserverhandler("B",&handleburstmsg
,7);
60 registerserverhandler("J",&handlejoinmsg
,2);
61 registerserverhandler("C",&handlecreatemsg
,2);
62 registerserverhandler("L",&handlepartmsg
,1);
63 registerserverhandler("K",&handlekickmsg
,3);
64 registerserverhandler("T",&handletopicmsg
,3);
65 registerserverhandler("M",&handlemodemsg
,8);
66 registerserverhandler("OM",&handlemodemsg
,8); /* Treat OPMODE just like MODE */
67 registerserverhandler("CM",&handleclearmodemsg
,2);
69 /* If we're connected to IRC, force a disconnect */
71 irc_send("%s SQ %s 0 :Resync [adding channel support]",mynumeric
->content
,myserver
->content
);
77 deregisterserverhandler("B",&handleburstmsg
);
78 deregisterserverhandler("J",&handlejoinmsg
);
79 deregisterserverhandler("C",&handlecreatemsg
);
80 deregisterserverhandler("L",&handlepartmsg
);
81 deregisterserverhandler("K",&handlekickmsg
);
82 deregisterserverhandler("T",&handletopicmsg
);
83 deregisterserverhandler("M",&handlemodemsg
);
84 deregisterserverhandler("OM",&handlemodemsg
);
85 deregisterserverhandler("CM",&handleclearmodemsg
);
87 deregisterhook(HOOK_NICK_NEWNICK
,&addordelnick
);
88 deregisterhook(HOOK_NICK_LOSTNICK
,&addordelnick
);
89 deregisterhook(HOOK_NICK_WHOISCHANNELS
,&handlewhoischannels
);
92 int addnicktochannel(channel
*cp
, long numeric
) {
98 /* Add the channel to the user first, since it might fail */
99 if ((np
=getnickbynumeric(numeric
&CU_NUMERICMASK
))==NULL
) {
100 Error("channel",ERR_ERROR
,"Non-existent numeric %ld joined channel %s",numeric
,cp
->index
->name
->content
);
104 if (getnumerichandlefromchanhash(cp
->users
,numeric
)) {
105 Error("channel",ERR_ERROR
,"User %s joined channel %s it was already on!",np
->nick
,cp
->index
->name
->content
);
109 i
=array_getfreeslot(np
->channels
);
110 ch
=(channel
**)(np
->channels
->content
);
113 /* Add the user to the channel.
114 * I don't expect this while loop to go round many times
115 * in the majority of cases */
116 while (addnumerictochanuserhash(cp
->users
,numeric
)) {
120 /* Trigger the hook */
123 triggerhook(HOOK_CHANNEL_NEWNICK
,args
);
128 void delnickfromchannel(channel
*cp
, long numeric
, int updateuser
) {
136 if ((np
=getnickbynumeric(numeric
&CU_NUMERICMASK
))==NULL
) {
137 Error("channel",ERR_ERROR
,"Trying to remove non-existent nick %d from channel %s",numeric
,cp
->index
->name
);
144 if ((lp
=getnumerichandlefromchanhash(cp
->users
,numeric
))==NULL
) {
145 /* User wasn't on the channel. It's perfectly legit that this can happen. */
148 triggerhook(HOOK_CHANNEL_LOSTNICK
,args
);
150 if (--cp
->users
->totalusers
==0) {
151 /* We're deleting the channel; flag it here */
152 triggerhook(HOOK_CHANNEL_LOSTCHANNEL
,cp
);
157 /* The updateuser part is optional; if the user is parting _all_ their channels
158 * at once we don't bother hunting each channel down we just blat the array */
161 ch
=(channel
**)(np
->channels
->content
);
162 for (i
=0;i
<np
->channels
->cursi
;i
++) {
164 array_delslot(np
->channels
,i
);
170 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
);
175 void delchannel(channel
*cp
) {
178 /* Remove entry from index */
181 releasechanindex(cip
);
183 freesstring(cp
->topic
);
184 freesstring(cp
->key
);
185 freechanuserhash(cp
->users
);
190 channel
*createchannel(char *name
) {
195 cip
=findorcreatechanindex(name
);
198 Error("channel",ERR_ERROR
,"Attempting to create existing channel %s (%s).",cip
->name
->content
,name
);
202 /* Check that chanindex record has the same capitalisation as actual channel */
203 if (strcmp(cip
->name
->content
,name
)) {
205 cip
->name
=getsstring(name
,CHANNELLEN
);
213 cp
->timestamp
=MAGIC_REMOTE_JOIN_TS
;
220 cp
->users
=newchanuserhash(1);
225 channel
*findchannel(char *name
) {
228 cip
=findchanindex(name
);
236 void channelstats(int hooknum
, void *arg
) {
238 int i
,curchain
,maxchain
=0,total
=0,buckets
=0,realchans
=0;
243 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
244 if (chantable
[i
]!=NULL
) {
247 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
250 if (cip
->channel
!=NULL
) {
252 users
+=cip
->channel
->users
->totalusers
;
253 slots
+=cip
->channel
->users
->hashsize
;
256 if (curchain
>maxchain
) {
264 sprintf(buf
,"Channel : %6d channels (HASH: %6d/%6d, chain %3d)",total
,buckets
,CHANNELHASHSIZE
,maxchain
);
265 triggerhook(HOOK_CORE_STATSREPLY
,buf
);
267 sprintf(buf
,"Channel :%7d channel users, %7d slots allocated, efficiency %.1f%%",users
,slots
,(float)(100*users
)/slots
);
268 triggerhook(HOOK_CORE_STATSREPLY
,buf
);
272 sprintf(buf
,"Channel : %6d channels formed.",realchans
);
273 triggerhook(HOOK_CORE_STATSREPLY
,buf
);
279 * Deal with users joining the network (create and initialise their channel array)
280 * or leaving the network (remove them from all channels and delete the array)
283 void addordelnick(int hooknum
, void *arg
) {
284 nick
*np
=(nick
*)arg
;
289 case HOOK_NICK_NEWNICK
:
290 np
->channels
=(array
*)malloc(sizeof(array
));
291 array_init(np
->channels
,sizeof(channel
**));
292 array_setlim1(np
->channels
,10);
293 array_setlim2(np
->channels
,15);
296 case HOOK_NICK_LOSTNICK
:
297 ch
=(channel
**)(np
->channels
->content
);
298 for(i
=0;i
<np
->channels
->cursi
;i
++) {
299 delnickfromchannel(ch
[i
],np
->numeric
,0);
301 array_free(np
->channels
);
308 * Spam our local burst on connect..
311 void sendchanburst(int hooknum
, void *arg
) {
323 long modeorder
[] = { 0, CUMODE_OP
, CUMODE_VOICE
, CUMODE_OP
|CUMODE_VOICE
};
326 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
327 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
332 /* Set up the burst */
333 sprintf(buf2
,"%d ",cp
->limit
);
334 bufpos
=sprintf(buf
,"%s B %s %lu %s %s%s%s",mynumeric
->content
,cip
->name
->content
,cp
->timestamp
,
335 printflags(cp
->flags
,cmodeflags
),IsLimit(cp
)?buf2
:"",
336 IsKey(cp
)?cp
->key
->content
:"",IsKey(cp
)?" ":"");
338 curmode
=modeorder
[j
];
340 for (k
=0;k
<cp
->users
->hashsize
;k
++) {
341 if (cp
->users
->content
[k
]!=nouser
&& ((cp
->users
->content
[k
]&(CU_MODEMASK
))==curmode
)) {
342 /* We found a user of the correct type for this pass */
343 if (BUFSIZE
-bufpos
<10) { /* Out of space.. wrap up the old line and send a new one */
346 bufpos
=sprintf(buf
,"%s B %s %lu ",mynumeric
->content
,cip
->name
->content
,cp
->timestamp
);
349 bufpos
+=sprintf(buf
+bufpos
,"%s%s%s%s%s",newline
?"":",",longtonumeric(cp
->users
->content
[k
]&CU_NUMERICMASK
,5),
350 (curmode
==0?"":":"),(curmode
&CUMODE_OP
)?"o":"",(curmode
&CUMODE_VOICE
)?"v":"");
352 bufpos
+=sprintf(buf
+bufpos
,",%s",longtonumeric(cp
->users
->content
[k
]&CU_NUMERICMASK
,5));
359 /* And now the bans */
361 for(ban
=cp
->bans
;ban
;ban
=ban
->next
) {
362 banstr
=bantostring(ban
);
363 if ((BUFSIZE
-bufpos
)<(strlen(banstr
)+10)) { /* Out of space.. wrap up the old line and send a new one */
366 bufpos
=sprintf(buf
,"%s B %s %lu",mynumeric
->content
,cip
->name
->content
,cp
->timestamp
);
368 bufpos
+=sprintf(buf
+bufpos
,"%s%s ",(newline
?" :%":""),banstr
);
378 * Uses the marker on all host records to count unique hosts
379 * on a channel in O(n) time (n is channel user hash size).
382 unsigned int countuniquehosts(channel
*cp
) {
388 marker
=nexthostmarker();
389 for (i
=0;i
<cp
->users
->hashsize
;i
++) {
390 if (cp
->users
->content
[i
]==nouser
)
393 if ((np
=getnickbynumeric(cp
->users
->content
[i
]))==NULL
) {
394 Error("channel",ERR_ERROR
,"Found unknown numeric %u on channel %s",cp
->users
->content
[i
],cp
->index
->name
->content
);
398 if (np
->host
->marker
==marker
)
401 np
->host
->marker
=marker
;