]>
Commit | Line | Data |
---|---|---|
c86edd1d Q |
1 | /* |
2 | * chanservuser.c: | |
3 | * This file maintains the functions associated with the | |
4 | * user on the network relating to the channel service | |
5 | */ | |
6 | ||
7 | #include "chanserv.h" | |
8 | ||
32562540 | 9 | #include "../core/hooks.h" |
c86edd1d Q |
10 | #include "../core/schedule.h" |
11 | #include "../core/config.h" | |
12 | #include "../localuser/localuser.h" | |
13 | #include "../localuser/localuserchannel.h" | |
14 | #include "../irc/irc_config.h" | |
15 | #include "../lib/sstring.h" | |
16 | #include "../nick/nick.h" | |
17 | #include "../parser/parser.h" | |
18 | #include "../lib/splitline.h" | |
19 | ||
20 | #include <string.h> | |
21 | #include <stdio.h> | |
22 | #include <stdarg.h> | |
23 | #include <stdlib.h> | |
24 | ||
25 | nick *chanservnick; | |
26 | CommandTree *cscommands; | |
27 | CommandTree *csctcpcommands; | |
28 | ||
29 | /* Local prototypes */ | |
30 | void chanservuserhandler(nick *target, int message, void **params); | |
31 | ||
32 | void chanservreguser(void *arg) { | |
33 | sstring *csnick,*csuser,*cshost,*csrealname; | |
34 | chanindex *cip; | |
35 | regchan *rcp; | |
36 | int i; | |
37 | ||
38 | csnick=getcopyconfigitem("chanserv","nick","Q",NICKLEN); | |
39 | csuser=getcopyconfigitem("chanserv","user","TheQBot",USERLEN); | |
40 | cshost=getcopyconfigitem("chanserv","host","some.host",HOSTLEN); | |
41 | csrealname=getcopyconfigitem("chanserv","realname","ChannelService",REALLEN); | |
42 | ||
316959c1 | 43 | Error("chanserv",ERR_INFO,"Connecting %s...",csnick->content); |
44 | ||
c86edd1d Q |
45 | chanservnick=registerlocaluser(csnick->content,csuser->content,cshost->content, |
46 | csrealname->content,NULL, | |
47 | UMODE_INV|UMODE_SERVICE|UMODE_DEAF, | |
48 | &chanservuserhandler); | |
49 | ||
50 | freesstring(csnick); | |
51 | freesstring(csuser); | |
52 | freesstring(cshost); | |
53 | freesstring(csrealname); | |
54 | ||
55 | /* Now join channels */ | |
56 | for (i=0;i<CHANNELHASHSIZE;i++) { | |
57 | for (cip=chantable[i];cip;cip=cip->next) { | |
58 | if (cip->channel && (rcp=cip->exts[chanservext]) && !CIsSuspended(rcp)) { | |
316959c1 | 59 | /* This will do timestamp faffing even if it won't actually join */ |
60 | chanservjoinchan(cip->channel); | |
c86edd1d Q |
61 | /* Do a check at some future time.. */ |
62 | cs_schedupdate(cip, 1, 5); | |
63 | rcp->status |= (QCSTAT_OPCHECK | QCSTAT_MODECHECK | QCSTAT_BANCHECK); | |
64 | if (CIsForceTopic(rcp) || CIsTopicSave(rcp)) | |
65 | localsettopic(chanservnick, cip->channel, (rcp->topic) ? rcp->topic->content : ""); | |
66 | } | |
67 | } | |
68 | } | |
69 | ||
316959c1 | 70 | Error("chanserv",ERR_INFO,"Loaded and joined channels."); |
71 | ||
c86edd1d Q |
72 | if (chanserv_init_status == CS_INIT_NOUSER) { |
73 | /* If this is the first time, perform last init tasks */ | |
74 | chanserv_finalinit(); | |
75 | } | |
76 | } | |
77 | ||
78 | void chanservuserhandler(nick *target, int message, void **params) { | |
79 | nick *sender; | |
80 | char *msg; | |
81 | Command *cmd; | |
82 | char *cargv[30]; | |
83 | int cargc; | |
84 | reguser *rup; | |
85 | char *chp; | |
86 | ||
87 | switch(message) { | |
88 | case LU_KILLED: | |
89 | scheduleoneshot(time(NULL),&chanservreguser,NULL); | |
90 | chanservnick=NULL; | |
91 | break; | |
92 | ||
93 | case LU_PRIVMSG: | |
94 | case LU_SECUREMSG: | |
95 | sender=(nick *)params[0]; | |
96 | msg=(char *)params[1]; | |
97 | ||
98 | if (!(cargc=splitline(msg,cargv,30,0))) | |
99 | return; /* Ignore blank lines */ | |
100 | ||
101 | if (cargv[0][0]==1) { | |
102 | /* CTCP */ | |
103 | for (chp=cargv[0]+1;*chp;chp++) { | |
104 | if (*chp=='\001') { | |
105 | *chp='\0'; | |
106 | break; | |
107 | } | |
108 | } | |
109 | cmd=findcommandintree(csctcpcommands, cargv[0]+1, 1); | |
110 | if (cmd) { | |
111 | rejoinline(cargv[1],cargc-1); | |
112 | cmd->handler((void *)sender, cargc-1, &(cargv[1])); | |
113 | } | |
114 | } else { | |
115 | cmd=findcommandintree(cscommands, cargv[0], 1); | |
116 | ||
117 | if (!cmd) { | |
118 | chanservstdmessage(sender, QM_UNKNOWNCMD, cargv[0]); | |
119 | break; | |
120 | } | |
121 | ||
122 | if ((cmd->level & QCMD_SECURE) && (message != LU_SECUREMSG)) { | |
123 | chanservstdmessage(sender, QM_SECUREONLY, cargv[0], chanservnick->nick, myserver->content); | |
124 | break; | |
125 | } | |
126 | ||
127 | if ((cmd->level & QCMD_AUTHED)) { | |
128 | if (!(rup=getreguserfromnick(sender))) { | |
129 | chanservstdmessage(sender, QM_AUTHEDONLY, cargv[0]); | |
130 | break; | |
131 | } | |
132 | if (UIsSuspended(rup) || (rup->status & QUSTAT_DEAD)) { | |
133 | chanservstdmessage(sender, QM_BADAUTH, cargv[0]); | |
134 | break; | |
135 | } | |
136 | } | |
137 | ||
138 | if ((cmd->level & QCMD_NOTAUTHED) && (getreguserfromnick(sender))) { | |
139 | chanservstdmessage(sender, QM_UNAUTHEDONLY, cargv[0]); | |
140 | break; | |
141 | } | |
142 | ||
143 | if ((cmd->level & QCMD_HELPER) && | |
144 | (!(rup=getreguserfromnick(sender)) || !UHasHelperPriv(rup))) { | |
145 | chanservstdmessage(sender, QM_NOACCESS, cargv[0]); | |
146 | break; | |
147 | } | |
148 | ||
149 | if ((cmd->level & QCMD_OPER) && | |
150 | (!IsOper(sender) || !(rup=getreguserfromnick(sender)) || !UHasOperPriv(rup))) { | |
151 | chanservstdmessage(sender, QM_NOACCESS, cargv[0]); | |
152 | break; | |
153 | } | |
154 | ||
155 | if ((cmd->level & QCMD_ADMIN) && | |
156 | (!IsOper(sender) || !(rup=getreguserfromnick(sender)) || !UHasAdminPriv(rup))) { | |
157 | chanservstdmessage(sender, QM_NOACCESS, cargv[0]); | |
158 | break; | |
159 | } | |
160 | ||
161 | if ((cmd->level & QCMD_DEV) && | |
162 | (!IsOper(sender) || !(rup=getreguserfromnick(sender)) || !UIsDev(rup))) { | |
163 | chanservstdmessage(sender, QM_NOACCESS, cargv[0]); | |
164 | break; | |
165 | } | |
166 | ||
167 | if (cmd->maxparams < (cargc-1)) { | |
168 | rejoinline(cargv[cmd->maxparams],cargc-(cmd->maxparams)); | |
169 | cargc=(cmd->maxparams)+1; | |
170 | } | |
171 | ||
172 | cmd->handler((void *)sender, cargc-1, &(cargv[1])); | |
173 | } | |
174 | break; | |
175 | ||
176 | default: | |
177 | break; | |
178 | } | |
179 | } | |
180 | ||
181 | void chanservcommandinit() { | |
182 | cscommands=newcommandtree(); | |
183 | csctcpcommands=newcommandtree(); | |
184 | } | |
185 | ||
186 | void chanservcommandclose() { | |
187 | destroycommandtree(cscommands); | |
188 | destroycommandtree(csctcpcommands); | |
189 | } | |
190 | ||
191 | void chanservaddcommand(char *command, int flags, int maxparams, CommandHandler handler, char *description) { | |
192 | Command *newcmd; | |
193 | cmdsummary *summary; | |
194 | ||
195 | newcmd=addcommandtotree(cscommands, command, flags, maxparams, handler); | |
196 | /* Allocate a summary record to put the description in */ | |
197 | summary=(cmdsummary *)malloc(sizeof(cmdsummary)); | |
198 | memset((void *)summary,0,sizeof(cmdsummary)); | |
199 | ||
200 | summary->def=getsstring(description, 250); | |
201 | ||
202 | newcmd->ext=(void *)summary; | |
203 | loadcommandsummary(newcmd); | |
204 | } | |
205 | ||
206 | void chanservaddctcpcommand(char *command, CommandHandler handler) { | |
207 | addcommandtotree(csctcpcommands, command, 0, 1, handler); | |
208 | } | |
209 | ||
210 | void chanservremovectcpcommand(char *command, CommandHandler handler) { | |
211 | deletecommandfromtree(csctcpcommands, command, handler); | |
212 | } | |
213 | ||
214 | void chanservremovecommand(char *command, CommandHandler handler) { | |
215 | Command *cmd; | |
216 | cmdsummary *summary; | |
217 | int i; | |
218 | ||
219 | if (!(cmd=findcommandintree(cscommands, command, 1))) { | |
220 | Error("chanserv",ERR_WARNING,"Tried to unregister unknown command %s",command); | |
221 | return; | |
222 | } | |
223 | ||
224 | summary=(cmdsummary *)cmd->ext; | |
225 | freesstring(summary->def); | |
226 | for (i=0;i<MAXLANG;i++) { | |
227 | if (summary->bylang[i]) | |
228 | freesstring(summary->bylang[i]); | |
229 | } | |
230 | ||
231 | free(summary); | |
232 | ||
233 | deletecommandfromtree(cscommands, command, handler); | |
234 | } | |
235 | ||
236 | void chanservjoinchan(channel *cp) { | |
237 | regchan *rcp; | |
a54e438d | 238 | |
239 | /* Skip unregistered channels */ | |
240 | if (!(rcp=cp->index->exts[chanservext])) | |
241 | return; | |
242 | ||
243 | /* Check for a new timestamp */ | |
244 | if ((!rcp->ltimestamp) || (cp->timestamp < rcp->ltimestamp)) { | |
245 | rcp->ltimestamp=cp->timestamp; | |
246 | csdb_updatechanneltimestamp(rcp); | |
247 | } | |
c86edd1d | 248 | |
a54e438d | 249 | /* We won't be doing any joining or parting if we're not online */ |
250 | if (!chanservnick) | |
c86edd1d Q |
251 | return; |
252 | ||
253 | if ((CIsSuspended(rcp) || !CIsJoined(rcp)) && getnumerichandlefromchanhash(cp->users, chanservnick->numeric)) { | |
334b567e P |
254 | if(rcp->suspendreason) { |
255 | localpartchannel(chanservnick, cp, rcp->suspendreason->content); | |
256 | } else { | |
257 | localpartchannel(chanservnick, cp, NULL); | |
258 | } | |
c86edd1d Q |
259 | } |
260 | ||
a54e438d | 261 | /* OK, we're going to join the channel. Since our timestamp must be less |
262 | * than or equal to the one already there it should be OK to burst on. | |
263 | * | |
264 | * We will try and burst our view of the world; if the timestamps are | |
265 | * actually equal this will be mostly ignored and we will have to fix it | |
266 | * up later */ | |
c86edd1d | 267 | if (!CIsSuspended(rcp) && CIsJoined(rcp) && !getnumerichandlefromchanhash(cp->users, chanservnick->numeric)) { |
a54e438d | 268 | localburstontochannel(cp, chanservnick, rcp->ltimestamp, rcp->forcemodes, rcp->limit, (rcp->key)?rcp->key->content:NULL); |
c86edd1d | 269 | } |
b263aa79 | 270 | |
271 | /* Maybe we're not joining, but the timestamp is wrong. Fix that too. | |
272 | * | |
273 | * XXX: Actually, let's not do this until we've considered the obvious | |
274 | * problem that this will make it impossible to re-establish a +k/+i | |
275 | * enforced channel which has become empty... | |
276 | */ | |
277 | /* | |
278 | if (!CIsSuspended(rcp) && !CIsJoined(rcp) && (cp->timestamp > rcp->ltimestamp)) { | |
279 | localburstontochannel(cp, NULL, rcp->ltimestamp, rcp->forcemodes, rcp->limit, (rcp->key)?rcp->key->content:NULL); | |
280 | } | |
281 | */ | |
c86edd1d Q |
282 | } |
283 | ||
284 | void chanservstdmessage(nick *np, int messageid, ... ) { | |
285 | char buf[5010]; | |
286 | char buf2[512]; | |
287 | int notice; | |
288 | reguser *rup; | |
289 | int language; | |
290 | va_list va; | |
291 | char *message; | |
292 | char *bp2,*bp; | |
293 | int len; | |
294 | ||
295 | if (getreguserfromnick(np) == NULL) { | |
296 | notice=1; | |
297 | language=0; | |
298 | } else { | |
299 | rup=getreguserfromnick(np); | |
300 | if(UIsNotice(rup)) { | |
301 | notice=1; | |
302 | } else { | |
303 | notice=0; | |
304 | } | |
305 | language=rup->languageid; | |
306 | } | |
307 | ||
308 | if (csmessages[language][messageid]) { | |
309 | message=csmessages[language][messageid]->content; | |
310 | } else if (csmessages[0][messageid]) { | |
311 | message=csmessages[0][messageid]->content; | |
312 | } else { | |
313 | message=defaultmessages[messageid]; | |
314 | } | |
315 | ||
316 | va_start(va,messageid); | |
317 | vsnprintf(buf,5000,message,va); | |
318 | va_end(va); | |
319 | ||
320 | len=0; | |
321 | bp2=buf2; | |
322 | for (bp=buf; ;bp++) { | |
323 | /* We send something if we hit a \n, fill the buffer or run out of source */ | |
324 | if (*bp=='\n' || len>490 || !*bp) { | |
325 | if (*bp && *bp!='\n') { | |
326 | bp--; | |
327 | } | |
328 | ||
329 | *bp2='\0'; | |
330 | ||
331 | if (chanservnick && *buf2) { | |
332 | if (notice) { | |
333 | sendnoticetouser(chanservnick,np,"%s",buf2); | |
334 | } else { | |
335 | sendmessagetouser(chanservnick,np,"%s",buf2); | |
336 | } | |
337 | } | |
338 | ||
339 | /* If we ran out of buffer, get out here */ | |
340 | if (!*bp) | |
341 | break; | |
342 | ||
343 | bp2=buf2; | |
344 | len=0; | |
345 | } else { | |
346 | len++; | |
347 | *bp2++=*bp; | |
348 | } | |
349 | } | |
350 | } | |
351 | ||
352 | void chanservsendmessage(nick *np, char *message, ... ) { | |
353 | char buf[5010]; /* Very large buffer.. */ | |
354 | char buf2[512], *bp, *bp2; | |
355 | int notice; | |
356 | int len; | |
357 | reguser *rup; | |
358 | ||
359 | va_list va; | |
360 | ||
361 | va_start(va,message); | |
362 | vsnprintf(buf,5000,message,va); | |
363 | va_end(va); | |
364 | ||
365 | if (getreguserfromnick(np) == NULL) { | |
366 | notice=1; | |
367 | } else { | |
368 | rup=getreguserfromnick(np); | |
369 | if(UIsNotice(rup)) { | |
370 | notice=1; | |
371 | } else { | |
372 | notice=0; | |
373 | } | |
374 | } | |
375 | ||
376 | len=0; | |
377 | bp2=buf2; | |
378 | for (bp=buf; ;bp++) { | |
379 | /* We send something if we hit a \n, fill the buffer or run out of source */ | |
380 | if (*bp=='\n' || len>490 || !*bp) { | |
381 | if (*bp && *bp!='\n') { | |
382 | bp--; | |
383 | } | |
384 | ||
385 | *bp2='\0'; | |
386 | ||
387 | if (chanservnick && *buf2) { | |
388 | if (notice) { | |
389 | sendnoticetouser(chanservnick,np,"%s",buf2); | |
390 | } else { | |
391 | sendmessagetouser(chanservnick,np,"%s",buf2); | |
392 | } | |
393 | } | |
394 | ||
395 | /* If we ran out of buffer, get out here */ | |
396 | if (!*bp) | |
397 | break; | |
398 | ||
399 | bp2=buf2; | |
400 | len=0; | |
401 | } else { | |
402 | len++; | |
403 | *bp2++=*bp; | |
404 | } | |
405 | } | |
406 | } | |
407 | ||
408 | void chanservwallmessage(char *message, ... ) { | |
409 | char buf[5010]; /* Very large buffer.. */ | |
410 | va_list va; | |
411 | nick *np; | |
412 | unsigned int i=0; | |
413 | ||
414 | va_start(va,message); | |
415 | vsnprintf(buf,5000,message,va); | |
416 | va_end(va); | |
417 | ||
418 | /* Scan for users */ | |
419 | for (i=0;i<NICKHASHSIZE;i++) | |
420 | for (np=nicktable[i];np;np=np->next) | |
421 | if (IsOper(np)) | |
422 | chanservsendmessage(np, "%s", buf); | |
423 | } | |
424 | ||
425 | void chanservkillstdmessage(nick *target, int messageid, ... ) { | |
426 | char buf[512]; | |
427 | int language; | |
428 | char* message; | |
429 | va_list va; | |
430 | reguser *rup; | |
431 | ||
432 | if (!(rup=getreguserfromnick(target))) | |
433 | language=0; | |
434 | else | |
435 | language=rup->languageid; | |
436 | ||
437 | if (csmessages[language][messageid]) | |
438 | message=csmessages[language][messageid]->content; | |
439 | else if (csmessages[0][messageid]) | |
440 | message=csmessages[0][messageid]->content; | |
441 | else | |
442 | message=defaultmessages[messageid]; | |
443 | ||
444 | va_start(va, messageid); | |
445 | vsnprintf(buf, 511, message, va); | |
446 | va_end(va); | |
447 | killuser(chanservnick, target, buf); | |
448 | } | |
449 | ||
450 | int checkmasterpassword(reguser *rup, const char *pass) { | |
451 | if (!strncmp(rup->masterpass, pass, PASSLEN)) | |
452 | return 1; | |
453 | return 0; | |
454 | } | |
455 | ||
456 | int checkpassword(reguser *rup, const char *pass) { | |
457 | if (!strncmp(rup->password, pass, PASSLEN)) | |
458 | return 1; | |
459 | return 0; | |
460 | } | |
461 | ||
462 | int setmasterpassword(reguser *rup, const char *pass) { | |
463 | strncpy(rup->masterpass, pass, PASSLEN); | |
464 | rup->masterpass[PASSLEN]='\0'; | |
465 | return 1; | |
466 | } | |
467 | ||
468 | int setpassword(reguser *rup, const char *pass) { | |
469 | strncpy(rup->password, pass, PASSLEN); | |
470 | rup->password[PASSLEN]='\0'; | |
471 | return 1; | |
472 | } | |
473 | ||
474 | void cs_checknick(nick *np) { | |
475 | activeuser* aup; | |
476 | reguser *rup; | |
477 | nicklist *nlp; | |
478 | char userhost[USERLEN+HOSTLEN+3]; | |
479 | ||
480 | if (!(aup=getactiveuserfromnick(np))) { | |
481 | aup=getactiveuser(); | |
482 | np->exts[chanservnext]=aup; | |
483 | } | |
484 | ||
485 | assert(getactiveuserfromnick(np)); | |
486 | ||
487 | if (IsAccount(np)) { | |
488 | if ((rup=findreguserbynick(np->authname))!=NULL) { | |
489 | aup->rup=rup; | |
490 | nlp=getnicklist(); | |
491 | nlp->np=np; | |
492 | nlp->next=rup->nicks; | |
493 | rup->nicks=nlp; | |
32562540 | 494 | triggerhook(HOOK_CHANSERV_SETUSERID, np); |
c86edd1d Q |
495 | cs_doallautomodes(np); |
496 | } else { | |
497 | aup->rup=NULL; | |
498 | /* Auto create user.. */ | |
499 | rup=getreguser(); | |
500 | rup->status=0; | |
501 | rup->ID=++lastuserID; | |
502 | strncpy(rup->username,np->authname,NICKLEN); rup->username[NICKLEN]='\0'; | |
503 | rup->created=time(NULL); | |
504 | rup->lastauth=time(NULL); | |
505 | rup->lastemailchange=0; | |
506 | rup->flags=QUFLAG_NOTICE; | |
507 | rup->languageid=0; | |
508 | rup->suspendby=0; | |
509 | rup->password[0]='\0'; | |
510 | rup->masterpass[0]='\0'; | |
511 | rup->email=NULL; | |
e3805f60 P |
512 | rup->localpart=NULL; |
513 | rup->domain=NULL; | |
c86edd1d Q |
514 | rup->info=NULL; |
515 | sprintf(userhost,"%s@%s",np->ident,np->host->name->content); | |
516 | rup->lastuserhost=getsstring(userhost,USERLEN+HOSTLEN+1); | |
517 | rup->suspendreason=NULL; | |
518 | rup->comment=NULL; | |
519 | rup->knownon=NULL; | |
520 | rup->checkshd=NULL; | |
521 | rup->stealcount=0; | |
522 | rup->fakeuser=NULL; | |
523 | rup->nicks=getnicklist(); | |
524 | rup->nicks->np=np; | |
525 | rup->nicks->next=NULL; | |
526 | addregusertohash(rup); | |
527 | ||
528 | csdb_createuser(rup); | |
529 | aup->rup=rup; | |
530 | } | |
531 | } else { | |
532 | aup->rup=NULL; | |
533 | } | |
534 | ||
535 | cs_checknickbans(np); | |
536 | } | |
537 | ||
538 | void cs_checkchanmodes(channel *cp) { | |
539 | modechanges changes; | |
540 | ||
541 | localsetmodeinit (&changes, cp, chanservnick); | |
542 | cs_docheckchanmodes(cp, &changes); | |
543 | localsetmodeflush (&changes, 1); | |
544 | } | |
545 | ||
546 | void cs_docheckchanmodes(channel *cp, modechanges *changes) { | |
547 | regchan *rcp; | |
548 | ||
549 | if (!cp) | |
550 | return; | |
551 | ||
552 | if ((rcp=cp->index->exts[chanservext])==NULL || CIsSuspended(rcp)) | |
553 | return; | |
554 | ||
555 | /* Take care of the simple modes */ | |
556 | localdosetmode_simple(changes, rcp->forcemodes & ~(cp->flags), rcp->denymodes & cp->flags); | |
557 | ||
558 | /* Check for forced key. Note that we ALWAYS call localdosetmode_key | |
559 | * in case the wrong key is set atm */ | |
560 | if (rcp->forcemodes & CHANMODE_KEY) { | |
561 | if (!rcp->key) { | |
562 | Error("chanserv",ERR_WARNING,"Key forced but no key specified for %s: cleared forcemode.",rcp->index->name->content); | |
563 | rcp->forcemodes &= ~CHANMODE_KEY; | |
a54e438d | 564 | csdb_updatechannel(rcp); |
c86edd1d Q |
565 | } else { |
566 | localdosetmode_key(changes, rcp->key->content, MCB_ADD); | |
567 | } | |
568 | } | |
569 | ||
570 | /* Check for denied key */ | |
571 | if ((rcp->denymodes & CHANMODE_KEY) && IsKey(cp)) { | |
572 | localdosetmode_key(changes, NULL, MCB_DEL); | |
573 | } | |
574 | ||
575 | /* Check for forced limit. Always call in case of wrong limit */ | |
576 | if (rcp->forcemodes & CHANMODE_LIMIT) { | |
577 | localdosetmode_limit(changes, rcp->limit, MCB_ADD); | |
578 | } | |
579 | } | |
580 | ||
581 | void cs_docheckopvoice(channel *cp, modechanges *changes) { | |
582 | regchan *rcp; | |
583 | reguser *rup; | |
584 | regchanuser *rcup; | |
585 | nick *np; | |
586 | int i; | |
587 | ||
588 | if (!cp) | |
589 | return; | |
590 | ||
591 | if ((rcp=cp->index->exts[chanservext])==NULL || CIsSuspended(rcp)) | |
592 | return; | |
593 | ||
594 | for (i=0;i<cp->users->hashsize;i++) { | |
595 | if (cp->users->content[i]==nouser) | |
596 | continue; | |
597 | ||
598 | if ((np=getnickbynumeric(cp->users->content[i]))==NULL) { | |
599 | Error("chanserv",ERR_ERROR,"Found non-existent numeric %d on channel %s",cp->users->content[i], | |
600 | cp->index->name->content); | |
601 | continue; | |
602 | } | |
603 | ||
604 | if ((rup=getreguserfromnick(np))) | |
605 | rcup=findreguseronchannel(rcp,rup); | |
606 | else | |
607 | rcup=NULL; | |
608 | ||
609 | if (rcup && CUIsBanned(rcup) && !IsService(np)) { | |
610 | cs_banuser(changes, cp->index, np, NULL); | |
611 | continue; | |
612 | } | |
613 | ||
614 | if (!IsService(np) && CIsKnownOnly(rcp) && !(rcup && CUKnown(rcup))) { | |
615 | cs_banuser(changes, cp->index, np, "Authorised users only."); | |
616 | continue; | |
617 | } | |
618 | ||
619 | if ((cp->users->content[i] & CUMODE_OP) && !IsService(np)) { | |
620 | if ((CIsBitch(rcp) && (!rcup || !CUHasOpPriv(rcup))) || | |
621 | (rcup && CUIsDeny(rcup))) | |
622 | localdosetmode_nick(changes, np, MC_DEOP); | |
623 | } else { | |
624 | if (rcup && (CUIsProtect(rcup) || CIsProtect(rcp)) && CUIsOp(rcup) && !CUIsDeny(rcup)) | |
625 | localdosetmode_nick(changes, np, MC_OP); | |
626 | } | |
627 | ||
628 | if (cp->users->content[i] & CUMODE_VOICE) { | |
629 | if (rcup && CUIsQuiet(rcup)) | |
630 | localdosetmode_nick(changes, np, MC_DEVOICE); | |
631 | } else { | |
632 | if (rcup && (CUIsProtect(rcup) || CIsProtect(rcp)) && CUIsVoice(rcup) && !CUIsQuiet(rcup) && !(cp->users->content[i] & CUMODE_OP)) | |
633 | localdosetmode_nick(changes, np, MC_VOICE); | |
634 | } | |
635 | } | |
636 | } | |
637 | ||
638 | void cs_doallautomodes(nick *np) { | |
639 | reguser *rup; | |
640 | regchanuser *rcup; | |
641 | unsigned long *lp; | |
642 | modechanges changes; | |
643 | ||
644 | if (!(rup=getreguserfromnick(np))) | |
645 | return; | |
646 | ||
647 | for (rcup=rup->knownon;rcup;rcup=rcup->nextbyuser) { | |
648 | if (rcup->chan->index->channel) { | |
649 | /* Channel exists */ | |
650 | if ((lp=getnumerichandlefromchanhash(rcup->chan->index->channel->users, np->numeric))) { | |
651 | ||
652 | /* User is on channel.. */ | |
653 | localsetmodeinit(&changes, rcup->chan->index->channel, chanservnick); | |
654 | if (*lp & CUMODE_OP) { | |
655 | if (!IsService(np) && (CUIsDeny(rcup) || (CIsBitch(rcup->chan) && !CUHasOpPriv(rcup)))) | |
656 | localdosetmode_nick(&changes, np, MC_DEOP); | |
657 | } else { | |
658 | if (!CUIsDeny(rcup) && CUIsOp(rcup) && | |
659 | (CUIsProtect(rcup) || CIsProtect(rcup->chan) || CUIsAutoOp(rcup))) | |
660 | localdosetmode_nick(&changes, np, MC_OP); | |
661 | } | |
662 | ||
663 | if (*lp & CUMODE_VOICE) { | |
664 | if (CUIsQuiet(rcup)) | |
665 | localdosetmode_nick(&changes, np, MC_DEVOICE); | |
666 | } else { | |
667 | if (!CUIsQuiet(rcup) && CUIsVoice(rcup) && !(*lp & CUMODE_OP) && | |
668 | (CUIsProtect(rcup) || CIsProtect(rcup->chan) || CUIsAutoVoice(rcup))) | |
669 | localdosetmode_nick(&changes, np, MC_VOICE); | |
670 | } | |
671 | localsetmodeflush(&changes, 1); | |
672 | } else { | |
673 | /* Channel exists but user is not joined: invite if they are +j-b */ | |
674 | if (CUIsAutoInvite(rcup) && CUKnown(rcup) && !CUIsBanned(rcup)) { | |
675 | localinvite(chanservnick, rcup->chan->index->channel, np); | |
676 | } | |
677 | } | |
678 | } | |
679 | } | |
680 | } | |
681 | ||
682 | void cs_checknickbans(nick *np) { | |
683 | channel **ca; | |
684 | regchan *rcp; | |
685 | int i,j; | |
686 | ||
687 | if (IsService(np)) | |
688 | return; | |
689 | ||
690 | /* Avoid races: memcpy the channel array */ | |
691 | i=np->channels->cursi; | |
692 | ca=(channel **)malloc(i*sizeof(channel *)); | |
693 | memcpy(ca, np->channels->content,i*sizeof(channel *)); | |
694 | ||
695 | for (j=0;j<i;j++) { | |
696 | if ((rcp=ca[j]->index->exts[chanservext]) && !CIsSuspended(rcp) && | |
697 | CIsEnforce(rcp) && nickbanned(np, ca[j])) | |
698 | localkickuser(chanservnick, ca[j], np, "Banned."); | |
699 | } | |
700 | ||
701 | free(ca); | |
702 | } | |
703 | ||
704 | void cs_checkbans(channel *cp) { | |
705 | regchan *rcp; | |
706 | int i; | |
707 | nick *np; | |
708 | chanban *cbp; | |
709 | regban *rbp; | |
710 | time_t now; | |
711 | modechanges changes; | |
712 | ||
713 | if (!cp) | |
714 | return; | |
715 | ||
716 | if ((rcp=cp->index->exts[chanservext])==NULL || CIsSuspended(rcp)) | |
717 | return; | |
718 | ||
719 | now=time(NULL); | |
720 | ||
721 | localsetmodeinit(&changes, cp, chanservnick); | |
722 | ||
723 | for (i=0;i<cp->users->hashsize;i++) { | |
724 | if (cp->users->content[i]==nouser) | |
725 | continue; | |
726 | ||
727 | if ((np=getnickbynumeric(cp->users->content[i]))==NULL) { | |
728 | Error("chanserv",ERR_ERROR,"Found numeric %d on channel %s who doesn't exist.", | |
729 | cp->users->content[i], cp->index->name->content); | |
730 | continue; | |
731 | } | |
732 | ||
733 | if (IsService(np)) | |
734 | continue; | |
735 | ||
736 | for (rbp=rcp->bans;rbp;rbp=rbp->next) { | |
737 | if (((!rbp->expiry) || (rbp->expiry <= now)) && | |
738 | nickmatchban(np, rbp->cbp)) { | |
739 | if (!nickbanned(np, cp)) { | |
740 | localdosetmode_ban(&changes, bantostring(rbp->cbp), MCB_ADD); | |
741 | } | |
742 | localkickuser(chanservnick,cp,np,rbp->reason?rbp->reason->content:"Banned."); | |
743 | break; | |
744 | } | |
745 | } | |
746 | ||
747 | if (rbp) /* If we broke out of above loop (due to kick) rbp will be set.. */ | |
748 | continue; | |
749 | ||
750 | if (CIsEnforce(rcp)) { | |
751 | for (cbp=cp->bans;cbp;cbp=cbp->next) { | |
752 | if ((cbp->timeset>=rcp->lastbancheck) && nickmatchban(np, cbp)) | |
753 | localkickuser(chanservnick,cp,np,"Banned."); | |
754 | } | |
755 | rcp->lastbancheck=time(NULL); | |
756 | } | |
757 | } | |
758 | ||
759 | localsetmodeflush(&changes,1); | |
760 | } | |
761 | ||
762 | /* | |
763 | * cs_schedupdate: | |
764 | * This function schedules an update check on a channel | |
765 | */ | |
766 | void cs_schedupdate(chanindex *cip, int mintime, int maxtime) { | |
767 | int delay=mintime+(rand()%(maxtime-mintime)); | |
768 | regchan *rcp; | |
769 | ||
770 | if (!(rcp=cip->exts[chanservext]) || CIsSuspended(rcp)) | |
771 | return; | |
772 | ||
773 | if (rcp->checksched) { | |
774 | deleteschedule(rcp->checksched, cs_timerfunc, cip); | |
775 | } | |
776 | ||
777 | rcp->checksched=scheduleoneshot(time(NULL)+delay, cs_timerfunc, cip); | |
778 | } | |
779 | ||
780 | /* | |
781 | * cs_timerfunc: | |
782 | * This function does several things: | |
783 | * * Updates auto-limit and/or removes bans as necessary | |
784 | * * Schedules the next timed call | |
785 | * * Does any pending op/ban/mode checks | |
786 | * It is safe to use either as a target for a schedule() call, or to call | |
787 | * directly when parameters change (e.g. autolimit settings or ban expire | |
788 | * timers). | |
789 | * To force a limit update, set rcp->limit to 0.. | |
790 | */ | |
791 | ||
792 | void cs_timerfunc(void *arg) { | |
793 | chanindex *cip=arg; | |
794 | channel *cp=cip->channel; | |
795 | regchan *rcp; | |
796 | chanban *cbp, *ncbp; | |
797 | regban **rbh, *rbp; | |
798 | time_t nextsched=0; | |
799 | time_t now=time(NULL); | |
800 | modechanges changes; | |
801 | ||
802 | if (!(rcp=cip->exts[chanservext])) | |
803 | return; | |
804 | ||
805 | verifyregchan(rcp); | |
806 | ||
807 | /* Always nullify the checksched even if the channel is suspended.. */ | |
808 | if (rcp->checksched) { | |
809 | deleteschedule(rcp->checksched, cs_timerfunc, arg); | |
810 | rcp->checksched=NULL; | |
811 | } | |
812 | ||
813 | if (!cp || CIsSuspended(rcp)) | |
814 | return; | |
815 | ||
816 | /* Check if we need to leave the channel first */ | |
817 | if (chanservnick && CIsJoined(rcp) && cip->channel && | |
818 | cip->channel->users->totalusers==1 && | |
819 | getnumerichandlefromchanhash(cip->channel->users, chanservnick->numeric)) { | |
820 | ||
821 | /* Only chanserv left in this channel */ | |
822 | if (now >= (rcp->lastpart + LINGERTIME)) { | |
823 | /* Time to go */ | |
334b567e | 824 | localpartchannel(chanservnick, cip->channel, "Empty Channel"); |
c86edd1d Q |
825 | return; |
826 | } else { | |
827 | if (!nextsched || nextsched > (rcp->lastpart + LINGERTIME)) | |
828 | nextsched=rcp->lastpart+LINGERTIME; | |
829 | } | |
830 | } | |
831 | ||
832 | localsetmodeinit(&changes, cp, chanservnick); | |
833 | ||
834 | if (CIsAutoLimit(rcp)) { | |
835 | if (!rcp->limit || rcp->autoupdate <= now) { | |
836 | /* Update limit.. */ | |
837 | rcp->limit=cp->users->totalusers+rcp->autolimit; | |
838 | rcp->status |= QCSTAT_MODECHECK; | |
839 | ||
840 | /* And set the schedule for the next update */ | |
841 | rcp->autoupdate = now + AUTOLIMIT_INTERVAL; | |
842 | } | |
843 | ||
844 | if (!nextsched || rcp->autoupdate <= nextsched) | |
845 | nextsched=rcp->autoupdate; | |
846 | } | |
847 | ||
848 | if (rcp->status & QCSTAT_OPCHECK) { | |
849 | rcp->status &= ~QCSTAT_OPCHECK; | |
850 | cs_docheckopvoice(cp, &changes); | |
851 | } | |
852 | ||
853 | if (rcp->status & QCSTAT_MODECHECK) { | |
854 | rcp->status &= ~QCSTAT_MODECHECK; | |
855 | cs_docheckchanmodes(cp, &changes); | |
856 | } | |
857 | ||
858 | if (rcp->status & QCSTAT_BANCHECK) { | |
859 | rcp->status &= ~QCSTAT_BANCHECK; | |
860 | cs_checkbans(cp); | |
861 | } | |
862 | ||
863 | /* This ban check is AFTER docheckopvoice in case checkopvoice set a | |
864 | * ban which we need to automatically remove later.. */ | |
865 | ||
866 | if (rcp->banduration) { | |
867 | for (cbp=cp->bans;cbp;cbp=ncbp) { | |
868 | ncbp=cbp->next; | |
869 | if (cbp->timeset+rcp->banduration<=now) { | |
870 | localdosetmode_ban(&changes, bantostring(cbp), MCB_DEL); | |
871 | } else { | |
872 | if (!nextsched || cbp->timeset+rcp->banduration <= nextsched) { | |
873 | nextsched=rcp->banduration+cbp->timeset; | |
874 | } | |
875 | } | |
876 | } | |
877 | } | |
878 | ||
879 | /* Check for expiry of registered bans */ | |
880 | if (rcp->bans) { | |
881 | for (rbh=&(rcp->bans);*rbh;) { | |
882 | rbp=*rbh; | |
883 | verifyregchanban(rbp); | |
884 | if (rbp->expiry && (rbp->expiry < now)) { | |
885 | /* Expired ban */ | |
886 | /* Remove ban if it's on the channel (localdosetmode_ban will silently | |
887 | * skip bans that don't exist) */ | |
888 | localdosetmode_ban(&changes, bantostring(rbp->cbp), MCB_DEL); | |
889 | /* Remove from database */ | |
890 | csdb_deleteban(rbp); | |
891 | /* Remove from list */ | |
892 | (*rbh)=rbp->next; | |
893 | /* Free ban/string and update setby refcount, and free actual regban */ | |
894 | freesstring(rbp->reason); | |
895 | freechanban(rbp->cbp); | |
896 | freeregban(rbp); | |
897 | } else { | |
898 | if (rbp->expiry && (!nextsched || rbp->expiry <= nextsched)) { | |
899 | nextsched=rbp->expiry; | |
900 | } | |
901 | rbh=&(rbp->next); | |
902 | } | |
903 | } | |
904 | } | |
905 | ||
906 | if (nextsched) | |
907 | rcp->checksched=scheduleoneshot(nextsched, cs_timerfunc, arg); | |
908 | ||
909 | localsetmodeflush(&changes, 1); | |
910 | } | |
911 | ||
912 | void cs_removechannel(regchan *rcp) { | |
913 | int i; | |
914 | chanindex *cip; | |
915 | regchanuser *rcup, *nrcup; | |
916 | regban *rbp, *nrbp; | |
917 | ||
918 | cip=rcp->index; | |
919 | ||
920 | for (i=0;i<REGCHANUSERHASHSIZE;i++) { | |
921 | for (rcup=rcp->regusers[i];rcup;rcup=nrcup) { | |
922 | nrcup=rcup->nextbychan; | |
923 | delreguserfromchannel(rcp, rcup->user); | |
924 | freeregchanuser(rcup); | |
925 | } | |
926 | } | |
927 | ||
928 | for (rbp=rcp->bans;rbp;rbp=nrbp) { | |
929 | nrbp=rbp->next; | |
930 | freesstring(rbp->reason); | |
931 | freechanban(rbp->cbp); | |
932 | freeregban(rbp); | |
933 | } | |
934 | ||
935 | rcp->bans=NULL; | |
936 | ||
937 | if (rcp->checksched) | |
938 | deleteschedule(rcp->checksched, cs_timerfunc, rcp->index); | |
939 | ||
940 | if (cip->channel) { | |
941 | rcp->flags=QCFLAG_SUSPENDED; | |
942 | chanservjoinchan(cip->channel); /* Force off the channel */ | |
943 | } | |
944 | ||
945 | csdb_deletechannel(rcp); | |
946 | ||
947 | freesstring(rcp->welcome); | |
948 | freesstring(rcp->topic); | |
949 | freesstring(rcp->key); | |
950 | freesstring(rcp->suspendreason); | |
951 | freesstring(rcp->comment); | |
952 | ||
953 | freeregchan(rcp); | |
954 | ||
955 | cip->exts[chanservext]=NULL; | |
956 | releasechanindex(cip); | |
957 | } | |
958 | ||
959 | void cs_removeuser(reguser *rup) { | |
960 | int i; | |
961 | regchanuser *rcup, *nrcup; | |
962 | regchan *rcp; | |
963 | ||
964 | /* Remove the user from all its channels */ | |
965 | for (rcup=rup->knownon;rcup;rcup=nrcup) { | |
966 | nrcup=rcup->nextbyuser; | |
967 | freesstring(rcup->info); | |
968 | rcp=rcup->chan; | |
969 | ||
970 | delreguserfromchannel(rcp, rup); | |
971 | ||
972 | for (i=0;i<REGCHANUSERHASHSIZE;i++) { | |
973 | if (rcp->regusers[i]) | |
974 | break; | |
975 | } | |
976 | ||
977 | if (i==REGCHANUSERHASHSIZE) { | |
978 | /* There are no users left on this channel! */ | |
979 | cs_log(NULL, "DELCHAN %s (last user removed)",rcp->index->name->content); | |
980 | cs_removechannel(rcp); | |
981 | } | |
982 | ||
983 | freeregchanuser(rcup); | |
984 | } | |
985 | ||
e3805f60 P |
986 | if(rup->domain) |
987 | delreguserfrommaildomain(rup,rup->domain); | |
988 | freesstring(rup->localpart); | |
c86edd1d Q |
989 | freesstring(rup->email); |
990 | freesstring(rup->lastuserhost); | |
991 | freesstring(rup->suspendreason); | |
992 | freesstring(rup->comment); | |
993 | freesstring(rup->info); | |
994 | ||
995 | csdb_deleteuser(rup); | |
996 | ||
997 | removereguserfromhash(rup); | |
998 | ||
999 | if (rup->nicks) { | |
1000 | rup->status |= QUSTAT_DEAD; | |
1001 | } else { | |
1002 | freereguser(rup); | |
1003 | } | |
1004 | } | |
1005 | ||
1006 | int cs_bancheck(nick *np, channel *cp) { | |
1007 | time_t now=time(NULL); | |
1008 | regban **rbh, *rbp; | |
1009 | modechanges changes; | |
1010 | regchan *rcp; | |
1011 | ||
1012 | if (!(rcp=cp->index->exts[chanservext])) | |
1013 | return 0; | |
1014 | ||
1015 | for (rbh=&(rcp->bans);*rbh;) { | |
1016 | if ((*rbh)->expiry && ((*rbh)->expiry < now)) { | |
1017 | /* Expired ban */ | |
1018 | csdb_deleteban(*rbh); | |
1019 | rbp=*rbh; | |
1020 | (*rbh)=rbp->next; | |
1021 | ||
1022 | freesstring(rbp->reason); | |
1023 | freechanban(rbp->cbp); | |
1024 | freeregban(rbp); | |
1025 | } else if (nickmatchban(np,(*rbh)->cbp)) { | |
1026 | /* This user matches this ban.. */ | |
1027 | if (!nickbanned(np,cp)) { | |
1028 | /* Only bother putting the ban on the channel if they're not banned already */ | |
1029 | /* (might be covered by this ban or a different one.. doesn't really matter */ | |
1030 | localsetmodeinit(&changes, cp, chanservnick); | |
1031 | localdosetmode_ban(&changes, bantostring((*rbh)->cbp), MCB_ADD); | |
1032 | localsetmodeflush(&changes, 1); | |
1033 | cs_timerfunc(cp->index); | |
1034 | } | |
1035 | localkickuser(chanservnick, cp, np, (*rbh)->reason ? (*rbh)->reason->content : "Banned."); | |
1036 | return 1; | |
1037 | } else { | |
1038 | rbh=&((*rbh)->next); | |
1039 | } | |
1040 | } | |
1041 | ||
1042 | return 0; | |
1043 | } | |
1044 | ||
1045 | void cs_setregban(chanindex *cip, regban *rbp) { | |
1046 | modechanges changes; | |
1047 | int i; | |
1048 | nick *np; | |
1049 | ||
1050 | if (!cip->channel) | |
1051 | return; | |
b263aa79 | 1052 | |
c86edd1d Q |
1053 | localsetmodeinit(&changes, cip->channel, chanservnick); |
1054 | localdosetmode_ban(&changes, bantostring(rbp->cbp), MCB_ADD); | |
1055 | localsetmodeflush(&changes, 1); | |
1056 | ||
1057 | for (i=0;(cip->channel) && i<cip->channel->users->hashsize;i++) { | |
1058 | if (cip->channel->users->content[i]!=nouser && | |
1059 | (np=getnickbynumeric(cip->channel->users->content[i])) && | |
1060 | !IsService(np) && | |
1061 | nickmatchban(np, rbp->cbp)) | |
1062 | localkickuser(chanservnick, cip->channel, np, rbp->reason ? rbp->reason->content : "Banned."); | |
1063 | } | |
1064 | ||
1065 | cs_timerfunc(cip); | |
1066 | } | |
1067 | ||
1068 | void cs_banuser(modechanges *changes, chanindex *cip, nick *np, const char *reason) { | |
1069 | modechanges lchanges; | |
1070 | char banmask[NICKLEN+USERLEN+HOSTLEN+3]; | |
1071 | ||
1072 | if (!cip->channel) | |
1073 | return; | |
1074 | ||
1075 | if (nickbanned(np, cip->channel)) { | |
1076 | localkickuser(chanservnick, cip->channel, np, reason?reason:"Banned."); | |
1077 | return; | |
1078 | } | |
1079 | ||
1080 | if (IsAccount(np)) { | |
1081 | /* Ban their account.. */ | |
1082 | sprintf(banmask,"*!*@%s.%s",np->authname,HIS_HIDDENHOST); | |
1083 | } else if (IsSetHost(np)) { | |
1084 | /* sethosted: ban ident@host */ | |
1085 | sprintf(banmask,"*!%s@%s",np->shident->content,np->sethost->content); | |
1086 | } else if (np->host->clonecount>3) { | |
1087 | /* >3 clones, ban user@host */ | |
1088 | sprintf(banmask,"*!%s@%s",np->ident,np->host->name->content); | |
1089 | } else { | |
1090 | sprintf(banmask,"*!*@%s",np->host->name->content); | |
1091 | } | |
1092 | ||
1093 | if (!changes) { | |
1094 | localsetmodeinit(&lchanges, cip->channel, chanservnick); | |
1095 | localdosetmode_ban(&lchanges, banmask, MCB_ADD); | |
1096 | localsetmodeflush(&lchanges, 1); | |
1097 | } else { | |
1098 | localdosetmode_ban(changes, banmask, MCB_ADD); | |
1099 | } | |
1100 | ||
1101 | localkickuser(chanservnick, cip->channel, np, reason?reason:"Banned."); | |
1102 | } | |
1103 | ||
b263aa79 | 1104 | /* |
1105 | * findreguser: | |
1106 | * This function does the standard "nick or #user" lookup. | |
1107 | * If "sender" is not NULL, a suitable error message will | |
1108 | * be sent if the lookup fails. | |
1109 | */ | |
1110 | ||
1111 | reguser *findreguser(nick *sender, const char *str) { | |
1112 | reguser *rup; | |
1113 | nick *np; | |
1114 | ||
1115 | if (!str || !*str) | |
1116 | return NULL; | |
1117 | ||
1118 | if (*str=='#') { | |
1119 | if (str[1]=='\0') { | |
1120 | if (sender) | |
1121 | chanservstdmessage(sender, QM_UNKNOWNUSER, str); | |
1122 | return NULL; | |
1123 | } | |
1124 | if (!(rup=findreguserbynick(str+1)) && sender) | |
1125 | chanservstdmessage(sender, QM_UNKNOWNUSER, str); | |
1126 | } else { | |
1127 | if (!(np=getnickbynick(str))) { | |
1128 | if (sender) | |
1129 | chanservstdmessage(sender, QM_UNKNOWNUSER, str); | |
1130 | return NULL; | |
1131 | } | |
1132 | if (!(rup=getreguserfromnick(np)) && sender) | |
1133 | chanservstdmessage(sender, QM_USERNOTAUTHED, str); | |
1134 | } | |
1135 | ||
1136 | if (rup && (UIsSuspended(rup) || (rup->status & QUSTAT_DEAD))) { | |
1137 | chanservstdmessage(sender, QM_USERHASBADAUTH, rup->username); | |
1138 | return NULL; | |
1139 | } | |
1140 | ||
1141 | return rup; | |
1142 | } |