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