Release 3.0.3.
[jelmer/ctrlproxy.git] / src / redirect.c
1 /* 
2         ctrlproxy: A modular IRC proxy
3         Send numerics to the right places
4         (c) 2002-2005 Jelmer Vernooij <jelmer@nl.linux.org>
5
6         This program is free software; you can redistribute it and/or modify
7         it under the terms of the GNU General Public License as published by
8         the Free Software Foundation; either version 2 of the License, or
9         (at your option) any later version.
10
11         This program is distributed in the hope that it will be useful,
12         but WITHOUT ANY WARRANTY; without even the implied warranty of
13         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14         GNU General Public License for more details.
15
16         You should have received a copy of the GNU General Public License
17         along with this program; if not, write to the Free Software
18         Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "ctrlproxy.h"
22 #include <string.h>
23 #include "irc.h"
24
25 /* TODO: Clean up stack occasionally */
26
27 struct query_stack {
28         const struct query *query;
29         const struct network *network;
30         struct client *client;  
31         time_t time;
32         struct query_stack *next;
33 };
34
35 static struct query_stack *stack = NULL;
36
37 /**
38  * IRC Query done by a client
39  */
40 struct query {
41         char *name;
42         int replies[20];
43         int end_replies[20];
44         int errors[20];
45         /* Should add this query to the stack. return TRUE if this has 
46          * been done successfully, FALSE otherwise */
47         int (*handle) (const struct line *, const struct network *n, struct client *c, struct query *);
48 };
49
50 static int handle_default(const struct line *, const struct network *n, 
51                                                   struct client *c, struct query *);
52 static int handle_topic(const struct line *, const struct network *n, 
53                                                 struct client *c, struct query *);
54
55 static struct query queries[] = {
56 /* Commands that get a one-client reply: 
57  * WHOIS [<server>] <nickmask>[,<nickmask>[,...]] */
58         {"WHOIS", 
59                 { RPL_WHOISUSER, RPL_WHOISCHANNELS, RPL_AWAY,
60                   RPL_WHOISIDLE, RPL_WHOISCHANNELS, RPL_WHOISIP,
61                   RPL_WHOISSERVER, RPL_WHOISOPERATOR, RPL_WHOISACTUALLY,
62                   RPL_WHOISACCOUNT, RPL_WHOISIDENTIFIED, 0 }, 
63                 { RPL_ENDOFWHOIS, 0 }, 
64                 { ERR_NOSUCHSERVER, ERR_NONICKNAMEGIVEN, ERR_NOSUCHNICK, 0 },
65                 handle_default
66         },
67
68         /* WHO [<name> [<o>]] */
69         {"WHO", 
70             { RPL_WHOREPLY, RPL_WHOSPCRPL, 0 }, 
71                 { RPL_ENDOFWHO, 0 },
72                 { ERR_NOSUCHSERVER, 0 },
73                 handle_default 
74         },
75
76         /* NAMES [<channel>{,<channel>}]*/
77         {"NAMES", 
78                 { RPL_NAMREPLY, 0 }, 
79                 { RPL_ENDOFNAMES, 0 },
80                 { ERR_TOOMANYMATCHES, ERR_NOSUCHSERVER, 0 },
81                 handle_default
82         },
83
84  /* LIST [<channel>{,<channel>} [<server>]*/
85         {"LIST", 
86                 { RPL_LIST, RPL_LISTSTART, 0 }, 
87                 { RPL_LISTEND, 0 },
88                 { ERR_TOOMANYMATCHES, ERR_NOSUCHSERVER, 0 },
89                 handle_default
90         },
91         
92  /* TOPIC <channel> [<topic>]*/
93         {"TOPIC", 
94                 { 0 }, 
95                 { RPL_NOTOPIC, RPL_TOPIC, 0 }, 
96                 { ERR_NOTONCHANNEL, ERR_NEEDMOREPARAMS, ERR_CHANOPPRIVSNEEDED, 
97                         ERR_NOCHANMODES, 0 },
98                 handle_topic
99         },
100         
101  /* WHOWAS <nickname> [<count> [<server>]]*/
102         {"WHOWAS", 
103                 { RPL_WHOWASUSER, RPL_WHOISSERVER, RPL_WHOWAS_TIME, 0 },
104                 { RPL_ENDOFWHOWAS, 0 },
105                 { ERR_NONICKNAMEGIVEN, ERR_WASNOSUCHNICK, 0 },
106                 handle_default
107         },
108         
109  /* STATS [<query> [<server>]]*/
110         {"STATS", 
111                 { RPL_STATSCLINE, RPL_STATSILINE, RPL_STATSQLINE, 
112                   RPL_STATSLINKINFO, RPL_STATSCOMMANDS, RPL_STATSHLINE, RPL_STATSNLINE,
113                   RPL_STATSKLINE, RPL_STATSLLINE, RPL_STATSUPTIME, RPL_STATSOLINE, 
114                   RPL_STATSTLINE,
115                   0 },
116                 { RPL_TRYAGAIN, RPL_ENDOFSTATS, 0 },
117                 { ERR_NOSUCHSERVER, 0 },
118                 handle_default
119         }, 
120         
121  /* VERSION [<server>]*/
122         {"VERSION",
123                 { 0 },
124                 { RPL_VERSION, 0 },
125                 { ERR_NOSUCHSERVER, 0 },
126                 handle_default
127         }, 
128                 
129  /* LINKS [[<remote server>] <server mask>]*/
130         {"LINKS",
131                 { RPL_LINKS, 0 },
132                 { RPL_ENDOFLINKS, 0 },
133                 { ERR_NOSUCHSERVER, 0 },
134                 handle_default
135         },
136         
137  /* TIME [<server>]*/
138         {"TIME",
139                 { 0 },
140                 { RPL_TIME, 0 },
141                 { ERR_NOSUCHSERVER, 0 },
142                 handle_default
143         },
144
145  /* TRACE [<server>]*/
146         {"TRACE",
147                 { RPL_TRACELINK, RPL_TRACECONNECTING, 
148                   RPL_TRACEUNKNOWN, RPL_TRACEUSER, RPL_TRACECLASS, 
149                   RPL_TRACEHANDSHAKE, RPL_TRACEOPERATOR,
150                   RPL_TRACESERVER, RPL_TRACENEWTYPE, 0 },
151                 { 0 },
152                 { ERR_NOSUCHSERVER, 0 },
153                 handle_default
154         },
155         
156  /* SUMMON <user> [<server>]*/
157         {"SUMMON",
158                 { 0 },
159                 { RPL_SUMMONING, 0 },
160                 { ERR_NORECIPIENT, ERR_FILEERROR, ERR_NOLOGIN, ERR_NOSUCHSERVER, 0 },
161                 handle_default
162         },
163         
164  /* USERS [<server>]*/
165         {"USERS",
166                 { RPL_USERSSTART, RPL_USERS, RPL_NOUSERS, 0 },
167                 { RPL_ENDOFUSERS, 0 },
168                 { ERR_NOSUCHSERVER, ERR_FILEERROR, ERR_USERSDISABLED, 0 },
169                 handle_default
170         },
171
172  /* USERHOST <nickname>{ <nickname>}{ ...}*/
173         {"USERHOST",
174                 { 0 },
175                 { RPL_USERHOST, 0 },
176                 { ERR_NEEDMOREPARAMS, 0 },
177                 handle_default
178         },
179                 
180  /* ISON <nickname>{ <nickname>}{ ...} */
181         {"ISON",
182                 { 0 },
183                 { RPL_ISON, 0 },
184                 { ERR_NEEDMOREPARAMS, 0 },
185             handle_default
186         },
187
188 /* JOIN <channel>{,<channel>} [<key>{,<key>}] */
189         {"JOIN",
190                 { 0 },
191                 { 0 },
192                 { ERR_NEEDMOREPARAMS, ERR_BANNEDFROMCHAN,
193                   ERR_INVITEONLYCHAN, ERR_BADCHANNELKEY,
194                   ERR_CHANNELISFULL, ERR_BADCHANMASK,
195                   ERR_FORWARDING, ERR_NOSUCHCHANNEL, 
196                   ERR_TOOMANYCHANNELS, 0 },
197                 handle_default
198         },
199
200  /* PART <channel> *( "," <channel> ) [ <Part Message> ] */
201         { "PART",
202                 { 0 },
203                 { 0 },
204                 { ERR_NEEDMOREPARAMS, ERR_NOSUCHCHANNEL, ERR_NOTONCHANNEL, 0 },
205                 handle_default
206         },
207
208  /* NICK <nickname> */
209         { "NICK",
210                 { 0 },
211                 { 0 },
212                 { ERR_NONICKNAMEGIVEN, ERR_ERRONEUSNICKNAME, ERR_NICKNAMEINUSE,
213               ERR_UNAVAILRESOURCE, ERR_RESTRICTED, ERR_NICKCOLLISION, 
214                   ERR_NICKTOOFAST, 0 },
215                 handle_default
216         },
217
218  /* USER <username> <hostname> <servername> <realname> */
219         { "USER",
220                 { 0 },
221                 { 0 },
222                 { ERR_NEEDMOREPARAMS, ERR_ALREADYREGISTERED, 0 },
223                 handle_default
224         },
225
226  /* QUIT [<quit message>] */
227         { "QUIT", 
228                 { 0 },
229                 { 0 },
230                 { 0 },
231                 handle_default
232         },
233
234  /* OPER <name> <password> */
235         { "OPER",
236             { 0 },
237             { RPL_YOUREOPER, 0 },
238                 { ERR_NEEDMOREPARAMS, ERR_NOOPERHOST, ERR_PASSWDMISMATCH, 0 },
239                 handle_default
240         },
241         
242  /* MODE <nick> <mode> */
243         { "MODE", 
244                 {  /* Replies to channel mode queries */
245                         RPL_BANLIST, RPL_EXCEPTLIST, RPL_INVITELIST, 
246                         0 },
247                 { 
248                         /* Replies to user mode queries */
249                         RPL_UMODEIS, 
250
251                         /* Replies to channel mode queries */
252                         RPL_CHANNELMODEIS, RPL_ENDOFBANLIST, RPL_ENDOFEXCEPTLIST,
253                         RPL_ENDOFINVITELIST, RPL_UNIQOPIS,
254                         
255                         0 },
256                 { 
257                         /* Common replies */
258                         ERR_NEEDMOREPARAMS, 
259
260                         /* Replies to user mode queries */
261                         ERR_UMODEUNKNOWNFLAG, ERR_USERSDONTMATCH, 
262                         
263                         /* Replies to channel mode queries */
264                         ERR_USERNOTINCHANNEL, ERR_KEYSET, ERR_CHANOPPRIVSNEEDED,
265                         ERR_UNKNOWNMODE, ERR_NOCHANMODES, ERR_NOSUCHCHANNEL,
266                         ERR_ILLEGALCHANNELNAME,
267                         
268                         0 },
269                 handle_default
270         },
271         
272  /* SERVICE <nick> <reserved> <distribution> <type> <reserved> <info> */
273         { "SERVICE", 
274                 { 0 },
275                 { RPL_YOURESERVICE, RPL_YOURHOST, RPL_MYINFO, 0 },
276                 { ERR_ALREADYREGISTERED, ERR_NEEDMOREPARAMS, ERR_ERRONEUSNICKNAME, 0 },
277                 handle_default
278         },
279         
280  /* SQUIT <server> <comment> */
281         { "SQUIT",
282                 { 0 },
283                 { 0 },
284                 { ERR_NOPRIVILEGES, ERR_NEEDMOREPARAMS, ERR_NOSUCHSERVER, 0 },
285                 handle_default
286         },
287         
288  /* INVITE <nick> <channel> */
289         { "INVITE", 
290                 { 0 },
291                 { RPL_INVITING, RPL_AWAY, 0 },
292                 { ERR_NEEDMOREPARAMS, ERR_NOTONCHANNEL, ERR_NOSUCHNICK, 
293                         ERR_CHANOPPRIVSNEEDED, ERR_USERONCHANNEL, 0 },
294                 handle_default
295         },
296         
297 /* KICK <channel> * ( "," <channel> ) <user> *( "," <user> ) [<comment>] */
298         { "KICK",
299                 { 0 },
300                 { 0 },
301                 { ERR_NEEDMOREPARAMS, ERR_BADCHANMASK, ERR_USERNOTINCHANNEL,
302                         ERR_NOSUCHCHANNEL, ERR_CHANOPPRIVSNEEDED, ERR_NOTONCHANNEL, 0 },
303                 handle_default
304         },
305  
306  /* PRIVMSG <msgtarget> <text> */
307         { "PRIVMSG",
308                 { 0 },
309                 { RPL_AWAY, 0 },
310                 { ERR_NORECIPIENT, ERR_NOTEXTTOSEND, ERR_CANNOTSENDTOCHAN, 
311                         ERR_NOTOPLEVEL, ERR_TOOMANYTARGETS, ERR_WILDTOPLEVEL, 
312                         ERR_NOSUCHNICK, ERR_NOSUCHCHANNEL, ERR_BLOCKING_NOTID, 0 },
313                 handle_default
314         },
315  
316  /* MOTD [<target>] */
317         { "MOTD",
318                 { RPL_MOTDSTART, RPL_MOTD, 0 },
319                 { RPL_ENDOFMOTD, 0 },
320                 { ERR_NOMOTD, 0 },
321                 handle_default
322         },
323
324  /* LUSERS [ <mask> [ <target> ] ] */
325         { "LUSERS",
326                 { RPL_LUSERCLIENT, RPL_LUSEROP, RPL_LUSERUNKNOWN, RPL_LUSERCHANNELS, 
327                         RPL_LUSERME, RPL_STATSTLINE, 0 },
328                 { 0 },
329                 { ERR_NOSUCHSERVER, 0 },
330                 handle_default
331         },
332         
333  /* CONNECT <target> <port> [ <remote server> ] */
334         { "CONNECT",
335                 { 0 },
336                 { 0 },
337                 { ERR_NOSUCHSERVER, ERR_NEEDMOREPARAMS, ERR_NOPRIVILEGES, 0 },
338                 handle_default
339         },
340
341  /* ADMIN [ <target> ] */
342         { "ADMIN",
343                 { RPL_ADMINME, RPL_ADMINLOC2, RPL_ADMINLOC1, 
344                         RPL_ADMINLOC3, 0 },
345                 { 0 },
346                 { ERR_NOSUCHSERVER, 0 },
347                 handle_default
348         },
349         
350  /* INFO [ <target> ] */
351         { "INFO",
352                 { RPL_INFO, 0 },
353                 { RPL_ENDOFINFO, 0 },
354                 { ERR_NOSUCHSERVER, 0 },
355                 handle_default
356         },
357         
358  /* SERVLIST [ <mask> [ <type> ] ] */
359         { "SERVLIST",
360                 { RPL_SERVLIST, 0},
361                 { RPL_SERVLISTEND, 0 },
362                 { 0 },
363                 handle_default
364         },
365         
366  /* SQUERY <servicename> <text> */
367         /* Same responses as for PRIVMSG */
368         { "SQUERY",
369                 { 0 },
370                 { RPL_AWAY, 0 },
371                 { ERR_NORECIPIENT, ERR_NOTEXTTOSEND, ERR_CANNOTSENDTOCHAN, 
372                         ERR_NOTOPLEVEL, ERR_TOOMANYTARGETS, ERR_WILDTOPLEVEL, 
373                         ERR_NOSUCHNICK, 0 },
374                 handle_default
375         },
376         
377  /* KILL <nick> <comment> */
378         { "KILL",
379                 { 0 },
380                 { 0 },
381                 { ERR_NOPRIVILEGES, ERR_NEEDMOREPARAMS, ERR_NOSUCHNICK, ERR_CANTKILLSERVER, 0 },
382                 handle_default
383         },
384
385  /* AWAY [ <text> ] */
386         { "AWAY",
387                 { 0 },
388                 { RPL_UNAWAY, RPL_NOWAWAY, 0 },
389                 { 0 },
390                 handle_default
391         },
392
393  /* REHASH */
394         { "REHASH",
395                 { 0 },
396                 { RPL_REHASHING, 0 },
397                 { ERR_NOPRIVILEGES, 0 },
398                 handle_default
399         },
400
401  /* DIE */
402         { "DIE",
403                 { 0 },
404                 { 0 },
405                 { ERR_NOPRIVILEGES, 0 },
406                 handle_default
407         },
408
409  /* RESTART */
410         { "RESTART",
411                 { 0 },
412                 { 0 },
413                 { ERR_NOPRIVILEGES, 0 },
414                 handle_default
415         },
416
417 /* PING */
418         { "PING", 
419                 { 0 },
420                 { 0 },
421                 { ERR_NOORIGIN, 0 },
422                 handle_default
423         },
424
425  /* WALLOPS */
426         { "WALLOPS",
427                 { 0 },
428                 { 0 },
429                 { ERR_NEEDMOREPARAMS, 0 },
430                 handle_default
431         },
432
433 /* PONG */
434         { "PONG",
435                 { 0 },
436                 { 0 },
437                 { ERR_NOORIGIN, ERR_NOSUCHSERVER, 0 },
438                 handle_default
439         },
440
441 /* NOTICE */
442         { "NOTICE",
443                 { 0 },
444                 { 0 },
445                 { 0 },
446                 handle_default
447         },
448
449 /* AUTH */
450         { "AUTH",
451                 { 0 },
452                 { 0 },
453                 { ERR_NEEDMOREPARAMS, ERR_ALREADYAUTHENTICATED, ERR_ALREADYREGISTERED,
454                   ERR_AUTHENTICATIONFAILED, ERR_AUTHENTICATIONSUSPENDED, 
455                   ERR_BADCOMMAND, ERR_UNKNOWNPACKAGE, 0 },
456                 handle_default
457         },
458
459         { NULL }
460 };
461
462 static struct query unknown_query = { 
463         NULL,
464         { 0 },
465         { 0 },
466         { ERR_UNKNOWNCOMMAND, 0 },
467         handle_default
468 };
469
470 static gboolean handle_465(struct network *n, struct line *l)
471 {
472         log_network(LOG_ERROR, n, "Banned from server: %s", l->args[1]);
473         return TRUE;
474 }
475
476 static gboolean handle_451(struct network *n, struct line *l)
477 {
478         log_network(LOG_ERROR, n, "Not registered error, this is probably a bug...");
479         return TRUE;
480 }
481
482 static gboolean handle_462(struct network *n, struct line *l)
483 {
484         log_network(LOG_ERROR, n, "Double registration error, this is probably a bug...");
485         return TRUE;
486 }
487
488 static gboolean handle_463(struct network *n, struct line *l)
489 {
490         log_network(LOG_ERROR, n, "Host not privileged to connect");
491         return TRUE;
492 }
493
494 static gboolean handle_464(struct network *n, struct line *l)
495 {
496         log_network(LOG_ERROR, n, "Password mismatch");
497         return TRUE;
498 }
499
500 /* List of responses that should be sent to all clients */
501 static int response_all[] = { RPL_NOWAWAY, RPL_UNAWAY, 
502         ERR_NO_OP_SPLIT, RPL_HIDINGHOST,
503         ERR_NEEDREGGEDNICK, RPL_UMODEIS, 
504         RPL_LUSERCLIENT, RPL_LUSEROP, RPL_LUSERUNKNOWN, RPL_LUSERCHANNELS,
505         RPL_LUSERME, ERR_NO_OP_SPLIT, RPL_LOCALUSERS, RPL_GLOBALUSERS, 
506         RPL_NAMREPLY, RPL_ENDOFNAMES, RPL_TOPIC, RPL_TOPICWHOTIME, 
507                 RPL_CREATIONTIME, 0 };
508 static int response_none[] = { ERR_NOMOTD, RPL_MOTDSTART, RPL_MOTD, 
509         RPL_ENDOFMOTD, 0 };
510 static struct {
511         int response;
512         gboolean (*handler) (struct network *n, struct line *);
513 } response_handler[] = {
514         { ERR_PASSWDMISMATCH, handle_464 },
515         { ERR_ALREADYREGISTERED, handle_462 },
516         { ERR_NOPERMFORHOST, handle_463 },
517         { ERR_NOTREGISTERED, handle_451 },
518         { ERR_YOUREBANNEDCREEP, handle_465 },
519         { 0, NULL }
520 };
521
522 /**
523  * Check whether reply r is part of a specified list
524  */
525 static int is_reply(const int *replies, int r)
526 {
527         int i;
528         g_assert(replies != NULL);
529
530         for(i = 0; i < 20 && replies[i]; i++) {
531                 if(replies[i] == r) return 1;
532         }
533         return 0;
534 }
535
536 static struct query *find_query(char *name)
537 {
538         int i;
539         for(i = 0; queries[i].name; i++) {
540                 if(!g_strcasecmp(queries[i].name, name)) return &queries[i];
541         }
542
543         return NULL;
544 }
545
546 /**
547  * Redirect a response received from the server.
548  *
549  * @return TRUE if the message was redirected to zero or more clients, 
550  *         FALSE if it was sent to all clients.
551  */
552 gboolean redirect_response(struct network *network, struct line *l)
553 {
554         struct query_stack *s, *p = NULL;
555         const struct client *c = NULL;
556         int n;
557         int i;
558         
559         g_assert(network);
560         g_assert(l->args[0]);
561
562         n = atoi(l->args[0]);
563
564         /* Find a request that this response is a reply to */
565         for (s = stack; s; s = s->next) {
566                 if(s->network == network && 
567                    (is_reply(s->query->replies, n) || 
568                         is_reply(s->query->errors, n) ||
569                         is_reply(s->query->end_replies, n))) {
570                         
571                         /* Send to client that queried, if that client still exists */
572                         if (s->client != NULL && verify_client(s->network, s->client)) {
573                                 c = s->client;
574                                 client_send_line(s->client, l);
575                         }
576
577                         if (!is_reply(s->query->replies, n)) {
578                                 /* Remove from stack */
579                                 if (p == NULL)stack = s->next;  
580                                 else p->next = s->next;
581                                 g_free(s);
582                         }
583
584                         return TRUE;
585                 }
586                 p = s; 
587         }
588
589         /* See if this is a response that should be sent to all clients */
590         for (i = 0; response_all[i]; i++) {
591                 if (response_all[i] == n) {
592                         clients_send(network->clients, l, c);
593                         return FALSE;
594                 }
595         }
596
597         /* See if this is a response that shouldn't be sent to clients at all */
598         for (i = 0; response_none[i]; i++) {
599                 if (response_none[i] == n) {
600                         return TRUE;
601                 }
602         }
603
604         /* Handle response using custom function */
605         for (i = 0; response_handler[i].handler; i++) {
606                 if (response_handler[i].response == n) {
607                         return response_handler[i].handler(network, l);
608                 }
609         }
610
611         if (!c) {
612                 log_network(LOG_WARNING, network, "Unable to redirect response %s", l->args[0]);
613                 clients_send(network->clients, l, NULL);
614         }
615
616         return FALSE;
617 }
618
619 void redirect_clear(const struct network *net)
620 {
621         struct query_stack *q, *p = NULL, *n;
622
623         g_assert(net);
624
625         q = stack;
626         while (q != NULL) {
627                 if (q->network != net) {
628                         p = q;
629                         q = q->next;
630                         continue;
631                 }
632
633                 /* Remove from stack */
634                 if (p == NULL)stack = q->next;  
635                 else p->next = q->next;
636                 n = q->next;
637                 g_free(q);
638                 q = n;
639         }
640 }
641
642 void redirect_record(const struct network *n, struct client *c, 
643                                          const struct line *l)
644 {
645         struct query *q;
646
647         g_assert(n);
648         g_assert(l);
649         g_assert(l->args[0]);
650
651         q = find_query(l->args[0]);
652         if (q == NULL) {
653                 if (c != NULL) {
654                         log_client(LOG_WARNING, c, "Unknown command from client: %s", 
655                                            l->args[0]);
656                 } else {
657                         log_network(LOG_WARNING, n, "Sending unknown command '%s'", 
658                                                 l->args[0]);
659                 }
660
661                 q = &unknown_query;
662         }
663
664         /* Push it up the stack! */
665         q->handle(l, n, c, q);
666 }
667
668 static int handle_default(const struct line *l, const struct network *n, 
669                                                   struct client *c, struct query *q)
670 {
671         struct query_stack *s = g_new(struct query_stack, 1);
672         g_assert(l != NULL);
673         g_assert(n != NULL);
674         g_assert(q != NULL);
675         s->network = n;
676         s->client = c;
677         s->time = time(NULL);
678         s->query = q;
679         s->next = stack;
680         stack = s;
681         return 1;
682 }
683
684 static int handle_topic(const struct line *l, const struct network *n, struct client *c, struct query *q)
685 {
686         if (l->args[2] != NULL)
687                 return 0;
688         return handle_default(l,n,c,q);
689 }