/*
* 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.
*
* $Id: parse.c,v 1.30.2.9 2004/01/11 00:23:32 isomer Exp $
*/
#include "config.h"
#include "parse.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_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 "struct.h"
#include "sys.h"
#include "whocmds.h"
#include "whowas.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
struct MessageTree {
char *final;
struct Message *msg;
struct MessageTree *pointers[26];
};
struct Message msgtab[] = {
{
MSG_PRIVATE,
TOK_PRIVATE,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_privmsg, ms_privmsg, mo_privmsg, m_ignore }
},
{
MSG_NICK,
TOK_NICK,
0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_nick, m_nick, ms_nick, m_nick, m_ignore }
},
{
MSG_NOTICE,
TOK_NOTICE,
0, MAXPARA, MFLG_SLOW | MFLG_IGNORE, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_ignore, m_notice, ms_notice, mo_notice, m_ignore }
},
{
MSG_WALLCHOPS,
TOK_WALLCHOPS,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_wallchops, ms_wallchops, m_wallchops, m_ignore }
},
{
MSG_WALLVOICES,
TOK_WALLVOICES,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_wallvoices, ms_wallvoices, m_wallvoices, m_ignore }
},
{
MSG_CPRIVMSG,
TOK_CPRIVMSG,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_cprivmsg, m_ignore, m_cprivmsg, m_ignore }
},
{
MSG_CNOTICE,
TOK_CNOTICE,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_cnotice, m_ignore, m_cnotice, m_ignore }
},
{
MSG_JOIN,
TOK_JOIN,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_join, ms_join, m_join, m_ignore }
},
{
MSG_MODE,
TOK_MODE,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_mode, ms_mode, m_mode, m_ignore }
},
{
MSG_BURST,
TOK_BURST,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_ignore, m_ignore, ms_burst, m_ignore, m_ignore }
},
{
MSG_CREATE,
TOK_CREATE,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_ignore, m_ignore, ms_create, m_ignore, m_ignore }
},
{
MSG_DESTRUCT,
TOK_DESTRUCT,
0, MAXPARA, MFLG_SLOW, 0,
/* 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, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_quit, m_quit, ms_quit, m_quit, m_ignore }
},
{
MSG_PART,
TOK_PART,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_part, ms_part, m_part, m_ignore }
},
{
MSG_TOPIC,
TOK_TOPIC,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_topic, ms_topic, m_topic, m_ignore }
},
{
MSG_INVITE,
TOK_INVITE,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_invite, ms_invite, m_invite, m_ignore }
},
{
MSG_KICK,
TOK_KICK,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_kick, ms_kick, m_kick, m_ignore }
},
{
MSG_WALLOPS,
TOK_WALLOPS,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_not_oper, ms_wallops, mo_wallops, m_ignore }
},
{
MSG_WALLUSERS,
TOK_WALLUSERS,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_not_oper, ms_wallusers, mo_wallusers, m_ignore }
},
{
MSG_DESYNCH,
TOK_DESYNCH,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_ignore, m_ignore, ms_desynch, m_ignore, m_ignore }
},
{
MSG_PING,
TOK_PING,
0, MAXPARA, MFLG_SLOW, 0,
/* 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, 0,
/* 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,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ mr_error, m_ignore, ms_error, m_ignore, m_ignore }
},
{
MSG_KILL,
TOK_KILL,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_not_oper, ms_kill, mo_kill, m_ignore }
},
{
MSG_USER,
TOK_USER,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_user, m_registered, m_ignore, m_registered, m_ignore }
},
{
MSG_AWAY,
TOK_AWAY,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_away, ms_away, m_away, m_ignore }
},
{
MSG_ISON,
TOK_ISON,
0, 1, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_ison, m_ignore, m_ison, m_ignore }
},
{
MSG_SERVER,
TOK_SERVER,
0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ mr_server, m_registered, ms_server, m_registered, m_ignore }
},
{
MSG_SQUIT,
TOK_SQUIT,
0, MAXPARA, MFLG_SLOW, 0,
/* 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,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_whois, ms_whois, m_whois, m_ignore }
},
{
MSG_WHO,
TOK_WHO,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_who, m_ignore, m_who, m_ignore }
},
{
MSG_WHOWAS,
TOK_WHOWAS,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_whowas, m_whowas, m_whowas, m_ignore }
},
{
MSG_LIST,
TOK_LIST,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_list, m_ignore, m_list, m_ignore }
},
{
MSG_NAMES,
TOK_NAMES,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_names, ms_names, m_names, m_ignore }
},
{
MSG_USERHOST,
TOK_USERHOST,
0, 1, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_userhost, m_ignore, m_userhost, m_ignore }
},
{
MSG_USERIP,
TOK_USERIP,
0, 1, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_userip, m_ignore, m_userip, m_ignore }
},
{
MSG_TRACE,
TOK_TRACE,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_trace, ms_trace, mo_trace, m_ignore }
},
{
MSG_PASS,
TOK_PASS,
0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ mr_pass, m_registered, m_ignore, m_registered, m_ignore }
},
{
MSG_LUSERS,
TOK_LUSERS,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_lusers, ms_lusers, m_lusers, m_ignore }
},
{
MSG_TIME,
TOK_TIME,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_time, m_time, m_time, m_ignore }
},
{
MSG_SETTIME,
TOK_SETTIME,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_ignore, ms_settime, mo_settime, m_ignore }
},
{
MSG_RPING,
TOK_RPING,
0, MAXPARA, MFLG_SLOW, 0,
/* 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,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_ignore, ms_rpong, m_ignore, m_ignore }
},
{
MSG_OPER,
TOK_OPER,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_oper, ms_oper, mo_oper, m_ignore }
},
{
MSG_CONNECT,
TOK_CONNECT,
0, MAXPARA, MFLG_SLOW, 0,
/* 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,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_map, m_ignore, m_map, m_ignore }
},
{
MSG_VERSION,
TOK_VERSION,
0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_version, m_version, ms_version, mo_version, m_ignore }
},
{
MSG_STATS,
TOK_STATS,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_stats, m_stats, m_stats, m_ignore }
},
{
MSG_LINKS,
TOK_LINKS,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_links, ms_links, m_links, m_ignore }
},
{
MSG_ADMIN,
TOK_ADMIN,
0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_admin, m_admin, ms_admin, mo_admin, m_ignore }
},
{
MSG_HELP,
TOK_HELP,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_help, m_ignore, m_help, m_ignore }
},
{
MSG_INFO,
TOK_INFO,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_info, ms_info, mo_info, m_ignore }
},
{
MSG_MOTD,
TOK_MOTD,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_motd, m_motd, m_motd, m_ignore }
},
{
MSG_CLOSE,
TOK_CLOSE,
0, MAXPARA, MFLG_SLOW, 0,
/* 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,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_silence, ms_silence, m_silence, m_ignore }
},
{
MSG_GLINE,
TOK_GLINE,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_gline, ms_gline, mo_gline, m_ignore }
},
{
MSG_JUPE,
TOK_JUPE,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_jupe, ms_jupe, mo_jupe, m_ignore }
},
{
MSG_OPMODE,
TOK_OPMODE,
0, MAXPARA, MFLG_SLOW, 0,
/* 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,
/* 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,
/* 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,
/* 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, 1, 0,
/* 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,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_hash, m_hash, m_hash, m_ignore }
},
{
MSG_DNS,
TOK_DNS,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_ignore, m_ignore, m_dns, m_ignore }
},
{
MSG_REHASH,
TOK_REHASH,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_not_oper, m_ignore, mo_rehash, m_ignore }
},
{
MSG_RESTART,
TOK_RESTART,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_not_oper, m_ignore, mo_restart, m_ignore }
},
{
MSG_DIE,
TOK_DIE,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_not_oper, m_ignore, mo_die, m_ignore }
},
{
MSG_PROTO,
TOK_PROTO,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_proto, m_proto, m_proto, m_proto, m_ignore }
},
{
MSG_SET,
TOK_SET,
0, MAXPARA, MFLG_SLOW, 0,
/* 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,
/* 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,
/* 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,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_unregistered, m_not_oper, m_ignore, mo_privs, m_ignore }
},
{
MSG_ACCOUNT,
TOK_ACCOUNT,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_ignore, m_ignore, ms_account, m_ignore, m_ignore }
},
{
MSG_ASLL,
TOK_ASLL,
0, MAXPARA, MFLG_SLOW, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_ignore, m_not_oper, ms_asll, mo_asll, 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, 0,
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_quit, m_ignore, m_ignore, m_ignore, m_ignore }
},
{ 0 }
};
static char *para[MAXPARA + 2]; /* leave room for prefix and null */
/*
* Message Tree stuff mostly written by orabidoo, with changes by Dianora.
* Adapted to Undernet, adding token support, etc by comstud 10/06/97
*/
static struct MessageTree msg_tree_cmd;
static struct MessageTree msg_tree_tok;
/*
* Guts of making the token tree...
*/
static struct Message **do_msg_tree_tok(struct MessageTree *mtree, char *prefix,
struct Message **mptr)
{
char newprefix[64]; /* Must be longer than every command name */
int c, c2, lp;
struct MessageTree *mtree1;
lp = strlen(prefix);
if (!lp || !strncmp((*mptr)->tok, prefix, lp))
{
if (!mptr[1] || (lp && strncmp(mptr[1]->tok, prefix, lp)))
{
/* last command in the message struct or last command in this prefix */
mtree->final = (*mptr)->tok + lp;
mtree->msg = *mptr;
for (c = 0; c < 26; ++c)
mtree->pointers[c] = NULL;
return mptr + 1;
}
/* command in this prefix */
if (0 == ircd_strcmp((*mptr)->tok, prefix))
{
mtree->final = "";
mtree->msg = *mptr++;
}
else
mtree->final = NULL;
for (c = 'A'; c <= 'Z'; ++c)
{
if ((*mptr)->tok[lp] == c)
{
mtree1 = (struct MessageTree *)MyMalloc(sizeof(struct MessageTree));
mtree1->final = NULL;
mtree->pointers[c - 'A'] = mtree1;
strcpy(newprefix, prefix);
newprefix[lp] = c;
newprefix[lp + 1] = '\0';
mptr = do_msg_tree_tok(mtree1, newprefix, mptr);
if (!*mptr || strncmp((*mptr)->tok, prefix, lp))
{
for (c2 = c + 1 - 'A'; c2 < 26; ++c2)
mtree->pointers[c2] = NULL;
return mptr;
}
}
else
mtree->pointers[c - 'A'] = NULL;
}
return mptr;
}
/*
* XXX - should never be here, quick hack, this can be done better
*/
assert(0);
exit(1);
}
/*
* Guts of making the command tree...
*/
static struct Message *do_msg_tree_cmd(struct MessageTree *mtree, char *prefix,
struct Message *mptr)
{
char newprefix[64]; /* Must be longer than every command name */
int c, c2, lp;
struct MessageTree *mtree1;
lp = strlen(prefix);
if (!lp || !strncmp(mptr->cmd, prefix, lp))
{
if (!mptr[1].cmd || (lp && strncmp(mptr[1].cmd, prefix, lp)))
{
/* last command in the message struct or last command in this prefix */
mtree->final = mptr->cmd + lp;
mtree->msg = mptr;
for (c = 0; c < 26; ++c)
mtree->pointers[c] = NULL;
return mptr + 1;
}
/* command in this prefix */
if (0 == ircd_strcmp(mptr->cmd, prefix))
{
mtree->final = "";
mtree->msg = mptr++;
}
else
mtree->final = NULL;
for (c = 'A'; c <= 'Z'; ++c)
{
if (mptr->cmd[lp] == c)
{
mtree1 = (struct MessageTree *)MyMalloc(sizeof(struct MessageTree));
mtree1->final = NULL;
mtree->pointers[c - 'A'] = mtree1;
strcpy(newprefix, prefix);
newprefix[lp] = c;
newprefix[lp + 1] = '\0';
mptr = do_msg_tree_cmd(mtree1, newprefix, mptr);
if (!mptr->cmd || strncmp(mptr->cmd, prefix, lp))
{
for (c2 = c + 1 - 'A'; c2 < 26; ++c2)
mtree->pointers[c2] = NULL;
return mptr;
}
}
else
mtree->pointers[c - 'A'] = NULL;
}
return mptr;
}
/*
* This should never happen
*/
assert(0);
exit(1);
}
static int mcmdcmp(const struct Message *m1, const struct Message *m2)
{
return strcmp(m1->cmd, m2->cmd);
}
static int mtokcmp(const struct Message **m1, const struct Message **m2)
{
return strcmp((*m1)->tok, (*m2)->tok);
}
/*
* Sort the command names.
* Create table of pointers into msgtab for tokens.
* Create trees for ->cmd and ->tok and free the token pointers.
*/
void initmsgtree(void)
{
int i;
struct Message *msg = msgtab;
int ii;
struct Message **msgtab_tok;
struct Message **msgtok;
for (i = 0; msg->cmd; ++i, ++msg)
continue;
qsort(msgtab, i, sizeof(struct Message),
(int (*)(const void *, const void *))mcmdcmp);
msgtab_tok = (struct Message **)MyMalloc((i + 1) * sizeof(struct Message *));
for (ii = 0; ii < i; ++ii)
msgtab_tok[ii] = msgtab + ii;
msgtab_tok[i] = NULL; /* Needed by `do_msg_tree_tok' */
qsort(msgtab_tok, i, sizeof(struct Message *),
(int (*)(const void *, const void *))mtokcmp);
msg = do_msg_tree_cmd(&msg_tree_cmd, "", msgtab);
msgtok = do_msg_tree_tok(&msg_tree_tok, "", msgtab_tok);
MyFree(msgtab_tok);
}
/*
* Generic tree parser which works for both commands and tokens.
* Optimized by Run.
*/
static struct Message *msg_tree_parse(char *cmd, struct MessageTree *root)
{
struct MessageTree *mtree;
unsigned char r = (0xdf & (unsigned char)*cmd) - 'A';
if (r > 25 || !(mtree = root->pointers[r]))
return NULL;
for (;;)
{
r = 0xdf & (unsigned char)*++cmd;
if (mtree->final && *mtree->final == r)
return mtree->msg;
if ((r -= 'A') > 25 || !(mtree = mtree->pointers[r]))
return NULL;
}
}
/*
* This one is identical to the one above, but it is slower because it
* makes sure that `cmd' matches the _full_ command, exactly.
* This is to avoid confusion with commands like /quake on clients
* that send unknown commands directly to the server.
*/
static struct Message *msg_tree_parse_client(char *cmd,
struct MessageTree *root)
{
struct MessageTree *mtree;
unsigned char q = (0xdf & (unsigned char)*cmd) - 'A';
if (q > 25 || !(mtree = root->pointers[q]))
return NULL;
for (;;)
{
q = 0xdf & (unsigned char)*++cmd;
if (mtree->final && 0 == ircd_strcmp(mtree->final, cmd))
return mtree->msg;
if ((q -= 'A') > 25 || !(mtree = mtree->pointers[q]))
return NULL;
}
}
/*
* parse a buffer.
*
* NOTE: parse_*() should not be called recusively by any other fucntions!
*/
int parse_client(struct Client *cptr, char *buffer, char *bufend)
{
struct Client* from = cptr;
char* ch;
char* s;
int i;
int paramcount;
int noprefix = 0;
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 */
}
else
noprefix = 1;
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';
/*
* This is a client/unregistered entity.
* Check long command list only.
*/
if (!(mptr = msg_tree_parse_client(ch, &msg_tree_cmd)))
{
/*
* 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')
{
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);
}
paramcount = mptr->parameters;
i = bufend - ((s) ? s : ch);
mptr->bytes += i;
if ((mptr->flags & MFLG_SLOW))
cli_since(cptr) += (2 + i / 120);
/*
* 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' !! */
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);
}
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 travelling
* upstream a SQUIT that removed the client
* --Run
*/
if (!from)
{
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 travelling
* 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)
{
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
*/
/*
* This is a server. Check the token command list.
* -record!jegelhof@cloud9.net
*/
mptr = msg_tree_parse(ch, &msg_tree_tok);
#if 1 /* for 2.10.0/2.10.10 */
/*
* This code supports 2.9 and 2.10.0 sending long commands.
* It makes more calls to ircd_strcmp() than the above
* so it will be somewhat slower.
*/
if (!mptr)
mptr = msg_tree_parse(ch, &msg_tree_cmd);
#endif /* 1 */
if (!mptr)
{
/*
* 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);
}
syntax highlighted by Code2HTML, v. 0.9.1