2 ctrlproxy: A modular IRC proxy
3 Send numerics to the right places
4 (c) 2002-2005 Jelmer Vernooij <jelmer@nl.linux.org>
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.
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.
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.
21 #include "ctrlproxy.h"
25 /* TODO: Clean up stack occasionally */
28 const struct query *query;
29 const struct network *network;
30 struct client *client;
32 struct query_stack *next;
35 static struct query_stack *stack = NULL;
38 * IRC Query done by a client
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 *);
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 *);
53 static struct query queries[] = {
54 /* Commands that get a one-client reply:
55 * WHOIS [<server>] <nickmask>[,<nickmask>[,...]] */
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 },
66 /* WHO [<name> [<o>]] */
70 { ERR_NOSUCHSERVER, 0 },
74 /* NAMES [<channel>{,<channel>}]*/
77 { RPL_ENDOFNAMES, 0 },
78 { ERR_TOOMANYMATCHES, ERR_NOSUCHSERVER, 0 },
82 /* LIST [<channel>{,<channel>} [<server>]*/
84 { RPL_LIST, RPL_LISTSTART, 0 },
86 { ERR_TOOMANYMATCHES, ERR_NOSUCHSERVER, 0 },
90 /* TOPIC <channel> [<topic>]*/
93 { RPL_NOTOPIC, RPL_TOPIC, 0 },
94 { ERR_NOTONCHANNEL, ERR_NEEDMOREPARAMS, ERR_CHANOPPRIVSNEEDED,
99 /* WHOWAS <nickname> [<count> [<server>]]*/
101 { RPL_WHOWASUSER, 0 },
102 { RPL_ENDOFWHOWAS, RPL_WHOISSERVER, 0 },
103 { ERR_NONICKNAMEGIVEN, ERR_WASNOSUCHNICK, 0 },
107 /* STATS [<query> [<server>]]*/
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,
113 { RPL_ENDOFSTATS, 0 },
114 { ERR_NOSUCHSERVER, 0 },
118 /* VERSION [<server>]*/
122 { ERR_NOSUCHSERVER, 0 },
126 /* LINKS [[<remote server>] <server mask>]*/
129 { RPL_ENDOFLINKS, 0 },
130 { ERR_NOSUCHSERVER, 0 },
138 { ERR_NOSUCHSERVER, 0 },
142 /* TRACE [<server>]*/
144 { RPL_TRACELINK, RPL_TRACECONNECTING,
145 RPL_TRACEUNKNOWN, RPL_TRACEUSER, RPL_TRACECLASS,
146 RPL_TRACEHANDSHAKE, RPL_TRACEOPERATOR,
147 RPL_TRACESERVER, RPL_TRACENEWTYPE, 0 },
149 { ERR_NOSUCHSERVER, 0 },
153 /* SUMMON <user> [<server>]*/
156 { RPL_SUMMONING, 0 },
157 { ERR_NORECIPIENT, ERR_FILEERROR, ERR_NOLOGIN, ERR_NOSUCHSERVER, 0 },
161 /* USERS [<server>]*/
163 { RPL_USERSSTART, RPL_USERS, RPL_NOUSERS, 0 },
164 { RPL_ENDOFUSERS, 0 },
165 { ERR_NOSUCHSERVER, ERR_FILEERROR, ERR_USERSDISABLED, 0 },
169 /* USERHOST <nickname>{ <nickname>}{ ...}*/
173 { ERR_NEEDMOREPARAMS, 0 },
177 /* ISON <nickname>{ <nickname>}{ ...} */
181 { ERR_NEEDMOREPARAMS, 0 },
185 /* JOIN <channel>{,<channel>} [<key>{,<key>}] */
187 { RPL_TOPIC, RPL_TOPICWHOTIME, RPL_CREATIONTIME, 0 },
189 { ERR_NEEDMOREPARAMS, ERR_BANNEDFROMCHAN,
190 ERR_INVITEONLYCHAN, ERR_BADCHANNELKEY,
191 ERR_CHANNELISFULL, ERR_BADCHANMASK,
192 ERR_NOSUCHCHANNEL, ERR_TOOMANYCHANNELS, 0 },
196 /* PART <channel> *( "," <channel> ) [ <Part Message> ] */
200 { ERR_NEEDMOREPARAMS, ERR_NOSUCHCHANNEL, ERR_NOTONCHANNEL, 0 },
204 /* NICK <nickname> */
208 { ERR_NONICKNAMEGIVEN, ERR_ERRONEUSNICKNAME, ERR_NICKNAMEINUSE,
209 ERR_UNAVAILRESOURCE, ERR_RESTRICTED, ERR_NICKCOLLISION,
210 ERR_NICKTOOFAST, 0 },
214 /* USER <username> <hostname> <servername> <realname> */
218 { ERR_NEEDMOREPARAMS, ERR_ALREADYREGISTERED, 0 },
222 /* QUIT [<quit message>] */
230 /* OPER <name> <password> */
233 { RPL_YOUREOPER, 0 },
234 { ERR_NEEDMOREPARAMS, ERR_NOOPERHOST, ERR_PASSWDMISMATCH, 0 },
238 /* MODE <nick> <mode> */
240 { /* Replies to channel mode queries */
241 RPL_BANLIST, RPL_EXCEPTLIST, RPL_INVITELIST,
245 /* Replies to user mode queries */
248 /* Replies to channel mode queries */
249 RPL_CHANNELMODEIS, RPL_ENDOFBANLIST, RPL_ENDOFEXCEPTLIST,
250 RPL_ENDOFINVITELIST, RPL_UNIQOPIS,
257 /* Replies to user mode queries */
258 ERR_UMODEUNKNOWNFLAG, ERR_USERSDONTMATCH,
260 /* Replies to channel mode queries */
261 ERR_USERNOTINCHANNEL, ERR_KEYSET, ERR_CHANOPPRIVSNEEDED,
262 ERR_UNKNOWNMODE, ERR_NOCHANMODES,
268 /* SERVICE <nick> <reserved> <distribution> <type> <reserved> <info> */
271 { RPL_YOURESERVICE, RPL_YOURHOST, RPL_MYINFO, 0 },
272 { ERR_ALREADYREGISTERED, ERR_NEEDMOREPARAMS, ERR_ERRONEUSNICKNAME, 0 },
276 /* SQUIT <server> <comment> */
280 { ERR_NOPRIVILEGES, ERR_NEEDMOREPARAMS, ERR_NOSUCHSERVER, 0 },
284 /* INVITE <nick> <channel> */
287 { RPL_INVITING, RPL_AWAY, 0 },
288 { ERR_NEEDMOREPARAMS, ERR_NOTONCHANNEL, ERR_NOSUCHNICK,
289 ERR_CHANOPPRIVSNEEDED, ERR_USERONCHANNEL, 0 },
293 /* KICK <channel> * ( "," <channel> ) <user> *( "," <user> ) [<comment>] */
297 { ERR_NEEDMOREPARAMS, ERR_BADCHANMASK, ERR_USERNOTINCHANNEL,
298 ERR_NOSUCHCHANNEL, ERR_CHANOPPRIVSNEEDED, ERR_NOTONCHANNEL, 0 },
302 /* PRIVMSG <msgtarget> <text> */
306 { ERR_NORECIPIENT, ERR_NOTEXTTOSEND, ERR_CANNOTSENDTOCHAN,
307 ERR_NOTOPLEVEL, ERR_TOOMANYTARGETS, ERR_WILDTOPLEVEL,
308 ERR_NOSUCHNICK, ERR_NOSUCHCHANNEL, ERR_BLOCKING_NOTID, 0 },
312 /* MOTD [<target>] */
314 { RPL_MOTDSTART, RPL_MOTD, 0 },
315 { RPL_ENDOFMOTD, 0 },
320 /* LUSERS [ <mask> [ <target> ] ] */
322 { RPL_LUSERCLIENT, RPL_LUSEROP, RPL_LUSERUNKNOWN, RPL_LUSERCHANNELS,
325 { ERR_NOSUCHSERVER, 0 },
329 /* CONNECT <target> <port> [ <remote server> ] */
333 { ERR_NOSUCHSERVER, ERR_NEEDMOREPARAMS, ERR_NOPRIVILEGES, 0 },
337 /* ADMIN [ <target> ] */
339 { RPL_ADMINME, RPL_ADMINLOC2, RPL_ADMINLOC1,
342 { ERR_NOSUCHSERVER, 0 },
346 /* INFO [ <target> ] */
349 { RPL_ENDOFINFO, 0 },
350 { ERR_NOSUCHSERVER, 0 },
354 /* SERVLIST [ <mask> [ <type> ] ] */
357 { RPL_SERVLISTEND, 0 },
362 /* SQUERY <servicename> <text> */
363 /* Same responses as for PRIVMSG */
367 { ERR_NORECIPIENT, ERR_NOTEXTTOSEND, ERR_CANNOTSENDTOCHAN,
368 ERR_NOTOPLEVEL, ERR_TOOMANYTARGETS, ERR_WILDTOPLEVEL,
373 /* KILL <nick> <comment> */
377 { ERR_NOPRIVILEGES, ERR_NEEDMOREPARAMS, ERR_NOSUCHNICK, ERR_CANTKILLSERVER, 0 },
381 /* AWAY [ <text> ] */
384 { RPL_UNAWAY, RPL_NOWAWAY, 0 },
392 { RPL_REHASHING, 0 },
393 { ERR_NOPRIVILEGES, 0 },
401 { ERR_NOPRIVILEGES, 0 },
409 { ERR_NOPRIVILEGES, 0 },
417 { ERR_NEEDMOREPARAMS, 0 },
425 { ERR_NOORIGIN, ERR_NOSUCHSERVER, 0 },
440 static struct query unknown_query = {
444 { ERR_UNKNOWNCOMMAND, 0 },
448 static void handle_465(struct network *n, struct line *l)
450 log_network(LOG_ERROR, n, "Banned from server: %s", l->args[1]);
453 static void handle_451(struct network *n, struct line *l)
455 log_network(LOG_ERROR, n, "Not registered error, this is probably a bug...");
458 static void handle_462(struct network *n, struct line *l)
460 log_network(LOG_ERROR, n, "Double registration error, this is probably a bug...");
463 static void handle_463(struct network *n, struct line *l)
465 log_network(LOG_ERROR, n, "Host not privileged to connect");
468 static void handle_464(struct network *n, struct line *l)
470 log_network(LOG_ERROR, n, "Password mismatch");
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 };
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 },
490 static int is_reply(const int *replies, int r)
495 for(i = 0; i < 20 && replies[i]; i++) {
496 if(replies[i] == r) return 1;
501 static struct query *find_query(char *name)
504 for(i = 0; queries[i].name; i++) {
505 if(!g_strcasecmp(queries[i].name, name)) return &queries[i];
511 void redirect_response(struct network *network, struct line *l)
513 struct query_stack *s, *p = NULL;
514 const struct client *c = NULL;
519 g_assert(l->args[0]);
521 n = atoi(l->args[0]);
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))) {
530 /* Send to client that queried, if that client still exists */
531 if (s->client && verify_client(s->network, s->client)) {
533 client_send_line(s->client, l);
536 if(!is_reply(s->query->replies, n)) {
537 /* Remove from stack */
538 if(!p)stack = s->next;
539 else p->next = s->next;
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);
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) {
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);
572 log_network(LOG_WARNING, network, "Unable to redirect response %s", l->args[0]);
573 clients_send(network, l, NULL);
577 void redirect_clear(const struct network *net)
579 struct query_stack *q, *p = NULL, *n;
585 if (q->network != net) {
591 /* Remove from stack */
592 if(!p)stack = q->next;
593 else p->next = q->next;
600 void redirect_record(const struct network *n, struct client *c, const struct line *l)
606 g_assert(l->args[0]);
608 q = find_query(l->args[0]);
611 log_client(LOG_WARNING, c, "Unknown command from client: %s", l->args[0]);
613 log_network(LOG_WARNING, n, "Sending unknown command '%s'", l->args[0]);
619 /* Push it up the stack! */
620 q->handle(l, n, c, q);
623 static int handle_default(const struct line *l, const struct network *n, struct client *c, struct query *q)
625 struct query_stack *s = g_new(struct query_stack,1);
631 s->time = time(NULL);
638 static int handle_topic(const struct line *l, const struct network *n, struct client *c, struct query *q)
640 if(l->args[2])return 0;
641 return handle_default(l,n,c,q);