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