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