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