]> jfr.im git - irc/quakenet/newserv.git/blame - qrequest/qrequest.c
allow forcing of settime commands (for devs only)
[irc/quakenet/newserv.git] / qrequest / qrequest.c
CommitLineData
c86edd1d
Q
1/*
2 * Q request system!
3 *
4 * Depends on "chanstats"
5 */
6
7#include "../nick/nick.h"
8#include "../channel/channel.h"
9#include "../chanstats/chanstats.h"
10#include "../localuser/localuser.h"
11#include "../lib/irc_string.h"
12#include "../core/schedule.h"
13
14#include <stdarg.h>
15#include <stdio.h>
16#include <string.h>
17
18#define QR_nick "R"
19#define QR_user "qrequest"
20#define QR_host "qrequest.quakenet.org"
21#define QR_acct "R"
22
23#define Q_nick "Q"
24#define L_nick "L"
25
26#define QRLstate_IDLE 0x0 /* No request active */
27#define QRLstate_AWAITINGCHAN 0x1 /* Awaiting "Users for channel.." */
28#define QRLstate_AWAITINGUSER 0x2 /* Looking for our user in the list */
29#define QRLstate_AWAITINGEND 0x3 /* Waiting for "End of chanlev" */
30
31#define QR_FAILED 0x0
32#define QR_OK 0x1
33
34#define QREQUIREDSIZE 50
35
36typedef struct requestrec {
37 unsigned int reqnumeric; /* Who made the request */
38 unsigned int tellnumeric; /* Who we are talking to about the request */
39 chanindex *cip; /* Which channel the request is for */
40 struct requestrec *next;
41} requestrec;
42
43requestrec *nextreq, *lastreq;
44requestrec *nextqreq, *lastqreq;
45
46nick *qr_np;
47int qrlstate;
48
49void qr_handler(nick *me, int type, void **args);
50
51void qr_registeruser(void *arg) {
52 qr_np=registerlocaluser(QR_nick, QR_user, QR_host, "Q Request 0.01",
53 QR_acct, UMODE_ACCOUNT|UMODE_OPER, qr_handler);
54}
55
56/*
57 * Deal with outcome of a queued request. The request should be freed
58 * as part of the process.
59 */
60
61void qr_result(requestrec *req, int outcome, char *message, ...) {
62 requestrec **rh;
63 char msgbuf[512];
64 va_list va;
65 nick *lnp, *qnp, *np, *tnp;
66
67 /* Delete the request from the list first.. */
68 for (rh=&nextreq;*rh;rh=&((*rh)->next)) {
69 if (*rh==req) {
70 *rh=req->next;
71 break;
72 }
73 }
74
75 /* If this was the last request (unlikely),
76 * we need to fix the last pointer */
77 if (lastreq==req) {
78 if (nextreq)
79 for (lastreq=nextreq;lastreq->next;lastreq=lastreq->next)
80 ; /* empty loop */
81 else
82 lastreq=NULL;
83 }
84
85 /* Check that the nick is still here. If not, drop the request. */
86 if (!(np=getnickbynumeric(req->reqnumeric)) ||
87 !(tnp=getnickbynumeric(req->tellnumeric))) {
88 free(req);
89 return;
90 }
91
92 if (outcome==QR_OK) {
93 /* Delete L, add Q. Check that they both exist first, though. */
94
95 if (!(lnp=getnickbynick(L_nick)) || !(qnp=getnickbynick(Q_nick))) {
96 sendnoticetouser(qr_np, tnp,
97 "Sorry, cannot find Q and L on the network. "
98 "Please request again later.");
99 free(req);
100 return;
101 }
102
103 /* /msg Q ADDCHAN <channel> <flags> <owners nick> <channeltype> */
104 sendmessagetouser(qr_np, qnp, "ADDCHAN %s +ap #%s upgrade",
105 req->cip->name->content,
106 np->authname);
107
108 sendnoticetouser(qr_np, tnp, "Adding Q to channel, please wait...");
109
110 if (lastqreq)
111 lastqreq->next=req;
112 else
113 lastqreq=nextqreq=req;
114
115 req->next=NULL;
116
117 /* Don't free, it's in new queue now */
118 } else {
119 /* Sort out the message.. */
120 va_start(va, message);
121 vsnprintf(msgbuf,511,message,va);
122 va_end(va);
123
124 sendnoticetouser(qr_np, tnp, "%s", msgbuf);
125 /* This is a failure message. Add disclaimer. */
f6df5e0e 126 sendnoticetouser(qr_np, tnp, "Please do not complain about this result.");
c86edd1d
Q
127 free(req);
128 }
129}
130
131/*
132 * qr_checksize:
133 * Checks that a channel is beeeeg enough for teh Q
134 */
135
136int qr_checksize(chanindex *cip) {
137 chanstats *csp;
138 int i,tot=0;
139
140 if (!(csp=cip->exts[csext]))
141 return 0;
142
143 for (i=0;i<HISTORYDAYS;i++) {
144 tot += csp->lastdays[i];
145 }
146
147 if (tot > (QREQUIREDSIZE * 140))
148 return 1;
149
150 return 0;
151}
152
153/* This function deals with notices from L: basically we track the
154 * responses to the L chanlev requests we've been making until we can
155 * decide what to do with the requests.
156 *
157 * Here's the L chanlev format:
158 * 11:12 -L(TheLBot@lightweight.quakenet.org)- Users for channel #twilightzone
159 * 11:12 -L(TheLBot@lightweight.quakenet.org)- Authname Access flags
160 * 11:12 -L(TheLBot@lightweight.quakenet.org)- -----------------------------
161 * 11:12 -L(TheLBot@lightweight.quakenet.org)- Bigfoot amno
162 * 11:12 -L(TheLBot@lightweight.quakenet.org)- End of chanlev for #twilightzone.
163 */
164
165void qr_handlenotice(nick *sender, char *message) {
166 char *ch;
167 chanindex *cip;
168 requestrec *rrp1, *rrp2;
169 nick *np;
170
171 if (!ircd_strcmp(sender->nick, Q_nick)) {
172 /* Message from Q */
173 if (!nextqreq) {
174 /* ERROR ERROR ERROR */
175 return;
176 }
177
178 if (!ircd_strcmp(message,"Done.")) {
179 /* Q added the channel: delete from L and tell the user. */
180 /* If L has conspired to vanish between the request and the outcome,
181 * we have a chan with Q and L... too bad. */
182
183 if ((np=getnickbynick(L_nick))) {
184 sendmessagetouser(qr_np, np, "SENDCHANLEV %s %s",
185 nextqreq->cip->name->content, Q_nick);
186
187 sendmessagetouser(qr_np, np, "DELCHAN %s",
188 nextqreq->cip->name->content);
189 }
190
191 if ((np=getnickbynumeric(nextqreq->tellnumeric))) {
192 sendnoticetouser(qr_np, np, "Request completed. Q added and L deleted.");
193 }
194 } else if (!ircd_strcmp(message,"That channel already exists.")) {
195 if ((np=getnickbynumeric(nextqreq->tellnumeric))) {
196 sendnoticetouser(qr_np, np,
197 "Your channel appears to have Q already "
198 "(it may be suspended).");
199 }
200 } else {
201 return;
202 }
203
204 /* For either of the two messages above we want to delete the request
205 * at the head of the queue. */
206 rrp1=nextqreq;
207
208 nextqreq=nextqreq->next;
209 if (!nextqreq)
210 lastqreq=NULL;
211
212 free(rrp1);
213 } else if (!ircd_strcmp(sender->nick, L_nick)) {
214 /* Message from L */
215 switch (qrlstate) {
216 case QRLstate_IDLE:
217 /* We're idle, do nothing */
218 return;
219
220 case QRLstate_AWAITINGCHAN:
221 /* We're waiting for conformation of the channel name */
222 if (!ircd_strncmp(message,"Users for",9)) {
223 /* Looks like the right message. Let's find a channel name */
224
225 for (ch=message;*ch;ch++)
226 if (*ch=='#')
227 break;
228
229 if (!*ch) {
230 Error("qrequest",ERR_WARNING,
231 "Unable to parse channel name from L message: %s",message);
232 return;
233 }
234
235 if (!(cip=findchanindex(ch))) {
236 Error("qrequest",ERR_WARNING,
237 "Unable to find channel from L message: %s",ch);
238 return;
239 }
240
241 if (cip==nextreq->cip) {
242 /* Ok, this is the correct channel, everything is proceeding
243 * exactly as I had forseen */
244 qrlstate = QRLstate_AWAITINGUSER;
245 return;
246 } else {
247 /* Uh-oh, not the channel we wanted. Something is fucked
248 * here. I think the only possible way out of this mess is
249 * to skip through in case we find a match for a later channel..
250 */
251 for (rrp1=nextreq;rrp1;rrp1=rrp1->next)
252 if (rrp1->cip==cip)
253 break;
254
255 if (rrp1) {
256 /* OK, we found a match further down the chain. This means
257 * that something bad has happened to every request between
258 * the head of the list and the one we just found - send
259 * error responses.
260 *
261 * Note weird loop head: qr_result will free up requests from
262 * the list as it goes, so we can just keep picking off the first
263 * entry
264 */
265 for(rrp2=nextreq;rrp2;rrp2=nextreq) {
266 if (rrp2==rrp1)
267 break;
268
269 Error("qrequest",ERR_WARNING,
270 "Lost response for channel %s; skipping.",
271 rrp2->cip->name->content);
272
273 qr_result(rrp2, QR_FAILED,
274 "Sorry, an error occurred while processing your request.");
275 }
276
277 if (rrp2) {
278 /* We seem to be back in sync. */
279 qrlstate=QRLstate_AWAITINGUSER;
280 return;
281 }
282 /* Some form of hole in the space time continuum exists
283 * if we get here. Unclear how to proceed. */
284 return;
285 } else {
286 /* No match - let's just ignore this completely */
287 Error("qrequest",ERR_WARNING,
288 "Ignoring L response for spurious channel %s",
289 cip->name->content);
290 return;
291 }
292 }
293 }
294 break;
295
296 case QRLstate_AWAITINGUSER:
297 if (!ircd_strncmp(message, "End of chanlev",14)) {
298 /* Oh dear, we got to the end of the chanlev in this state.
299 * This means that we didn't find the user.
300 */
301
302 qr_result(nextreq, QR_FAILED,
303 "Sorry, you are not known on %s.",
304 nextreq->cip->name->content);
305
306 if (nextreq)
307 qrlstate=QRLstate_AWAITINGCHAN;
308 else
309 qrlstate=QRLstate_IDLE;
310
311 return;
312 } else {
313 /* Brutalise the message :-) */
314
315 if (!(ch=strchr(message, ' ')))
316 return;
317
318 *ch++='\0';
319
320 if (!(np=getnickbynumeric(nextreq->reqnumeric)))
321 return;
322
323 if (ircd_strcmp(message, np->authname)) {
324 /* This is not the user you are looking for */
325 return;
326 }
327
328 /* Check for owner flag. Both branches of this if will
329 * take the request off the list, one way or the other. */
330 if (strchr(ch, 'n')) {
331 /* They iz teh +n! */
332 if (qr_checksize(nextreq->cip)) {
333 qr_result(nextreq, QR_OK, "OK");
334 } else {
335 qr_result(nextreq, QR_FAILED,
f6df5e0e 336 "Sorry, your channel is not large enough to request Q. Please continue to use L instead.");
c86edd1d
Q
337 }
338 } else {
339 qr_result(nextreq, QR_FAILED,
340 "Sorry, you don't hold the +n flag on %s.",
341 nextreq->cip->name->content);
342 }
343
344 /* OK, we found what we wanted so make sure we skip the rest */
345 qrlstate=QRLstate_AWAITINGEND;
346
347 return;
348 }
349 break;
350
351 case QRLstate_AWAITINGEND:
352 if (!ircd_strncmp(message, "End of chanlev",14)) {
353 /* Found end of list */
354
355 if (nextreq)
356 qrlstate=QRLstate_AWAITINGCHAN;
357 else
358 qrlstate=QRLstate_IDLE;
359
360 return;
361 }
362 break;
363 }
364 }
365}
366
367/*
368 * This function deals with requests from users for Q.
369 * Some sanity checks are made (on channel, opped) and the
370 * request is added to the queue.
371 */
372
373void qr_handlemsg(nick *sender, char *message) {
374 char *ch, *ch2;
375 chanindex *cip;
376 nick *lnp;
377 unsigned long *lp;
378 nick *requester;
379
380 if (!ircd_strncmp(message,"requestq ",9)) {
381 /* Looks like a sensible request. */
382 if (!(ch=strchr(message,'#'))) {
383 sendnoticetouser(qr_np, sender,
384 "Usage: requestq #channel");
385 return;
386 }
387
388 /* If an oper is sending the request, allow them to specify
389 * a second parameter indicating which user they are making the
390 * request on behalf of. All status messages will be sent to the
391 * oper issuing "requestq", but the move will be done on behalf of
392 * the named user */
393
394 if (IsOper(sender) && (ch2=strchr(ch,' '))) {
395 *ch2++='\0';
396 if (!(requester=getnickbynick(ch2))) {
397 sendnoticetouser(qr_np,sender,"Can't find user: %s",ch2);
398 return;
399 }
400 } else {
401 requester=sender;
402 }
403
404 /* Check:
405 * - user is authed
406 * - channel exists
407 * - Q exists
408 * - L exists
409 * - L is on the channel
410 * - user is on the channel
411 * - user is opped on the channel
412 * - we have some form of channel stats for the channel
413 *
414 * Note that the actual channel stats will not be checked
415 * until we're sure the user has +n on the channel.
416 */
417
418 if (!IsAccount(requester)) {
419 sendnoticetouser(qr_np, sender,
420 "Sorry, you must be authed to make a request.");
421 return;
422 }
423
424 if (!(cip=findchanindex(ch)) || !cip->channel) {
425 sendnoticetouser(qr_np, sender, "Unable to find channel %s.",ch);
426 return;
427 }
428
429 if (!(lnp=getnickbynick(L_nick))) {
430 sendnoticetouser(qr_np, sender,
431 "Can't find L on the network, please try later.");
432 return;
433 }
434
435 if (!getnickbynick(Q_nick)) {
436 sendnoticetouser(qr_np, sender,
437 "Can't find Q on the network, please try later.");
438 return;
439 }
440
441 if (!getnumerichandlefromchanhash(cip->channel->users, lnp->numeric)) {
442 sendnoticetouser(qr_np, sender,
443 "L is not on %s.", cip->name->content);
444 return;
445 }
446
447 if (!(lp=getnumerichandlefromchanhash(cip->channel->users,
448 requester->numeric))) {
449 sendnoticetouser(qr_np, sender,
450 "You are not on %s.", cip->name->content);
451 return;
452 }
453
454 if (!(*lp & CUMODE_OP)) {
455 sendnoticetouser(qr_np, sender,
456 "You are not opped on %s.", cip->name->content);
457 return;
458 }
459
460 if (!cip->exts[csext]) {
461 sendnoticetouser(qr_np, sender,
462 "Sorry, no historical record exists for %s.",
463 cip->name->content);
464 return;
465 }
466
467 /* Request stats from L */
468 sendmessagetouser(qr_np, lnp, "CHANLEV %s", cip->name->content);
469
470 /* Sort out a request record */
471 if (lastreq) {
472 lastreq->next = (requestrec *)malloc(sizeof(requestrec));
473 lastreq=lastreq->next;
474 } else {
475 lastreq=nextreq=(requestrec *)malloc(sizeof(requestrec));
476 }
477
478 lastreq->next = NULL;
479 lastreq->cip = cip;
480 lastreq->reqnumeric = requester->numeric;
481 lastreq->tellnumeric = sender->numeric;
482
483 if (qrlstate == QRLstate_IDLE)
484 qrlstate = QRLstate_AWAITINGCHAN;
485
486 sendnoticetouser(qr_np, sender,
487 "Checking your L access. "
488 "This may take a while, please be patient...");
489 }
490}
491
492void qr_handler(nick *me, int type, void **args) {
493
494 switch(type) {
495 case LU_KILLED:
496 qr_np=NULL;
497 scheduleoneshot(time(NULL)+1, qr_registeruser, NULL);
498 return;
499
500 case LU_PRIVNOTICE:
501 qr_handlenotice(args[0], args[1]);
502 return;
503
504 case LU_PRIVMSG:
505 qr_handlemsg(args[0], args[1]);
506 return;
507 }
508}
509
510void _init() {
511 nextreq=lastreq=NULL;
512 nextqreq=lastqreq=NULL;
513 qr_np=NULL;
514 qr_registeruser(NULL);
515}
516
517void _fini() {
518 struct requestrec *rp;
519
520 while (nextreq) {
521 rp=nextreq;
522 nextreq=nextreq->next;
523 free(rp);
524 }
525
526 while (nextqreq) {
527 rp=nextqreq;
528 nextqreq=nextqreq->next;
529 free(rp);
530 }
531
532 if (qr_np)
533 deregisterlocaluser(qr_np, NULL);
534
535 deleteallschedules(qr_registeruser);
536
537}