]> jfr.im git - irc/quakenet/newserv.git/blame - request/request.c
Update stats message for Q requests.
[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
327 retval = RQ_ERROR;
328
95440fe7 329 retval = lr_requestl(rqnick, np, cp, qnick);
cbdd5aab 330
95440fe7
P
331 if (rq_logfd != NULL) {
332 now[0] = '\0';
333 now_ts = time(NULL);
334 strftime(now, sizeof(now), "%c", localtime(&now_ts));
cbdd5aab 335
e5363a30
C
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");
95440fe7 340 fflush(rq_logfd);
25b7d0fa
P
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
351int rqcmd_requestspamscan(void *user, int cargc, char **cargv) {
352 nick *np = (nick*)user;
353 channel *cp;
95440fe7
P
354 nick *qnick, *snick;
355 unsigned long *qhand, *shand;
25b7d0fa
P
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
95440fe7 366 if (rq_genericrequestcheck(np, cargv[0], &cp, &qnick) == RQ_ERROR) {
25b7d0fa
P
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. "
43d341bd 376 "Please try again later.", RQ_SNICK);
25b7d0fa
P
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
95440fe7 394 /* we need Q */
25b7d0fa
P
395 qhand = getnumerichandlefromchanhash(cp->users, qnick->numeric);
396
95440fe7 397 if (qhand) {
25b7d0fa 398 /* great, now try to request */
95440fe7 399 retval = qr_requests(rqnick, np, cp, qnick);
25b7d0fa
P
400
401 if (retval == RQ_OK)
402 rq_success++;
403 else if (retval == RQ_ERROR)
404 rq_failed++;
405
406 return retval;
407 } else {
95440fe7 408 /* channel apparently doesn't have Q */
25b7d0fa 409
95440fe7
P
410 sendnoticetouser(rqnick, np, "Error: You need %s in order to be "
411 "able to request %s.", RQ_QNICK, RQ_SNICK);
25b7d0fa
P
412
413 rq_failed++;
414
415 return RQ_ERROR;
416 }
417}
418
052247fa
CP
419int 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) {
43d341bd
C
436 sendnoticetouser(rqnick, np, "Error: Channel '%s' does not exist on the network.",
437 cargv[0]);
052247fa
CP
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) {
43d341bd 473 sendnoticetouser(rqnick, np, "Error: User %s is not on channel '%s'.", user->nick, cargv[0]);
052247fa
CP
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
f3a993c7 488 if (IsService(np2) && (np2->nick[1] == '\0') && !(cp->users->content[a] & CUMODE_OP)) {
052247fa
CP
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)) {
43d341bd 508 sendnoticetouser(rqnick, np, "There are ops on channel '%s'. This command can only be"
052247fa
CP
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
25b7d0fa
P
542int rqcmd_addblock(void *user, int cargc, char **cargv) {
543 nick *np = (nick*)user;
544 rq_block *block;
545 time_t expires;
546 char *account;
f3c45977 547
58f9e8de 548 if (!IsOper(np)) {
f3c45977 549 sendnoticetouser(rqnick, np, "You do not have access to this command.");
25b7d0fa 550
f3c45977
P
551 return RQ_ERROR;
552 }
553
25b7d0fa
P
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
584int rqcmd_delblock(void *user, int cargc, char **cargv) {
585 nick *np = (nick*)user;
58f9e8de 586 int result;
f3c45977 587
58f9e8de 588 if (!IsOper(np)) {
f3c45977 589 sendnoticetouser(rqnick, np, "You do not have access to this command.");
25b7d0fa 590
f3c45977
P
591 return RQ_ERROR;
592 }
593
25b7d0fa 594 if (cargc < 1) {
f3c45977 595 sendnoticetouser(rqnick, np, "Syntax: delblock <mask>");
25b7d0fa
P
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
613int rqcmd_listblocks(void *user, int cargc, char **cargv) {
614 nick *np = (nick*)user;
615 rq_block block;
58f9e8de 616 int i;
f3c45977 617
58f9e8de 618 if (!IsOper(np)) {
f3c45977 619 sendnoticetouser(rqnick, np, "You do not have access to this command.");
25b7d0fa 620
f3c45977
P
621 return RQ_ERROR;
622 }
623
25b7d0fa
P
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
646int rqcmd_stats(void *user, int cargc, char **cargv) {
647 nick *np = (nick*)user;
f3c45977 648
58f9e8de 649 if (!IsOper(np)) {
f3c45977 650 sendnoticetouser(rqnick, np, "You do not have access to this command.");
25b7d0fa 651
f3c45977
P
652 return RQ_ERROR;
653 }
654
25b7d0fa
P
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