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