]> jfr.im git - irc/quakenet/newserv.git/blob - request/request.c
Merge default.
[irc/quakenet/newserv.git] / request / request.c
1 /* shroud's service request */
2
3 #include <stdio.h>
4 #include <string.h>
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"
10 #include "../lib/version.h"
11 #include "../control/control.h"
12 #include "../splitlist/splitlist.h"
13 #include "request.h"
14 #include "request_block.h"
15 #include "request_fasttrack.h"
16 #include "lrequest.h"
17 #include "sqrequest.h"
18
19 MODULE_VERSION("");
20
21 nick *rqnick;
22 CommandTree *rqcommands;
23
24 void rq_registeruser(void);
25 void rq_handler(nick *target, int type, void **args);
26
27 int rqcmd_showcommands(void *user, int cargc, char **cargv);
28 int rqcmd_request(void *user, int cargc, char **cargv);
29 int rqcmd_requestspamscan(void *user, int cargc, char **cargv);
30 int rqcmd_addblock(void *user, int cargc, char **cargv);
31 int rqcmd_delblock(void *user, int cargc, char **cargv);
32 int rqcmd_listblocks(void *user, int cargc, char **cargv);
33 int rqcmd_stats(void *user, int cargc, char **cargv);
34 int rqcmd_requestop(void *user, int cargc, char **cargv);
35
36 #define min(a,b) ((a > b) ? b : a)
37
38 /* stats counters */
39 int rq_count = 0;
40 int rq_failed = 0;
41 int rq_success = 0;
42 int rq_blocked = 0;
43
44 /* log fd */
45 FILE *rq_logfd;
46
47 static int extloaded = 0;
48
49 void _init(void) {
50 if(!rq_initblocks())
51 return;
52
53 if(!rq_initfasttrack())
54 return;
55
56 extloaded = 1;
57
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);
63 addcommandtotree(rqcommands, "requestop", RQU_ANY, 2, &rqcmd_requestop);
64
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);
69
70 qr_initrequest();
71
72 rq_logfd = fopen(RQ_LOGFILE, "a");
73
74 scheduleoneshot(time(NULL) + 1, (ScheduleCallback)&rq_registeruser, NULL);
75 }
76
77 void _fini(void) {
78 if(!extloaded)
79 return;
80
81 deregisterlocaluser(rqnick, NULL);
82
83 deletecommandfromtree(rqcommands, "showcommands", &rqcmd_showcommands);
84 deletecommandfromtree(rqcommands, "requestbot", &rqcmd_request);
85 deletecommandfromtree(rqcommands, "requestspamscan", &rqcmd_requestspamscan);
86 deletecommandfromtree(rqcommands, "requestop", &rqcmd_requestop);
87
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
93 destroycommandtree(rqcommands);
94
95 rq_finiblocks();
96 rq_finifasttrack();
97 qr_finirequest();
98
99 if (rq_logfd != NULL)
100 fclose(rq_logfd);
101
102 deleteallschedules((ScheduleCallback)&rq_registeruser);
103 }
104
105 void rq_registeruser(void) {
106 channel *cp;
107
108 rqnick = registerlocaluserflags(RQ_REQUEST_NICK, RQ_REQUEST_USER, RQ_REQUEST_HOST,
109 RQ_REQUEST_REAL, RQ_REQUEST_AUTH, RQ_REQUEST_AUTHID, 0,
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
121 char *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
133 void 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
158 if ((cmd->level & RQU_OPER) && !IsOper(user)) {
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:
177 qr_handle_notice(params[0], params[1]);
178
179 break;
180 }
181 }
182
183 int rqcmd_showcommands(void *user, int cargc, char **cargv) {
184 nick* np = (nick*)user;
185 int n, i;
186 Command* cmdlist[50];
187
188 n = getcommandlist(rqcommands, cmdlist, 50);
189
190 sendnoticetouser(rqnick, np, "Available commands:");
191 sendnoticetouser(rqnick, np, "-------------------");
192
193 for (i = 0; i < n; i++) {
194 if ((cmdlist[i]->level & RQU_OPER) && !IsOper(np))
195 continue;
196
197 sendnoticetouser(rqnick, np, "%s", cmdlist[i]->command->content);
198 }
199
200 sendnoticetouser(rqnick, np, "End of SHOWCOMMANDS");
201
202 return 0;
203 }
204
205 int rq_genericrequestcheck(nick *np, char *channelname, channel **cp, nick **qnick) {
206 unsigned long *userhand;
207 rq_block *block;
208
209 if (!IsAccount(np)) {
210 sendnoticetouser(rqnick, np, "Error: You must be authed to perform a service request.");
211
212 return RQ_ERROR;
213 }
214
215 *cp = findchannel(channelname);
216
217 if (*cp == NULL) {
218 sendnoticetouser(rqnick, np, "Error: Channel '%s' does not exist on the network.",
219 channelname);
220
221 return RQ_ERROR;
222 }
223
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. "
228 "Please try again later.", RQ_QNICK);
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) {
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 "
254 "service to '%s'. Keep waiting for at least %s before you try again.",
255 channelname, rq_longtoduration(block->expires - getnettime()));
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 "
261 "service to '%s'.", channelname);
262 }
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
294 int rqcmd_request(void *user, int cargc, char **cargv) {
295 nick *np = (nick*)user;
296 nick *qnick;
297 unsigned long *qhand;
298 channel *cp;
299 int retval;
300 time_t now_ts;
301 char now[50];
302
303 if (cargc < 1) {
304 sendnoticetouser(rqnick, np, "Syntax: requestbot <#channel>");
305
306 return RQ_ERROR;
307 }
308
309 rq_count++;
310
311 if (rq_genericrequestcheck(np, cargv[0], &cp, &qnick) == RQ_ERROR) {
312 rq_failed++;
313
314 return RQ_ERROR;
315 }
316
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
327 retval = RQ_ERROR;
328
329 retval = lr_requestl(rqnick, np, cp, qnick);
330
331 if (rq_logfd != NULL) {
332 now[0] = '\0';
333 now_ts = time(NULL);
334 strftime(now, sizeof(now), "%c", localtime(&now_ts));
335
336 fprintf(rq_logfd, "%s: request (%s) for %s from %s!%s@%s%s%s: Request was %s.\n",
337 now, RQ_QNICK, cp->index->name->content,
338 np->nick, np->ident, np->host->name->content, IsAccount(np)?"/":"", IsAccount(np)?np->authname:"",
339 (retval == RQ_OK) ? "accepted" : "denied");
340 fflush(rq_logfd);
341 }
342
343 if (retval == RQ_ERROR)
344 rq_failed++;
345 else if (retval == RQ_OK)
346 rq_success++;
347
348 return retval;
349 }
350
351 int rqcmd_requestspamscan(void *user, int cargc, char **cargv) {
352 nick *np = (nick*)user;
353 channel *cp;
354 nick *qnick, *snick;
355 unsigned long *qhand, *shand;
356 int retval;
357
358 if (cargc < 1) {
359 sendnoticetouser(rqnick, np, "Syntax: requestspamscan <#channel>");
360
361 return RQ_ERROR;
362 }
363
364 rq_count++;
365
366 if (rq_genericrequestcheck(np, cargv[0], &cp, &qnick) == RQ_ERROR) {
367 rq_failed++;
368
369 return RQ_ERROR;
370 }
371
372 snick = getnickbynick(RQ_SNICK);
373
374 if (snick == NULL || findserver(RQ_SSERVER) < 0) {
375 sendnoticetouser(rqnick, np, "Error: %s does not seem to be online. "
376 "Please try again later.", RQ_SNICK);
377
378 rq_failed++;
379
380 return RQ_ERROR;
381 }
382
383 /* does the user already have S on that channel? */
384 shand = getnumerichandlefromchanhash(cp->users, snick->numeric);
385
386 if (shand != NULL) {
387 sendnoticetouser(rqnick, np, "Error: %s is already on that channel.", RQ_SNICK);
388
389 rq_failed++;
390
391 return RQ_ERROR;
392 }
393
394 /* we need Q */
395 qhand = getnumerichandlefromchanhash(cp->users, qnick->numeric);
396
397 if (qhand) {
398 /* great, now try to request */
399 retval = qr_requests(rqnick, np, cp, qnick);
400
401 if (retval == RQ_OK)
402 rq_success++;
403 else if (retval == RQ_ERROR)
404 rq_failed++;
405
406 return retval;
407 } else {
408 /* channel apparently doesn't have Q */
409
410 sendnoticetouser(rqnick, np, "Error: You need %s in order to be "
411 "able to request %s.", RQ_QNICK, RQ_SNICK);
412
413 rq_failed++;
414
415 return RQ_ERROR;
416 }
417 }
418
419 int rqcmd_requestop(void *source, int cargc, char **cargv) {
420 nick *np2, *np = (nick *)source;
421 nick *user = np;
422 channel *cp;
423 int ret, a, count;
424 unsigned long *hand;
425 modechanges changes;
426
427 if (cargc < 1) {
428 sendnoticetouser(rqnick, np, "Syntax: requestop <#channel> [nick]");
429
430 return CMD_ERROR;
431 }
432
433 cp = findchannel(cargv[0]);
434
435 if (cp == NULL) {
436 sendnoticetouser(rqnick, np, "Error: Channel '%s' does not exist on the network.",
437 cargv[0]);
438
439 return CMD_ERROR;
440 }
441
442 if (cargc > 1) {
443 user = getnickbynick(cargv[1]);
444
445 if (!user) {
446 sendnoticetouser(rqnick, np, "Error: No such user.");
447
448 return CMD_ERROR;
449 }
450 }
451
452 if (getnettime() - np->timestamp < 300) {
453 sendnoticetouser(rqnick, np, "Error: You connected %s ago. To"
454 " request ops you must have been on the network for"
455 " at least 5 minutes.",
456 rq_longtoduration(getnettime() - np->timestamp));
457
458 return CMD_ERROR;
459 }
460
461 if (getnettime() - user->timestamp < 300) {
462 sendnoticetouser(rqnick, np, "Error: The nick you requested op for"
463 " connected %s ago. To request op, it must have"
464 "been on the network for at least 5 minutes.",
465 rq_longtoduration(getnettime() - user->timestamp));
466
467 return CMD_ERROR;
468 }
469
470 hand = getnumerichandlefromchanhash(cp->users, user->numeric);
471
472 if (!hand) {
473 sendnoticetouser(rqnick, np, "Error: User %s is not on channel '%s'.", user->nick, cargv[0]);
474
475 return CMD_ERROR;
476 }
477
478
479 count = 0;
480
481 localsetmodeinit(&changes, cp, rqnick);
482
483 /* reop any services first */
484 for(a=0;a<cp->users->hashsize;a++) {
485 if(cp->users->content[a] != nouser) {
486 np2 = getnickbynumeric(cp->users->content[a]);
487
488 if (IsService(np2) && (np2->nick[1] == '\0') && !(cp->users->content[a] & CUMODE_OP)) {
489 localdosetmode_nick(&changes, np2, MC_OP);
490 count++;
491 }
492 }
493 }
494
495 localsetmodeflush(&changes, 1);
496
497 if (count > 0) {
498 if (count == 1)
499 sendnoticetouser(rqnick, np, "1 service was reopped.");
500 else
501 sendnoticetouser(rqnick, np, "%d services were reopped.", count);
502
503 return CMD_ERROR;
504 }
505
506 for (a=0;a<cp->users->hashsize;a++) {
507 if ((cp->users->content[a] != nouser) && (cp->users->content[a] & CUMODE_OP)) {
508 sendnoticetouser(rqnick, np, "There are ops on channel '%s'. This command can only be"
509 " used if there are no ops.", cargv[0]);
510
511 return CMD_ERROR;
512 }
513 }
514
515 if (sp_countsplitservers() > 0) {
516 sendnoticetouser(rqnick, np, "One or more servers are currently split. Wait until the"
517 " netsplit is over and try again.");
518
519 return CMD_ERROR;
520 }
521
522 if (cf_wouldreop(user, cp)) {
523 localsetmodeinit(&changes, cp, rqnick);
524 localdosetmode_nick(&changes, user, MC_OP);
525 localsetmodeflush(&changes, 1);
526
527 sendnoticetouser(rqnick, np, "Chanfix opped you on the specified channel.");
528 } else {
529 ret = cf_fixchannel(cp);
530
531 if (ret == CFX_NOUSERSAVAILABLE)
532 sendnoticetouser(rqnick, np, "Chanfix knows regular ops for that channel. They will"
533 " be opped when they return.");
534 else
535 sendnoticetouser(rqnick, np, "Chanfix has opped known ops for that channel.");
536 }
537
538 return CMD_OK;
539
540 }
541
542 int rqcmd_addblock(void *user, int cargc, char **cargv) {
543 nick *np = (nick*)user;
544 rq_block *block;
545 time_t expires;
546 char *account;
547
548 if (!IsOper(np)) {
549 sendnoticetouser(rqnick, np, "You do not have access to this command.");
550
551 return RQ_ERROR;
552 }
553
554 if (cargc < 3) {
555 sendnoticetouser(rqnick, np, "Syntax: addblock <mask> <duration> <reason>");
556
557 return RQ_ERROR;
558 }
559
560 block = rq_findblock(cargv[0]);
561
562 if (block != NULL) {
563 sendnoticetouser(rqnick, np, "That mask is already blocked by %s "
564 "(reason: %s).", block->creator->content, block->reason->content);
565
566 return RQ_ERROR;
567 }
568
569 if (IsAccount(np))
570 account = np->authname;
571 else
572 account = "unknown";
573
574 expires = getnettime() + durationtolong(cargv[1]);
575
576 rq_addblock(cargv[0], cargv[2], account, 0, expires);
577
578 sendnoticetouser(rqnick, np, "Blocked channels/accounts matching '%s' from "
579 "requesting a service.", cargv[0]);
580
581 return RQ_OK;
582 }
583
584 int rqcmd_delblock(void *user, int cargc, char **cargv) {
585 nick *np = (nick*)user;
586 int result;
587
588 if (!IsOper(np)) {
589 sendnoticetouser(rqnick, np, "You do not have access to this command.");
590
591 return RQ_ERROR;
592 }
593
594 if (cargc < 1) {
595 sendnoticetouser(rqnick, np, "Syntax: delblock <mask>");
596
597 return RQ_ERROR;
598 }
599
600 result = rq_removeblock(cargv[0]);
601
602 if (result > 0) {
603 sendnoticetouser(rqnick, np, "Block for '%s' was removed.", cargv[0]);
604
605 return RQ_OK;
606 } else {
607 sendnoticetouser(rqnick, np, "There is no such block.");
608
609 return RQ_OK;
610 }
611 }
612
613 int rqcmd_listblocks(void *user, int cargc, char **cargv) {
614 nick *np = (nick*)user;
615 rq_block block;
616 int i;
617
618 if (!IsOper(np)) {
619 sendnoticetouser(rqnick, np, "You do not have access to this command.");
620
621 return RQ_ERROR;
622 }
623
624 sendnoticetouser(rqnick, np, "Mask By Expires"
625 " Reason");
626
627 for (i = 0; i < rqblocks.cursi; i++) {
628 block = ((rq_block*)rqblocks.content)[i];
629
630 if (block.expires != 0 && block.expires < getnettime())
631 continue; /* ignore blocks which have already expired,
632 rq_findblock will deal with them later on */
633
634 if (cargc < 1 || match2strings(block.pattern->content, cargv[0]))
635 sendnoticetouser(rqnick, np, "%-11s %-9s %-25s %s",
636 block.pattern->content, block.creator->content,
637 rq_longtoduration(block.expires - getnettime()),
638 block.reason->content);
639 }
640
641 sendnoticetouser(rqnick, np, "--- End of blocklist");
642
643 return RQ_OK;
644 }
645
646 int rqcmd_stats(void *user, int cargc, char **cargv) {
647 nick *np = (nick*)user;
648
649 if (!IsOper(np)) {
650 sendnoticetouser(rqnick, np, "You do not have access to this command.");
651
652 return RQ_ERROR;
653 }
654
655 sendnoticetouser(rqnick, np, "Total requests: %d", rq_count);
656 sendnoticetouser(rqnick, np, "Successful requests: %d", rq_success);
657 sendnoticetouser(rqnick, np, "Failed requests: %d", rq_failed);
658 sendnoticetouser(rqnick, np, "- Blocked: %d", rq_blocked);
659
660 lr_requeststats(rqnick, np);
661 qr_requeststats(rqnick, np);
662
663 return RQ_OK;
664 }
665