]> jfr.im git - irc/quakenet/newserv.git/blame - channel/channel.c
Remove trusts2 from paulbranch.
[irc/quakenet/newserv.git] / channel / channel.c
CommitLineData
c86edd1d
Q
1#/* channel.c */
2
3#include "channel.h"
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"
87698d77 11#include "../lib/version.h"
11032584 12#include "../core/nsmalloc.h"
c86edd1d
Q
13
14#include <stdio.h>
4ad1cf7a 15#include <string.h>
c86edd1d 16
70b0a4e5 17MODULE_VERSION("");
87698d77 18
c86edd1d
Q
19#define channelhash(x) (crc32i(x)%CHANNELHASHSIZE)
20
21unsigned long nouser;
c86edd1d
Q
22
23const 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 },
73a2c60a 38 { 'M', CHANMODE_MODNOAUTH },
39 { 'T', CHANMODE_SINGLETARG },
c86edd1d
Q
40 { '\0', 0 } };
41
42void channelstats(int hooknum, void *arg);
43void sendchanburst(int hooknum, void *arg);
44
45void _init() {
46 /* Initialise internal structures */
47 initchannelalloc();
c86edd1d
Q
48
49 /* Set up the nouser marker according to our own numeric */
50 nouser=(mylongnum<<18)|CU_NOUSERMASK;
51
e340eee8 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. */
55 if (connected) {
56 irc_send("%s SQ %s 0 :Resync [adding channel support]",mynumeric->content,myserver->content); irc_disconnected();
57 }
58
c86edd1d
Q
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);
f2b36aa8 64 registerhook(HOOK_NICK_WHOISCHANNELS,&handlewhoischannels);
c86edd1d
Q
65
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);
c86edd1d
Q
75}
76
77void _fini() {
11032584 78 unsigned int i;
79 struct channel *cp;
80 struct chanindex *cip, *ncip;
81 nick *np;
82
c86edd1d
Q
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);
92
93 deregisterhook(HOOK_NICK_NEWNICK,&addordelnick);
94 deregisterhook(HOOK_NICK_LOSTNICK,&addordelnick);
65f2c6a3 95 deregisterhook(HOOK_CORE_STATSREQUEST,&channelstats);
96 deregisterhook(HOOK_IRC_SENDBURSTBURSTS,&sendchanburst);
f2b36aa8 97 deregisterhook(HOOK_NICK_WHOISCHANNELS,&handlewhoischannels);
11032584 98
99 /* Free all the channels */
100 for(i=0;i<CHANNELHASHSIZE;i++) {
101 for (cip=chantable[i];cip;cip=ncip) {
102 ncip=cip->next;
9b8227da 103 if ((cp=cip->channel))
e340eee8 104 delchannel(cp);
11032584 105 }
106 }
107
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);
112 free(np->channels);
113 }
114 }
115
116 nsfreeall(POOL_CHANNEL);
c86edd1d
Q
117}
118
119int addnicktochannel(channel *cp, long numeric) {
120 nick *np;
121 int i;
122 channel **ch;
123 void *args[2];
124
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);
128 return 1;
129 }
130
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);
133 return 1;
134 }
135
136 i=array_getfreeslot(np->channels);
137 ch=(channel **)(np->channels->content);
138 ch[i]=cp;
139
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)) {
144 rehashchannel(cp);
145 }
146
147 /* Trigger the hook */
148 args[0]=(void *)cp;
149 args[1]=(void *)np;
150 triggerhook(HOOK_CHANNEL_NEWNICK,args);
151
152 return 0;
153}
154
155void delnickfromchannel(channel *cp, long numeric, int updateuser) {
156 int i;
157 channel **ch;
158 nick *np;
159 unsigned long *lp;
160 int found=0;
161 void *args[2];
162
163 if ((np=getnickbynumeric(numeric&CU_NUMERICMASK))==NULL) {
16739dbe 164 Error("channel",ERR_ERROR,"Trying to remove non-existent nick %lu from channel %s",numeric,cp->index->name->content);
c86edd1d
Q
165 return;
166 }
167
168 args[0]=(void *)cp;
169 args[1]=(void *)np;
170
171 if ((lp=getnumerichandlefromchanhash(cp->users,numeric))==NULL) {
172 /* User wasn't on the channel. It's perfectly legit that this can happen. */
173 return;
174 } else {
175 triggerhook(HOOK_CHANNEL_LOSTNICK,args);
176 *lp=nouser;
177 if (--cp->users->totalusers==0) {
178 /* We're deleting the channel; flag it here */
179 triggerhook(HOOK_CHANNEL_LOSTCHANNEL,cp);
180 delchannel(cp);
181 }
182 }
183
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 */
186
187 if (updateuser) {
188 ch=(channel **)(np->channels->content);
189 for (i=0;i<np->channels->cursi;i++) {
190 if (ch[i]==cp) {
191 array_delslot(np->channels,i);
192 found=1;
193 break;
194 }
195 }
196 if (found==0) {
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);
198 }
199 }
200}
201
202void delchannel(channel *cp) {
203 chanindex *cip;
204
205 /* Remove entry from index */
206 cip=cp->index;
207 cip->channel=NULL;
208 releasechanindex(cip);
209
210 freesstring(cp->topic);
211 freesstring(cp->key);
212 freechanuserhash(cp->users);
213 clearallbans(cp);
214 freechan(cp);
215}
216
217channel *createchannel(char *name) {
218 channel *cp;
219 chanindex *cip;
220 sstring *ssp;
221
222 cip=findorcreatechanindex(name);
223
224 if (cip->channel) {
225 Error("channel",ERR_ERROR,"Attempting to create existing channel %s (%s).",cip->name->content,name);
226 return cip->channel;
227 }
228
229 /* Check that chanindex record has the same capitalisation as actual channel */
230 if (strcmp(cip->name->content,name)) {
231 ssp=cip->name;
232 cip->name=getsstring(name,CHANNELLEN);
233 freesstring(ssp);
234 }
235
236 cp=newchan();
237 cip->channel=cp;
238 cp->index=cip;
239
240 cp->timestamp=MAGIC_REMOTE_JOIN_TS;
241 cp->topic=NULL;
242 cp->topictime=0;
243 cp->flags=0;
244 cp->key=NULL;
245 cp->limit=0;
246 cp->bans=NULL;
247 cp->users=newchanuserhash(1);
248
249 return cp;
250}
251
252channel *findchannel(char *name) {
253 chanindex *cip;
254
255 cip=findchanindex(name);
256 if (cip==NULL) {
257 return NULL;
258 } else {
259 return cip->channel;
260 }
261}
262
263void channelstats(int hooknum, void *arg) {
c3db6f7e 264 long level=(long)arg;
c86edd1d
Q
265 int i,curchain,maxchain=0,total=0,buckets=0,realchans=0;
266 int users=0,slots=0;
267 chanindex *cip;
268 char buf[100];
269
270 for (i=0;i<CHANNELHASHSIZE;i++) {
271 if (chantable[i]!=NULL) {
272 buckets++;
273 curchain=0;
274 for (cip=chantable[i];cip;cip=cip->next) {
275 total++;
276 curchain++;
277 if (cip->channel!=NULL) {
278 realchans++;
279 users+=cip->channel->users->totalusers;
280 slots+=cip->channel->users->hashsize;
281 }
282 }
283 if (curchain>maxchain) {
284 maxchain=curchain;
285 }
286 }
287 }
288
289 if (level>5) {
290 /* Full stats */
291 sprintf(buf,"Channel : %6d channels (HASH: %6d/%6d, chain %3d)",total,buckets,CHANNELHASHSIZE,maxchain);
292 triggerhook(HOOK_CORE_STATSREPLY,buf);
293
294 sprintf(buf,"Channel :%7d channel users, %7d slots allocated, efficiency %.1f%%",users,slots,(float)(100*users)/slots);
295 triggerhook(HOOK_CORE_STATSREPLY,buf);
296 }
297
298 if (level>2) {
299 sprintf(buf,"Channel : %6d channels formed.",realchans);
300 triggerhook(HOOK_CORE_STATSREPLY,buf);
301 }
302}
303
304
305/*
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)
308 */
309
310void addordelnick(int hooknum, void *arg) {
311 nick *np=(nick *)arg;
312 channel **ch;
313 int i;
314
315 switch(hooknum) {
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);
321 break;
322
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);
327 }
328 array_free(np->channels);
329 free(np->channels);
330 break;
331 }
332}
333
334/*
335 * Spam our local burst on connect..
336 */
337
338void sendchanburst(int hooknum, void *arg) {
339 chanindex *cip;
340 channel *cp;
341 chanban *ban;
342 char buf[BUFSIZE];
343 char buf2[20];
344 char *banstr;
345 int i;
346 int j,k;
347 int bufpos;
348 int newmode=1;
349 int newline=1;
350 long modeorder[] = { 0, CUMODE_OP, CUMODE_VOICE, CUMODE_OP|CUMODE_VOICE };
351 long curmode;
352
353 for (i=0;i<CHANNELHASHSIZE;i++) {
354 for (cip=chantable[i];cip;cip=cip->next) {
355 cp=cip->channel;
356 if (cp==NULL) {
357 continue;
358 }
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)?" ":"");
364 for(j=0;j<4;j++) {
365 curmode=modeorder[j];
366 newmode=1;
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 */
371 newmode=newline=1;
372 irc_send("%s",buf);
373 bufpos=sprintf(buf,"%s B %s %lu ",mynumeric->content,cip->name->content,cp->timestamp);
374 }
375 if (newmode) {
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":"");
378 } else {
379 bufpos+=sprintf(buf+bufpos,",%s",longtonumeric(cp->users->content[k]&CU_NUMERICMASK,5));
380 }
381 newmode=newline=0;
382 } /* if(...) */
383 } /* for (k=..) */
384 } /* for (j=..) */
385
386 /* And now the bans */
387 newline=1;
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 */
391 newline=1;
392 irc_send("%s",buf);
393 bufpos=sprintf(buf,"%s B %s %lu",mynumeric->content,cip->name->content,cp->timestamp);
394 }
395 bufpos+=sprintf(buf+bufpos,"%s%s ",(newline?" :%":""),banstr);
396 newline=0;
397 }
398 irc_send("%s",buf);
399 } /* for(cp..) */
400 } /* for (i..) */
401}
402
403/*
404 * countuniquehosts:
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).
407 */
408
409unsigned int countuniquehosts(channel *cp) {
410 unsigned int marker;
411 int i;
412 int count=0;
413 nick *np;
414
415 marker=nexthostmarker();
416 for (i=0;i<cp->users->hashsize;i++) {
417 if (cp->users->content[i]==nouser)
418 continue;
419
420 if ((np=getnickbynumeric(cp->users->content[i]))==NULL) {
16739dbe 421 Error("channel",ERR_ERROR,"Found unknown numeric %lu on channel %s",cp->users->content[i],cp->index->name->content);
c86edd1d
Q
422 continue;
423 }
424
425 if (np->host->marker==marker)
426 continue;
427
428 np->host->marker=marker;
429 count++;
430 }
431
432 return count;
433}
11032584 434
bc5e802e 435/*
436 * clean_key: returns a "cleaned" version of the key like ircu does.
437 *
438 * Note that s is a signed char, so we are basically allowing everything from 33-127 except : or ,
439 *
440 * Unlike ircu we don't check against KEYLEN here, this is done elsewhere.
441 */
442void clean_key(char *key) {
23c9dc38 443 for (;*key;key++) {
bc5e802e 444 if (*key<=32 || *key==':' || *key==',') {
445 *key=0;
446 return;
447 }
448 }
449}