/*
Copyright (C) 2002 Erik Fears
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 2
of the License, 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.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
*/
#include "setup.h"
#ifdef STDC_HEADERS
# include <string.h>
# include <stdlib.h>
#endif
#include <sys/types.h>
#include <netinet/in.h>
#include <time.h>
#include "options.h"
#include "irc.h"
#include "log.h"
#include "misc.h"
#include "opercmd.h"
#include "scan.h"
#include "config.h"
#include "extern.h"
#include "malloc.h"
#include "list.h"
#include "stats.h"
/* List of active commands */
list_t *COMMANDS = NULL;
static struct Command *command_create(unsigned short type, char *param, char *irc_nick, struct ChannelConf *target);
static void command_free(struct Command *);
static void cmd_check(char *, char *, struct ChannelConf *);
static void cmd_stat(char *, char *, struct ChannelConf *);
static void cmd_fdstat(char *, char *, struct ChannelConf *);
#if 0
static void cmd_op(char *, char *, struct ChannelConf *);
#endif
static struct OperCommandHash COMMAND_TABLE[] =
{
{"CHECK", cmd_check },
{"SCAN", cmd_check },
{"STAT", cmd_stat },
{"STATS", cmd_stat },
{"STATUS", cmd_stat },
{"FDSTAT", cmd_fdstat },
/* {"OP", cmd_op } */
};
/* command_init
*
* Do command initialization
*
* Parameters: NONE
* Return: NONE
*
*/
void command_init()
{
if(COMMANDS == NULL)
COMMANDS = list_create();
}
/* command_timer
*
* Perform ~1 second actions.
*
* Parameters: NONE
*
* Return: NONE
*
*/
void command_timer()
{
static unsigned short interval;
node_t *node, *next;
struct Command *cs;
time_t present;
/* Only perform command removal every COMMANDINTERVAL seconds */
if(interval++ < COMMANDINTERVAL)
return;
else
interval = 0;
time(&present);
LIST_FOREACH_SAFE(node, next, COMMANDS->head)
{
cs = (struct Command *) node->data;
if((present - cs->added) > COMMANDTIMEOUT)
{
command_free(cs);
list_remove(COMMANDS, node);
node_free(node);
}
else /* Since the queue is in order, it's also ordered by time, no nodes after this will be timed out */
return;
}
}
/* command_parse
*
* Parse a command to bopm (sent to a channel bopm is on). The command is parsed
* from the parameters, and if it is a known command it is stored in a queue. A
* userhost is performed on the user to check if they are an IRC operator. When
* a reply is returned (command_userhost), the command will be executed.
*
* Parameters:
* command: Command sent (including parameters)
* msg: Original PRIVMSG containing the command
* target: Channel command was sent to (we only got this far if there was only one recipient)
* source_p: Operator (hopefully) that sent the command.
*
*/
void command_parse(char *command, char *msg, struct ChannelConf *target,
struct UserInfo *source_p)
{
unsigned int i;
char *param; /* Parsed parameters */
struct Command *cs;
node_t *node;
USE_VAR(msg);
if(OPT_DEBUG)
{
log_printf("COMMAND -> Parsing command (%s) from %s [%s]", command,
source_p->irc_nick, target->name);
}
/* Only allow COMMANDMAX commands in the queue */
if(LIST_SIZE(COMMANDS) >= COMMANDMAX)
return;
/* Parameter is the first character in command after the first space.
param will be NULL if:
1. There was no space
2. There was a space but it was the last character in command, in which case
param = '\0'
*/
/* Skip past the botname/!all */
command = strchr(command, ' ');
/* There is no command OR
there is at least nothing
past that first space. */
if(command == NULL ||
command++ == NULL)
return;
/* Find the parameters */
param = strchr(command, ' ');
if(param != NULL)
{
*param = '\0';
param++;
}
else
param = "";
log_printf("COMMAND -> parsed [%s] [%s]", command, param);
/* Lookup the command in the table */
for(i = 0; i < sizeof(COMMAND_TABLE) / sizeof(struct OperCommandHash); i++)
{
if(strcasecmp(command, COMMAND_TABLE[i].command) == 0)
{
/* Queue this command */
cs = command_create(i, param, source_p->irc_nick, target);
node = node_create(cs);
list_add(COMMANDS, node);
}
}
irc_send("USERHOST %s", source_p->irc_nick);
}
/* command_create
*
* Create a Command struct.
*
* Parameters:
* type: Index in COMMAND_TABLE
* param: Parameters to the command (NULL if there are not any)
* irc_nick: Nickname of user that initiated the command
* target: Target channel (target is ALWAYS a channel)
*
* Return:
* Pointer to new Command
*/
static struct Command *command_create(unsigned short type, char *param, char *irc_nick, struct ChannelConf *target)
{
struct Command *ret;
ret = MyMalloc(sizeof *ret);
ret->type = type;
if(param != NULL)
ret->param = DupString(param);
else
ret->param = NULL;
ret->irc_nick = (char *) DupString(irc_nick);
ret->target = target; /* FIXME: This needs fixed if rehash is implemented */
time(&(ret->added));
return ret;
}
/* command_free
*
* Free a command struct
*
* Parameters:
* command: Command struct to free
*
* Return: NONE
*/
static void command_free(struct Command *command)
{
if(command->param != NULL)
MyFree(command->param);
MyFree(command->irc_nick);
MyFree(command);
}
/* command_userhost
*
* A 302 reply was received. The reply is parsed to check if the
* user was an operator. If so any commands they had queued are
* executed.
*
* Parameters:
* reply: Reply to USERHOST (ex: :grifferz*=+goats@pc-62-30-219-54-pb.blueyonder.co.uk)
*
* Return: NONE
*
*/
void command_userhost(char *reply)
{
node_t *node, *next;
struct Command *cs;
char *tmp;
int oper = 0;
tmp = strchr(reply, '=');
/* They quit, ignore it */
if (!tmp)
return;
/* Operators have a * flag in a USERHOST reply */
if (*(tmp - 1) == '*')
oper = 1;
/* Null terminate it so tmp = the oper's nick */
*(--tmp) = '\0';
/* Find any queued commands that match this user */
LIST_FOREACH_SAFE(node, next, COMMANDS->head)
{
cs = (struct Command *) node->data;
if(strcmp(cs->irc_nick, reply) == 0)
{
if(oper)
COMMAND_TABLE[cs->type].handler(cs->param, cs->irc_nick, cs->target);
/* Cleanup the command */
command_free(cs);
list_remove(COMMANDS, node);
node_free(node);
}
}
}
/* cmd_check
*
* Start a manual scan on given IP. Parameter MUST be an IP. BOPM should not
* have to waste any time resolving a hostname.
*
* Parameters:
* param: Parameters of the command
* source: irc_nick of user who requested the command
* target: channel command was sent to
*
*/
static void cmd_check(char *param, char *source, struct ChannelConf *target)
{
USE_VAR(source);
scan_manual(param, target);
}
/* cmd_stat
*
* Send output of stats to channel.
*
* Parameters:
* param: Parameters of the command
* source: irc_nick of user who requested the command
* target: channel command was sent to
*/
static void cmd_stat(char *param, char *source, struct ChannelConf *target)
{
USE_VAR(param);
USE_VAR(source);
stats_output(target->name);
}
/* cmd_fdstat
*
* Send output of stats to channel.
*
* Parameters:
* param: Parameters of the command
* source: irc_nick of user who requested the command
* target: channel command was sent to
*/
static void cmd_fdstat(char *param, char *source, struct ChannelConf *target)
{
USE_VAR(param);
USE_VAR(source);
fdstats_output(target->name);
}
/* cmd_op
*
* Op a user on the channel.
*
* Parameters:
* param: Parameters of the command
* source: irc_nick of user who requested the command
* target: channel command was sent to
*
* XXX - Doesn't seem to currently be in use anywhere?
* -grifferz
*/
#if 0
static void cmd_op(char *param, char *source, struct ChannelConf *target)
{
irc_send("MODE %s +o %s", target->name, param);
USE_VAR(source);
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1