]>
Commit | Line | Data |
---|---|---|
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 | ||
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; | |
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 | ||
79 | void 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 | ||
182 | void chanservcommandinit() { | |
183 | cscommands=newcommandtree(); | |
184 | csctcpcommands=newcommandtree(); | |
185 | } | |
186 | ||
187 | void chanservcommandclose() { | |
188 | destroycommandtree(cscommands); | |
189 | destroycommandtree(csctcpcommands); | |
190 | } | |
191 | ||
1f0d7c8b | 192 | void 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 | ||
208 | void chanservaddctcpcommand(char *command, CommandHandler handler) { | |
209 | addcommandtotree(csctcpcommands, command, 0, 1, handler); | |
210 | } | |
211 | ||
212 | void chanservremovectcpcommand(char *command, CommandHandler handler) { | |
213 | deletecommandfromtree(csctcpcommands, command, handler); | |
214 | } | |
215 | ||
216 | void 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 | ||
238 | void 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 | ||
315 | void 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 | 393 | void 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 | ||
449 | void 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 | ||
466 | void 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 |
491 | int checkpassword(reguser *rup, const char *pass) { |
492 | if (!strncmp(rup->password, pass, PASSLEN)) | |
493 | return 1; | |
494 | return 0; | |
495 | } | |
496 | ||
b7a95f03 CP |
497 | int 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 |
507 | int 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 |
517 | int setpassword(reguser *rup, const char *pass) { |
518 | strncpy(rup->password, pass, PASSLEN); | |
519 | rup->password[PASSLEN]='\0'; | |
520 | return 1; | |
521 | } | |
522 | ||
523 | void 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 | ||
584 | void cs_checkchanmodes(channel *cp) { | |
585 | modechanges changes; | |
586 | ||
587 | localsetmodeinit (&changes, cp, chanservnick); | |
588 | cs_docheckchanmodes(cp, &changes); | |
589 | localsetmodeflush (&changes, 1); | |
590 | } | |
591 | ||
592 | void 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 | ||
627 | void 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 | ||
686 | void 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 | ||
735 | void 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 | ||
757 | void 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 | */ | |
819 | void 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 | ||
845 | void 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 | ||
965 | void 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 | ||
1012 | void cs_removeuser(reguser *rup) { | |
1013 | int i; | |
1014 | regchanuser *rcup, *nrcup; | |
1015 | regchan *rcp; | |
23b85e10 | 1016 | struct authname *anp; |
c86edd1d Q |
1017 | |
1018 | /* Remove the user from all its channels */ | |
1019 | for (rcup=rup->knownon;rcup;rcup=nrcup) { | |
1020 | nrcup=rcup->nextbyuser; | |
1021 | freesstring(rcup->info); | |
1022 | rcp=rcup->chan; | |
1023 | ||
1024 | delreguserfromchannel(rcp, rup); | |
1025 | ||
1026 | for (i=0;i<REGCHANUSERHASHSIZE;i++) { | |
1027 | if (rcp->regusers[i]) | |
1028 | break; | |
1029 | } | |
1030 | ||
1031 | if (i==REGCHANUSERHASHSIZE) { | |
1032 | /* There are no users left on this channel! */ | |
1033 | cs_log(NULL, "DELCHAN %s (last user removed)",rcp->index->name->content); | |
1034 | cs_removechannel(rcp); | |
1035 | } | |
1036 | ||
1037 | freeregchanuser(rcup); | |
1038 | } | |
1039 | ||
e3805f60 P |
1040 | if(rup->domain) |
1041 | delreguserfrommaildomain(rup,rup->domain); | |
1042 | freesstring(rup->localpart); | |
c86edd1d | 1043 | freesstring(rup->email); |
9708f78f | 1044 | freesstring(rup->lastemail); |
c86edd1d Q |
1045 | freesstring(rup->lastuserhost); |
1046 | freesstring(rup->suspendreason); | |
1047 | freesstring(rup->comment); | |
1048 | freesstring(rup->info); | |
1049 | ||
1050 | csdb_deleteuser(rup); | |
1051 | ||
1052 | removereguserfromhash(rup); | |
1053 | ||
23b85e10 | 1054 | if ((anp=findauthname(rup->ID)) && anp->nicks) { |
c86edd1d Q |
1055 | rup->status |= QUSTAT_DEAD; |
1056 | } else { | |
1057 | freereguser(rup); | |
1058 | } | |
1059 | } | |
1060 | ||
1061 | int cs_bancheck(nick *np, channel *cp) { | |
1062 | time_t now=time(NULL); | |
1063 | regban **rbh, *rbp; | |
1064 | modechanges changes; | |
1065 | regchan *rcp; | |
1066 | ||
1067 | if (!(rcp=cp->index->exts[chanservext])) | |
1068 | return 0; | |
1069 | ||
1070 | for (rbh=&(rcp->bans);*rbh;) { | |
1071 | if ((*rbh)->expiry && ((*rbh)->expiry < now)) { | |
1072 | /* Expired ban */ | |
1073 | csdb_deleteban(*rbh); | |
1074 | rbp=*rbh; | |
1075 | (*rbh)=rbp->next; | |
1076 | ||
1077 | freesstring(rbp->reason); | |
1078 | freechanban(rbp->cbp); | |
1079 | freeregban(rbp); | |
1080 | } else if (nickmatchban(np,(*rbh)->cbp)) { | |
1081 | /* This user matches this ban.. */ | |
1082 | if (!nickbanned(np,cp)) { | |
1083 | /* Only bother putting the ban on the channel if they're not banned already */ | |
1084 | /* (might be covered by this ban or a different one.. doesn't really matter */ | |
1085 | localsetmodeinit(&changes, cp, chanservnick); | |
1086 | localdosetmode_ban(&changes, bantostring((*rbh)->cbp), MCB_ADD); | |
1087 | localsetmodeflush(&changes, 1); | |
1088 | cs_timerfunc(cp->index); | |
1089 | } | |
1090 | localkickuser(chanservnick, cp, np, (*rbh)->reason ? (*rbh)->reason->content : "Banned."); | |
1091 | return 1; | |
1092 | } else { | |
1093 | rbh=&((*rbh)->next); | |
1094 | } | |
1095 | } | |
1096 | ||
1097 | return 0; | |
1098 | } | |
1099 | ||
1100 | void cs_setregban(chanindex *cip, regban *rbp) { | |
1101 | modechanges changes; | |
1102 | int i; | |
1103 | nick *np; | |
1104 | ||
1105 | if (!cip->channel) | |
1106 | return; | |
b263aa79 | 1107 | |
c86edd1d Q |
1108 | localsetmodeinit(&changes, cip->channel, chanservnick); |
1109 | localdosetmode_ban(&changes, bantostring(rbp->cbp), MCB_ADD); | |
1110 | localsetmodeflush(&changes, 1); | |
1111 | ||
1112 | for (i=0;(cip->channel) && i<cip->channel->users->hashsize;i++) { | |
1113 | if (cip->channel->users->content[i]!=nouser && | |
1114 | (np=getnickbynumeric(cip->channel->users->content[i])) && | |
1115 | !IsService(np) && | |
1116 | nickmatchban(np, rbp->cbp)) | |
1117 | localkickuser(chanservnick, cip->channel, np, rbp->reason ? rbp->reason->content : "Banned."); | |
1118 | } | |
1119 | ||
1120 | cs_timerfunc(cip); | |
1121 | } | |
1122 | ||
1123 | void cs_banuser(modechanges *changes, chanindex *cip, nick *np, const char *reason) { | |
1124 | modechanges lchanges; | |
1125 | char banmask[NICKLEN+USERLEN+HOSTLEN+3]; | |
1126 | ||
1127 | if (!cip->channel) | |
1128 | return; | |
1129 | ||
1130 | if (nickbanned(np, cip->channel)) { | |
1131 | localkickuser(chanservnick, cip->channel, np, reason?reason:"Banned."); | |
1132 | return; | |
1133 | } | |
1134 | ||
1135 | if (IsAccount(np)) { | |
1136 | /* Ban their account.. */ | |
1137 | sprintf(banmask,"*!*@%s.%s",np->authname,HIS_HIDDENHOST); | |
1138 | } else if (IsSetHost(np)) { | |
1139 | /* sethosted: ban ident@host */ | |
1140 | sprintf(banmask,"*!%s@%s",np->shident->content,np->sethost->content); | |
1141 | } else if (np->host->clonecount>3) { | |
1142 | /* >3 clones, ban user@host */ | |
1143 | sprintf(banmask,"*!%s@%s",np->ident,np->host->name->content); | |
1144 | } else { | |
1145 | sprintf(banmask,"*!*@%s",np->host->name->content); | |
1146 | } | |
1147 | ||
1148 | if (!changes) { | |
1149 | localsetmodeinit(&lchanges, cip->channel, chanservnick); | |
1150 | localdosetmode_ban(&lchanges, banmask, MCB_ADD); | |
1151 | localsetmodeflush(&lchanges, 1); | |
1152 | } else { | |
1153 | localdosetmode_ban(changes, banmask, MCB_ADD); | |
1154 | } | |
1155 | ||
1156 | localkickuser(chanservnick, cip->channel, np, reason?reason:"Banned."); | |
1157 | } | |
95332d7b | 1158 | |
1159 | /* | |
1160 | * cs_sanitisechanlev: Removes impossible combinations from chanlev flags. | |
1161 | * chanservuser.c is probably not the right file for this, but nowhere better | |
1162 | * presented itself... | |
1163 | */ | |
1164 | flag_t cs_sanitisechanlev(flag_t flags) { | |
1165 | /* +m or +n cannot have any "punishment" flags */ | |
1166 | if (flags & (QCUFLAG_MASTER | QCUFLAG_OWNER)) | |
1167 | flags &= ~(QCUFLAG_BANNED | QCUFLAG_QUIET | QCUFLAG_DENY); | |
1168 | ||
1169 | /* +d can't be +o */ | |
1170 | if (flags & QCUFLAG_DENY) | |
1171 | flags &= ~QCUFLAG_OP; | |
1172 | ||
1173 | /* +q can't be +v */ | |
1174 | if (flags & QCUFLAG_QUIET) | |
1175 | flags &= ~QCUFLAG_VOICE; | |
1176 | ||
1177 | /* +p trumps +a and +g */ | |
1178 | if (flags & QCUFLAG_PROTECT) | |
1179 | flags &= ~(QCUFLAG_AUTOOP | QCUFLAG_AUTOVOICE); | |
1180 | ||
1181 | /* -o can't be +a */ | |
1182 | if (!(flags & QCUFLAG_OP)) | |
1183 | flags &= ~QCUFLAG_AUTOOP; | |
1184 | ||
1185 | /* +a or -v can't be +g. +a implies +o at this stage (see above) */ | |
1186 | if (!(flags & QCUFLAG_VOICE) || (flags & QCUFLAG_AUTOOP)) | |
1187 | flags &= ~QCUFLAG_AUTOVOICE; | |
1188 | ||
1189 | /* +p requires +o or +v */ | |
1190 | if (!(flags & (QCUFLAG_VOICE | QCUFLAG_OP))) | |
1191 | flags &= ~QCUFLAG_PROTECT; | |
1192 | ||
1193 | /* The personal flags require one of +mnovk */ | |
1194 | if (!(flags & (QCUFLAG_OWNER | QCUFLAG_MASTER | QCUFLAG_OP | QCUFLAG_VOICE | QCUFLAG_KNOWN))) | |
1195 | flags &= ~QCUFLAGS_PERSONAL; | |
1196 | ||
1197 | return flags; | |
1198 | } | |
c86edd1d | 1199 | |
b263aa79 | 1200 | /* |
1201 | * findreguser: | |
1202 | * This function does the standard "nick or #user" lookup. | |
1203 | * If "sender" is not NULL, a suitable error message will | |
1204 | * be sent if the lookup fails. | |
4630ab32 CP |
1205 | * "sender" MUST be sent when a user is requesting a lookup |
1206 | * as there is some policy code here. | |
b263aa79 | 1207 | */ |
1208 | ||
1209 | reguser *findreguser(nick *sender, const char *str) { | |
4630ab32 | 1210 | reguser *rup, *vrup = getreguserfromnick(sender);; |
b263aa79 | 1211 | nick *np; |
1212 | ||
1213 | if (!str || !*str) | |
1214 | return NULL; | |
1215 | ||
1216 | if (*str=='#') { | |
1217 | if (str[1]=='\0') { | |
1218 | if (sender) | |
1219 | chanservstdmessage(sender, QM_UNKNOWNUSER, str); | |
1220 | return NULL; | |
1221 | } | |
1222 | if (!(rup=findreguserbynick(str+1)) && sender) | |
1223 | chanservstdmessage(sender, QM_UNKNOWNUSER, str); | |
4630ab32 CP |
1224 | } else if (*str=='&' && vrup && UHasHelperPriv(vrup)) { |
1225 | if (str[1]=='\0') { | |
1226 | if (sender) | |
1227 | chanservstdmessage(sender, QM_UNKNOWNUSER, str); | |
1228 | return NULL; | |
1229 | } | |
1230 | if (!(rup=findreguserbyID(atoi(str+1))) && sender) | |
1231 | chanservstdmessage(sender, QM_UNKNOWNUSER, str); | |
b263aa79 | 1232 | } else { |
1233 | if (!(np=getnickbynick(str))) { | |
1234 | if (sender) | |
1235 | chanservstdmessage(sender, QM_UNKNOWNUSER, str); | |
1236 | return NULL; | |
1237 | } | |
1238 | if (!(rup=getreguserfromnick(np)) && sender) | |
1239 | chanservstdmessage(sender, QM_USERNOTAUTHED, str); | |
1240 | } | |
1241 | ||
4002da60 CP |
1242 | /* removed the suspended check from here, I don't see the point... */ |
1243 | if (rup && (rup->status & QUSTAT_DEAD)) { | |
b263aa79 | 1244 | chanservstdmessage(sender, QM_USERHASBADAUTH, rup->username); |
1245 | return NULL; | |
1246 | } | |
1247 | ||
1248 | return rup; | |
1249 | } | |
336765cd CP |
1250 | |
1251 | /* | |
1252 | * Unbans a mask from a channel, including permbans if user has correct privs. | |
0a53499c | 1253 | * |
1254 | * Return 0 if it works, 1 if it don't. | |
336765cd | 1255 | */ |
0a53499c | 1256 | int cs_unbanfn(nick *sender, chanindex *cip, UnbanFN fn, void *arg, int removepermbans, int abortonfailure) { |
336765cd CP |
1257 | regban **rbh, *rbp; |
1258 | chanban **cbh, *cbp; | |
1259 | regchan *rcp; | |
1260 | modechanges changes; | |
1261 | char *banstr; | |
1262 | ||
1263 | rcp=cip->exts[chanservext]; | |
1264 | ||
1265 | if (cip->channel) | |
1266 | localsetmodeinit(&changes, cip->channel, chanservnick); | |
1267 | ||
1268 | for (rbh=&(rcp->bans); *rbh; ) { | |
1269 | rbp=*rbh; | |
1270 | if (fn(arg, rbp->cbp)) { | |
1271 | banstr=bantostring(rbp->cbp); | |
1272 | /* Check perms and remove */ | |
1273 | if(!removepermbans) { | |
1274 | chanservstdmessage(sender, QM_WARNNOTREMOVEDPERMBAN, banstr, cip->name->content); | |
1275 | rbh=&(rbp->next); | |
1276 | } else if (!cs_checkaccess(sender, NULL, CA_MASTERPRIV, cip, NULL, 0, 1)) { | |
1277 | chanservstdmessage(sender, QM_NOTREMOVEDPERMBAN, banstr, cip->name->content); | |
0a53499c | 1278 | if (abortonfailure) return 1; /* Just give up... */ |
336765cd CP |
1279 | rbh=&(rbp->next); |
1280 | } else { | |
1281 | chanservstdmessage(sender, QM_REMOVEDPERMBAN, banstr, cip->name->content); | |
1282 | if (cip->channel) | |
1283 | localdosetmode_ban(&changes, banstr, MCB_DEL); | |
1284 | /* Remove from database */ | |
1285 | csdb_deleteban(rbp); | |
1286 | /* Remove from list */ | |
1287 | (*rbh)=rbp->next; | |
1288 | /* Free ban/string and update setby refcount, and free actual regban */ | |
1289 | freesstring(rbp->reason); | |
1290 | freechanban(rbp->cbp); | |
1291 | freeregban(rbp); | |
1292 | } | |
1293 | } else { | |
1294 | rbh=&(rbp->next); | |
1295 | } | |
1296 | } | |
1297 | ||
1298 | if (cip->channel) { | |
1299 | for (cbh=&(cip->channel->bans); *cbh; ) { | |
1300 | cbp=*cbh; | |
1301 | if (fn(arg, cbp)) { | |
1302 | /* Remove */ | |
1303 | banstr=bantostring(cbp); | |
1304 | chanservstdmessage(sender, QM_REMOVEDCHANBAN, banstr, cip->name->content); | |
1305 | localdosetmode_ban(&changes, banstr, MCB_DEL); | |
1306 | } else { | |
1307 | cbh=&(cbp->next); | |
1308 | } | |
1309 | } | |
1310 | localsetmodeflush(&changes,1); | |
1311 | } | |
0a53499c | 1312 | |
1313 | return 0; | |
336765cd | 1314 | } |
1482fb78 | 1315 | |
1316 | /* Add entry to channel op history when someone gets ops. */ | |
1317 | void cs_logchanop(regchan *rcp, char *nick, reguser *rup) { | |
1318 | strncpy(rcp->chanopnicks[rcp->chanoppos], nick, NICKLEN); | |
1319 | rcp->chanopnicks[rcp->chanoppos][NICKLEN]='\0'; | |
1320 | rcp->chanopaccts[rcp->chanoppos]=rup->ID; | |
1321 | rcp->chanoppos=(rcp->chanoppos+1)%CHANOPHISTORY; | |
1322 | } |