]>
Commit | Line | Data |
---|---|---|
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 | ||
36 | typedef 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 | ||
43 | requestrec *nextreq, *lastreq; | |
44 | requestrec *nextqreq, *lastqreq; | |
45 | ||
46 | nick *qr_np; | |
47 | int qrlstate; | |
48 | ||
49 | void qr_handler(nick *me, int type, void **args); | |
50 | ||
51 | void 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 | ||
61 | void 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 | ||
136 | int 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 | ||
165 | void 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 | ||
373 | void 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 | ||
492 | void 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 | ||
510 | void _init() { | |
511 | nextreq=lastreq=NULL; | |
512 | nextqreq=lastqreq=NULL; | |
513 | qr_np=NULL; | |
514 | qr_registeruser(NULL); | |
515 | } | |
516 | ||
517 | void _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 | } |