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