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