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