]>
jfr.im git - irc/quakenet/newserv.git/blob - request/sqrequest.c
2 * S and Q request system!
4 * Depends on "chanstats" and "chanfix"
10 #include "request_block.h"
11 #include "../chanfix/chanfix.h"
12 #include "../chanstats/chanstats.h"
13 #include "../localuser/localuser.h"
14 #include "../lib/irc_string.h"
15 #include "../core/schedule.h"
16 #include "../core/config.h"
18 #include "../spamscan2/spamscan2.h"
25 #define QRLstate_IDLE 0x0 /* No request active */
26 #define QRLstate_AWAITINGCHAN 0x1 /* Awaiting "Users for channel.." */
27 #define QRLstate_AWAITINGUSER 0x2 /* Looking for our user in the list */
28 #define QRLstate_AWAITINGEND 0x3 /* Waiting for "End of chanlev" */
34 #define QR_SPAMSCAN 0x1
39 #define min(a,b) ((a > b) ? b : a)
41 typedef struct requestrec
{
42 unsigned int reqnumeric
; /* Who made the request */
43 chanindex
*cip
; /* Which channel the request is for */
44 int what
; /* Which service does the user want? */
45 int who
; /* Who are we talking to about CHANLEV? */
46 struct requestrec
*next
;
49 requestrec
*nextreql
, *lastreql
;
50 requestrec
*nextreqq
, *lastreqq
;
53 static requestrec
*nextqreq
, *lastqreq
;
72 /* Check whether the user is blocked */
73 int qr_blockcheck(requestrec
*req
) {
77 np
= getnickbynumeric(req
->reqnumeric
);
79 /* user is not online anymore */
83 block
= rq_findblock(np
->authname
);
86 return 1; /* user is blocked */
91 unsigned int rq_countchanusers(channel
*cp
) {
94 for (i
=0;i
<cp
->users
->hashsize
;i
++) {
95 if (cp
->users
->content
[i
]==nouser
)
105 * Deal with outcome of a queued request. The request should be freed
106 * as part of the process.
109 static void qr_result(requestrec
*req
, int outcome
, char failcode
, char *message
, ...) __attribute__ ((format (printf
, 4, 5)));
110 static void qr_result(requestrec
*req
, int outcome
, char failcode
, char *message
, ...) {
111 sstring
*user
, *password
;
115 nick
*np
, *tnp
, *snp
;
118 unsigned int unique
, total
;
120 /* Delete the request from the list first.. */
121 for (rh
=&nextreql
;*rh
;rh
=&((*rh
)->next
)) {
128 for (rh
=&nextreqq
;*rh
;rh
=&((*rh
)->next
)) {
135 /* If this was the last request (unlikely),
136 * we need to fix the last pointer */
139 for (lastreql
=nextreql
;lastreql
->next
;lastreql
=lastreql
->next
)
147 for (lastreqq
=nextreqq
;lastreqq
->next
;lastreqq
=lastreqq
->next
)
153 /* Check that the nick is still here. If not, drop the request. */
154 if (!(tnp
=np
=getnickbynumeric(req
->reqnumeric
))) {
159 if (rq_logfd
!= NULL
) {
163 if (req
->cip
->channel
) {
164 unique
= countuniquehosts(req
->cip
->channel
);
165 total
= rq_countchanusers(req
->cip
->channel
);
171 strftime(now
, sizeof(now
), "%c", localtime(&now_ts
));
172 fprintf(rq_logfd
, "%s: request (%s) for %s (%d unique users, "
173 "%d total users) from %s!%s@%s%s%s: Request was %s (%c).\n", now
,
174 (req
->what
== QR_CSERVE
) ? RQ_QNICK
: RQ_SNICK
,
175 req
->cip
->name
->content
, unique
, total
,
176 tnp
->nick
, tnp
->ident
, tnp
->host
->name
->content
, IsAccount(tnp
)?"/":"", IsAccount(tnp
)?tnp
->authname
:"",
177 (outcome
== QR_OK
) ? "accepted" : "denied", failcode
);
181 if (outcome
==QR_OK
) {
182 if (req
->what
== QR_SPAMSCAN
) {
185 if (!(snp
=getnickbynick(RQ_SNICK
))) {
186 sendnoticetouser(rqnick
, tnp
,
187 "Cannot find %s on the network. "
188 "Please try your request again later.", RQ_SNICK
);
194 sendnoticetouser(rqnick
, tnp
, "Success! %s has been added to '%s' "
195 "(contact #help if you require further assistance).",
196 RQ_SNICK
, req
->cip
->name
->content
);
199 user
= (sstring
*)getcopyconfigitem("request", "user", "R", 30);
200 password
= (sstring
*)getcopyconfigitem("request", "password", "bla", 30);
201 sendmessagetouser(rqnick
, snp
, "AUTH %s %s", user
->content
, password
->content
);
203 freesstring(password
);
205 /* /msg S addchan <channel> default */
206 sendmessagetouser(rqnick
, snp
, "ADDCHAN %s default +o", req
->cip
->name
->content
);
210 spamscan_channelprofile *cp;
211 spamscan_channelsettings *cs;
212 spamscan_channelext *ce;
215 cs = spamscan_getchannelsettings(req->cip->name->content, 0);
218 cp = spamscan_getchannelprofile("Default", 0);
221 cs = spamscan_getchannelsettings(req->cip->name->content, 1);
225 cs->addedby = spamscan_getaccountsettings("R", 0);
226 cs->flags = SPAMSCAN_CF_IGNOREOPS;
228 sc_index = findorcreatechanindex(req->cip->name->content);
231 ce = spamscan_getchanext(sc_index, 1);
234 if ( s_nickname && !CFIsSuspended(cs) && sc_index->channel ) {
236 localjoinchannel(s_nickname, sc_index->channel);
237 localgetops(s_nickname, sc_index->channel);
241 spamscan_insertchanneldb(cs);
248 /* we do not put the request into another queue, so free it here */
257 lastqreq
=nextqreq
=req
;
263 /* Don't free, it's in new queue now */
265 /* Sort out the message.. */
266 va_start(va
, message
);
267 vsnprintf(msgbuf
,511,message
,va
);
270 sendnoticetouser(rqnick
, tnp
, "%s", msgbuf
);
271 /* This is a failure message. Add disclaimer. */
272 /*sendnoticetouser(rqnick, tnp, "Do not complain about this result in #help or #feds.");*/
281 * Checks that a channel is beeeeg enough for teh Q
284 static int qr_checksize(chanindex
*cip
, int what
, char *failcode
) {
288 int i
, avg
, tot
=0, authedcount
=0, count
=0, uniquecount
, avgcount
;
293 return 0; /* this shouldn't ever happen */
299 /* make sure we can actually add Q */
300 if (what
== QR_CSERVE
) {
301 qbot
= getnickbynick(RQ_QNICK
);
306 if (QR_MAXQCHANS
!= 0 && qbot
->channels
->cursi
> QR_MAXQCHANS
) {
309 return 0; /* no Q for you :p */
313 /* make sure that there are enough authed users */
314 for (i
=0;i
<cp
->users
->hashsize
;i
++) {
315 if (cp
->users
->content
[i
] != nouser
) {
316 np
= getnickbynumeric(cp
->users
->content
[i
]);
327 if (what
== QR_CSERVE
) {
328 if ( count
<= QR_AUTHEDPCT_SCALE
) {
329 authedpct
= QR_AUTHEDPCT_CSERVE
;
330 } else if ( count
>= QR_AUTHEDPCT_SCALEMAX
) {
331 authedpct
= QR_AUTHEDPCT_CSERVEMIN
;
333 authedpct
= (QR_AUTHEDPCT_CSERVEMIN
+ (((QR_AUTHEDPCT_CSERVE
- QR_AUTHEDPCT_CSERVEMIN
) * (100 - (((count
- QR_AUTHEDPCT_SCALE
) * 100) / (QR_AUTHEDPCT_SCALEMAX
- QR_AUTHEDPCT_SCALE
)))) / 100));
337 if ( count
<= QR_AUTHEDPCT_SCALE
) {
338 authedpct
= QR_AUTHEDPCT_SPAMSCAN
;
339 } else if ( count
>= QR_AUTHEDPCT_SCALEMAX
) {
340 authedpct
= QR_AUTHEDPCT_SPAMSCANMIN
;
342 authedpct
= (QR_AUTHEDPCT_SPAMSCANMIN
+ (((QR_AUTHEDPCT_SPAMSCAN
- QR_AUTHEDPCT_SPAMSCANMIN
) * (100 - (((count
- QR_AUTHEDPCT_SCALE
) * 100) / (QR_AUTHEDPCT_SCALEMAX
- QR_AUTHEDPCT_SCALE
)))) / 100));
346 if (authedcount
* 100 / count
< authedpct
) {
349 return 0; /* too few authed users */
352 if (!(csp
=cip
->exts
[csext
]))
355 for (i
=0;i
<HISTORYDAYS
;i
++) {
356 tot
+= csp
->lastdays
[i
];
359 uniquecount
= countuniquehosts(cp
);
360 avgcount
= tot
/ HISTORYDAYS
/ 10;
362 /* chan needs at least QR_MINUSERPCT% of the avg usercount, and can't have
363 * more than QR_MAXUSERPCT% */
364 if ( what
== QR_CSERVE
) {
365 if ((avgcount
* QR_MINUSERSPCT
/ 100 > uniquecount
)) {
370 if ((avgcount
* QR_MAXUSERSPCT
/ 100 < uniquecount
)) {
377 avg
= (what
== QR_CSERVE
) ? QR_REQUIREDSIZE_CSERVE
: QR_REQUIREDSIZE_SPAMSCAN
;
379 if (tot
> (avg
* 140))
388 /* This function deals with notices from L: basically we track the
389 * responses to the L chanlev requests we've been making until we can
390 * decide what to do with the requests.
392 * Here's the L chanlev format:
393 * 11:12 -L(TheLBot@lightweight.quakenet.org)- Users for channel #twilightzone
394 * 11:12 -L(TheLBot@lightweight.quakenet.org)- Authname Access flags
395 * 11:12 -L(TheLBot@lightweight.quakenet.org)- -----------------------------
396 * 11:12 -L(TheLBot@lightweight.quakenet.org)- Bigfoot amno
397 * 11:12 -L(TheLBot@lightweight.quakenet.org)- End of chanlev for #twilightzone.
400 void qr_handle_notice(nick
*sender
, char *message
) {
403 requestrec
*rrp1
, *rrp2
;
405 int delrequest
= 0, state
, who
;
408 if (!ircd_strcmp(sender
->nick
, RQ_QNICK
) && nextqreq
) {
410 if (!ircd_strcmp(message
,"Done.")) {
411 /* Q added the channel: delete from L and tell the user. */
412 /* If L has conspired to vanish between the request and the outcome,
413 * we have a chan with Q and L... too bad. */
416 } else if (!ircd_strcmp(message
,"That channel already exists.")) {
421 /* For either of the two messages above we want to delete the request
422 * at the head of the queue. */
426 nextqreq
=nextqreq
->next
;
434 if (!ircd_strcmp(sender
->nick
, RQ_QNICK
)) {
436 state
= (who
== QR_Q
) ? rqstate
: rlstate
;
437 nextreq
= (who
== QR_Q
) ? nextreqq
: nextreql
;
439 /* Message from L or Q */
442 /* We're idle, do nothing */
445 case QRLstate_AWAITINGCHAN
:
446 /* We're waiting for conformation of the channel name */
447 if ((!ircd_strncmp(message
,"Users for",9) && who
== QR_L
) ||
448 (!ircd_strncmp(message
,"Known users on",14) && who
== QR_Q
)
450 /* Looks like the right message. Let's find a channel name */
451 for (ch
=message
;*ch
;ch
++)
456 Error("qrequest",ERR_WARNING
,
457 "Unable to parse channel name from L/Q message: %s",message
);
461 /* chop off any remaining words */
464 if (*chop
== ' ' || *chop
== ':') {
469 if (!(cip
=findchanindex(ch
))) {
470 Error("qrequest",ERR_WARNING
,
471 "Unable to find channel from L/Q message: %s",ch
);
475 if (cip
==nextreq
->cip
) {
476 /* Ok, this is the correct channel, everything is proceeding
477 * exactly as I had forseen */
479 rlstate
= QRLstate_AWAITINGUSER
;
481 rqstate
= QRLstate_AWAITINGUSER
;
484 /* Uh-oh, not the channel we wanted. Something is fucked
485 * here. I think the only possible way out of this mess is
486 * to skip through in case we find a match for a later channel..
488 for (rrp1
=nextreq
;rrp1
;rrp1
=rrp1
->next
)
493 /* OK, we found a match further down the chain. This means
494 * that something bad has happened to every request between
495 * the head of the list and the one we just found - send
498 * Note weird loop head: qr_result will free up requests from
499 * the list as it goes, so we can just keep picking off the first
502 for(rrp2
=nextreq
;rrp2
;) {
506 Error("qrequest",ERR_WARNING
,
507 "Lost response for channel %s; skipping.",
508 rrp2
->cip
->name
->content
);
510 qr_result(rrp2
, QR_FAILED
, 'X',
511 "Sorry, an error occurred while processing your request.");
513 rrp2
= nextreq
= (who
== QR_Q
) ? nextreqq
: nextreql
;
517 /* We seem to be back in sync. */
519 rlstate
= QRLstate_AWAITINGUSER
;
521 rqstate
= QRLstate_AWAITINGUSER
;
525 /* Some form of hole in the space time continuum exists
526 * if we get here. Unclear how to proceed. */
529 /* No match - let's just ignore this completely */
530 Error("qrequest",ERR_WARNING
,
531 "Ignoring L/Q response for spurious channel %s",
539 case QRLstate_AWAITINGUSER
:
540 if ((!ircd_strncmp(message
, "End of chanlev",14) && who
== QR_L
) ||
541 (!ircd_strncmp(message
, "End of list.",12) && who
== QR_Q
)) {
542 /* Oh dear, we got to the end of the chanlev in this state.
543 * This means that we didn't find the user.
545 qr_result(nextreq
, QR_FAILED
, 'X',
546 "Error: You are not known on %s.",
547 nextreq
->cip
->name
->content
);
549 /* need to reset nextreq .. just in case
550 * qr_result has cleaned up records */
552 nextreq
= (who
== QR_Q
) ? nextreqq
: nextreql
;
556 rlstate
= QRLstate_AWAITINGUSER
;
558 rqstate
= QRLstate_AWAITINGUSER
;
561 rlstate
= QRLstate_IDLE
;
563 rqstate
= QRLstate_IDLE
;
570 /* Brutalise the message :-) */
573 while (*message
== ' ')
577 if (!(ch
=strchr(message
, ' ')))
582 if (!(np
=getnickbynumeric(nextreq
->reqnumeric
)))
585 if (ircd_strcmp(message
, np
->authname
)) {
586 /* This is not the user you are looking for */
589 /* Check for owner flag. Both branches of this if will
590 * take the request off the list, one way or the other. */
592 /* chop off any remaining words */
599 /* chop off any remaining words */
602 if (*chop
== ' ' || *chop
== ':') {
608 if (strchr(ch
, 'n')) {
610 /* They iz teh +n! */
611 /* Note: We're checking for blocks kind of late, so the request
612 system gets a chance to send other error messages first (like
613 'no chanstats', 'not known on channel', etc.). This is required
614 so that the user doesn't notice that he's being blocked. */
615 if (qr_checksize(nextreq
->cip
, nextreq
->what
, &failcode
) && !qr_blockcheck(nextreq
)) {
616 qr_result(nextreq
, QR_OK
, '-', "OK");
618 qr_result(nextreq
, QR_FAILED
, failcode
,
619 "Error: Sorry, Your channel '%s' does not require %s. Please try again in a few days.", nextreq
->cip
->name
->content
, RQ_SNICK
);
624 qr_result(nextreq
, QR_FAILED
, 'X',
625 "Error: You don't hold the +n flag on %s.",
626 nextreq
->cip
->name
->content
);
631 /* OK, we found what we wanted so make sure we skip the rest */
633 rlstate
= QRLstate_AWAITINGEND
;
635 rqstate
= QRLstate_AWAITINGEND
;
641 case QRLstate_AWAITINGEND
:
642 if (!ircd_strncmp(message
, "End of chanlev",14) ||
643 !ircd_strncmp(message
, "End of list.",12)) {
644 /* Found end of list */
648 rlstate
= QRLstate_AWAITINGCHAN
;
650 rqstate
= QRLstate_AWAITINGCHAN
;
653 rlstate
= QRLstate_IDLE
;
655 rqstate
= QRLstate_IDLE
;
666 * This function deals with requests from users for Q.
667 * Some sanity checks are made and the request is
668 * added to the queue.
671 int qr_requestq(nick
*rqnick
, nick
*sender
, channel
*cp
, nick
*lnick
, nick
*qnick
) {
675 int qr_instantrequestq(nick
*sender
, channel
*cp
) {
676 requestrec
*fakerequest
;
681 regop
*rolist
[QR_TOPX
];
683 if (!qr_checksize(cp
->index
, QR_CSERVE
, &failcode
))
686 cf
= cf_findchanfix(cp
->index
);
691 rocount
= cf_getsortedregops(cf
, QR_TOPX
, rolist
);
695 for (i
= 0; i
< min(QR_TOPX
, rocount
); i
++) {
696 if (cf_cmpregopnick(rolist
[i
], sender
)) {
702 /* not in top 5 - we don't have to worry about that error here
703 as the L request code will detect it again and send the user
704 an appropriate message */
708 /* allocate a fake request */
709 fakerequest
= (requestrec
*)malloc(sizeof(requestrec
));
711 fakerequest
->reqnumeric
= sender
->numeric
;
712 fakerequest
->cip
= cp
->index
;
713 fakerequest
->what
= QR_CSERVE
;
714 fakerequest
->who
= QR_L
; /* pretend that we asked L about the chanlev */
716 /* add it to the queue */
717 if (nextreql
== NULL
) {
718 fakerequest
->next
= NULL
;
719 nextreql
= fakerequest
;
720 lastreql
= fakerequest
;
722 fakerequest
->next
= nextreql
;
723 nextreql
= fakerequest
;
726 qr_result(fakerequest
, QR_OK
, '-', "OK");
731 int qr_requests(nick
*rqnick
, nick
*sender
, channel
*cp
, nick
*qnick
) {
732 chanindex
*cip
= cp
->index
;
734 requestrec
*nextreq
, *lastreq
;
736 if (rq_isspam(sender
)) {
737 sendnoticetouser(rqnick
, sender
, "Do not flood the request system."
738 " Try again in %s.", rq_longtoduration(rq_blocktime(sender
)));
743 /* check which service is on the channel */
744 if (getnumerichandlefromchanhash(cp
->users
, qnick
->numeric
) != NULL
) {
748 /* Request stats from Q */
749 sendmessagetouser(rqnick
, qnick
, "CHANLEV %s", cip
->name
->content
);
751 if (rqstate
== QRLstate_IDLE
)
752 rqstate
= QRLstate_AWAITINGCHAN
;
753 } /* 'else' cannot happen as R has already checked whether the user has L or Q */
758 /* Sort out a request record */
760 lastreq
->next
= (requestrec
*)malloc(sizeof(requestrec
));
761 lastreq
=lastreq
->next
;
763 lastreq
=nextreq
=(requestrec
*)malloc(sizeof(requestrec
));
766 lastreq
->next
= NULL
;
768 lastreq
->what
= QR_SPAMSCAN
;
769 lastreq
->reqnumeric
= sender
->numeric
;
774 sendnoticetouser(rqnick
, sender
,
775 "Checking your %s access in '%s'. "
776 "This may take a while, please be patient...",
777 RQ_QNICK
, cip
->name
->content
);
782 void qr_initrequest(void) {
783 nextreql
=lastreql
=NULL
;
784 nextreqq
=lastreqq
=NULL
;
785 nextqreq
=lastqreq
=NULL
;
788 void qr_finirequest(void) {
789 struct requestrec
*rp
;
793 nextreqq
=nextreqq
->next
;
799 nextreql
=nextreql
->next
;
805 nextqreq
=nextqreq
->next
;
810 void qr_requeststats(nick
*rqnick
, nick
*np
) {
811 sendnoticetouser(rqnick
, np
, "- Suspended (S): %d", qr_suspended
);
812 sendnoticetouser(rqnick
, np
, "- No chanstats (S): %d", qr_nohist
);
813 sendnoticetouser(rqnick
, np
, "- Too small (S): %d", qr_toosmall
);
814 sendnoticetouser(rqnick
, np
, "- User was not on chanlev (S): %d", qr_nochanlev
);
815 sendnoticetouser(rqnick
, np
, "- User was not the owner (S): %d", qr_notowner
);
816 sendnoticetouser(rqnick
, np
, "- A: %d", qr_a
);
817 sendnoticetouser(rqnick
, np
, "- B: %d", qr_b
);
818 sendnoticetouser(rqnick
, np
, "- C: %d", qr_c
);
819 sendnoticetouser(rqnick
, np
, "- D: %d", qr_d
);
820 sendnoticetouser(rqnick
, np
, "- E: %d", qr_e
);