/* * IRC - Internet Relay Chat, common/parse.c * Copyright (C) 1990 Jarkko Oikarinen and * University of Oulu, Computing Center * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /** @file * @brief Parse input from IRC clients and other servers. * @version $Id: parse.c 1925 2010-01-02 20:33:10Z klmitch $ */ #include "config.h" #include "parse.h" #include "class.h" #include "client.h" #include "channel.h" #include "handlers.h" #include "hash.h" #include "ircd.h" #include "ircd_alloc.h" #include "ircd_chattr.h" #include "ircd_features.h" #include "ircd_log.h" #include "ircd_reply.h" #include "ircd_string.h" #include "msg.h" #include "numeric.h" #include "numnicks.h" #include "opercmds.h" #include "querycmds.h" #include "res.h" #include "s_bsd.h" #include "s_conf.h" #include "s_debug.h" #include "s_misc.h" #include "s_numeric.h" #include "s_user.h" #include "send.h" #include "shun.h" #include "struct.h" #include "sys.h" #include "whocmds.h" #include "whowas.h" /* #include -- Now using assert in ircd_log.h */ #include #include /* * Message Tree stuff mostly written by orabidoo, with changes by Dianora. * Adapted to Undernet, adding token support, etc by comstud 10/06/97 * * completely rewritten June 2, 2003 - Dianora * * This has always just been a trie. Look at volume III of Knuth ACP * * * ok, you start out with an array of pointers, each one corresponds * to a letter at the current position in the command being examined. * * so roughly you have this for matching 'trie' or 'tie' * * 't' points -> [MessageTree *] 'r' -> [MessageTree *] -> 'i' * -> [MessageTree *] -> [MessageTree *] -> 'e' and matches * * 'i' -> [MessageTree *] -> 'e' and matches */ /** Number of children under a trie node. */ #define MAXPTRLEN 32 /* Must be a power of 2, and * larger than 26 [a-z]|[A-Z] * its used to allocate the set * of pointers at each node of the tree * There are MAXPTRLEN pointers at each node. * Obviously, there have to be more pointers * Than ASCII letters. 32 is a nice number * since there is then no need to shift * 'A'/'a' to base 0 index, at the expense * of a few never used pointers. For a small * parser like this, this is a good compromise * and does make it somewhat faster. * * - Dianora */ /** Node in the command lookup trie. */ struct MessageTree { struct Message *msg; /**< Message (if any) if the string ends now. */ struct MessageTree *pointers[MAXPTRLEN]; /**< Child nodes for each letter. */ }; /** Root of command lookup trie. */ static struct MessageTree msg_tree; static struct MessageTree tok_tree; /** Array of all supported commands. */ struct Message msgtab[] = { { MSG_PRIVATE, TOK_PRIVATE, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_privmsg, ms_privmsg, mo_privmsg, m_ignore }, " : - Sends a message to a channel or a user", }, { MSG_NICK, TOK_NICK, 0, MAXPARA, MFLG_SLOW | MFLG_UNREG | MFLG_NOSHUN, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_nick, m_nick, ms_nick, m_nick, m_ignore }, " - Changes your nick" }, { MSG_NOTICE, TOK_NOTICE, 0, MAXPARA, MFLG_SLOW | MFLG_UNREG | MFLG_IGNORE, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { mr_notice, m_notice, ms_notice, mo_notice, m_ignore }, " : - Sends a message to a channel or a user", }, { MSG_WALLCHOPS, TOK_WALLCHOPS, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_wallchops, ms_wallchops, m_wallchops, m_ignore }, " : - Sends a message to all channel operators in the channel", }, { MSG_WALLVOICES, TOK_WALLVOICES, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_wallvoices, ms_wallvoices, m_wallvoices, m_ignore }, " : - Sends a message to all voiced users in the channel", }, { MSG_CPRIVMSG, TOK_CPRIVMSG, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_cprivmsg, m_ignore, m_cprivmsg, m_ignore }, " : - Whispers a message to one person as if it was sent to the given channel", }, { MSG_CNOTICE, TOK_CNOTICE, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_cnotice, m_ignore, m_cnotice, m_ignore }, " : - Whispers a message to one person as if it was sent to the given channel", }, { MSG_JOIN, TOK_JOIN, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_join, ms_join, m_join, m_ignore }, " - Joins a channel" }, { MSG_MODE, TOK_MODE, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_mode, ms_mode, m_mode, m_ignore }, " [ [...]] - Changes the parameter of a user or channel" }, { MSG_BURST, TOK_BURST, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_burst, m_ignore, m_ignore }, "" }, { MSG_CREATE, TOK_CREATE, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_create, m_ignore, m_ignore }, "" }, { MSG_DESTRUCT, TOK_DESTRUCT, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_destruct, m_ignore, m_ignore }, "" }, { MSG_QUIT, TOK_QUIT, 0, MAXPARA, MFLG_SLOW | MFLG_UNREG | MFLG_NOSHUN, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_quit, m_quit, ms_quit, m_quit, m_ignore }, "[:] - Disconnect from the network, optionally with a message." }, { MSG_PART, TOK_PART, 0, MAXPARA, MFLG_SLOW | MFLG_NOSHUN, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_part, ms_part, m_part, m_ignore }, " [:] - Leaves a channel, optionally with a message." }, { MSG_TOPIC, TOK_TOPIC, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_topic, ms_topic, m_topic, m_ignore }, " [:] - Gets or sets the current topic of the channel." }, { MSG_INVITE, TOK_INVITE, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_invite, ms_invite, m_invite, m_ignore }, " - Invites someone to join the channel." }, { MSG_KICK, TOK_KICK, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_kick, ms_kick, m_kick, m_ignore }, " - Kicks someone out of the channel." }, { MSG_WALLOPS, TOK_WALLOPS, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_wallops, mo_wallops, m_ignore }, ": - Sends a message to everyone" }, { MSG_WALLUSERS, TOK_WALLUSERS, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_wallusers, mo_wallusers, m_ignore }, ": - Sends a message to everyone" }, { MSG_DESYNCH, TOK_DESYNCH, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_desynch, m_ignore, m_ignore }, "" }, { MSG_PING, TOK_PING, 0, MAXPARA, MFLG_SLOW | MFLG_NOSHUN, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_ping, ms_ping, mo_ping, m_ignore }, "" }, { MSG_PONG, TOK_PONG, 0, MAXPARA, MFLG_SLOW | MFLG_UNREG | MFLG_NOSHUN, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { mr_pong, m_pong, ms_pong, m_pong, m_ignore }, "" }, { MSG_ERROR, TOK_ERROR, 0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { mr_error, m_ignore, ms_error, m_ignore, m_ignore }, "" }, { MSG_KILL, TOK_KILL, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_kill, mo_kill, m_ignore }, " [:] - Disconnects someone else." }, { MSG_USER, TOK_USER, 0, MAXPARA, MFLG_SLOW | MFLG_NOSHUN, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_user, m_registered, m_ignore, m_registered, m_ignore }, "" }, { MSG_AWAY, TOK_AWAY, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_away, ms_away, m_away, m_ignore }, "[:] - Marks yourself as away, or back." }, { MSG_ISON, TOK_ISON, 0, 1, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_ison, m_ignore, m_ison, m_ignore }, " - Returns whether someone is online." }, { MSG_SERVER, TOK_SERVER, 0, MAXPARA, MFLG_SLOW | MFLG_UNREG | MFLG_NOSHUN, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { mr_server, m_registered, ms_server, m_registered, m_ignore }, "" }, { MSG_SQUIT, TOK_SQUIT, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_squit, mo_squit, m_ignore }, "" }, { MSG_WHOIS, TOK_WHOIS, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_whois, ms_whois, m_whois, m_ignore }, " - Returns information on someone" }, { MSG_WHO, TOK_WHO, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_who, m_ignore, m_who, m_ignore }, " - Returns information on someone" }, { MSG_WHOWAS, TOK_WHOWAS, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_whowas, m_whowas, m_whowas, m_ignore }, " - Returns information on someone who left the network" }, { MSG_LIST, TOK_LIST, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_list, m_ignore, m_list, m_ignore }, "- Lists all channels" }, { MSG_NAMES, TOK_NAMES, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_names, m_names, m_names, m_ignore }, " - Returns a list of users in the channel" }, { MSG_USERHOST, TOK_USERHOST, 0, 1, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_userhost, m_ignore, m_userhost, m_ignore }, "" }, { MSG_USERIP, TOK_USERIP, 0, 1, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_userip, m_ignore, m_userip, m_ignore }, "" }, { MSG_TRACE, TOK_TRACE, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_trace, ms_trace, mo_trace, m_ignore }, " - Returns the list of links between the local server and the other one" }, { MSG_PASS, TOK_PASS, 0, MAXPARA, MFLG_SLOW | MFLG_UNREG | MFLG_NOSHUN, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { mr_pass, m_registered, m_ignore, m_registered, m_ignore }, "" }, { MSG_LUSERS, TOK_LUSERS, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_lusers, ms_lusers, m_lusers, m_ignore }, "- Returns user statistics of the local server and the network" }, { MSG_TIME, TOK_TIME, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_time, m_time, m_time, m_ignore }, "- Returns the current time" }, { MSG_SETTIME, TOK_SETTIME, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_settime, mo_settime, m_ignore }, "" }, { MSG_RPING, TOK_RPING, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_rping, mo_rping, m_ignore }, "" }, { MSG_RPONG, TOK_RPONG, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_ignore, ms_rpong, m_ignore, m_ignore }, "" }, { MSG_OPER, TOK_OPER, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_oper, ms_oper, mo_oper, m_ignore }, "" }, { MSG_CONNECT, TOK_CONNECT, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_connect, mo_connect, m_ignore }, "" }, { MSG_MAP, TOK_MAP, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_map, m_ignore, m_map, m_ignore }, "- Returns the complete tree of the network's server" }, { MSG_VERSION, TOK_VERSION, 0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_version, m_version, ms_version, mo_version, m_ignore }, "- Returns the version of the local server" }, { MSG_STATS, TOK_STATS, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_stats, m_stats, m_stats, m_ignore }, "" }, { MSG_LINKS, TOK_LINKS, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_links, ms_links, m_links, m_ignore }, "- Returns the list of server links in the network" }, { MSG_ADMIN, TOK_ADMIN, 0, MAXPARA, MFLG_SLOW | MFLG_UNREG | MFLG_NOSHUN, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_admin, m_admin, ms_admin, mo_admin, m_ignore }, "- Returns details about the local administrator" }, { MSG_HELP, TOK_HELP, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_help, m_ignore, m_help, m_ignore }, "- Returns this message" }, { MSG_INFO, TOK_INFO, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_info, ms_info, mo_info, m_ignore }, "- Returns information about the local server" }, { MSG_MOTD, TOK_MOTD, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_motd, m_motd, m_motd, m_ignore }, "- Returns the message of the day" }, { MSG_CLOSE, TOK_CLOSE, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, m_ignore, mo_close, m_ignore }, "" }, { MSG_SILENCE, TOK_SILENCE, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_silence, ms_silence, m_silence, m_ignore }, "" }, { MSG_GLINE, TOK_GLINE, 0, MAXPARA, 0, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_gline, ms_gline, mo_gline, m_ignore }, "" }, { MSG_JUPE, TOK_JUPE, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_jupe, mo_jupe, m_ignore }, "" }, { MSG_OPMODE, TOK_OPMODE, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_opmode, mo_opmode, m_ignore }, "" }, { MSG_CLEARMODE, TOK_CLEARMODE, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_clearmode, mo_clearmode, m_ignore }, "" }, { MSG_UPING, TOK_UPING, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_uping, mo_uping, m_ignore }, "" }, { MSG_END_OF_BURST, TOK_END_OF_BURST, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_end_of_burst, m_ignore, m_ignore }, "" }, { MSG_END_OF_BURST_ACK, TOK_END_OF_BURST_ACK, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_end_of_burst_ack, m_ignore, m_ignore }, "" }, { MSG_HASH, TOK_HASH, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_hash, m_hash, m_hash, m_ignore }, "" }, { MSG_REHASH, TOK_REHASH, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_rehash, mo_rehash, m_ignore }, "- Reloads the server's configuration" }, { MSG_RESTART, TOK_RESTART, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, m_ignore, mo_restart, m_ignore }, "- Restarts the server" }, { MSG_DIE, TOK_DIE, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, m_ignore, mo_die, m_ignore }, "- Stops the server" }, { MSG_PROTO, TOK_PROTO, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_proto, m_proto, m_proto, m_proto, m_ignore }, "" }, { MSG_SET, TOK_SET, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, m_ignore, mo_set, m_ignore }, "" }, { MSG_RESET, TOK_RESET, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, m_ignore, mo_reset, m_ignore }, "" }, { MSG_GET, TOK_GET, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, m_ignore, mo_get, m_ignore }, "" }, { MSG_PRIVS, TOK_PRIVS, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_privs, mo_privs, m_ignore }, "" }, { MSG_ACCOUNT, TOK_ACCOUNT, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_account, m_ignore, m_ignore }, "" }, { MSG_ASLL, TOK_ASLL, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_not_oper, ms_asll, mo_asll, m_ignore }, "" }, { MSG_XQUERY, TOK_XQUERY, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_xquery, mo_xquery, m_ignore }, "" }, { MSG_XREPLY, TOK_XREPLY, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_xreply, m_ignore, m_ignore }, "" }, { MSG_CAP, TOK_CAP, 0, MAXPARA, 0, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_cap, m_cap, m_ignore, m_cap, m_ignore }, "" }, { MSG_ISNEF, TOK_ISNEF, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_isnef, m_ignore, m_isnef, m_ignore }, "" }, { MSG_CHECK, TOK_CHECK, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, mo_check, mo_check, m_ignore }, "" }, { MSG_PROTOCTL, TOK_PROTOCTL, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_protoctl, m_ignore, m_protoctl, m_ignore }, "" }, { MSG_SVSIDENT, TOK_SVSIDENT, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_svsident, m_ignore, m_ignore }, "" }, { MSG_SVSINFO, TOK_SVSINFO, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_svsinfo, m_ignore, m_ignore }, "" }, { MSG_SVSQUIT, TOK_SVSQUIT, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_svsquit, m_ignore, m_ignore }, "" }, { MSG_SNO, TOK_SNO, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_sno, m_ignore, m_ignore }, "" }, { MSG_SMO, TOK_SMO, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_smo, m_ignore, m_ignore }, "" }, { MSG_WEBIRC, TOK_WEBIRC, 0, MAXPARA, MFLG_SLOW | MFLG_UNREG | MFLG_NOSHUN, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_webirc, m_registered, m_ignore, m_registered, m_ignore }, "" }, { MSG_MARK, TOK_MARK, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_mark, m_ignore, m_ignore }, "" }, { MSG_SVSNOOP, TOK_SVSNOOP, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_svsnoop, m_ignore, m_ignore }, "" }, { MSG_SVSMODE, TOK_SVSMODE, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_svsmode, m_ignore, m_ignore }, "" }, { MSG_SVSNICK, TOK_SVSNICK, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_svsnick, m_ignore, m_ignore }, "" }, { MSG_SVSPART, TOK_SVSPART, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_svspart, m_ignore, m_ignore }, "" }, { MSG_SVSJOIN, TOK_SVSJOIN, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_svsjoin, m_ignore, m_ignore }, "" }, { MSG_SWHOIS, TOK_SWHOIS, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_swhois, m_ignore, m_ignore }, "" }, { MSG_FAKE, TOK_FAKE, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_fake, m_ignore, m_ignore }, "" }, { MSG_MKPASSWD, TOK_MKPASSWD, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_mkpasswd, m_ignore, m_mkpasswd, m_ignore }, "" }, { MSG_OPERMOTD, TOK_OPERMOTD, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_opermotd, m_opermotd, m_ignore }, "- Returns the current Operator Message of the Day" }, { MSG_RULES, TOK_RULES, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_rules, ms_rules, m_rules, m_ignore }, "- Returns server rules" }, { MSG_SHUN, TOK_SHUN, 0, MAXPARA, 0, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_shun, ms_shun, mo_shun, m_ignore }, "" }, { MSG_SETHOST, TOK_SETHOST, 0, MAXPARA, 0, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_sethost, m_ignore, mo_sethost, m_ignore }, "" }, { MSG_FINGERPRINT, TOK_FINGERPRINT, 0, MAXPARA, 0, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_fingerprint, m_ignore, m_fingerprint, m_ignore }, "" }, { MSG_STARTTLS, TOK_STARTTLS, 0, MAXPARA, 0, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_starttls, m_registered, m_ignore, m_registered, m_ignore }, "" }, { MSG_WALLHOPS, TOK_WALLHOPS, 0, MAXPARA, 0, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_wallhops, ms_wallhops, m_wallhops, m_ignore }, " : - Sends a message to all halfops in the channel", }, { MSG_AUTHENTICATE, TOK_AUTHENTICATE, 0, MAXPARA, MFLG_UNREG | MFLG_NOSHUN, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_authenticate, m_registered, m_ignore, m_registered, m_ignore }, "" }, { MSG_SASL, TOK_SASL, 0, MAXPARA, 0, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_sasl, m_ignore, m_ignore }, "" }, { MSG_REMOVE, TOK_REMOVE, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_remove, mo_remove, m_ignore }, "" }, { MSG_WATCH, TOK_WATCH, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_watch, m_ignore, m_watch, m_ignore }, "" }, { MSG_ZLINE, TOK_ZLINE, 0, MAXPARA, 0, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_zline, ms_zline, mo_zline, m_ignore }, "" }, { MSG_IRCOPS, TOK_IRCOPS, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_ircops, m_ignore, m_ircops, m_ignore }, "" }, { MSG_TEMPSHUN, TOK_TEMPSHUN, 0, MAXPARA, MFLG_SLOW, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_tempshun, mo_tempshun, m_ignore }, "" }, /* This command is an alias for QUIT during the unregistered part of * of the server. This is because someone jumping via a broken web * proxy will send a 'POST' as their first command - which we will * obviously disconnect them immediately for, stopping people abusing * open gateways */ { MSG_POST, TOK_POST, 0, MAXPARA, MFLG_SLOW | MFLG_NOSHUN, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_quit, m_ignore, m_ignore, m_ignore, m_ignore }, "" }, /* These commands are currently unimplimented and have associated MSG/TOK * defines so we will define them here as m_ignore handlers */ { MSG_DNS, TOK_DNS, 0, MAXPARA, MFLG_SLOW | MFLG_IGNORE, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, m_ignore, m_ignore, m_ignore }, "" }, { MSG_SERVLIST, TOK_SERVLIST, 0, MAXPARA, MFLG_SLOW | MFLG_IGNORE, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, m_ignore, m_ignore, m_ignore }, "" }, { MSG_SERVSET, TOK_SERVSET, 0, MAXPARA, MFLG_SLOW | MFLG_IGNORE, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, m_ignore, m_ignore, m_ignore }, "" }, { MSG_SQUERY, TOK_SQUERY, 0, MAXPARA, MFLG_SLOW | MFLG_IGNORE, 0, NULL, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, m_ignore, m_ignore, m_ignore }, "" }, { 0 } }; /** Array of command parameters. */ static char *para[MAXPARA + 2]; /* leave room for prefix and null */ /** Add a message to the lookup trie. * @param[in,out] mtree_p Trie node to insert under. * @param[in] msg_p Message to insert. * @param[in] cmd Text of command to insert. */ void add_msg_element(struct MessageTree *mtree_p, struct Message *msg_p, char *cmd) { struct MessageTree *ntree_p; if (*cmd == '\0') { mtree_p->msg = msg_p; return; } if ((ntree_p = mtree_p->pointers[*cmd & (MAXPTRLEN-1)]) != NULL) { add_msg_element(ntree_p, msg_p, cmd+1); } else { ntree_p = (struct MessageTree *)MyCalloc(sizeof(struct MessageTree), 1); mtree_p->pointers[*cmd & (MAXPTRLEN-1)] = ntree_p; add_msg_element(ntree_p, msg_p, cmd+1); } } /** Remove a message from the lookup trie. * @param[in,out] mtree_p Trie node to remove command from. * @param[in] cmd Text of command to remove. */ struct MessageTree * del_msg_element(struct MessageTree *mtree_p, char *cmd) { int slot = *cmd & (MAXPTRLEN-1); /* Either remove leaf message or from appropriate child. */ if (*cmd == '\0') mtree_p->msg = NULL; else mtree_p->pointers[slot] = del_msg_element(mtree_p->pointers[slot], cmd + 1); /* If current message or any child still exists, keep this node. */ if (mtree_p->msg) return mtree_p; for (slot = 0; slot < MAXPTRLEN; ++slot) if (mtree_p->pointers[slot]) return mtree_p; /* Otherwise, if we're not a root node, free it and return null. */ if (mtree_p != &msg_tree && mtree_p != &tok_tree) MyFree(mtree_p); return NULL; } /** Initialize the message lookup trie with all known commands. */ void initmsgtree(void) { int i; memset(&msg_tree, 0, sizeof(msg_tree)); memset(&tok_tree, 0, sizeof(tok_tree)); for (i = 0; msgtab[i].cmd != NULL ; i++) { add_msg_element(&msg_tree, &msgtab[i], msgtab[i].cmd); add_msg_element(&tok_tree, &msgtab[i], msgtab[i].tok); } } /** Look up a command in the message trie. * @param cmd Text of command to look up. * @param root Root of message trie. * @return Pointer to matching message, or NULL if non exists. */ static struct Message * msg_tree_parse(char *cmd, struct MessageTree *root) { struct MessageTree *mtree; for (mtree = root; mtree; mtree = mtree->pointers[(*cmd++) & (MAXPTRLEN-1)]) { if (*cmd == '\0' && mtree->msg) return mtree->msg; else if (!IsNickChar(*cmd)) return NULL; } return NULL; } /** Registers a service mapping to the pseudocommand handler. * @param[in] map Service mapping to add. * @return Non-zero on success; zero if a command already used the name. */ int register_mapping(struct s_map *map) { struct Message *msg; if (msg_tree_parse(map->command, &msg_tree)) return 0; msg = (struct Message *)MyMalloc(sizeof(struct Message)); msg->cmd = map->command; msg->tok = map->command; msg->count = 0; msg->parameters = 2; msg->flags = MFLG_EXTRA; if (!(map->flags & SMAP_FAST)) msg->flags |= MFLG_SLOW; msg->bytes = 0; msg->extra = map; msg->handlers[UNREGISTERED_HANDLER] = m_ignore; msg->handlers[CLIENT_HANDLER] = m_pseudo; msg->handlers[SERVER_HANDLER] = m_ignore; msg->handlers[OPER_HANDLER] = m_pseudo; msg->handlers[SERVICE_HANDLER] = m_ignore; add_msg_element(&msg_tree, msg, msg->cmd); map->msg = msg; return 1; } /** Removes a service mapping. * @param[in] map Service mapping to remove. * @return Non-zero on success; zero if no command used the name. */ int unregister_mapping(struct s_map *map) { if (!msg_tree_parse(map->command, &msg_tree)) { /* This simply should never happen. */ assert(0); return 0; } del_msg_element(&msg_tree, map->msg->cmd); map->msg->extra = NULL; MyFree(map->msg); map->msg = NULL; return 1; } /** Parse a line of data from a user. * NOTE: parse_*() should not be called recursively by any other * functions! * @param[in] cptr Client that sent the data. * @param[in] buffer Start of input line. * @param[in] bufend End of input line. * @return 0 on success, -1 on parse error, or CPTR_KILLED if message * handler returns it. */ int parse_client(struct Client *cptr, char *buffer, char *bufend) { struct Client* from = cptr; char* ch; char* s; int i; int paramcount; int isshun = 0; int lagmin = -1; int lagfactor = -1; struct Message* mptr; MessageHandler handler = 0; Debug((DEBUG_DEBUG, "Client Parsing: %s", buffer)); if (IsDead(cptr)) return 0; para[0] = cli_name(from); for (ch = buffer; *ch == ' '; ch++); /* Eat leading spaces */ if (*ch == ':') /* Is any client doing this ? */ { for (++ch; *ch && *ch != ' '; ++ch) ; /* Ignore sender prefix from client */ while (*ch == ' ') ch++; /* Advance to command */ } if (*ch == '\0') { ServerStats->is_empt++; Debug((DEBUG_NOTICE, "Empty message from host %s:%s", cli_name(cptr), cli_name(from))); return (-1); } if ((s = strchr(ch, ' '))) *s++ = '\0'; if (IsRegistered(cptr)) { if (*(cli_user(cptr)->username) && *(cli_user(cptr)->host)) { if (IsTempShun(cptr) || shun_lookup(cptr, 0)) isshun = 1; } } if ((mptr = msg_tree_parse(ch, &msg_tree)) == NULL) { /* * Note: Give error message *only* to recognized * persons. It's a nightmare situation to have * two programs sending "Unknown command"'s or * equivalent to each other at full blast.... * If it has got to person state, it at least * seems to be well behaving. Perhaps this message * should never be generated, though... --msa * Hm, when is the buffer empty -- if a command * code has been found ?? -Armin */ if (buffer[0] != '\0' && !isshun) { if (IsUser(from)) send_reply(from, ERR_UNKNOWNCOMMAND, ch); Debug((DEBUG_ERROR, "Unknown (%s) from %s", ch, get_client_name(cptr, HIDE_IP))); } ServerStats->is_unco++; return (-1); } if (isshun && !(mptr->flags & MFLG_NOSHUN)) return 0; paramcount = mptr->parameters; i = bufend - ((s) ? s : ch); mptr->bytes += i; lagmin = get_lag_min(cptr); lagfactor = get_lag_factor(cptr); if (lagmin < 0) lagmin = 2; if (lagfactor < 0) lagfactor = 120; if (((mptr->flags & MFLG_SLOW) || !IsAnOper(cptr)) && lagfactor > 0) cli_since(cptr) += (lagmin + i / lagfactor); /* * Allow only 1 msg per 2 seconds * (on average) to prevent dumping. * to keep the response rate up, * bursts of up to 5 msgs are allowed * -SRB */ /* * Must the following loop really be so devious? On * surface it splits the message to parameters from * blank spaces. But, if paramcount has been reached, * the rest of the message goes into this last parameter * (about same effect as ":" has...) --msa */ /* Note initially true: s==NULL || *(s-1) == '\0' !! */ if (mptr->flags & MFLG_EXTRA) { /* This is a horrid kludge to avoid changing the command handler * argument list. */ para[1] = (char*)mptr->extra; i = 1; } else { i = 0; } if (s) { if (paramcount > MAXPARA) paramcount = MAXPARA; for (;;) { /* * Never "FRANCE " again!! ;-) Clean * out *all* blanks.. --msa */ while (*s == ' ') *s++ = '\0'; if (*s == '\0') break; if (*s == ':') { /* * The rest is single parameter--can * include blanks also. */ para[++i] = s + 1; break; } para[++i] = s; if (i >= paramcount) break; for (; *s != ' ' && *s; s++); } } para[++i] = NULL; ++mptr->count; handler = mptr->handlers[cli_handler(cptr)]; assert(0 != handler); if (!feature_bool(FEAT_IDLE_FROM_MSG) && IsUser(cptr) && handler != m_ping && handler != m_ignore) cli_user(from)->last = CurrentTime; return (*handler) (cptr, from, i, para); } /** Parse a line of data from a server. * @param[in] cptr Client that sent the data. * @param[in] buffer Start of input line. * @param[in] bufend End of input line. * @return 0 on success, -1 on parse error, or CPTR_KILLED if message * handler returns it. */ int parse_server(struct Client *cptr, char *buffer, char *bufend) { struct Client* from = cptr; char* ch = buffer; char* s; int len; int i; int numeric = 0; int paramcount; struct Message* mptr; Debug((DEBUG_DEBUG, "Server Parsing: %s", buffer)); if (IsDead(cptr)) return 0; para[0] = cli_name(from); /* * A server ALWAYS sends a prefix. When it starts with a ':' it's the * protocol 9 prefix: a nick or a server name. Otherwise it's a numeric * nick or server */ if (*ch == ':') { /* Let para[0] point to the name of the sender */ para[0] = ch + 1; if (!(ch = strchr(ch, ' '))) return -1; *ch++ = '\0'; /* And let `from' point to its client structure, opps.. a server is _also_ a client --Nem */ from = FindClient(para[0]); /* * If the client corresponding to the * prefix is not found. We must ignore it, * it is simply a lagged message traveling * upstream a SQUIT that removed the client * --Run */ if (from == NULL) { Debug((DEBUG_NOTICE, "Unknown prefix (%s)(%s) from (%s)", para[0], buffer, cli_name(cptr))); ++ServerStats->is_unpf; while (*ch == ' ') ch++; /* * However, the only thing that MUST be * allowed to travel upstream against an * squit, is an SQUIT itself (the timestamp * protects us from being used wrong) */ if (ch[1] == 'Q') { para[0] = cli_name(cptr); from = cptr; } else return 0; } else if (cli_from(from) != cptr) { ++ServerStats->is_wrdi; Debug((DEBUG_NOTICE, "Fake direction: Message (%s) coming from (%s)", buffer, cli_name(cptr))); return 0; } } else { char numeric_prefix[6]; int i; for (i = 0; i < 5; ++i) { if ('\0' == ch[i] || ' ' == (numeric_prefix[i] = ch[i])) { break; } } numeric_prefix[i] = '\0'; /* * We got a numeric nick as prefix * 1 or 2 character prefixes are from servers * 3 or 5 chars are from clients */ if (0 == i) { protocol_violation(cptr,"Missing Prefix"); from = cptr; } else if (' ' == ch[1] || ' ' == ch[2]) from = FindNServer(numeric_prefix); else from = findNUser(numeric_prefix); do { ++ch; } while (*ch != ' ' && *ch); /* * If the client corresponding to the * prefix is not found. We must ignore it, * it is simply a lagged message traveling * upstream a SQUIT that removed the client * --Run * There turned out to be other reasons that * a prefix is unknown, needing an upstream * KILL. Also, next to an SQUIT we better * allow a KILL to pass too. * --Run */ if (from == NULL) { ServerStats->is_unpf++; while (*ch == ' ') ch++; if (*ch == 'N' && (ch[1] == ' ' || ch[1] == 'I')) /* Only sent a KILL for a nick change */ { struct Client *server; /* Kill the unknown numeric prefix upstream if * it's server still exists: */ if ((server = FindNServer(numeric_prefix)) && cli_from(server) == cptr) sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (Unknown numeric nick)", numeric_prefix, cli_name(&me)); } /* * Things that must be allowed to travel * upstream against an squit: */ if (ch[1] == 'Q' || (*ch == 'D' && ch[1] == ' ') || (*ch == 'K' && ch[2] == 'L')) from = cptr; else return 0; } /* Let para[0] point to the name of the sender */ para[0] = cli_name(from); if (cli_from(from) != cptr) { ServerStats->is_wrdi++; Debug((DEBUG_NOTICE, "Fake direction: Message (%s) coming from (%s)", buffer, cli_name(cptr))); return 0; } } while (*ch == ' ') ch++; if (*ch == '\0') { ServerStats->is_empt++; Debug((DEBUG_NOTICE, "Empty message from host %s:%s", cli_name(cptr), cli_name(from))); return (-1); } /* * Extract the command code from the packet. Point s to the end * of the command code and calculate the length using pointer * arithmetic. Note: only need length for numerics and *all* * numerics must have parameters and thus a space after the command * code. -avalon */ s = strchr(ch, ' '); /* s -> End of the command code */ len = (s) ? (s - ch) : 0; if (len == 3 && IsDigit(*ch)) { numeric = (*ch - '0') * 100 + (*(ch + 1) - '0') * 10 + (*(ch + 2) - '0'); paramcount = 2; /* destination, and the rest of it */ ServerStats->is_num++; mptr = NULL; /* Init. to avoid stupid compiler warning :/ */ } else { if (s) *s++ = '\0'; /* Version Receive Send * 2.9 Long Long * 2.10.0 Tkn/Long Long * 2.10.10 Tkn/Long Tkn * 2.10.20 Tkn Tkn * * Clients/unreg servers always receive/ * send long commands -record * * And for the record, this trie parser really does not care. - Dianora */ mptr = msg_tree_parse(ch, &tok_tree); if (mptr == NULL) { mptr = msg_tree_parse(ch, &msg_tree); } if (mptr == NULL) { /* * Note: Give error message *only* to recognized * persons. It's a nightmare situation to have * two programs sending "Unknown command"'s or * equivalent to each other at full blast.... * If it has got to person state, it at least * seems to be well behaving. Perhaps this message * should never be generated, though... --msa * Hm, when is the buffer empty -- if a command * code has been found ?? -Armin */ #ifdef DEBUGMODE if (buffer[0] != '\0') { Debug((DEBUG_ERROR, "Unknown (%s) from %s", ch, get_client_name(cptr, HIDE_IP))); } #endif ServerStats->is_unco++; return (-1); } paramcount = mptr->parameters; i = bufend - ((s) ? s : ch); mptr->bytes += i; } /* * Must the following loop really be so devious? On * surface it splits the message to parameters from * blank spaces. But, if paramcount has been reached, * the rest of the message goes into this last parameter * (about same effect as ":" has...) --msa */ /* Note initially true: s==NULL || *(s-1) == '\0' !! */ i = 0; if (s) { if (paramcount > MAXPARA) paramcount = MAXPARA; for (;;) { /* * Never "FRANCE " again!! ;-) Clean * out *all* blanks.. --msa */ while (*s == ' ') *s++ = '\0'; if (*s == '\0') break; if (*s == ':') { /* * The rest is single parameter--can * include blanks also. */ if (numeric) para[++i] = s; /* preserve the colon to make do_numeric happy */ else para[++i] = s + 1; break; } para[++i] = s; if (i >= paramcount) break; for (; *s != ' ' && *s; s++); } } para[++i] = NULL; if (numeric) return (do_numeric(numeric, (*buffer != ':'), cptr, from, i, para)); mptr->count++; return (*mptr->handlers[cli_handler(cptr)]) (cptr, from, i, para); }