aboutsummaryrefslogtreecommitdiffstats
path: root/src/command_parse.cpp
blob: 7a26f209ba0d537e949fecec536270cbbf95b543 (about) (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888 } /* Comment */
.highlight .err { color: #A61717; background-color: #E3D2D2 } /* Error */
.highlight .k { color: #080; font-weight: bold } /* Keyword */
.highlight .ch { color: #888 } /* Comment.Hashbang */
.highlight .cm { color: #888 } /* Comment.Multiline */
.highlight .cp { color: #C00; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888 } /* Comment.Single */
.highlight .cs { color: #C00; font-weight: bold; background-color: #FFF0F0 } /* Comment.Special */
.highlight .gd { color: #000; background-color: #FDD } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #A00 } /* Generic.Error */
.highlight .gh { color: #333 } /* Generic.Heading */
.highlight .gi { color: #000; background-color: #DFD } /* Generic.Inserted */
.highlight .go { color: #888 } /* Generic.Output */
.highlight .gp { color: #555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666 } /* Generic.Subheading */
.highlight .gt { color: #A00 } /* Generic.Traceback */
.highlight .kc { color: #080; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #080; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #080; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #080 } /* Keyword.Pseudo */
.highlight .kr { color: #080; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #00D; font-weight: bold } /* Literal.Number */
.highlight .s { color: #D20; background-color: #FFF0F0 } /* Literal.String */
.highlight .na { color: #369 } /* Name.Attribute */
.highlight .nb { color: #038 } /* Name.Builtin */
.highlight .nc { color: #B06; font-weight: bold } /* Name.Class */
.highlight .no { color: #036; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555 } /* Name.Decorator */
.highlight .ne { color: #B06; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #06B; font-weight: bold } /* Name.Function */
.highlight .nl { color: #369; font-style: italic } /* Name.Label */
.highlight .nn { color: #B06; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #369; font-weight: bold } /* Name.Property */
.highlight .nt { color: #B06; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #369 } /* Name.Variable */
.highlight .ow { color: #080 } /* Operator.Word */
.highlight .w { color: #BBB } /* Text.Whitespace */
.highlight .mb { color: #00D; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #00D; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #00D; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #00D; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #00D; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #D20; background-color: #FFF0F0 } /* Literal.String.Affix */
.highlight .sb { color: #D20; background-color: #FFF0F0 } /* Literal.String.Backtick */
.highlight .sc { color: #D20; background-color: #FFF0F0 } /* Literal.String.Char */
.highlight .dl { color: #D20; background-color: #FFF0F0 } /* Literal.String.Delimiter */
.highlight .sd { color: #D20; background-color: #FFF0F0 } /* Literal.String.Doc */
.highlight .s2 { color: #D20; background-color: #FFF0F0 } /* Literal.String.Double */
.highlight .se { color: #04D; background-color: #FFF0F0 } /* Literal.String.Escape */
.highlight .sh { color: #D20; background-color: #FFF0F0 } /* Literal.String.Heredoc */
.highlight .si { color: #33B; background-color: #FFF0F0 } /* Literal.String.Interpol */
.highlight .sx { color: #2B2; background-color: #F0FFF0 } /* Literal.String.Other */
.highlight .sr { color: #080; background-color: #FFF0FF } /* Literal.String.Regex */
.highlight .s1 { color: #D20; background-color: #FFF0F0 } /* Literal.String.Single */
.highlight .ss { color: #A60; background-color: #FFF0F0 } /* Literal.String.Symbol */
.highlight .bp { color: #038 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #06B; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #369 } /* Name.Variable.Class */
.highlight .vg { color: #D70 } /* Name.Variable.Global */
.highlight .vi { color: #33B } /* Name.Variable.Instance */
.highlight .vm { color: #369 } /* Name.Variable.Magic */
.highlight .il { color: #00D; font-weight: bold } /* Literal.Number.Integer.Long */
/*
 * Copyright (c) 2005 Atheme Development Group
 * Rights to this code are as documented in doc/LICENSE.
 *
 * This file contains code for the Memoserv FORWARD function
 *
 * $Id: forward.c 3335 2005-10-31 03:48:14Z nenolod $
 */

#include "atheme.h"

DECLARE_MODULE_V1
(
	"memoserv/forward", FALSE, _modinit, _moddeinit,
	"$Id: forward.c 3335 2005-10-31 03:48:14Z nenolod $",
	"Atheme Development Group <http://www.atheme.org>"
);

static void ms_cmd_forward(char *origin);

command_t ms_forward = { "FORWARD", "Forwards a memo",
                        AC_NONE, ms_cmd_forward };

list_t *ms_cmdtree;
list_t *ms_helptree;

void _modinit(module_t *m)
{
	ms_cmdtree = module_locate_symbol("memoserv/main", "ms_cmdtree");
        command_add(&ms_forward, ms_cmdtree);
	
	ms_helptree = module_locate_symbol("memoserv/main", "ms_helptree");
	help_addentry(ms_helptree, "forward", "help/memoserv/forward", NULL);
}

void _moddeinit()
{
	command_delete(&ms_forward, ms_cmdtree);
	help_delentry(ms_helptree, "FORWARD");
}

static void ms_cmd_forward(char *origin)
{
	/* Misc structs etc */
	user_t *u = user_find(origin), *tu;
	myuser_t *mu = u->myuser, *tmu;
	mymemo_t *memo, *newmemo;
	node_t *n, *temp;
	uint8_t i = 1, memonum = 0;
	
	/* Grab args */
	char *target = strtok(NULL, " ");
	char *arg = strtok(NULL, " ");
	
	/* Arg validator */
	if (!target || !arg)
	{
		notice(memosvs.nick, origin, 
			"Insufficient parameters specified for \2FORWARD\2.");
		
		notice(memosvs.nick, origin, 
			"Syntax: FORWARD <account> <memo number>");
		
		return;
	}
	else
		memonum = atoi(arg);
	
	/* user logged in? */
	if (mu == NULL)
	{
		notice(memosvs.nick, origin, "You are not logged in.");
		return;
	}
	
	if (mu->flags & MU_WAITAUTH)
	{
		notice(memosvs.nick, origin, "You need to verify your email address before you may send memos.");
		return;
	}

	/* Check to see if any memos */
	if (!mu->memos.count)
	{
		notice(memosvs.nick, origin, "You have no memos to forward.");
		return;
	}

	/* Check to see if target user exists */
	if (!(tmu = myuser_find(target)))
	{
		notice(memosvs.nick, origin, "%s is not registered.", target);
		return;
	}
	
	/* Make sure target isn't sender */
	if (mu == tmu)
	{
		notice(memosvs.nick, origin, "You cannot send yourself a memo.");
		return;
	}
	
	/* Make sure arg is an int */
	if (!memonum)
	{
		notice(memosvs.nick, origin, "Invalid message index.");
		return;
	}
	
	/* check if targetuser has nomemo set */
	if (tmu->flags & MU_NOMEMO)
	{
		notice(memosvs.nick, origin,
			"\2%s\2
/*       +------------------------------------+
 *       | Inspire Internet Relay Chat Daemon |
 *       +------------------------------------+
 *
 *  InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
 *                       E-mail:
 *                <brain@chatspike.net>
 *                <Craig@chatspike.net>
 *
 * Written by Craig Edwards, Craig McLure, and others.
 * This program is free but copyrighted software; see
 *            the file COPYING for details.
 *
 * ---------------------------------------------------
 */

#include "inspircd_config.h"
#include "inspircd.h"
#include "configreader.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/utsname.h>
#include <time.h>
#include <string>
#include <sstream>
#include <vector>
#include <sched.h>
#ifdef THREADED_DNS
#include <pthread.h>
#endif
#include "users.h"
#include "globals.h"
#include "modules.h"
#include "dynamic.h"
#include "wildcard.h"
#include "message.h"
#include "mode.h"
#include "commands.h"
#include "xline.h"
#include "inspstring.h"
#include "dnsqueue.h"
#include "helperfuncs.h"
#include "hashcomp.h"
#include "socketengine.h"
#include "userprocess.h"
#include "socket.h"
#include "dns.h"
#include "typedefs.h"
#include "command_parse.h"
#include "ctables.h"

#ifdef GCC3
#define nspace __gnu_cxx
#else
#define nspace std
#endif


extern InspIRCd* ServerInstance;

extern std::vector<Module*> modules;
extern std::vector<ircd_module*> factory;
extern std::vector<InspSocket*> module_sockets;
extern std::vector<userrec*> local_users;

extern int MODCOUNT;
extern InspSocket* socket_ref[MAX_DESCRIPTORS];
extern time_t TIME;

// This table references users by file descriptor.
// its an array to make it VERY fast, as all lookups are referenced
// by an integer, meaning there is no need for a scan/search operation.
extern userrec* fd_ref_table[MAX_DESCRIPTORS];

extern Server* MyServer;
extern ServerConfig *Config;

extern user_hash clientlist;
extern chan_hash chanlist;

/* Special commands which may occur without registration of the user */
cmd_user* command_user;
cmd_nick* command_nick;
cmd_pass* command_pass;

/* This function pokes and hacks at a parameter list like the following:
 *
 * PART #winbot,#darkgalaxy :m00!
 *
 * to turn it into a series of individual calls like this:
 *
 * PART #winbot :m00!
 * PART #darkgalaxy :m00!
 *
 * The seperate calls are sent to a callback function provided by the caller
 * (the caller will usually call itself recursively). The callback function
 * must be a command handler. Calling this function on a line with no list causes
 * no action to be taken. You must provide a starting and ending parameter number
 * where the range of the list can be found, useful if you have a terminating
 * parameter as above which is actually not part of the list, or parameters
 * before the actual list as well. This code is used by many functions which
 * can function as "one to list" (see the RFC) */

int CommandParser::LoopCall(command_t* fn, char **parameters, int pcnt, userrec *u, int start, int end, int joins)
{
	/* Copy of the parameter list, because like strltok, we make a bit of
	 * a mess of the parameter string we're given, and we want to keep this
	 * private.
	 */
	char paramlist[MAXBUF];
	/* Temporary variable used to hold one split parameter
	 */
	char *param;
	/* Parameter list, we can have up to 32 of these
	 */
	char *pars[32];
	/* Seperated items, e.g. holds the #one and #two from "#one,#two"
	 */
	char *sep_items[32];
	/* Seperated keys, holds the 'two' and 'three' of "two,three"
	 */
	char *sep_keys[32];
	/* Misc. counters, the total values hold the total number of
	 * seperated items in sep_items (total) and the total number of
	 * seperated items in sep_keys (total2)
	 */
	int j = 0, q = 0, total = 0, total2 = 0;
	/* A temporary copy of the comma seperated key list (see the description
	 * of the paramlist variable)
	 */
	char keylist[MAXBUF];
	/* Exactly what it says. Its nothing. We point invalid parameters at this.
	 */
	char nothing = 0;

	/* First, initialize our arrays */
	for (int i = 0; i < 32; i++)
		sep_items[i] = sep_keys[i] = NULL;

	/* Now find all parameters that are NULL, maybe above pcnt,
	 * and for safety, point them at 'nothing'
	 */
	for (int i = 0; i < 10; i++)
	{
		if (!parameters[i])
		{
			parameters[i] = &nothing;
		}
	}
	/* Check if we're doing JOIN handling. JOIN has two lists in
	 * it, potentially, if we have keys, so this is a special-case
	 * to handle the keys if they are provided.
	 */
	if (joins)
	{
		if (pcnt > 1) /* we have a key to copy */
		{
			strlcpy(keylist,parameters[1],MAXBUF);
		}
	}

	/* There's nothing to split! We don't do anything
	 */
	if (!parameters[start] || (!strchr(parameters[start],',')))
	{
		return 0;
	}

	/* This function can handle multiple comma seperated
	 * lists as one, which is a feature no actual commands
	 * have yet -- this is futureproofing in case we encounter
	 * a command that  does.
	 */
	*paramlist = 0;

	for (int i = start; i <= end; i++)
	{
		if (parameters[i])
		{
			strlcat(paramlist,parameters[i],MAXBUF);
		}
	}

	/* Now we split off paramlist into seperate parameters using
	 * pointer voodoo, this parameter list goes into sep_items
	 */
	j = 0;
	param = paramlist;

	for (char* i = paramlist; *i; i++)
	{
		/* Found an item */
		if (*i == ',')
		{
			*i = '\0';
			sep_items[j++] = param;
			/* Iterate along to next item, if there is one */
			param = i+1;
			if ((unsigned int)j > Config->MaxTargets)
			{
				/* BZZT! Too many items */
				WriteServ(u->fd,"407 %s %s :Too many targets in list, message not delivered.",u->nick,sep_items[j-1]);
				return 1;
			}
		}
	}
	sep_items[j++] = param;
	total = j;

	/* We add this extra comma here just to ensure we get all the keys
	 * in the event the user gave a malformed key string (yes, you guessed
	 * it, this is a mirc-ism)
	 */
	if ((joins) && (*keylist) && (total>0)) // more than one channel and is joining
	{
		charlcat(keylist,',',MAXBUF);
	}

	/* If we're doing JOIN handling (e.g. we've had to kludge for two
	 * lists being handled at once, real smart by the way JRO </sarcasm>)
	 * and we also have keys, we must seperate out this key list into
	 * our char** list sep_keys for later use.
	 */
	if ((joins) && (*keylist))
	{
		/* There is more than one key */
		if (strchr(keylist,','))
		{
			j = 0;
			param = keylist;
			for (char* i = keylist; *i; i++)
			{
				if (*i == ',')
				{
					*i = '\0';
					sep_keys[j++] = param;
					param = i+1;
				}
			}

			sep_keys[j++] = param;
			total2 = j;
		}
	}

	/* Now the easier bit. We call the command class's Handle function
	 * X times, where x is the number of parameters we've 'collated'.
	 */
	for (j = 0; j < total; j++)
	{
		if (sep_items[j])
		{
			pars[0] = sep_items[j];
		}

		for (q = end; q < pcnt-1; q++)
		{
			if (parameters[q+1])
			{
				pars[q-end+1] = parameters[q+1];
			}
		}

		if ((joins) && (parameters[1]))
		{
			if (pcnt > 1)
			{
				pars[1] = sep_keys[j];
			}
			else
			{
				pars[1] = NULL;
			}
		}

		/* repeatedly call the function with the hacked parameter list */
		if ((joins) && (pcnt > 1))
		{
			if (pars[1])
			{
				/* pars[1] already set up and containing key from sep_keys[j] */
				fn->Handle(pars,2,u);
			}
			else
			{
				pars[1] = parameters[1];
				fn->Handle(pars,2,u);
			}
		}
		else
		{
			fn->Handle(pars,pcnt-(end-start),u);
		}
	}

	return 1;
}

bool CommandParser::IsValidCommand(const std::string &commandname, int pcnt, userrec * user)
{
	nspace::hash_map<std::string,command_t*>::iterator n = cmdlist.find(commandname);

	if (n != cmdlist.end())
	{
		if ((pcnt>=n->second->min_params) && (n->second->source != "<core>"))
		{
			if ((strchr(user->modes,n->second->flags_needed)) || (!n->second->flags_needed))
			{
				if (n->second->flags_needed)
				{
					return ((user->HasPermission(commandname)) || (is_uline(user->server)));
				}
				return true;
			}
		}
	}
	return false;
}

// calls a handler function for a command

bool CommandParser::CallHandler(const std::string &commandname,char **parameters, int pcnt, userrec *user)
{
	nspace::hash_map<std::string,command_t*>::iterator n = cmdlist.find(commandname);

	if (n != cmdlist.end())
	{
		if (pcnt >= n->second->min_params)
		{
			if ((strchr(user->modes,n->second->flags_needed)) || (!n->second->flags_needed))
			{
				if (n->second->flags_needed)
				{
					if ((user->HasPermission(commandname)) || (!IS_LOCAL(user)))
					{
						n->second->Handle(parameters,pcnt,user);
						return true;
					}
				}
				else
				{
					n->second->Handle(parameters,pcnt,user);
					return true;
				}
			}
		}
	}
	return false;
}

int CommandParser::ProcessParameters(char **command_p,char *parameters)
{
	int j = 0;

	if (!*parameters)
	{
		/* no parameters, command_p invalid! */
		return 0;
	}

	if (*parameters == ':')
	{
		command_p[0] = parameters+1;
		return 1;
	}

	if (*parameters)
	{
		char* n = strchr(parameters,' ');
		if ((!n) || (*parameters == ':'))
		{
			/* only one parameter */
			command_p[0] = parameters;
			if (*parameters == ':')
			{
				if (n)
				{
					command_p[0]++;
				}
			}

			return 1;
		}
	}

	command_p[j++] = parameters;

	for (char* i = parameters; *i; i++)
	{
		if (*i == ' ')
		{
			command_p[j++] = i+1;
			*i = '\0';

			if (*command_p[j-1] == ':')
			{
				*command_p[j-1]++; /* remove dodgy ":" */
				break;
				/* parameter like this marks end of the sequence */
			}
		}
	}
	return j; /* returns total number of items in the list */
}

void CommandParser::ProcessCommand(userrec *user, char* cmd)
{
	char *parameters;
	char *command;
	char *command_p[127];
	char p[MAXBUF], temp[MAXBUF];
	int j, items, cmd_found;
	int total_params = 0;

	for (int i = 0; i < 127; i++)
		command_p[i] = NULL;

	if (!user || !cmd || !*cmd)
	{
		return;
	}

	char* first_space = NULL;

	/* If the command is > 2 characters (quick and dirty way to find out) */
	if (*cmd && *(cmd+1) && *(cmd+2))
	{
		for (char* q = cmd; *q; q++)
		{
			if (*q == ' ')
			{
				first_space = q;
				if (*(q+1) == ':')
				{
					total_params++;
					// found a 'trailing', we dont count them after this.
					break;
				}
				else
					total_params++;
			}
		}
	}

	// another phidjit bug...
	if (total_params > 126)
	{
		*first_space = 0;
		WriteServ(user->fd,"421 %s %s :Too many parameters given",user->nick,cmd);
		return;
	}

	strlcpy(temp,cmd,MAXBUF);

	std::string tmp = cmd;

	for (int i = 0; i <= MODCOUNT; i++)
	{
		std::string oldtmp = tmp;
		modules[i]->OnServerRaw(tmp,true,user);
		if (oldtmp != tmp)
		{
			log(DEBUG,"A Module changed the input string!");
			log(DEBUG,"New string: %s",tmp.c_str());
			log(DEBUG,"Old string: %s",oldtmp.c_str());
			break;
		}
	}

	strlcpy(cmd,tmp.c_str(),MAXBUF);
	strlcpy(temp,cmd,MAXBUF);

	char* has_space = strchr(cmd,' ');
	int cm_length = 0;
	if (!has_space)
	{
		/*
		 * no parameters, lets skip the formalities and not chop up
		 * the string
		 */
		log(DEBUG,"About to preprocess command with no params");
		items = 0;
		command_p[0] = NULL;
		parameters = NULL;
		for (char* i = cmd; *i; i++,cm_length++)
			*i = toupper(*i);
		command = cmd;
	}
	else
	{
		*cmd = 0;
		j = 0;

		/* strip out extraneous linefeeds through mirc's crappy pasting (thanks Craig) */
		for (char* i = temp; *i; i++)
		{
			if ((*i != 10) && (*i != 13) && (*i != 0) && (*i != 7))
			{
				cmd[j++] = *i;
			}
		}
		cmd[j] = 0;

		/* split the full string into a command plus parameters */
		parameters = p;
		p[0] = ' ';
		p[1] = 0;

		command = cmd;

		if (has_space)
		{
			for (char* i = cmd; *i; i++)
			{
				/* capitalise the command ONLY, leave params intact */
				*i = toupper(*i);
				/* are we nearly there yet?! :P */
				if (*i == ' ')
				{
					command = cmd;
					parameters = i+1;
					*i = 0;
					break;
				}
			}
		}
		else
		{
			for (char* i = cmd; *i; i++,cm_length++)
			{
				*i = toupper(*i);
			}
		}
	}

	if (cm_length > MAXCOMMAND)
	{
		WriteServ(user->fd,"421 %s %s :Command too long",user->nick,command);
		return;
	}

	for (char* x = command; *x; x++)
	{
		if (((*x < 'A') || (*x > 'Z')) && (*x != '.'))
		{
			if (((*x < '0') || (*x> '9')) && (*x != '-'))
			{
				if (strchr("@!\"$%^&*(){}[]_=+;:'#~,<>/?\\|`",*x))
				{
					ServerInstance->stats->statsUnknown++;
					WriteServ(user->fd,"421 %s %s :Unknown command",user->nick,command);
					return;
				}
			}
		}
	}

	std::string xcommand = command;
	if ((user->registered != 7) && (xcommand == "SERVER"))
	{
		kill_link(user,"Server connection to non-server port");
		return;
	}
	
	/* Tweak by brain - why was this INSIDE the mainloop? */
	if (parameters)
	{
		 if (parameters[0])
		 {
			 items = this->ProcessParameters(command_p,parameters);
		 }
		 else
		 {
			 items = 0;
			 command_p[0] = NULL;
		 }
	}
	else
	{
		items = 0;
		command_p[0] = NULL;
	}

	int MOD_RESULT = 0;
	FOREACH_RESULT(I_OnPreCommand,OnPreCommand(command,command_p,items,user,false));
	if (MOD_RESULT == 1) {
		return;
	}
	
	nspace::hash_map<std::string,command_t*>::iterator cm = cmdlist.find(xcommand);
	
	if (cm != cmdlist.end())
	{
		if (user)
		{
			/* activity resets the ping pending timer */
			user->nping = TIME + user->pingmax;
			if ((items) < cm->second->min_params)
			{
				log(DEBUG,"not enough parameters: %s %s",user->nick,command);
				WriteServ(user->fd,"461 %s %s :Not enough parameters",user->nick,command);
				return;
			}
			if ((!strchr(user->modes,cm->second->flags_needed)) && (cm->second->flags_needed))
			{
				log(DEBUG,"permission denied: %s %s",user->nick,command);
				WriteServ(user->fd,"481 %s :Permission Denied- You do not have the required operator privilages",user->nick);
				cmd_found = 1;
				return;
			}
			if ((cm->second->flags_needed) && (!user->HasPermission(xcommand)))
			{
				log(DEBUG,"permission denied: %s %s",user->nick,command);
				WriteServ(user->fd,"481 %s :Permission Denied- Oper type %s does not have access to command %s",user->nick,user->oper,command);
				if (!IS_LOCAL(user))
					WriteOpers("*** \2WARNING\2: Command '%s' not allowed for oper '%s', dropped.",command,user->nick);
				cmd_found = 1;
				return;
			}
			/* if the command isnt USER, PASS, or NICK, and nick is empty,
			 * deny command! */
			if ((cm->second != command_user) && (cm->second != command_nick) && (cm->second != command_pass))
			{
				if ((!isnick(user->nick)) || (user->registered != 7))
				{
					log(DEBUG,"not registered: %s %s",user->nick,command);
					WriteServ(user->fd,"451 %s :You have not registered",command);
					return;
				}
			}
			if ((user->registered == 7) && (!*user->oper) && (*Config->DisabledCommands))
			{
				std::stringstream dcmds(Config->DisabledCommands);
				std::string thiscmd;
				while (dcmds >> thiscmd)
				{
					if (!strcasecmp(thiscmd.c_str(),command))
					{
						// command is disabled!
						WriteServ(user->fd,"421 %s %s :This command has been disabled.",user->nick,command);
						return;
					}
				}
			}
			if ((user->registered == 7) || (cm->second == command_user) || (cm->second == command_nick) || (cm->second == command_pass))
			{
				/* ikky /stats counters */
				if (temp)
				{
					cm->second->use_count++;
					cm->second->total_bytes+=strlen(temp);
				}

				int MOD_RESULT = 0;
				FOREACH_RESULT(I_OnPreCommand,OnPreCommand(command,command_p,items,user,true));
				if (MOD_RESULT == 1)
				{
					return;
				}

				/*
				 * WARNING: nothing may come after the
				 * command handler call, as the handler
				 * may free the user structure!
				 */

				cm->second->Handle(command_p,items,user);
				return;
			}
			else
			{
				WriteServ(user->fd,"451 %s :You have not registered",command);
				return;
			}
		}
	}
	else if (user)
	{
		ServerInstance->stats->statsUnknown++;
		WriteServ(user->fd,"421 %s %s :Unknown command",user->nick,command);
	}
}

bool CommandParser::RemoveCommands(const char* source)
{
	bool go_again = true;

	while (go_again)
	{
		go_again = false;

		for (nspace::hash_map<std::string,command_t*>::iterator i = cmdlist.begin(); i != cmdlist.end(); i++)
		{
			command_t* x = i->second;
			if (x->source == std::string(source))
			{
				log(DEBUG,"removecommands(%s) Removing dependent command: %s",x->source.c_str(),x->command.c_str());
				cmdlist.erase(i);
				go_again = true;
				break;
			}
		}
	}

	return true;
}

void CommandParser::ProcessBuffer(const char* cmdbuf,userrec *user)
{
	char cmd[MAXBUF];

	if (!user || !cmdbuf || !*cmdbuf)
	{
		log(DEFAULT,"*** BUG *** process_buffer was given an invalid parameter");
		return;
	}

	while (*cmdbuf == ' ') cmdbuf++; // strip leading spaces

	if (!*cmdbuf)
		return;
	
	strlcpy(cmd,cmdbuf,MAXBUF);

	while (charremove(cmd,10));
	while (charremove(cmd,13));

	int sl = strlen(cmd)-1;
	while (sl && (cmd[sl] == ' ')) // strip trailing spaces
	{
		cmd[sl--] = 0;
	}

	if (!sl)
		return;

	log(DEBUG,"CMDIN: %s %s",user->nick,cmd);

	tidystring(cmd);

	if (user && cmd)
	{
		this->ProcessCommand(user,cmd);
	}
}

bool CommandParser::CreateCommand(command_t *f)
{
	/* create the command and push it onto the table */
	if (cmdlist.find(f->command) == cmdlist.end())
	{
		cmdlist[f->command] = f;
		log(DEBUG,"Added command %s (%lu parameters)",f->command.c_str(),(unsigned long)f->min_params);
		return true;
	}
	else return false;
}

CommandParser::CommandParser()
{
	this->SetupCommandTable();
}

void CommandParser::SetupCommandTable()
{
	/* These three are special (can occur without
	 * full user registration) and so are saved
	 * for later use.
	 */
	command_user = new cmd_user;
	command_nick = new cmd_nick;
	command_pass = new cmd_pass;
	this->CreateCommand(command_user);
	this->CreateCommand(command_nick);
	this->CreateCommand(command_pass);

	/* The rest of these arent special. boo hoo.
	 */
	this->CreateCommand(new cmd_quit);
	this->CreateCommand(new cmd_version);
	this->CreateCommand(new cmd_ping);
	this->CreateCommand(new cmd_pong);
	this->CreateCommand(new cmd_admin);
	this->CreateCommand(new cmd_privmsg);
	this->CreateCommand(new cmd_info);
	this->CreateCommand(new cmd_time);
	this->CreateCommand(new cmd_whois);
	this->CreateCommand(new cmd_wallops);
	this->CreateCommand(new cmd_notice);
	this->CreateCommand(new cmd_join);
	this->CreateCommand(new cmd_names);
	this->CreateCommand(new cmd_part);
	this->CreateCommand(new cmd_kick);
	this->CreateCommand(new cmd_mode);
	this->CreateCommand(new cmd_topic);
	this->CreateCommand(new cmd_who);
	this->CreateCommand(new cmd_motd);
	this->CreateCommand(new cmd_rules);
	this->CreateCommand(new cmd_oper);
	this->CreateCommand(new cmd_list);
	this->CreateCommand(new cmd_die);
	this->CreateCommand(new cmd_restart);
	this->CreateCommand(new cmd_kill);
	this->CreateCommand(new cmd_rehash);
	this->CreateCommand(new cmd_lusers);
	this->CreateCommand(new cmd_stats);
	this->CreateCommand(new cmd_userhost);
	this->CreateCommand(new cmd_away);
	this->CreateCommand(new cmd_ison);
	this->CreateCommand(new cmd_summon);
	this->CreateCommand(new cmd_users);
	this->CreateCommand(new cmd_invite);
	this->CreateCommand(new cmd_trace);
	this->CreateCommand(new cmd_whowas);
	this->CreateCommand(new cmd_connect);
	this->CreateCommand(new cmd_squit);
	this->CreateCommand(new cmd_modules);
	this->CreateCommand(new cmd_links);
	this->CreateCommand(new cmd_map);
	this->CreateCommand(new cmd_kline);
	this->CreateCommand(new cmd_gline);
	this->CreateCommand(new cmd_zline);
	this->CreateCommand(new cmd_qline);
	this->CreateCommand(new cmd_eline);
	this->CreateCommand(new cmd_loadmodule);
	this->CreateCommand(new cmd_unloadmodule);
	this->CreateCommand(new cmd_server);
	this->CreateCommand(new cmd_commands);
}