]>
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) { | |
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 | ||
81 | void chanservuserhandler(nick *target, int message, void **params) { | |
82 | nick *sender; | |
83 | char *msg; | |
84 | Command *cmd; | |
85 | char *cargv[30]; | |
86 | int cargc; | |
87 | reguser *rup; | |
88 | char *chp; | |
89 | ||
90 | switch(message) { | |
91 | case LU_KILLED: | |
92 | scheduleoneshot(time(NULL),&chanservreguser,NULL); | |
93 | chanservnick=NULL; | |
94 | break; | |
95 | ||
96 | case LU_PRIVMSG: | |
97 | case LU_SECUREMSG: | |
98 | sender=(nick *)params[0]; | |
99 | msg=(char *)params[1]; | |
100 | ||
101 | if (!(cargc=splitline(msg,cargv,30,0))) | |
102 | return; /* Ignore blank lines */ | |
103 | ||
104 | if (cargv[0][0]==1) { | |
105 | /* CTCP */ | |
106 | for (chp=cargv[0]+1;*chp;chp++) { | |
107 | if (*chp=='\001') { | |
108 | *chp='\0'; | |
109 | break; | |
110 | } | |
111 | } | |
112 | cmd=findcommandintree(csctcpcommands, cargv[0]+1, 1); | |
113 | if (cmd) { | |
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 | ||
209 | void chanservcommandinit() { | |
210 | cscommands=newcommandtree(); | |
211 | csctcpcommands=newcommandtree(); | |
212 | } | |
213 | ||
214 | void chanservcommandclose() { | |
215 | destroycommandtree(cscommands); | |
216 | destroycommandtree(csctcpcommands); | |
217 | } | |
218 | ||
1f0d7c8b | 219 | void 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 | ||
235 | void chanservaddctcpcommand(char *command, CommandHandler handler) { | |
236 | addcommandtotree(csctcpcommands, command, 0, 1, handler); | |
237 | } | |
238 | ||
239 | void chanservremovectcpcommand(char *command, CommandHandler handler) { | |
240 | deletecommandfromtree(csctcpcommands, command, handler); | |
241 | } | |
242 | ||
243 | void 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 | 265 | void 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 |
277 | void 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 |
358 | static 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 | ||
365 | void 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 |
432 | static void message_callback(nick *np, char *buf) { |
433 | sendmessagetouser(chanservnick,np,"%s",buf); | |
434 | } | |
435 | ||
436 | static void notice_callback(nick *np, char *buf) { | |
437 | sendnoticetouser(chanservnick,np,"%s",buf); | |
438 | } | |
439 | ||
440 | void 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 | 453 | void 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 | ||
509 | void 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 | ||
533 | void 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 | 559 | int 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 |
568 | int 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 |
581 | int 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 |
591 | int setpassword(reguser *rup, const char *pass) { |
592 | strncpy(rup->password, pass, PASSLEN); | |
593 | rup->password[PASSLEN]='\0'; | |
594 | return 1; | |
595 | } | |
596 | ||
597 | void 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 | ||
670 | void cs_checkchanmodes(channel *cp) { | |
671 | modechanges changes; | |
672 | ||
673 | localsetmodeinit (&changes, cp, chanservnick); | |
674 | cs_docheckchanmodes(cp, &changes); | |
675 | localsetmodeflush (&changes, 1); | |
676 | } | |
677 | ||
678 | void 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 |
716 | void 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 | ||
783 | void 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 | ||
857 | void 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 | ||
879 | void 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 | */ | |
941 | void 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 | ||
967 | void 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 | 1092 | void 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.. */ |
1139 | int 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 | 1165 | void 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 | ||
1200 | int 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 | ||
1239 | void 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 | ||
1262 | void 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 | */ | |
1303 | flag_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 | ||
1348 | reguser *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 | 1395 | int 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. */ | |
1456 | void 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 | |
1463 | int 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 | 1472 | regchan *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 | } |