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