]> jfr.im git - irc/quakenet/newserv.git/blob - chanserv/chanservnetevents.c
32cadf43ab05be824bdc5aff006acbaa1d0e6d0b
[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)) && CUKnown(rcup) && cp->users->totalusers >= 3)
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 /* Check for "Q ban" */
179 if (!IsService(np) && cs_bancheck(np,cp)) {
180 /* They got kicked.. */
181 return;
182 }
183
184 /* Check for other ban lurking on channel which we are enforcing */
185 if (!IsService(np) && CIsEnforce(rcp) && nickbanned_visible(np,cp)) {
186 localkickuser(chanservnick,cp,np,"Banned.");
187 return;
188 }
189
190 /* Check for +b chanlev flag */
191 if (!IsService(np) && rcup && CUIsBanned(rcup)) {
192 cs_banuser(NULL, cip, np, NULL);
193 cs_timerfunc(cip);
194 return;
195 }
196
197 /* Check for +k chan flag */
198 if (!IsService(np) && CIsKnownOnly(rcp) && !(rcup && CUKnown(rcup))) {
199 /* Don't ban if they are already "visibly" banned for some reason. */
200 if (IsInviteOnly(cp) || (IsRegOnly(cp) && !IsAccount(np))) {
201 localkickuser(chanservnick,cp,np,"Authorised users only.");
202 } else {
203 cs_banuser(NULL, cip, np, "Authorised users only.");
204 cs_timerfunc(cip);
205 }
206 return;
207 }
208
209 if (!rup || !rcup) {
210 /* They're not a registered user, so deop if it is a create */
211 if (isopped && !IsService(np)) {
212 modes |= MC_DEOP;
213 }
214 if (CIsVoiceAll(rcp)) {
215 modes |= MC_VOICE;
216 }
217
218 if (CIsWelcome(rcp)) {
219 dowelcome=1; /* Send welcome message */
220 } else if (!CIsJoined(rcp) && iscreate) {
221 dowelcome=2; /* Send a generic warning */
222 }
223 } else {
224
225 /* DB update removed for efficiency..
226 * csdb_updatelastjoin(rcup); */
227
228 /* They are registered, let's see what to do with them */
229 if (CUIsOp(rcup) && (CIsAutoOp(rcp) || CUIsAutoOp(rcup) || CUIsProtect(rcup) || CIsProtect(rcp)) &&
230 !CUIsDeny(rcup)) {
231 /* Auto op */
232 if (!isopped) {
233 modes |= MC_OP;
234 cs_logchanop(rcp, np->nick, rup);
235 }
236 } else {
237 /* Not auto op; deop them if they are opped and are not allowed them */
238 if (isopped && !CUHasOpPriv(rcup) && !IsService(np)) {
239 modes |= MC_DEOP;
240 }
241
242 if (!CUIsQuiet(rcup) && /* Not +q */
243 ((CUIsVoice(rcup) && (CIsAutoVoice(rcp) || CUIsAutoVoice(rcup) ||
244 CIsProtect(rcp) || CUIsProtect(rcup))) || /* +[gp]v */
245 (CIsVoiceAll(rcp)))) { /* Or voice-all chan */
246 modes |= MC_VOICE;
247 }
248 }
249
250 if (CIsWelcome(rcp) && !CUIsHideWelcome(rcup)) {
251 dowelcome=1;
252 }
253 }
254
255 /* Actually do the mode change, if any */
256 if (modes) {
257 localsetmodes(chanservnick, cp, np, modes);
258 }
259
260 switch(dowelcome) {
261
262 case 1: if (rcp->welcome)
263 chanservsendmessage(np,"[%s] %s",cip->name->content, rcp->welcome->content);
264 break;
265
266 case 2: if (chanservnick) {
267 /* Channel x is protected by y */
268 chanservstdmessage(np,QM_PROTECTED,cip->name->content,chanservnick->nick);
269 }
270 break;
271 }
272
273 /* Display infoline if... (deep breath) user is registered, known on channel,
274 * user,channel,chanlev all +i and user,channel,chanlev all -s AND Q online */
275 if (rup && rcup &&
276 CIsInfo(rcp) && UIsInfo(rcup->user) && CUIsInfo(rcup) &&
277 !CIsNoInfo(rcp) && !UIsNoInfo(rcup->user) && !CUIsNoInfo(rcup) && chanservnick) {
278 if (rcup->info && *(rcup->info->content)) {
279 /* Chan-specific info */
280 sendmessagetochannel(chanservnick, cp, "[%s] %s",np->nick, rcup->info->content);
281 } else if (rup->info && *(rup->info->content)) {
282 /* Default info */
283 sendmessagetochannel(chanservnick, cp, "[%s] %s",np->nick, rup->info->content);
284 }
285 }
286 }
287
288 /* cs_handlemodechange:
289 * Handle mode change on channel
290 */
291
292 void cs_handlemodechange(int hooknum, void *arg) {
293 void **arglist=(void **)arg;
294 channel *cp=(channel *)arglist[0];
295 long changeflags=(long)arglist[2];
296 regchan *rcp;
297
298 if ((rcp=cp->index->exts[chanservext])==NULL || CIsSuspended(rcp))
299 return;
300
301 if (changeflags & MODECHANGE_MODES)
302 rcp->status |= QCSTAT_MODECHECK;
303
304 if (changeflags & MODECHANGE_USERS)
305 rcp->status |= QCSTAT_OPCHECK;
306
307 if (changeflags & MODECHANGE_BANS)
308 rcp->status |= QCSTAT_BANCHECK;
309
310 cs_timerfunc(rcp->index);
311 }
312
313 void cs_handleburst(int hooknum, void *arg) {
314 channel *cp=(channel *)arg;
315 regchan *rcp;
316
317 if ((rcp=cp->index->exts[chanservext])==NULL || CIsSuspended(rcp))
318 return;
319
320 /* Check everything at some future time */
321 /* If autolimit is on, make sure the limit gets reset at that point too */
322 if (CIsAutoLimit(rcp)) {
323 rcp->limit=0;
324 }
325 cs_schedupdate(cp->index, 1, 5);
326 rcp->status |= (QCSTAT_OPCHECK | QCSTAT_MODECHECK | QCSTAT_BANCHECK);
327 rcp->lastbancheck=0; /* Force re-check of all bans on channel */
328 }
329
330 /* cs_handleopchange:
331 * Handle [+-][ov] event
332 */
333
334 void cs_handleopchange(int hooknum, void *arg) {
335 void **arglist=(void **)arg;
336 channel *cp=(channel *)arglist[0];
337 regchan *rcp;
338 regchanuser *rcup;
339 reguser *rup;
340 nick *target=(nick *)arglist[2];
341 short modes=0;
342
343 /* Check that the channel is registered and active */
344 if ((rcp=cp->index->exts[chanservext])==NULL || CIsSuspended(rcp))
345 return;
346
347 rup=getreguserfromnick(target);
348
349 switch(hooknum) {
350 case HOOK_CHANNEL_OPPED:
351 if (CIsBitch(rcp) && !IsService(target)) {
352 if (!rup || (rcup=findreguseronchannel(rcp,rup))==NULL || !CUIsOp(rcup) || CUIsDeny(rcup))
353 modes |= MC_DEOP;
354 }
355 break;
356
357 /* Open question:
358 * Should +b prevent extra voices?
359 * If not, should we have a seperate mode that does?
360 */
361
362 case HOOK_CHANNEL_DEOPPED:
363 if (CIsProtect(rcp)) {
364 if (rup && (rcup=findreguseronchannel(rcp,rup))!=NULL && CUIsOp(rcup) && !CUIsDeny(rcup))
365 modes |= MC_OP;
366 }
367 break;
368
369 case HOOK_CHANNEL_DEVOICED:
370 if (CIsProtect(rcp)) {
371 if (rcp && (rcup=findreguseronchannel(rcp,rup))!=NULL && CUIsVoice(rcup) && !CUIsQuiet(rcup))
372 modes |= MC_VOICE;
373 }
374 break;
375 }
376
377 if (modes)
378 localsetmodes(chanservnick, cp, target, modes);
379 }
380
381 /* cs_handlenewban:
382 * Handle ban being added to channel
383 */
384
385 void cs_handlenewban(int hooknum, void *arg) {
386 void **arglist=(void **)arg;
387 regchan *rcp;
388 channel *cp=(channel *)arglist[0];
389 chanban *cbp;
390 int i;
391 nick *np;
392
393 if ((rcp=cp->index->exts[chanservext])==NULL || CIsSuspended(rcp))
394 return;
395
396 if ((cbp=cp->bans)==NULL) {
397 Error("chanserv",ERR_WARNING,"Told ban added but no bans on channel?");
398 return;
399 }
400
401 if (CIsEnforce(rcp)) {
402 for (i=0;i<cp->users->hashsize;i++) {
403 if (cp->users->content[i]!=nouser) {
404 if ((np=getnickbynumeric(cp->users->content[i]))==NULL) {
405 Error("chanserv",ERR_WARNING,"Found user on channel %s who doesn't exist!",cp->index->name->content);
406 continue;
407 }
408 if (!IsService(np) && nickmatchban_visible(np,cbp)) {
409 localkickuser(chanservnick,cp,np,"Banned.");
410 }
411 }
412 }
413 }
414 }
415
416 /* cs_handletopicchange:
417 * Handle topic change on channel
418 */
419
420 void cs_handletopicchange(int hooknum, void *arg) {
421 void **arglist=(void **)arg;
422 channel *cp=(channel *)arglist[0];
423 regchan *rcp;
424
425 if ((rcp=cp->index->exts[chanservext])==NULL || CIsSuspended(rcp))
426 return;
427
428 if (CIsForceTopic(rcp)) {
429 if (rcp->topic) {
430 /* Forced topic: change it back */
431 localsettopic(chanservnick, cp, rcp->topic->content);
432 }
433 } else if (CIsTopicSave(rcp)) {
434 if (rcp->topic) {
435 freesstring(rcp->topic);
436 }
437 if (cp->topic) {
438 rcp->topic=getsstring(cp->topic->content,TOPICLEN);
439 } else {
440 rcp->topic=NULL;
441 }
442 csdb_updatetopic(rcp);
443 }
444 }