]> jfr.im git - irc/quakenet/newserv.git/blob - chanserv/chanservnetevents.c
BUILD: add require-all build mode
[irc/quakenet/newserv.git] / chanserv / chanservnetevents.c
1 /*
2 * chanservnetevents.c:
3 * Handles the bot reaction to network events (joins, parts, mode changes
4 * etc.)
5 */
6
7 #include "chanserv.h"
8
9 #include "../channel/channel.h"
10 #include "../nick/nick.h"
11 #include "../localuser/localuser.h"
12 #include "../localuser/localuserchannel.h"
13
14 #include <stdio.h>
15
16 /* cs_handlenick:
17 * This is for HOOK_NICK_NEWNICK and HOOK_NICK_ACCOUNT.
18 * Associate the nick with it's correct account.
19 */
20
21 void cs_handlenick(int hooknum, void *arg) {
22 nick *np=(nick *)arg;
23
24 cs_checknick(np);
25 }
26
27 void cs_handlesethost(int hooknum, void *arg) {
28 nick *np=arg;
29
30 cs_checknickbans(np);
31 }
32
33 void cs_handlelostnick(int hooknum, void *arg) {
34 nick *np=(nick *)arg;
35 reguser *rup;
36
37 if ((rup=getreguserfromnick(np))) {
38 /* Clean up if this is the last user. auth->usercount is decremented
39 * AFTER the hook is sent... */
40 if ((rup->status & QUSTAT_DEAD) && (np->auth->usercount==1)) {
41 freereguser(rup);
42 }
43 }
44
45 if (getactiveuserfromnick(np))
46 freeactiveuser(getactiveuserfromnick(np));
47 }
48
49 /*
50 * cs_handlenewchannel:
51 * This is for the HOOK_CHANNEL_NEWCHANNEL message.
52 * A new channel has just been created on the network, associate
53 * with the registered channel (if one exists) unless it's suspended.
54 */
55
56 void cs_handlenewchannel(int hooknum, void *arg) {
57 channel *cp=(channel *)arg;
58 regchan *rcp;
59
60 /* Get the registered channel */
61 if ((rcp=(regchan *)cp->index->exts[chanservext])==NULL || CIsSuspended(rcp))
62 return;
63
64 /* chanservjoinchan() will deal with joining the channel and/or setting the timestamp */
65 chanservjoinchan(cp);
66
67 /* Make sure the right modes are set/cleared */
68 cs_checkchanmodes(cp);
69
70 /* Start the timer rolling */
71 cs_timerfunc(cp->index);
72
73 /* If topicsave or forcetopic is set, update the topic */
74 if (CIsForceTopic(rcp) || CIsTopicSave(rcp)) {
75 localsettopic(chanservnick, cp, (rcp->topic) ? rcp->topic->content : "");
76 }
77 }
78
79 void cs_handlechanlostuser(int hooknum, void *arg) {
80 void **args=(void **)arg;
81 channel *cp=args[0];
82 regchan *rcp;
83
84 if (!(rcp=cp->index->exts[chanservext]) || CIsSuspended(rcp) || !chanservnick ||
85 (chanservnick==args[1]))
86 return;
87
88 if (!CIsJoined(rcp))
89 return;
90
91 if (cp->users->totalusers==2 &&
92 getnumerichandlefromchanhash(cp->users, chanservnick->numeric)) {
93 /* OK, Q is on the channel and not the one leaving.. */
94 rcp->lastpart=time(NULL);
95 cs_schedupdate(cp->index, 1, 5);
96 }
97 }
98
99
100 /*
101 * cs_handlelostchannel:
102 * This is for the HOOK_CHANNEL_LOSTCHANNEL message.
103 * A channel has just disappeared, clear our association with it.
104 */
105
106 void cs_handlelostchannel(int hooknum, void *arg) {
107 /* channel *cp=(channel *)arg; */
108
109 /* Do we actually need to do anything here? */
110 }
111
112 /*
113 * cs_handlejoin:
114 * A user joined a channel. See if we need to op/etc. them.
115 * Use this for JOIN or CREATE.
116 */
117
118 void cs_handlejoin(int hooknum, void *arg) {
119 void **arglist=(void **)arg;
120 channel *cp=(channel *)arglist[0];
121 nick *np=(nick *)arglist[1];
122 regchan *rcp;
123 reguser *rup;
124 regchanuser *rcup=NULL;
125 chanindex *cip;
126 int iscreate, isopped;
127 int dowelcome=0;
128 unsigned long *lp;
129
130 short modes=0;
131
132 /* If not registered or suspended, ignore */
133 if (!(rcp=cp->index->exts[chanservext]) || CIsSuspended(rcp))
134 return;
135
136 cip=cp->index;
137
138 rcp->tripjoins++;
139 rcp->totaljoins++;
140 if (cp->users->totalusers > rcp->maxusers)
141 rcp->maxusers=cp->users->totalusers;
142 if (cp->users->totalusers > rcp->tripusers)
143 rcp->tripusers=cp->users->totalusers;
144
145 /* If their auth is deleted, pretend they don't exist */
146 rup=getreguserfromnick(np);
147 if (rup && (rup->status & QUSTAT_DEAD))
148 rup=NULL;
149
150 if (rup && (rcup=findreguseronchannel(rcp,rup)) && CUHasOpPriv(rcup) && cs_ischannelactive(cp, NULL))
151 rcp->lastactive=time(NULL);
152
153 /* Update last use time */
154 if (rcup)
155 rcup->usetime=getnettime();
156
157 if (rcp->lastcountersync < (time(NULL) - COUNTERSYNCINTERVAL)) {
158 csdb_updatechannelcounters(rcp);
159 rcp->lastcountersync=time(NULL);
160 }
161
162 /* OK, this may be a CREATE but it's possible we have already bursted onto
163 * the channel and deopped them. So let's just check that out now.
164 *
165 * There's a distinction between "is it a create?" and "are they opped
166 * already?", since we need to send the generic "This is a Q9 channel"
167 * message on create even if we already deopped them. */
168 if (hooknum==HOOK_CHANNEL_CREATE) {
169 iscreate=1;
170 if ((lp=getnumerichandlefromchanhash(cp->users, np->numeric)) && (*lp & CUMODE_OP))
171 isopped=1;
172 else
173 isopped=0;
174 } else {
175 isopped=iscreate=0;
176 }
177
178 /* Various things that can ban the user on join. Don't apply these to anyone
179 * with one of +k, +X, +o */
180 if (!IsService(np) && !IsOper(np) && !IsXOper(np)) {
181 /* Check for "Q ban" */
182 if (cs_bancheck(np,cp)) {
183 /* They got kicked.. */
184 return;
185 }
186
187 /* Check for other ban lurking on channel which we are enforcing */
188 if (CIsEnforce(rcp) && nickbanned(np,cp,1)) {
189 localkickuser(chanservnick,cp,np,"Banned.");
190 return;
191 }
192
193 /* Check for +b chanlev flag */
194 if (rcup && CUIsBanned(rcup)) {
195 cs_banuser(NULL, cip, np, NULL);
196 cs_timerfunc(cip);
197 return;
198 }
199
200 /* Check for +k chan flag */
201 if (CIsKnownOnly(rcp) && !(rcup && CUKnown(rcup))) {
202 /* Don't ban if they are already "visibly" banned for some reason. */
203 if (IsInviteOnly(cp) || (IsRegOnly(cp) && !IsAccount(np))) {
204 localkickuser(chanservnick,cp,np,"Authorised users only.");
205 } else {
206 cs_banuser(NULL, cip, np, "Authorised users only.");
207 cs_timerfunc(cip);
208 }
209 return;
210 }
211 }
212
213 if (!rup || !rcup) {
214 /* They're not a registered user, so deop if it is a create */
215 if (isopped && !IsService(np)) {
216 modes |= MC_DEOP;
217 }
218 if (CIsVoiceAll(rcp)) {
219 modes |= MC_VOICE;
220 }
221
222 if (CIsWelcome(rcp)) {
223 dowelcome=1; /* Send welcome message */
224 } else if (!CIsJoined(rcp) && iscreate) {
225 dowelcome=2; /* Send a generic warning */
226 }
227 } else {
228
229 /* DB update removed for efficiency..
230 * csdb_updatelastjoin(rcup); */
231
232 /* They are registered, let's see what to do with them */
233 if (CUIsOp(rcup) && (CIsAutoOp(rcp) || CUIsAutoOp(rcup) || CUIsProtect(rcup) || CIsProtect(rcp)) &&
234 !CUIsDeny(rcup)) {
235 /* Auto op */
236 if (!isopped) {
237 modes |= MC_OP;
238 cs_logchanop(rcp, np->nick, rup);
239 }
240 } else {
241 /* Not auto op; deop them if they are opped and are not allowed them */
242 if (isopped && !CUHasOpPriv(rcup) && !IsService(np)) {
243 modes |= MC_DEOP;
244 }
245
246 if (!CUIsQuiet(rcup) && /* Not +q */
247 ((CUIsVoice(rcup) && (CIsAutoVoice(rcp) || CUIsAutoVoice(rcup) ||
248 CIsProtect(rcp) || CUIsProtect(rcup))) || /* +[gp]v */
249 (CIsVoiceAll(rcp)))) { /* Or voice-all chan */
250 modes |= MC_VOICE;
251 }
252 }
253
254 if (CIsWelcome(rcp) && !CUIsHideWelcome(rcup)) {
255 dowelcome=1;
256 }
257 }
258
259 /* Actually do the mode change, if any */
260 if (modes) {
261 localsetmodes(chanservnick, cp, np, modes);
262 }
263
264 switch(dowelcome) {
265
266 case 1: if (rcp->welcome)
267 chanservsendmessage(np,"[%s] %s",cip->name->content, rcp->welcome->content);
268 break;
269
270 case 2: if (chanservnick) {
271 /* Channel x is protected by y */
272 chanservstdmessage(np,QM_PROTECTED,cip->name->content,chanservnick->nick);
273 }
274 break;
275 }
276
277 /* Display infoline if... (deep breath) user is registered, known on channel,
278 * user,channel,chanlev all +i and user,channel,chanlev all -s AND Q online */
279 if (rup && rcup &&
280 CIsInfo(rcp) && UIsInfo(rcup->user) && CUIsInfo(rcup) &&
281 !CIsNoInfo(rcp) && !UIsNoInfo(rcup->user) && !CUIsNoInfo(rcup) && chanservnick) {
282 if (rcup->info && *(rcup->info->content)) {
283 /* Chan-specific info */
284 sendmessagetochannel(chanservnick, cp, "[%s] %s",np->nick, rcup->info->content);
285 } else if (rup->info && *(rup->info->content)) {
286 /* Default info */
287 sendmessagetochannel(chanservnick, cp, "[%s] %s",np->nick, rup->info->content);
288 }
289 }
290 }
291
292 /* cs_handlemodechange:
293 * Handle mode change on channel
294 */
295
296 void cs_handlemodechange(int hooknum, void *arg) {
297 void **arglist=(void **)arg;
298 channel *cp=(channel *)arglist[0];
299 long changeflags=(long)arglist[2];
300 regchan *rcp;
301
302 if ((rcp=cp->index->exts[chanservext])==NULL || CIsSuspended(rcp))
303 return;
304
305 if (changeflags & MODECHANGE_MODES)
306 rcp->status |= QCSTAT_MODECHECK;
307
308 if (changeflags & MODECHANGE_USERS)
309 rcp->status |= QCSTAT_OPCHECK;
310
311 if (changeflags & MODECHANGE_BANS)
312 rcp->status |= QCSTAT_BANCHECK;
313
314 cs_timerfunc(rcp->index);
315 }
316
317 void cs_handleburst(int hooknum, void *arg) {
318 channel *cp=(channel *)arg;
319 regchan *rcp;
320
321 if ((rcp=cp->index->exts[chanservext])==NULL || CIsSuspended(rcp))
322 return;
323
324 /* Check everything at some future time */
325 /* If autolimit is on, make sure the limit gets reset at that point too */
326 if (CIsAutoLimit(rcp)) {
327 rcp->limit=0;
328 }
329 cs_schedupdate(cp->index, 1, 5);
330 rcp->status |= (QCSTAT_OPCHECK | QCSTAT_MODECHECK | QCSTAT_BANCHECK);
331 rcp->lastbancheck=0; /* Force re-check of all bans on channel */
332 }
333
334 /* cs_handleopchange:
335 * Handle [+-][ov] event
336 */
337
338 void cs_handleopchange(int hooknum, void *arg) {
339 void **arglist=(void **)arg;
340 channel *cp=(channel *)arglist[0];
341 regchan *rcp;
342 regchanuser *rcup;
343 reguser *rup;
344 nick *target=(nick *)arglist[2];
345 short modes=0;
346
347 /* Check that the channel is registered and active */
348 if ((rcp=cp->index->exts[chanservext])==NULL || CIsSuspended(rcp))
349 return;
350
351 rup=getreguserfromnick(target);
352
353 switch(hooknum) {
354 case HOOK_CHANNEL_OPPED:
355 if (CIsBitch(rcp) && !IsService(target)) {
356 if (!rup || (rcup=findreguseronchannel(rcp,rup))==NULL || !CUIsOp(rcup) || CUIsDeny(rcup))
357 modes |= MC_DEOP;
358 }
359 break;
360
361 /* Open question:
362 * Should +b prevent extra voices?
363 * If not, should we have a seperate mode that does?
364 */
365
366 case HOOK_CHANNEL_DEOPPED:
367 if (CIsProtect(rcp)) {
368 if (rup && (rcup=findreguseronchannel(rcp,rup))!=NULL && CUIsOp(rcup) && !CUIsDeny(rcup))
369 modes |= MC_OP;
370 }
371 break;
372
373 case HOOK_CHANNEL_DEVOICED:
374 if (CIsProtect(rcp)) {
375 if (rcp && (rcup=findreguseronchannel(rcp,rup))!=NULL && CUIsVoice(rcup) && !CUIsQuiet(rcup))
376 modes |= MC_VOICE;
377 }
378 break;
379 }
380
381 if (modes)
382 localsetmodes(chanservnick, cp, target, modes);
383 }
384
385 /* cs_handlenewban:
386 * Handle ban being added to channel
387 */
388
389 void cs_handlenewban(int hooknum, void *arg) {
390 void **arglist=(void **)arg;
391 regchan *rcp;
392 channel *cp=(channel *)arglist[0];
393 chanban *cbp;
394 int i;
395 nick *np;
396
397 if ((rcp=cp->index->exts[chanservext])==NULL || CIsSuspended(rcp))
398 return;
399
400 if ((cbp=cp->bans)==NULL) {
401 Error("chanserv",ERR_WARNING,"Told ban added but no bans on channel?");
402 return;
403 }
404
405 if (CIsEnforce(rcp)) {
406 for (i=0;i<cp->users->hashsize;i++) {
407 if (cp->users->content[i]!=nouser) {
408 if ((np=getnickbynumeric(cp->users->content[i]))==NULL) {
409 Error("chanserv",ERR_WARNING,"Found user on channel %s who doesn't exist!",cp->index->name->content);
410 continue;
411 }
412 if (!IsService(np) && nickmatchban(np,cbp,1)) {
413 localkickuser(chanservnick,cp,np,"Banned.");
414 }
415 }
416 }
417 }
418 }
419
420 /* cs_handletopicchange:
421 * Handle topic change on channel
422 */
423
424 void cs_handletopicchange(int hooknum, void *arg) {
425 void **arglist=(void **)arg;
426 channel *cp=(channel *)arglist[0];
427 regchan *rcp;
428
429 if ((rcp=cp->index->exts[chanservext])==NULL || CIsSuspended(rcp))
430 return;
431
432 if (CIsForceTopic(rcp)) {
433 /* Forced topic: change it back even if blank */
434 localsettopic(chanservnick, cp, (rcp->topic)?rcp->topic->content:"");
435 } else if (CIsTopicSave(rcp)) {
436 if (rcp->topic) {
437 freesstring(rcp->topic);
438 }
439 if (cp->topic) {
440 rcp->topic=getsstring(cp->topic->content,TOPICLEN);
441 } else {
442 rcp->topic=NULL;
443 }
444 csdb_updatetopic(rcp);
445 }
446 }
447
448 /*
449 * active is defined as at least 2 real users (no snailbot) currently present
450 * and at least one op/master/owner.
451 * this is O(n) worst case, but very very likely to just be 2 checks.
452 */
453 int cs_ischannelactive(channel *cp, regchan *rcp) {
454 int real_users = 0;
455 int seen_op = 0;
456 nick *np;
457 int i;
458
459 for (i=0;i<cp->users->hashsize;i++) {
460 if(cp->users->content[i]==nouser)
461 continue;
462
463 if((np=getnickbynumeric(cp->users->content[i]))==NULL)
464 continue;
465
466 if(NickOnServiceServer(np)) /* bad snailbot */
467 continue;
468
469 real_users++;
470
471 if(rcp && !seen_op) { /* only look for ops if we haven't seen one yet */
472 reguser *rup = getreguserfromnick(np); /* O(1) */
473 if(rup) {
474 regchanuser *rcup = findreguseronchannel(rcp, rup); /* O(1) */
475 if(rcup && CUHasOpPriv(rcup))
476 seen_op = 1;
477 }
478 }
479
480 /* so once we've seen X real users AND:
481 * - we're not looking for ops as a check had already been done by the caller
482 * - OR we're looking for ops AND we found them
483 * then the channel is active, and we're done
484 */
485 if((real_users >= CLEANUP_MIN_CHAN_SIZE) && (!rcp || (rcp && seen_op)))
486 return 1;
487 }
488
489 return 0;
490 }
491