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