Remove argument from log functions.
[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, 
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, 0 },
102                 { RPL_ENDOFWHOWAS, RPL_WHOISSERVER, 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                         
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  /* WALLOPS */
414         { "WALLOPS",
415                 { 0 },
416                 { 0 },
417                 { ERR_NEEDMOREPARAMS, 0 },
418                 handle_default
419         },
420
421 /* PONG */
422         { "PONG",
423                 { 0 },
424                 { 0 },
425                 { ERR_NOORIGIN, ERR_NOSUCHSERVER, 0 },
426                 handle_default
427         },
428
429 /* NOTICE */
430         { "NOTICE",
431                 { 0 },
432                 { 0 },
433                 { 0 },
434                 handle_default
435         },
436
437         { NULL }
438 };
439
440 static struct query unknown_query = { 
441         NULL,
442         { 0 },
443         { 0 },
444         { ERR_UNKNOWNCOMMAND, 0 },
445         handle_default
446 };
447
448 static void handle_465(struct network *n, struct line *l)
449 {
450         log_network(LOG_ERROR, n, "Banned from server: %s", l->args[1]);
451 }
452
453 static void handle_451(struct network *n, struct line *l)
454 {
455         log_network(LOG_ERROR, n, "Not registered error, this is probably a bug...");
456 }
457
458 static void handle_462(struct network *n, struct line *l)
459 {
460         log_network(LOG_ERROR, n, "Double registration error, this is probably a bug...");
461 }
462
463 static void handle_463(struct network *n, struct line *l)
464 {
465         log_network(LOG_ERROR, n, "Host not privileged to connect");
466 }
467
468 static void handle_464(struct network *n, struct line *l)
469 {
470         log_network(LOG_ERROR, n, "Password mismatch");
471 }
472
473 /* List of responses that should be sent to all clients */
474 static int response_all[] = { RPL_NOWAWAY, RPL_UNAWAY, RPL_NAMREPLY, 
475         RPL_ENDOFNAMES, ERR_NEEDREGGEDNICK, RPL_UMODEIS, 
476         ERR_NO_OP_SPLIT, 0 };
477 static int response_none[] = { ERR_NOMOTD, RPL_ENDOFMOTD, 0 };
478 static struct {
479         int response;
480         void (*handler) (struct network *n, struct line *);
481 } response_handler[] = {
482         { ERR_PASSWDMISMATCH, handle_464 },
483         { ERR_ALREADYREGISTERED, handle_462 },
484         { ERR_NOPERMFORHOST, handle_463 },
485         { ERR_NOTREGISTERED, handle_451 },
486         { ERR_YOUREBANNEDCREEP, handle_465 },
487         { 0, NULL }
488 };
489
490 static int is_reply(const int *replies, int r)
491 {
492         int i;
493         g_assert(replies);
494
495         for(i = 0; i < 20 && replies[i]; i++) {
496                 if(replies[i] == r) return 1;
497         }
498         return 0;
499 }
500
501 static struct query *find_query(char *name)
502 {
503         int i;
504         for(i = 0; queries[i].name; i++) {
505                 if(!g_strcasecmp(queries[i].name, name)) return &queries[i];
506         }
507
508         return NULL;
509 }
510
511 void redirect_response(struct network *network, struct line *l)
512 {
513         struct query_stack *s, *p = NULL;
514         const struct client *c = NULL;
515         int n;
516         int i;
517         
518         g_assert(network);
519         g_assert(l->args[0]);
520
521         n = atoi(l->args[0]);
522
523         /* Find a request that this response is a reply to */
524         for (s = stack; s; s = s->next) {
525                 if(s->network == network && 
526                    (is_reply(s->query->replies, n) || 
527                         is_reply(s->query->errors, n) ||
528                         is_reply(s->query->end_replies, n))) {
529                         
530                         /* Send to client that queried, if that client still exists */
531                         if (s->client && verify_client(s->network, s->client)) {
532                                 c = s->client;
533                                 client_send_line(s->client, l);
534                         }
535
536                         if(!is_reply(s->query->replies, n)) {
537                                 /* Remove from stack */
538                                 if(!p)stack = s->next;  
539                                 else p->next = s->next;
540                                 g_free(s);
541                         }
542
543                         return;
544                 }
545                 p = s; 
546         }
547
548         /* See if this is a response that should be sent to all clients */
549         for (i = 0; response_all[i]; i++) {
550                 if (response_all[i] == n) {
551                         clients_send(network, l, c);
552                         return;
553                 }
554         }
555
556         /* See if this is a response that shouldn't be sent to clients at all */
557         for (i = 0; response_none[i]; i++) {
558                 if (response_none[i] == n) {
559                         return;
560                 }
561         }
562
563         /* Handle response using custom function */
564         for (i = 0; response_handler[i].handler; i++) {
565                 if (response_handler[i].response == n) {
566                         response_handler[i].handler(network, l);
567                         return;
568                 }
569         }
570
571         if (!c) {
572                 log_network(LOG_WARNING, network, "Unable to redirect response %s", l->args[0]);
573                 clients_send(network, l, NULL);
574         }
575 }
576
577 void redirect_clear(const struct network *net)
578 {
579         struct query_stack *q, *p = NULL, *n;
580
581         g_assert(net);
582
583         q = stack;
584         while (q) {
585                 if (q->network != net) {
586                         p = q;
587                         q = q->next;
588                         continue;
589                 }
590
591                 /* Remove from stack */
592                 if(!p)stack = q->next;  
593                 else p->next = q->next;
594                 n = q->next;
595                 g_free(q);
596                 q = n;
597         }
598 }
599
600 void redirect_record(const struct network *n, struct client *c, const struct line *l)
601 {
602         struct query *q;
603
604         g_assert(n);
605         g_assert(l);
606         g_assert(l->args[0]);
607
608         q = find_query(l->args[0]);
609         if(!q) {
610                 if (c) {
611                         log_client(LOG_WARNING, c, "Unknown command from client: %s", l->args[0]);
612                 } else {
613                         log_network(LOG_WARNING, n, "Sending unknown command '%s'", l->args[0]);
614                 }
615
616                 q = &unknown_query;
617         }
618
619         /* Push it up the stack! */
620         q->handle(l, n, c, q);
621 }
622
623 static int handle_default(const struct line *l, const struct network *n, struct client *c, struct query *q)
624 {
625         struct query_stack *s = g_new(struct query_stack,1);
626         g_assert(l);
627         g_assert(n);
628         g_assert(q);
629         s->network = n;
630         s->client = c;
631         s->time = time(NULL);
632         s->query = q;
633         s->next = stack;
634         stack = s;
635         return 1;
636 }
637
638 static int handle_topic(const struct line *l, const struct network *n, struct client *c, struct query *q)
639 {
640         if(l->args[2])return 0;
641         return handle_default(l,n,c,q);
642 }