]> jfr.im git - irc/quakenet/newserv.git/blame - request/request.c
Clean up fakeusers even more.
[irc/quakenet/newserv.git] / request / request.c
CommitLineData
25b7d0fa
P
1 /* shroud's service request */
2
052247fa 3#include <stdio.h>
f011b15a 4#include <string.h>
25b7d0fa
P
5#include "../localuser/localuser.h"
6#include "../localuser/localuserchannel.h"
7#include "../core/schedule.h"
8#include "../lib/irc_string.h"
9#include "../lib/splitline.h"
df3bf970 10#include "../lib/version.h"
25b7d0fa 11#include "../control/control.h"
f011b15a 12#include "../splitlist/splitlist.h"
25b7d0fa
P
13#include "request.h"
14#include "request_block.h"
6fec1818 15#include "request_fasttrack.h"
25b7d0fa
P
16#include "lrequest.h"
17#include "sqrequest.h"
18
d1142182
P
19MODULE_VERSION("");
20
25b7d0fa
P
21nick *rqnick;
22CommandTree *rqcommands;
23
24void rq_registeruser(void);
25void rq_handler(nick *target, int type, void **args);
26
27int rqcmd_showcommands(void *user, int cargc, char **cargv);
28int rqcmd_request(void *user, int cargc, char **cargv);
29int rqcmd_requestspamscan(void *user, int cargc, char **cargv);
30int rqcmd_addblock(void *user, int cargc, char **cargv);
31int rqcmd_delblock(void *user, int cargc, char **cargv);
32int rqcmd_listblocks(void *user, int cargc, char **cargv);
33int rqcmd_stats(void *user, int cargc, char **cargv);
052247fa 34int rqcmd_requestop(void *user, int cargc, char **cargv);
25b7d0fa
P
35
36#define min(a,b) ((a > b) ? b : a)
37
38/* stats counters */
39int rq_count = 0;
40int rq_failed = 0;
41int rq_success = 0;
42int rq_blocked = 0;
43
052247fa
CP
44/* log fd */
45FILE *rq_logfd;
46
8ee2e8b2
CP
47static int extloaded = 0;
48
25b7d0fa 49void _init(void) {
8ee2e8b2
CP
50 if(!rq_initblocks())
51 return;
52
6fec1818
GB
53 if(!rq_initfasttrack())
54 return;
55
8ee2e8b2
CP
56 extloaded = 1;
57
25b7d0fa
P
58 rqcommands = newcommandtree();
59
60 addcommandtotree(rqcommands, "showcommands", RQU_ANY, 1, &rqcmd_showcommands);
61 addcommandtotree(rqcommands, "requestbot", RQU_ANY, 1, &rqcmd_request);
62 addcommandtotree(rqcommands, "requestspamscan", RQU_ANY, 1, &rqcmd_requestspamscan);
052247fa
CP
63 addcommandtotree(rqcommands, "requestop", RQU_ANY, 2, &rqcmd_requestop);
64
58f9e8de
GB
65 addcommandtotree(rqcommands, "addblock", RQU_OPER, 3, &rqcmd_addblock);
66 addcommandtotree(rqcommands, "delblock", RQU_OPER, 1, &rqcmd_delblock);
67 addcommandtotree(rqcommands, "listblocks", RQU_OPER, 1, &rqcmd_listblocks);
68 addcommandtotree(rqcommands, "stats", RQU_OPER, 1, &rqcmd_stats);
f3c45977 69
25b7d0fa
P
70 qr_initrequest();
71
052247fa
CP
72 rq_logfd = fopen(RQ_LOGFILE, "a");
73
25b7d0fa
P
74 scheduleoneshot(time(NULL) + 1, (ScheduleCallback)&rq_registeruser, NULL);
75}
76
77void _fini(void) {
8ee2e8b2
CP
78 if(!extloaded)
79 return;
80
25b7d0fa
P
81 deregisterlocaluser(rqnick, NULL);
82
83 deletecommandfromtree(rqcommands, "showcommands", &rqcmd_showcommands);
84 deletecommandfromtree(rqcommands, "requestbot", &rqcmd_request);
85 deletecommandfromtree(rqcommands, "requestspamscan", &rqcmd_requestspamscan);
052247fa
CP
86 deletecommandfromtree(rqcommands, "requestop", &rqcmd_requestop);
87
25b7d0fa
P
88 deletecommandfromtree(rqcommands, "addblock", &rqcmd_addblock);
89 deletecommandfromtree(rqcommands, "delblock", &rqcmd_delblock);
90 deletecommandfromtree(rqcommands, "listblocks", &rqcmd_listblocks);
91 deletecommandfromtree(rqcommands, "stats", &rqcmd_stats);
92
25b7d0fa
P
93 destroycommandtree(rqcommands);
94
95 rq_finiblocks();
6fec1818 96 rq_finifasttrack();
25b7d0fa
P
97 qr_finirequest();
98
052247fa
CP
99 if (rq_logfd != NULL)
100 fclose(rq_logfd);
101
25b7d0fa
P
102 deleteallschedules((ScheduleCallback)&rq_registeruser);
103}
104
105void rq_registeruser(void) {
106 channel *cp;
107
dbbee796 108 rqnick = registerlocaluserflags(RQ_REQUEST_NICK, RQ_REQUEST_USER, RQ_REQUEST_HOST,
1df901a7 109 RQ_REQUEST_REAL, RQ_REQUEST_AUTH, RQ_REQUEST_AUTHID, 0,
25b7d0fa
P
110 UMODE_ACCOUNT | UMODE_SERVICE | UMODE_OPER,
111 rq_handler);
112
113 cp = findchannel(RQ_TLZ);
114
115 if (cp == NULL)
116 localcreatechannel(rqnick, RQ_TLZ);
117 else
118 localjoinchannel(rqnick, cp);
119}
120
121char *rq_longtoduration(unsigned long interval) {
122 static char buf[100];
123
124 strncpy(buf, longtoduration(interval, 0), sizeof(buf));
125
126 /* chop off last character if it's a space */
127 if (buf[strlen(buf)] == ' ')
128 buf[strlen(buf)] = '\0';
129
130 return buf;
131}
132
133void rq_handler(nick *target, int type, void **params) {
134 Command* cmd;
135 nick* user;
136 char* line;
137 int cargc;
138 char* cargv[30];
139
140 switch (type) {
141 case LU_PRIVMSG:
142 case LU_SECUREMSG:
143 user = params[0];
144 line = params[1];
145 cargc = splitline(line, cargv, 30, 0);
146
147 if (cargc == 0)
148 return;
149
150 cmd = findcommandintree(rqcommands, cargv[0], 1);
151
152 if (cmd == NULL) {
153 sendnoticetouser(rqnick, user, "Unknown command.");
154
155 return;
156 }
157
f3c45977 158 if ((cmd->level & RQU_OPER) && !IsOper(user)) {
25b7d0fa
P
159 sendnoticetouser(rqnick, user, "Sorry, this command is not "
160 "available to you.");
161
162 return;
163 }
164
165 if (cargc - 1 > cmd->maxparams)
166 rejoinline(cargv[cmd->maxparams], cargc - cmd->maxparams);
167
168 /* handle the command */
169 cmd->handler((void*)user, min(cargc - 1, cmd->maxparams), &(cargv[1]));
170
171 break;
172 case LU_KILLED:
173 scheduleoneshot(time(NULL) + 5, (ScheduleCallback)&rq_registeruser, NULL);
174
175 break;
176 case LU_PRIVNOTICE:
b98ba21f 177 qr_handle_notice(params[0], params[1]);
25b7d0fa
P
178
179 break;
180 }
181}
182
183int rqcmd_showcommands(void *user, int cargc, char **cargv) {
9a01447e 184 nick* np = (nick*)user;
25b7d0fa
P
185 int n, i;
186 Command* cmdlist[50];
187
188 n = getcommandlist(rqcommands, cmdlist, 50);
189
9a01447e
CP
190 sendnoticetouser(rqnick, np, "Available commands:");
191 sendnoticetouser(rqnick, np, "-------------------");
25b7d0fa
P
192
193 for (i = 0; i < n; i++) {
9a01447e
CP
194 if ((cmdlist[i]->level & RQU_OPER) && !IsOper(np))
195 continue;
196
caf9a966 197 sendnoticetouser(rqnick, np, "%s", cmdlist[i]->command->content);
25b7d0fa
P
198 }
199
caf9a966 200 sendnoticetouser(rqnick, np, "End of SHOWCOMMANDS");
25b7d0fa
P
201
202 return 0;
203}
204
95440fe7 205int rq_genericrequestcheck(nick *np, char *channelname, channel **cp, nick **qnick) {
25b7d0fa
P
206 unsigned long *userhand;
207 rq_block *block;
208
209 if (!IsAccount(np)) {
43d341bd 210 sendnoticetouser(rqnick, np, "Error: You must be authed to perform a service request.");
25b7d0fa
P
211
212 return RQ_ERROR;
213 }
214
215 *cp = findchannel(channelname);
216
217 if (*cp == NULL) {
43d341bd 218 sendnoticetouser(rqnick, np, "Error: Channel '%s' does not exist on the network.",
25b7d0fa
P
219 channelname);
220
221 return RQ_ERROR;
222 }
223
25b7d0fa
P
224 *qnick = getnickbynick(RQ_QNICK);
225
226 if (*qnick == NULL || findserver(RQ_QSERVER) < 0) {
227 sendnoticetouser(rqnick, np, "Error: %s does not seem to be online. "
43d341bd 228 "Please try again later.", RQ_QNICK);
25b7d0fa
P
229
230 return RQ_ERROR;
231 }
232
233 userhand = getnumerichandlefromchanhash((*cp)->users, np->numeric);
234
235 if (userhand == NULL) {
236 sendnoticetouser(rqnick, np, "Error: You're not on that channel.");
237
238 return RQ_ERROR;
239 }
240
241 if ((*userhand & CUMODE_OP) == 0) {
242 sendnoticetouser(rqnick, np, "Error: You must be op'd on the channel to "
243 "request a service.");
244
245 return RQ_ERROR;
246 }
247
248 block = rq_findblock(channelname);
249
250 if (block != NULL) {
8ee2e8b2
CP
251 /* only say when block expires if <7 days */
252 if ( block->expires < getnettime() + 3600 * 24 * 7) {
253 sendnoticetouser(rqnick, np, "Error: You are not allowed to request a "
43d341bd
C
254 "service to '%s'. Keep waiting for at least %s before you try again.",
255 channelname, rq_longtoduration(block->expires - getnettime()));
8ee2e8b2
CP
256 /* give them another 5 minutes to think about it */
257 block->expires += 300;
258 rq_saveblocks();
259 } else {
260 sendnoticetouser(rqnick, np, "Error: You are not allowed to request a "
43d341bd 261 "service to '%s'.", channelname);
8ee2e8b2 262 }
25b7d0fa
P
263 sendnoticetouser(rqnick, np, "Reason: %s", block->reason->content);
264
265 rq_blocked++;
266
267 return RQ_ERROR;
268 }
269
270 block = rq_findblock(np->authname);
271
272 /* only tell the user if the block is going to expire in the next 48 hours
273 so we can have our fun with longterm blocks.
274 the request subsystems should deal with longterm blocks on their own */
275 if (block != NULL && block->expires < getnettime() + 3600 * 24 * 2) {
276 sendnoticetouser(rqnick, np, "Error: You are not allowed to request a "
277 "service. Keep waiting for at least %s before you try again.",
278 rq_longtoduration(block->expires - getnettime()));
279
280 sendnoticetouser(rqnick, np, "Reason: %s", block->reason->content);
281
282 /* give them another 5 minutes to think about it */
283 block->expires += 300;
284 rq_saveblocks();
285
286 rq_blocked++;
287
288 return RQ_ERROR;
289 }
290
291 return RQ_OK;
292}
293
294int rqcmd_request(void *user, int cargc, char **cargv) {
295 nick *np = (nick*)user;
95440fe7
P
296 nick *qnick;
297 unsigned long *qhand;
f011b15a 298 channel *cp;
25b7d0fa 299 int retval;
052247fa
CP
300 time_t now_ts;
301 char now[50];
25b7d0fa
P
302
303 if (cargc < 1) {
304 sendnoticetouser(rqnick, np, "Syntax: requestbot <#channel>");
305
306 return RQ_ERROR;
307 }
308
309 rq_count++;
310
95440fe7 311 if (rq_genericrequestcheck(np, cargv[0], &cp, &qnick) == RQ_ERROR) {
25b7d0fa
P
312 rq_failed++;
313
314 return RQ_ERROR;
315 }
316
25b7d0fa
P
317 qhand = getnumerichandlefromchanhash(cp->users, qnick->numeric);
318
319 if (qhand != NULL) {
320 sendnoticetouser(rqnick, np, "Error: %s is already on that channel.", RQ_QNICK);
321
322 rq_failed++;
323
324 return RQ_ERROR;
325 }
326
95440fe7 327 retval = lr_requestl(rqnick, np, cp, qnick);
cbdd5aab 328
95440fe7
P
329 if (rq_logfd != NULL) {
330 now[0] = '\0';
331 now_ts = time(NULL);
332 strftime(now, sizeof(now), "%c", localtime(&now_ts));
cbdd5aab 333
e5363a30
C
334 fprintf(rq_logfd, "%s: request (%s) for %s from %s!%s@%s%s%s: Request was %s.\n",
335 now, RQ_QNICK, cp->index->name->content,
336 np->nick, np->ident, np->host->name->content, IsAccount(np)?"/":"", IsAccount(np)?np->authname:"",
337 (retval == RQ_OK) ? "accepted" : "denied");
95440fe7 338 fflush(rq_logfd);
25b7d0fa
P
339 }
340
341 if (retval == RQ_ERROR)
342 rq_failed++;
343 else if (retval == RQ_OK)
344 rq_success++;
345
346 return retval;
347}
348
349int rqcmd_requestspamscan(void *user, int cargc, char **cargv) {
350 nick *np = (nick*)user;
351 channel *cp;
95440fe7
P
352 nick *qnick, *snick;
353 unsigned long *qhand, *shand;
25b7d0fa
P
354 int retval;
355
356 if (cargc < 1) {
357 sendnoticetouser(rqnick, np, "Syntax: requestspamscan <#channel>");
358
359 return RQ_ERROR;
360 }
361
362 rq_count++;
363
95440fe7 364 if (rq_genericrequestcheck(np, cargv[0], &cp, &qnick) == RQ_ERROR) {
25b7d0fa
P
365 rq_failed++;
366
367 return RQ_ERROR;
368 }
369
370 snick = getnickbynick(RQ_SNICK);
371
372 if (snick == NULL || findserver(RQ_SSERVER) < 0) {
373 sendnoticetouser(rqnick, np, "Error: %s does not seem to be online. "
43d341bd 374 "Please try again later.", RQ_SNICK);
25b7d0fa
P
375
376 rq_failed++;
377
378 return RQ_ERROR;
379 }
380
381 /* does the user already have S on that channel? */
382 shand = getnumerichandlefromchanhash(cp->users, snick->numeric);
383
384 if (shand != NULL) {
385 sendnoticetouser(rqnick, np, "Error: %s is already on that channel.", RQ_SNICK);
386
387 rq_failed++;
388
389 return RQ_ERROR;
390 }
391
95440fe7 392 /* we need Q */
25b7d0fa
P
393 qhand = getnumerichandlefromchanhash(cp->users, qnick->numeric);
394
95440fe7 395 if (qhand) {
25b7d0fa 396 /* great, now try to request */
95440fe7 397 retval = qr_requests(rqnick, np, cp, qnick);
25b7d0fa
P
398
399 if (retval == RQ_OK)
400 rq_success++;
401 else if (retval == RQ_ERROR)
402 rq_failed++;
403
404 return retval;
405 } else {
95440fe7 406 /* channel apparently doesn't have Q */
25b7d0fa 407
95440fe7
P
408 sendnoticetouser(rqnick, np, "Error: You need %s in order to be "
409 "able to request %s.", RQ_QNICK, RQ_SNICK);
25b7d0fa
P
410
411 rq_failed++;
412
413 return RQ_ERROR;
414 }
415}
416
052247fa
CP
417int rqcmd_requestop(void *source, int cargc, char **cargv) {
418 nick *np2, *np = (nick *)source;
419 nick *user = np;
420 channel *cp;
421 int ret, a, count;
422 unsigned long *hand;
423 modechanges changes;
424
425 if (cargc < 1) {
426 sendnoticetouser(rqnick, np, "Syntax: requestop <#channel> [nick]");
427
428 return CMD_ERROR;
429 }
430
431 cp = findchannel(cargv[0]);
432
433 if (cp == NULL) {
43d341bd
C
434 sendnoticetouser(rqnick, np, "Error: Channel '%s' does not exist on the network.",
435 cargv[0]);
052247fa
CP
436
437 return CMD_ERROR;
438 }
439
440 if (cargc > 1) {
441 user = getnickbynick(cargv[1]);
442
443 if (!user) {
444 sendnoticetouser(rqnick, np, "Error: No such user.");
445
446 return CMD_ERROR;
447 }
448 }
449
450 if (getnettime() - np->timestamp < 300) {
451 sendnoticetouser(rqnick, np, "Error: You connected %s ago. To"
452 " request ops you must have been on the network for"
453 " at least 5 minutes.",
454 rq_longtoduration(getnettime() - np->timestamp));
455
456 return CMD_ERROR;
457 }
458
459 if (getnettime() - user->timestamp < 300) {
460 sendnoticetouser(rqnick, np, "Error: The nick you requested op for"
461 " connected %s ago. To request op, it must have"
462 "been on the network for at least 5 minutes.",
463 rq_longtoduration(getnettime() - user->timestamp));
464
465 return CMD_ERROR;
466 }
467
468 hand = getnumerichandlefromchanhash(cp->users, user->numeric);
469
470 if (!hand) {
43d341bd 471 sendnoticetouser(rqnick, np, "Error: User %s is not on channel '%s'.", user->nick, cargv[0]);
052247fa
CP
472
473 return CMD_ERROR;
474 }
475
476
477 count = 0;
478
479 localsetmodeinit(&changes, cp, rqnick);
480
481 /* reop any services first */
482 for(a=0;a<cp->users->hashsize;a++) {
483 if(cp->users->content[a] != nouser) {
484 np2 = getnickbynumeric(cp->users->content[a]);
485
f3a993c7 486 if (IsService(np2) && (np2->nick[1] == '\0') && !(cp->users->content[a] & CUMODE_OP)) {
052247fa
CP
487 localdosetmode_nick(&changes, np2, MC_OP);
488 count++;
489 }
490 }
491 }
492
493 localsetmodeflush(&changes, 1);
494
495 if (count > 0) {
496 if (count == 1)
497 sendnoticetouser(rqnick, np, "1 service was reopped.");
498 else
499 sendnoticetouser(rqnick, np, "%d services were reopped.", count);
500
501 return CMD_ERROR;
502 }
503
504 for (a=0;a<cp->users->hashsize;a++) {
505 if ((cp->users->content[a] != nouser) && (cp->users->content[a] & CUMODE_OP)) {
43d341bd 506 sendnoticetouser(rqnick, np, "There are ops on channel '%s'. This command can only be"
052247fa
CP
507 " used if there are no ops.", cargv[0]);
508
509 return CMD_ERROR;
510 }
511 }
512
513 if (sp_countsplitservers() > 0) {
514 sendnoticetouser(rqnick, np, "One or more servers are currently split. Wait until the"
515 " netsplit is over and try again.");
516
517 return CMD_ERROR;
518 }
519
520 if (cf_wouldreop(user, cp)) {
521 localsetmodeinit(&changes, cp, rqnick);
522 localdosetmode_nick(&changes, user, MC_OP);
523 localsetmodeflush(&changes, 1);
524
525 sendnoticetouser(rqnick, np, "Chanfix opped you on the specified channel.");
526 } else {
527 ret = cf_fixchannel(cp);
528
529 if (ret == CFX_NOUSERSAVAILABLE)
530 sendnoticetouser(rqnick, np, "Chanfix knows regular ops for that channel. They will"
531 " be opped when they return.");
532 else
533 sendnoticetouser(rqnick, np, "Chanfix has opped known ops for that channel.");
534 }
535
536 return CMD_OK;
537
538}
539
25b7d0fa
P
540int rqcmd_addblock(void *user, int cargc, char **cargv) {
541 nick *np = (nick*)user;
542 rq_block *block;
543 time_t expires;
544 char *account;
f3c45977 545
58f9e8de 546 if (!IsOper(np)) {
f3c45977 547 sendnoticetouser(rqnick, np, "You do not have access to this command.");
25b7d0fa 548
f3c45977
P
549 return RQ_ERROR;
550 }
551
25b7d0fa
P
552 if (cargc < 3) {
553 sendnoticetouser(rqnick, np, "Syntax: addblock <mask> <duration> <reason>");
554
555 return RQ_ERROR;
556 }
557
558 block = rq_findblock(cargv[0]);
559
560 if (block != NULL) {
561 sendnoticetouser(rqnick, np, "That mask is already blocked by %s "
562 "(reason: %s).", block->creator->content, block->reason->content);
563
564 return RQ_ERROR;
565 }
566
567 if (IsAccount(np))
568 account = np->authname;
569 else
570 account = "unknown";
571
572 expires = getnettime() + durationtolong(cargv[1]);
573
574 rq_addblock(cargv[0], cargv[2], account, 0, expires);
575
576 sendnoticetouser(rqnick, np, "Blocked channels/accounts matching '%s' from "
577 "requesting a service.", cargv[0]);
578
579 return RQ_OK;
580}
581
582int rqcmd_delblock(void *user, int cargc, char **cargv) {
583 nick *np = (nick*)user;
58f9e8de 584 int result;
f3c45977 585
58f9e8de 586 if (!IsOper(np)) {
f3c45977 587 sendnoticetouser(rqnick, np, "You do not have access to this command.");
25b7d0fa 588
f3c45977
P
589 return RQ_ERROR;
590 }
591
25b7d0fa 592 if (cargc < 1) {
f3c45977 593 sendnoticetouser(rqnick, np, "Syntax: delblock <mask>");
25b7d0fa
P
594
595 return RQ_ERROR;
596 }
597
598 result = rq_removeblock(cargv[0]);
599
600 if (result > 0) {
601 sendnoticetouser(rqnick, np, "Block for '%s' was removed.", cargv[0]);
602
603 return RQ_OK;
604 } else {
605 sendnoticetouser(rqnick, np, "There is no such block.");
606
607 return RQ_OK;
608 }
609}
610
611int rqcmd_listblocks(void *user, int cargc, char **cargv) {
612 nick *np = (nick*)user;
613 rq_block block;
58f9e8de 614 int i;
f3c45977 615
58f9e8de 616 if (!IsOper(np)) {
f3c45977 617 sendnoticetouser(rqnick, np, "You do not have access to this command.");
25b7d0fa 618
f3c45977
P
619 return RQ_ERROR;
620 }
621
25b7d0fa
P
622 sendnoticetouser(rqnick, np, "Mask By Expires"
623 " Reason");
624
625 for (i = 0; i < rqblocks.cursi; i++) {
626 block = ((rq_block*)rqblocks.content)[i];
627
628 if (block.expires != 0 && block.expires < getnettime())
629 continue; /* ignore blocks which have already expired,
630 rq_findblock will deal with them later on */
631
632 if (cargc < 1 || match2strings(block.pattern->content, cargv[0]))
633 sendnoticetouser(rqnick, np, "%-11s %-9s %-25s %s",
634 block.pattern->content, block.creator->content,
635 rq_longtoduration(block.expires - getnettime()),
636 block.reason->content);
637 }
638
639 sendnoticetouser(rqnick, np, "--- End of blocklist");
640
641 return RQ_OK;
642}
643
644int rqcmd_stats(void *user, int cargc, char **cargv) {
645 nick *np = (nick*)user;
f3c45977 646
58f9e8de 647 if (!IsOper(np)) {
f3c45977 648 sendnoticetouser(rqnick, np, "You do not have access to this command.");
25b7d0fa 649
f3c45977
P
650 return RQ_ERROR;
651 }
652
25b7d0fa
P
653 sendnoticetouser(rqnick, np, "Total requests: %d", rq_count);
654 sendnoticetouser(rqnick, np, "Successful requests: %d", rq_success);
655 sendnoticetouser(rqnick, np, "Failed requests: %d", rq_failed);
656 sendnoticetouser(rqnick, np, "- Blocked: %d", rq_blocked);
657
658 lr_requeststats(rqnick, np);
659 qr_requeststats(rqnick, np);
660
661 return RQ_OK;
662}
663