01badc8eae10710f58b119b47ecf2eb29696d7ef
[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,
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_NOSUCHCHANNEL, ERR_TOOMANYCHANNELS, 0 },
193                 handle_default
194         },
195
196  /* PART <channel> *( "," <channel> ) [ <Part Message> ] */
197         { "PART",
198                 { 0 },
199                 { 0 },
200                 { ERR_NEEDMOREPARAMS, ERR_NOSUCHCHANNEL, ERR_NOTONCHANNEL, 0 },
201                 handle_default
202         },
203
204  /* NICK <nickname> */
205         { "NICK",
206                 { 0 },
207                 { 0 },
208                 { ERR_NONICKNAMEGIVEN, ERR_ERRONEUSNICKNAME, ERR_NICKNAMEINUSE,
209               ERR_UNAVAILRESOURCE, ERR_RESTRICTED, ERR_NICKCOLLISION, 
210                   ERR_NICKTOOFAST, 0 },
211                 handle_default
212         },
213
214  /* USER <username> <hostname> <servername> <realname> */
215         { "USER",
216                 { 0 },
217                 { 0 },
218                 { ERR_NEEDMOREPARAMS, ERR_ALREADYREGISTERED, 0 },
219                 handle_default
220         },
221
222  /* QUIT [<quit message>] */
223         { "QUIT", 
224                 { 0 },
225                 { 0 },
226                 { 0 },
227                 handle_default
228         },
229
230  /* OPER <name> <password> */
231         { "OPER",
232             { 0 },
233             { RPL_YOUREOPER, 0 },
234                 { ERR_NEEDMOREPARAMS, ERR_NOOPERHOST, ERR_PASSWDMISMATCH, 0 },
235                 handle_default
236         },
237         
238  /* MODE <nick> <mode> */
239         { "MODE", 
240                 {  /* Replies to channel mode queries */
241                         RPL_BANLIST, RPL_EXCEPTLIST, RPL_INVITELIST, 
242                         0 },
243                 { 
244                         /* Replies to user mode queries */
245                         RPL_UMODEIS, 
246
247                         /* Replies to channel mode queries */
248                         RPL_CHANNELMODEIS, RPL_ENDOFBANLIST, RPL_ENDOFEXCEPTLIST,
249                         RPL_ENDOFINVITELIST, RPL_UNIQOPIS,
250                         
251                         0 },
252                 { 
253                         /* Common replies */
254                         ERR_NEEDMOREPARAMS, 
255
256                         /* Replies to user mode queries */
257                         ERR_UMODEUNKNOWNFLAG, ERR_USERSDONTMATCH, 
258                         
259                         /* Replies to channel mode queries */
260                         ERR_USERNOTINCHANNEL, ERR_KEYSET, ERR_CHANOPPRIVSNEEDED,
261                         ERR_UNKNOWNMODE, ERR_NOCHANMODES, 
262                         
263                         0 },
264                 handle_default
265         },
266         
267  /* SERVICE <nick> <reserved> <distribution> <type> <reserved> <info> */
268         { "SERVICE", 
269                 { 0 },
270                 { RPL_YOURESERVICE, RPL_YOURHOST, RPL_MYINFO, 0 },
271                 { ERR_ALREADYREGISTERED, ERR_NEEDMOREPARAMS, ERR_ERRONEUSNICKNAME, 0 },
272                 handle_default
273         },
274         
275  /* SQUIT <server> <comment> */
276         { "SQUIT",
277                 { 0 },
278                 { 0 },
279                 { ERR_NOPRIVILEGES, ERR_NEEDMOREPARAMS, ERR_NOSUCHSERVER, 0 },
280                 handle_default
281         },
282         
283  /* INVITE <nick> <channel> */
284         { "INVITE", 
285                 { 0 },
286                 { RPL_INVITING, RPL_AWAY, 0 },
287                 { ERR_NEEDMOREPARAMS, ERR_NOTONCHANNEL, ERR_NOSUCHNICK, 
288                         ERR_CHANOPPRIVSNEEDED, ERR_USERONCHANNEL, 0 },
289                 handle_default
290         },
291         
292 /* KICK <channel> * ( "," <channel> ) <user> *( "," <user> ) [<comment>] */
293         { "KICK",
294                 { 0 },
295                 { 0 },
296                 { ERR_NEEDMOREPARAMS, ERR_BADCHANMASK, ERR_USERNOTINCHANNEL,
297                         ERR_NOSUCHCHANNEL, ERR_CHANOPPRIVSNEEDED, ERR_NOTONCHANNEL, 0 },
298                 handle_default
299         },
300  
301  /* PRIVMSG <msgtarget> <text> */
302         { "PRIVMSG",
303                 { 0 },
304                 { RPL_AWAY, 0 },
305                 { ERR_NORECIPIENT, ERR_NOTEXTTOSEND, ERR_CANNOTSENDTOCHAN, 
306                         ERR_NOTOPLEVEL, ERR_TOOMANYTARGETS, ERR_WILDTOPLEVEL, 
307                         ERR_NOSUCHNICK, ERR_NOSUCHCHANNEL, ERR_BLOCKING_NOTID, 0 },
308                 handle_default
309         },
310  
311  /* MOTD [<target>] */
312         { "MOTD",
313                 { RPL_MOTDSTART, RPL_MOTD, 0 },
314                 { RPL_ENDOFMOTD, 0 },
315                 { ERR_NOMOTD, 0 },
316                 handle_default
317         },
318
319  /* LUSERS [ <mask> [ <target> ] ] */
320         { "LUSERS",
321                 { RPL_LUSERCLIENT, RPL_LUSEROP, RPL_LUSERUNKNOWN, RPL_LUSERCHANNELS, 
322                         RPL_LUSERME, 0 },
323                 { 0 },
324                 { ERR_NOSUCHSERVER, 0 },
325                 handle_default
326         },
327         
328  /* CONNECT <target> <port> [ <remote server> ] */
329         { "CONNECT",
330                 { 0 },
331                 { 0 },
332                 { ERR_NOSUCHSERVER, ERR_NEEDMOREPARAMS, ERR_NOPRIVILEGES, 0 },
333                 handle_default
334         },
335
336  /* ADMIN [ <target> ] */
337         { "ADMIN",
338                 { RPL_ADMINME, RPL_ADMINLOC2, RPL_ADMINLOC1, 
339                         RPL_ADMINLOC3, 0 },
340                 { 0 },
341                 { ERR_NOSUCHSERVER, 0 },
342                 handle_default
343         },
344         
345  /* INFO [ <target> ] */
346         { "INFO",
347                 { RPL_INFO, 0 },
348                 { RPL_ENDOFINFO, 0 },
349                 { ERR_NOSUCHSERVER, 0 },
350                 handle_default
351         },
352         
353  /* SERVLIST [ <mask> [ <type> ] ] */
354         { "SERVLIST",
355                 { RPL_SERVLIST, 0},
356                 { RPL_SERVLISTEND, 0 },
357                 { 0 },
358                 handle_default
359         },
360         
361  /* SQUERY <servicename> <text> */
362         /* Same responses as for PRIVMSG */
363         { "SQUERY",
364                 { 0 },
365                 { RPL_AWAY, 0 },
366                 { ERR_NORECIPIENT, ERR_NOTEXTTOSEND, ERR_CANNOTSENDTOCHAN, 
367                         ERR_NOTOPLEVEL, ERR_TOOMANYTARGETS, ERR_WILDTOPLEVEL, 
368                         ERR_NOSUCHNICK, 0 },
369                 handle_default
370         },
371         
372  /* KILL <nick> <comment> */
373         { "KILL",
374                 { 0 },
375                 { 0 },
376                 { ERR_NOPRIVILEGES, ERR_NEEDMOREPARAMS, ERR_NOSUCHNICK, ERR_CANTKILLSERVER, 0 },
377                 handle_default
378         },
379
380  /* AWAY [ <text> ] */
381         { "AWAY",
382                 { 0 },
383                 { RPL_UNAWAY, RPL_NOWAWAY, 0 },
384                 { 0 },
385                 handle_default
386         },
387
388  /* REHASH */
389         { "REHASH",
390                 { 0 },
391                 { RPL_REHASHING, 0 },
392                 { ERR_NOPRIVILEGES, 0 },
393                 handle_default
394         },
395
396  /* DIE */
397         { "DIE",
398                 { 0 },
399                 { 0 },
400                 { ERR_NOPRIVILEGES, 0 },
401                 handle_default
402         },
403
404  /* RESTART */
405         { "RESTART",
406                 { 0 },
407                 { 0 },
408                 { ERR_NOPRIVILEGES, 0 },
409                 handle_default
410         },
411
412 /* PING */
413         { "PING", 
414                 { 0 },
415                 { 0 },
416                 { ERR_NOORIGIN, 0 },
417                 handle_default
418         },
419
420  /* WALLOPS */
421         { "WALLOPS",
422                 { 0 },
423                 { 0 },
424                 { ERR_NEEDMOREPARAMS, 0 },
425                 handle_default
426         },
427
428 /* PONG */
429         { "PONG",
430                 { 0 },
431                 { 0 },
432                 { ERR_NOORIGIN, ERR_NOSUCHSERVER, 0 },
433                 handle_default
434         },
435
436 /* NOTICE */
437         { "NOTICE",
438                 { 0 },
439                 { 0 },
440                 { 0 },
441                 handle_default
442         },
443
444 /* AUTH */
445         { "AUTH",
446                 { 0 },
447                 { 0 },
448                 { ERR_NEEDMOREPARAMS, ERR_ALREADYAUTHENTICATED, ERR_ALREADYREGISTERED,
449                   ERR_AUTHENTICATIONFAILED, ERR_AUTHENTICATIONSUSPENDED, 
450                   ERR_BADCOMMAND, ERR_UNKNOWNPACKAGE, 0 },
451                 handle_default
452         },
453
454         { NULL }
455 };
456
457 static struct query unknown_query = { 
458         NULL,
459         { 0 },
460         { 0 },
461         { ERR_UNKNOWNCOMMAND, 0 },
462         handle_default
463 };
464
465 static gboolean handle_465(struct network *n, struct line *l)
466 {
467         log_network(LOG_ERROR, n, "Banned from server: %s", l->args[1]);
468         return TRUE;
469 }
470
471 static gboolean handle_451(struct network *n, struct line *l)
472 {
473         log_network(LOG_ERROR, n, "Not registered error, this is probably a bug...");
474         return TRUE;
475 }
476
477 static gboolean handle_462(struct network *n, struct line *l)
478 {
479         log_network(LOG_ERROR, n, "Double registration error, this is probably a bug...");
480         return TRUE;
481 }
482
483 static gboolean handle_463(struct network *n, struct line *l)
484 {
485         log_network(LOG_ERROR, n, "Host not privileged to connect");
486         return TRUE;
487 }
488
489 static gboolean handle_464(struct network *n, struct line *l)
490 {
491         log_network(LOG_ERROR, n, "Password mismatch");
492         return TRUE;
493 }
494
495 /* List of responses that should be sent to all clients */
496 static int response_all[] = { RPL_NOWAWAY, RPL_UNAWAY, RPL_NAMREPLY, 
497         ERR_NO_OP_SPLIT, RPL_HIDINGHOST,
498         RPL_ENDOFNAMES, ERR_NEEDREGGEDNICK, RPL_UMODEIS, 
499         RPL_LUSERCLIENT, RPL_LUSEROP, RPL_LUSERUNKNOWN, RPL_LUSERCHANNELS,
500         RPL_LUSERME, ERR_NO_OP_SPLIT, RPL_LOCALUSERS, RPL_GLOBALUSERS, 0 };
501 static int response_none[] = { ERR_NOMOTD, RPL_ENDOFMOTD, 0 };
502 static struct {
503         int response;
504         gboolean (*handler) (struct network *n, struct line *);
505 } response_handler[] = {
506         { ERR_PASSWDMISMATCH, handle_464 },
507         { ERR_ALREADYREGISTERED, handle_462 },
508         { ERR_NOPERMFORHOST, handle_463 },
509         { ERR_NOTREGISTERED, handle_451 },
510         { ERR_YOUREBANNEDCREEP, handle_465 },
511         { 0, NULL }
512 };
513
514 static int is_reply(const int *replies, int r)
515 {
516         int i;
517         g_assert(replies);
518
519         for(i = 0; i < 20 && replies[i]; i++) {
520                 if(replies[i] == r) return 1;
521         }
522         return 0;
523 }
524
525 static struct query *find_query(char *name)
526 {
527         int i;
528         for(i = 0; queries[i].name; i++) {
529                 if(!g_strcasecmp(queries[i].name, name)) return &queries[i];
530         }
531
532         return NULL;
533 }
534
535 /**
536  * Redirect a response received from the server.
537  *
538  * @return TRUE if the message was redirected to zero or more clients, 
539  *         FALSE if it was sent to all clients.
540  */
541 gboolean redirect_response(struct network *network, struct line *l)
542 {
543         struct query_stack *s, *p = NULL;
544         const struct client *c = NULL;
545         int n;
546         int i;
547         
548         g_assert(network);
549         g_assert(l->args[0]);
550
551         n = atoi(l->args[0]);
552
553         /* Find a request that this response is a reply to */
554         for (s = stack; s; s = s->next) {
555                 if(s->network == network && 
556                    (is_reply(s->query->replies, n) || 
557                         is_reply(s->query->errors, n) ||
558                         is_reply(s->query->end_replies, n))) {
559                         
560                         /* Send to client that queried, if that client still exists */
561                         if (s->client && verify_client(s->network, s->client)) {
562                                 c = s->client;
563                                 client_send_line(s->client, l);
564                         }
565
566                         if(!is_reply(s->query->replies, n)) {
567                                 /* Remove from stack */
568                                 if(!p)stack = s->next;  
569                                 else p->next = s->next;
570                                 g_free(s);
571                         }
572
573                         return TRUE;
574                 }
575                 p = s; 
576         }
577
578         /* See if this is a response that should be sent to all clients */
579         for (i = 0; response_all[i]; i++) {
580                 if (response_all[i] == n) {
581                         clients_send(network->clients, l, c);
582                         return FALSE;
583                 }
584         }
585
586         /* See if this is a response that shouldn't be sent to clients at all */
587         for (i = 0; response_none[i]; i++) {
588                 if (response_none[i] == n) {
589                         return TRUE;
590                 }
591         }
592
593         /* Handle response using custom function */
594         for (i = 0; response_handler[i].handler; i++) {
595                 if (response_handler[i].response == n) {
596                         return response_handler[i].handler(network, l);
597                 }
598         }
599
600         if (!c) {
601                 log_network(LOG_WARNING, network, "Unable to redirect response %s", l->args[0]);
602                 clients_send(network->clients, l, NULL);
603         }
604
605         return FALSE;
606 }
607
608 void redirect_clear(const struct network *net)
609 {
610         struct query_stack *q, *p = NULL, *n;
611
612         g_assert(net);
613
614         q = stack;
615         while (q) {
616                 if (q->network != net) {
617                         p = q;
618                         q = q->next;
619                         continue;
620                 }
621
622                 /* Remove from stack */
623                 if(!p)stack = q->next;  
624                 else p->next = q->next;
625                 n = q->next;
626                 g_free(q);
627                 q = n;
628         }
629 }
630
631 void redirect_record(const struct network *n, struct client *c, const struct line *l)
632 {
633         struct query *q;
634
635         g_assert(n);
636         g_assert(l);
637         g_assert(l->args[0]);
638
639         q = find_query(l->args[0]);
640         if(!q) {
641                 if (c) {
642                         log_client(LOG_WARNING, c, "Unknown command from client: %s", l->args[0]);
643                 } else {
644                         log_network(LOG_WARNING, n, "Sending unknown command '%s'", l->args[0]);
645                 }
646
647                 q = &unknown_query;
648         }
649
650         /* Push it up the stack! */
651         q->handle(l, n, c, q);
652 }
653
654 static int handle_default(const struct line *l, const struct network *n, struct client *c, struct query *q)
655 {
656         struct query_stack *s = g_new(struct query_stack,1);
657         g_assert(l);
658         g_assert(n);
659         g_assert(q);
660         s->network = n;
661         s->client = c;
662         s->time = time(NULL);
663         s->query = q;
664         s->next = stack;
665         stack = s;
666         return 1;
667 }
668
669 static int handle_topic(const struct line *l, const struct network *n, struct client *c, struct query *q)
670 {
671         if (l->args[2] != NULL)
672                 return 0;
673         return handle_default(l,n,c,q);
674 }