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