Cleaned up command handling code, by splitting into multiple files.
This commit is contained in:
parent
9efe4bce57
commit
74ca5a0a33
@ -136,6 +136,7 @@ OLD_REVISION=$(shell grep GIT_REVISION revision.h 2>/dev/null | cut -f 3 -d " "
|
||||
# Sources
|
||||
libuhub_SOURCES := \
|
||||
src/core/auth.c \
|
||||
src/core/command_parser.c \
|
||||
src/core/commands.c \
|
||||
src/core/config.c \
|
||||
src/core/eventqueue.c \
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <uhub.h>
|
||||
|
||||
static struct hub_info* hub = NULL;
|
||||
static struct hub_command* cmd = NULL;
|
||||
static struct hub_user user;
|
||||
static struct command_base* cbase = NULL;
|
||||
static struct command_handle* c_test1 = NULL;
|
||||
@ -17,6 +18,7 @@ static int result = 0;
|
||||
EXO_TEST(setup, {
|
||||
hub = hub_malloc_zero(sizeof(struct hub_info));
|
||||
cbase = command_initialize(hub);
|
||||
hub->commands = cbase;
|
||||
return cbase && hub && uman_init(hub) == 0;
|
||||
});
|
||||
|
||||
@ -79,7 +81,7 @@ extern void command_destroy(struct hub_command* cmd);
|
||||
|
||||
static int verify(const char* str, enum command_parse_status expected)
|
||||
{
|
||||
struct hub_command* cmd = command_parse(cbase, &user, str);
|
||||
struct hub_command* cmd = command_parse(cbase, hub, &user, str);
|
||||
enum command_parse_status status = cmd->status;
|
||||
command_free(cmd);
|
||||
return status == expected;
|
||||
@ -160,66 +162,76 @@ EXO_TEST(command_parse_3, { return verify("!fail", cmd_status_not_found); });
|
||||
EXO_TEST(command_parse_4, { return verify("!help", cmd_status_ok); });
|
||||
|
||||
|
||||
#define SETUP_COMMAND(string) \
|
||||
do { \
|
||||
if (cmd) command_free(cmd); \
|
||||
cmd = command_parse(cbase, hub, &user, string); \
|
||||
} while(0)
|
||||
|
||||
EXO_TEST(command_argument_integer_1, {
|
||||
struct hub_command* cmd = command_parse(cbase, &user, "!test3");
|
||||
SETUP_COMMAND("!test3");
|
||||
return verify_argument(cmd, type_integer) == NULL;
|
||||
});
|
||||
|
||||
EXO_TEST(command_argument_integer_2, {
|
||||
struct hub_command* cmd = command_parse(cbase, &user, "!test3 10 42");
|
||||
SETUP_COMMAND("!test3 10 42");
|
||||
return verify_arg_integer(cmd, 10) && verify_arg_integer(cmd, 42) && verify_argument(cmd, type_integer) == NULL;
|
||||
});
|
||||
|
||||
EXO_TEST(command_argument_integer_3, {
|
||||
struct hub_command* cmd = command_parse(cbase, &user, "!test3 10 42 6784");
|
||||
SETUP_COMMAND("!test3 10 42 6784");
|
||||
return verify_arg_integer(cmd, 10) && verify_arg_integer(cmd, 42) && verify_arg_integer(cmd, 6784);
|
||||
});
|
||||
|
||||
EXO_TEST(command_argument_user_1, {
|
||||
struct hub_command* cmd = command_parse(cbase, &user, "!test4 tester");
|
||||
SETUP_COMMAND("!test4 tester");
|
||||
return verify_arg_user(cmd, &user) ;
|
||||
});
|
||||
|
||||
EXO_TEST(command_argument_cid_1, {
|
||||
struct hub_command* cmd = command_parse(cbase, &user, "!test5 3AGHMAASJA2RFNM22AA6753V7B7DYEPNTIWHBAY");
|
||||
SETUP_COMMAND("!test5 3AGHMAASJA2RFNM22AA6753V7B7DYEPNTIWHBAY");
|
||||
return verify_arg_user(cmd, &user) ;
|
||||
});
|
||||
|
||||
EXO_TEST(command_argument_cred_1, {
|
||||
struct hub_command* cmd = command_parse(cbase, &user, "!test7 admin");
|
||||
SETUP_COMMAND("!test7 admin");
|
||||
return verify_arg_cred(cmd, auth_cred_admin);;
|
||||
});
|
||||
|
||||
EXO_TEST(command_argument_cred_2, {
|
||||
struct hub_command* cmd = command_parse(cbase, &user, "!test7 op");
|
||||
SETUP_COMMAND("!test7 op");
|
||||
return verify_arg_cred(cmd, auth_cred_operator);;
|
||||
});
|
||||
|
||||
EXO_TEST(command_argument_cred_3, {
|
||||
struct hub_command* cmd = command_parse(cbase, &user, "!test7 operator");
|
||||
SETUP_COMMAND("!test7 operator");
|
||||
return verify_arg_cred(cmd, auth_cred_operator);
|
||||
});
|
||||
|
||||
EXO_TEST(command_argument_cred_4, {
|
||||
struct hub_command* cmd = command_parse(cbase, &user, "!test7 super");
|
||||
SETUP_COMMAND("!test7 super");
|
||||
return verify_arg_cred(cmd, auth_cred_super);
|
||||
});
|
||||
|
||||
EXO_TEST(command_argument_cred_5, {
|
||||
struct hub_command* cmd = command_parse(cbase, &user, "!test7 guest");
|
||||
SETUP_COMMAND("!test7 guest");
|
||||
return verify_arg_cred(cmd, auth_cred_guest);
|
||||
});
|
||||
|
||||
EXO_TEST(command_argument_cred_6, {
|
||||
struct hub_command* cmd = command_parse(cbase, &user, "!test7 user");
|
||||
SETUP_COMMAND("!test7 user");
|
||||
return verify_arg_cred(cmd, auth_cred_user);
|
||||
});
|
||||
|
||||
#undef SETUP_COMMAND
|
||||
|
||||
EXO_TEST(command_user_destroy, { return uman_remove(hub, &user) == 0; });
|
||||
|
||||
EXO_TEST(command_destroy, {
|
||||
|
||||
|
||||
command_free(cmd);
|
||||
cmd = NULL;
|
||||
|
||||
DEL_TEST(c_test1);
|
||||
DEL_TEST(c_test2);
|
||||
DEL_TEST(c_test3);
|
||||
|
298
src/core/command_parser.c
Normal file
298
src/core/command_parser.c
Normal file
@ -0,0 +1,298 @@
|
||||
/*
|
||||
* uhub - A tiny ADC p2p connection hub
|
||||
* Copyright (C) 2007-2012, Jan Vidar Krey
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "uhub.h"
|
||||
|
||||
static void hub_command_args_free(struct hub_command* cmd)
|
||||
{
|
||||
struct hub_command_arg_data* data = NULL;
|
||||
|
||||
if (!cmd->args)
|
||||
return;
|
||||
|
||||
for (data = (struct hub_command_arg_data*) list_get_first(cmd->args); data; data = (struct hub_command_arg_data*) list_get_next(cmd->args))
|
||||
{
|
||||
switch (data->type)
|
||||
{
|
||||
case type_string:
|
||||
hub_free(data->data.string);
|
||||
break;
|
||||
case type_range:
|
||||
hub_free(data->data.range);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
list_clear(cmd->args, hub_free);
|
||||
list_destroy(cmd->args);
|
||||
cmd->args = NULL;
|
||||
}
|
||||
|
||||
void command_free(struct hub_command* cmd)
|
||||
{
|
||||
if (!cmd) return;
|
||||
|
||||
hub_free(cmd->prefix);
|
||||
hub_command_args_free(cmd);
|
||||
hub_free(cmd);
|
||||
}
|
||||
|
||||
static enum command_parse_status command_extract_arguments(struct hub_info* hub, const struct hub_user* user, struct command_handle* command, struct linked_list* tokens, struct linked_list* args)
|
||||
{
|
||||
int arg = 0;
|
||||
int opt = 0;
|
||||
int greedy = 0;
|
||||
char arg_code;
|
||||
char* token = NULL;
|
||||
char* tmp = NULL;
|
||||
size_t size = 0;
|
||||
struct hub_command_arg_data* data = NULL;
|
||||
enum command_parse_status status = cmd_status_ok;
|
||||
|
||||
// Ignore the first token since it is the prefix.
|
||||
token = list_get_first(tokens);
|
||||
list_remove(tokens, token);
|
||||
hub_free(token);
|
||||
|
||||
while (status == cmd_status_ok && (arg_code = command->args[arg++]))
|
||||
{
|
||||
if (greedy)
|
||||
{
|
||||
size = 1;
|
||||
for (tmp = (char*) list_get_first(tokens); tmp; tmp = (char*) list_get_next(tokens))
|
||||
size += (strlen(tmp) + 1);
|
||||
token = hub_malloc_zero(size);
|
||||
|
||||
while ((tmp = list_get_first(tokens)))
|
||||
{
|
||||
if (*token)
|
||||
strcat(token, " ");
|
||||
strcat(token, tmp);
|
||||
list_remove(tokens, tmp);
|
||||
hub_free(tmp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
token = list_get_first(tokens);
|
||||
}
|
||||
|
||||
if (!token || !*token)
|
||||
{
|
||||
if (arg_code == '?' || opt == 1)
|
||||
status = cmd_status_ok;
|
||||
else
|
||||
status = cmd_status_missing_args;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (arg_code)
|
||||
{
|
||||
case '?':
|
||||
opt = 1;
|
||||
continue;
|
||||
|
||||
case '+':
|
||||
greedy = 1;
|
||||
continue;
|
||||
|
||||
case 'u':
|
||||
data = hub_malloc(sizeof(*data));
|
||||
data->type = type_user;
|
||||
data->data.user = uman_get_user_by_nick(hub, token);
|
||||
if (!data->data.user)
|
||||
{
|
||||
hub_free(data);
|
||||
data = NULL;
|
||||
status = cmd_status_arg_nick;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
data = hub_malloc(sizeof(*data));
|
||||
data->type = type_user;
|
||||
data->data.user = uman_get_user_by_cid(hub, token);
|
||||
if (!data->data.user)
|
||||
{
|
||||
hub_free(data);
|
||||
data = NULL;
|
||||
status = cmd_status_arg_cid;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
data = hub_malloc(sizeof(*data));
|
||||
data->type = type_address;
|
||||
if (ip_convert_to_binary(token, data->data.address) == -1)
|
||||
{
|
||||
hub_free(data);
|
||||
data = NULL;
|
||||
status = cmd_status_arg_address;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
data = hub_malloc(sizeof(*data));
|
||||
data->type = type_range;
|
||||
data->data.range = hub_malloc_zero(sizeof(struct ip_range));
|
||||
if (!ip_convert_address_to_range(token, data->data.range))
|
||||
{
|
||||
hub_free(data->data.range);
|
||||
hub_free(data);
|
||||
data = NULL;
|
||||
status = cmd_status_arg_address;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
case 'm':
|
||||
case 'p':
|
||||
data = hub_malloc(sizeof(*data));
|
||||
data->type = type_string;
|
||||
data->data.string = strdup(token);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
data = hub_malloc(sizeof(*data));
|
||||
data->type = type_command;
|
||||
data->data.command = command_handler_lookup(hub->commands, token);
|
||||
if (!data->data.command)
|
||||
{
|
||||
hub_free(data);
|
||||
data = NULL;
|
||||
status = cmd_status_arg_command;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
data = hub_malloc(sizeof(*data));
|
||||
data->type = type_credentials;
|
||||
if (!auth_string_to_cred(token, &data->data.credentials))
|
||||
{
|
||||
hub_free(data);
|
||||
data = NULL;
|
||||
status = cmd_status_arg_cred;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
data = hub_malloc(sizeof(*data));
|
||||
data->type = type_integer;
|
||||
if (!is_number(token, &data->data.integer))
|
||||
{
|
||||
hub_free(data);
|
||||
data = NULL;
|
||||
status = cmd_status_arg_number;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\0':
|
||||
if (!opt)
|
||||
{
|
||||
status = cmd_status_missing_args;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = cmd_status_ok;
|
||||
}
|
||||
}
|
||||
|
||||
if (data)
|
||||
{
|
||||
list_append(args, data);
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
list_remove(tokens, token);
|
||||
hub_free(token);
|
||||
}
|
||||
|
||||
hub_free(data);
|
||||
return status;
|
||||
}
|
||||
|
||||
static struct command_handle* command_get_handler(struct command_base* cbase, const char* prefix, const struct hub_user* user, struct hub_command* cmd)
|
||||
{
|
||||
struct command_handle* handler = NULL;
|
||||
uhub_assert(cmd != NULL);
|
||||
|
||||
if (prefix && prefix[0] && prefix[1])
|
||||
{
|
||||
handler = command_handler_lookup(cbase, prefix + 1);
|
||||
if (handler)
|
||||
{
|
||||
cmd->ptr = handler->ptr;
|
||||
cmd->handler = handler->handler;
|
||||
cmd->status = command_is_available(handler, user->credentials) ? cmd_status_ok : cmd_status_access_error;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd->status = cmd_status_not_found;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd->status = cmd_status_syntax_error;
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse a command and break it down into a struct hub_command.
|
||||
*/
|
||||
struct hub_command* command_parse(struct command_base* cbase, struct hub_info* hub, const struct hub_user* user, const char* message)
|
||||
{
|
||||
struct linked_list* tokens = list_create();
|
||||
struct hub_command* cmd = NULL;
|
||||
struct command_handle* handle = NULL;
|
||||
|
||||
cmd = hub_malloc_zero(sizeof(struct hub_command));
|
||||
cmd->status = cmd_status_ok;
|
||||
cmd->message = message;
|
||||
cmd->prefix = NULL;
|
||||
cmd->args = list_create();
|
||||
cmd->user = user;
|
||||
|
||||
if (split_string(message, " ", tokens, 0) <= 0)
|
||||
{
|
||||
cmd->status = cmd_status_syntax_error;
|
||||
goto command_parse_cleanup;
|
||||
}
|
||||
|
||||
// Setup hub command.
|
||||
cmd->prefix = strdup(((char*) list_get_first(tokens)) + 1);
|
||||
|
||||
// Find a matching command handler
|
||||
handle = command_get_handler(cbase, list_get_first(tokens), user, cmd);
|
||||
if (cmd->status != cmd_status_ok)
|
||||
goto command_parse_cleanup;
|
||||
|
||||
// Parse arguments
|
||||
cmd->status = command_extract_arguments(hub, user, handle, tokens, cmd->args);
|
||||
goto command_parse_cleanup;
|
||||
|
||||
command_parse_cleanup:
|
||||
list_clear(tokens, &hub_free);
|
||||
list_destroy(tokens);
|
||||
return cmd;
|
||||
}
|
||||
|
131
src/core/command_parser.h
Normal file
131
src/core/command_parser.h
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* uhub - A tiny ADC p2p connection hub
|
||||
* Copyright (C) 2007-2012, Jan Vidar Krey
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HAVE_UHUB_COMMAND_PARSER_H
|
||||
#define HAVE_UHUB_COMMAND_PARSER_H
|
||||
|
||||
struct hub_command;
|
||||
struct hub_user;
|
||||
struct command_base;
|
||||
|
||||
/**
|
||||
* Parse a message as a command and return a status indicating if the command
|
||||
* is valid and that the arguments are sane.
|
||||
*
|
||||
* @param cbase Command base pointer.
|
||||
* @param user User who invoked the command.
|
||||
* @param message The message that is to be interpreted as a command (including the invokation prefix '!' or '+')
|
||||
*
|
||||
* @return a hub_command that must be freed with command_free(). @See struct hub_command.
|
||||
*/
|
||||
extern struct hub_command* command_parse(struct command_base* cbase, struct hub_info* hub, const struct hub_user* user, const char* message);
|
||||
|
||||
/**
|
||||
* Free a hub_command that was created in command_parse().
|
||||
*/
|
||||
extern void command_free(struct hub_command* command);
|
||||
|
||||
|
||||
enum command_parse_status
|
||||
{
|
||||
cmd_status_ok, /** <<< "Everything seems to OK" */
|
||||
cmd_status_not_found, /** <<< "Command was not found" */
|
||||
cmd_status_access_error, /** <<< "You don't have access to this command" */
|
||||
cmd_status_syntax_error, /** <<< "Not a valid command." */
|
||||
cmd_status_missing_args, /** <<< "Missing some or all required arguments." */
|
||||
cmd_status_arg_nick, /** <<< "A nick argument does not match an online user. ('n')" */
|
||||
cmd_status_arg_cid, /** <<< "A cid argument does not match an online user. ('i')." */
|
||||
cmd_status_arg_address, /** <<< "A address range argument is not valid ('a')." */
|
||||
cmd_status_arg_number, /** <<< "A number argument is not valid ('N')" */
|
||||
cmd_status_arg_cred, /** <<< "A credentials argument is not valid ('C')" */
|
||||
cmd_status_arg_command, /** <<< "A command argument is not valid ('c')" */
|
||||
};
|
||||
|
||||
enum hub_command_arg_type
|
||||
{
|
||||
type_integer,
|
||||
type_string,
|
||||
type_user,
|
||||
type_address,
|
||||
type_range,
|
||||
type_credentials,
|
||||
type_command
|
||||
};
|
||||
|
||||
struct hub_command_arg_data
|
||||
{
|
||||
enum hub_command_arg_type type;
|
||||
union {
|
||||
int integer;
|
||||
char* string;
|
||||
struct hub_user* user;
|
||||
struct ip_addr_encap* address;
|
||||
struct ip_range* range;
|
||||
enum auth_credentials credentials;
|
||||
struct command_handle* command;
|
||||
} data;
|
||||
};
|
||||
|
||||
typedef int (*command_handler)(struct command_base* cbase, struct hub_user* user, struct hub_command* cmd);
|
||||
|
||||
/**
|
||||
* This struct contains all information needed to invoke
|
||||
* a command, which includes the whole message, the prefix,
|
||||
* the decoded arguments (according to parameter list), and
|
||||
* the user pointer (ptr) which comes from the command it was matched to.
|
||||
*
|
||||
* The message and prefix is generally always available, but args only
|
||||
* if status == cmd_status_ok.
|
||||
* Handler and ptr are NULL if status == cmd_status_not_found, or status == cmd_status_access_error.
|
||||
* Ptr might also be NULL if cmd_status_ok because the command that handles it was added with a NULL ptr.
|
||||
*/
|
||||
struct hub_command
|
||||
{
|
||||
const char* message; /**<<< "The complete message." */
|
||||
char* prefix; /**<<< "The prefix extracted from the message." */
|
||||
struct linked_list* args; /**<<< "List of arguments of type struct hub_command_arg_data. Parsed from message." */
|
||||
enum command_parse_status status; /**<<< "Status of the parsed hub_command." */
|
||||
command_handler handler; /**<<< "The function handler to call in order to invoke this command." */
|
||||
const struct hub_user* user; /**<<< "The user who invoked this command." */
|
||||
void* ptr; /**<<< "A pointer of data which came from struct command_handler" */
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset the command argument iterator and return the number of arguments
|
||||
* that can be extracted from a parsed command.
|
||||
*
|
||||
* @param cmd the command to start iterating arguments
|
||||
* @return returns the number of arguments provided for the command
|
||||
*/
|
||||
extern size_t hub_command_arg_reset(struct hub_command* cmd);
|
||||
|
||||
/**
|
||||
* Obtain the current argument and place it in data and increments the iterator.
|
||||
* If no argument exists, or the argument is of a different type than \param type, then 0 is returned.
|
||||
*
|
||||
* NOTE: when calling hub_command_arg_next the first time during a command callback it is safe to assume
|
||||
* that the first argument will be extracted. Thus you don't need to call hub_command_arg_reset().
|
||||
*
|
||||
* @param cmd the command used for iterating arguments.
|
||||
* @param type the expected type of this argument
|
||||
* @return NULL if no argument is found or if the argument found does not match the expected type.
|
||||
*/
|
||||
extern struct hub_command_arg_data* hub_command_arg_next(struct hub_command* cmd, enum hub_command_arg_type type);
|
||||
|
||||
#endif /* HAVE_UHUB_COMMAND_PARSER_H */
|
@ -38,7 +38,6 @@ struct command_base
|
||||
struct hub_info* hub;
|
||||
struct linked_list* handlers;
|
||||
size_t prefix_length_max;
|
||||
struct ip_range range; // cached for parsing. Only one can be used per parameter.
|
||||
};
|
||||
|
||||
struct command_base* command_initialize(struct hub_info* hub)
|
||||
@ -87,50 +86,14 @@ int command_del(struct command_base* cbase, struct command_handle* cmd)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int command_is_available(struct command_handle* handle, const struct hub_user* user)
|
||||
int command_is_available(struct command_handle* handle, enum auth_credentials credentials)
|
||||
{
|
||||
uhub_assert(handle != NULL);
|
||||
uhub_assert(user != NULL);
|
||||
return handle->cred <= user->credentials;
|
||||
return handle->cred <= credentials;
|
||||
}
|
||||
|
||||
void hub_command_args_free(struct hub_command* cmd)
|
||||
{
|
||||
struct hub_command_arg_data* data = NULL;
|
||||
|
||||
if (!cmd->args)
|
||||
return;
|
||||
|
||||
for (data = (struct hub_command_arg_data*) list_get_first(cmd->args); data; data = (struct hub_command_arg_data*) list_get_next(cmd->args))
|
||||
{
|
||||
switch (data->type)
|
||||
{
|
||||
case type_string:
|
||||
hub_free(data->data.string);
|
||||
break;
|
||||
case type_range:
|
||||
hub_free(data->data.range);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
list_clear(cmd->args, hub_free);
|
||||
list_destroy(cmd->args);
|
||||
cmd->args = NULL;
|
||||
}
|
||||
|
||||
void command_free(struct hub_command* cmd)
|
||||
{
|
||||
if (!cmd) return;
|
||||
|
||||
hub_free(cmd->prefix);
|
||||
hub_command_args_free(cmd);
|
||||
hub_free(cmd);
|
||||
}
|
||||
|
||||
static struct command_handle* command_handler_lookup(struct command_base* cbase, const char* prefix)
|
||||
struct command_handle* command_handler_lookup(struct command_base* cbase, const char* prefix)
|
||||
{
|
||||
struct command_handle* handler = NULL;
|
||||
size_t prefix_len = strlen(prefix);
|
||||
@ -147,246 +110,6 @@ static struct command_handle* command_handler_lookup(struct command_base* cbase,
|
||||
}
|
||||
|
||||
|
||||
static enum command_parse_status command_extract_arguments(struct command_base* cbase, const struct hub_user* user, struct command_handle* command, struct linked_list* tokens, struct linked_list* args)
|
||||
{
|
||||
int arg = 0;
|
||||
int opt = 0;
|
||||
int greedy = 0;
|
||||
char arg_code;
|
||||
char* token = NULL;
|
||||
char* tmp = NULL;
|
||||
size_t size = 0;
|
||||
struct hub_command_arg_data* data = NULL;
|
||||
enum command_parse_status status = cmd_status_ok;
|
||||
|
||||
// Ignore the first token since it is the prefix.
|
||||
token = list_get_first(tokens);
|
||||
list_remove(tokens, token);
|
||||
hub_free(token);
|
||||
|
||||
while (status == cmd_status_ok && (arg_code = command->args[arg++]))
|
||||
{
|
||||
if (greedy)
|
||||
{
|
||||
size = 1;
|
||||
for (tmp = (char*) list_get_first(tokens); tmp; tmp = (char*) list_get_next(tokens))
|
||||
size += (strlen(tmp) + 1);
|
||||
token = hub_malloc_zero(size);
|
||||
|
||||
while ((tmp = list_get_first(tokens)))
|
||||
{
|
||||
if (*token)
|
||||
strcat(token, " ");
|
||||
strcat(token, tmp);
|
||||
list_remove(tokens, tmp);
|
||||
hub_free(tmp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
token = list_get_first(tokens);
|
||||
}
|
||||
|
||||
if (!token || !*token)
|
||||
{
|
||||
if (arg_code == '?' || opt == 1)
|
||||
status = cmd_status_ok;
|
||||
else
|
||||
status = cmd_status_missing_args;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (arg_code)
|
||||
{
|
||||
case '?':
|
||||
opt = 1;
|
||||
continue;
|
||||
|
||||
case '+':
|
||||
greedy = 1;
|
||||
continue;
|
||||
|
||||
case 'u':
|
||||
data = hub_malloc(sizeof(*data));
|
||||
data->type = type_user;
|
||||
data->data.user = uman_get_user_by_nick(cbase->hub, token);
|
||||
if (!data->data.user)
|
||||
{
|
||||
hub_free(data);
|
||||
data = NULL;
|
||||
status = cmd_status_arg_nick;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
data = hub_malloc(sizeof(*data));
|
||||
data->type = type_user;
|
||||
data->data.user = uman_get_user_by_cid(cbase->hub, token);
|
||||
if (!data->data.user)
|
||||
{
|
||||
hub_free(data);
|
||||
data = NULL;
|
||||
status = cmd_status_arg_cid;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
data = hub_malloc(sizeof(*data));
|
||||
data->type = type_address;
|
||||
if (ip_convert_to_binary(token, data->data.address) == -1)
|
||||
{
|
||||
hub_free(data);
|
||||
data = NULL;
|
||||
status = cmd_status_arg_address;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
data = hub_malloc(sizeof(*data));
|
||||
data->type = type_range;
|
||||
data->data.range = hub_malloc_zero(sizeof(struct ip_range));
|
||||
if (!ip_convert_address_to_range(token, data->data.range))
|
||||
{
|
||||
hub_free(data->data.range);
|
||||
hub_free(data);
|
||||
data = NULL;
|
||||
status = cmd_status_arg_address;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
case 'm':
|
||||
case 'p':
|
||||
data = hub_malloc(sizeof(*data));
|
||||
data->type = type_string;
|
||||
data->data.string = strdup(token);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
data = hub_malloc(sizeof(*data));
|
||||
data->type = type_command;
|
||||
data->data.command = command_handler_lookup(cbase, token);
|
||||
if (!data->data.command)
|
||||
{
|
||||
hub_free(data);
|
||||
data = NULL;
|
||||
status = cmd_status_arg_command;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
data = hub_malloc(sizeof(*data));
|
||||
data->type = type_credentials;
|
||||
if (!auth_string_to_cred(token, &data->data.credentials))
|
||||
{
|
||||
hub_free(data);
|
||||
data = NULL;
|
||||
status = cmd_status_arg_cred;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
data = hub_malloc(sizeof(*data));
|
||||
data->type = type_integer;
|
||||
if (!is_number(token, &data->data.integer))
|
||||
{
|
||||
hub_free(data);
|
||||
data = NULL;
|
||||
status = cmd_status_arg_number;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\0':
|
||||
if (!opt)
|
||||
{
|
||||
status = cmd_status_missing_args;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = cmd_status_ok;
|
||||
}
|
||||
}
|
||||
|
||||
if (data)
|
||||
{
|
||||
list_append(args, data);
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
list_remove(tokens, token);
|
||||
hub_free(token);
|
||||
}
|
||||
|
||||
hub_free(data);
|
||||
return status;
|
||||
}
|
||||
|
||||
static struct command_handle* command_get_handler(struct command_base* cbase, const char* prefix, const struct hub_user* user, struct hub_command* cmd)
|
||||
{
|
||||
struct command_handle* handler = NULL;
|
||||
uhub_assert(cmd != NULL);
|
||||
|
||||
if (prefix && prefix[0] && prefix[1])
|
||||
{
|
||||
handler = command_handler_lookup(cbase, prefix + 1);
|
||||
if (handler)
|
||||
{
|
||||
cmd->ptr = handler->ptr;
|
||||
cmd->handler = handler->handler;
|
||||
cmd->status = command_is_available(handler, user) ? cmd_status_ok : cmd_status_access_error;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd->status = cmd_status_not_found;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd->status = cmd_status_syntax_error;
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a command and break it down into a struct hub_command.
|
||||
*/
|
||||
struct hub_command* command_parse(struct command_base* cbase, const struct hub_user* user, const char* message)
|
||||
{
|
||||
struct linked_list* tokens = list_create();
|
||||
struct hub_command* cmd = NULL;
|
||||
struct command_handle* handle = NULL;
|
||||
|
||||
cmd = hub_malloc_zero(sizeof(struct hub_command));
|
||||
cmd->status = cmd_status_ok;
|
||||
cmd->message = message;
|
||||
cmd->prefix = NULL;
|
||||
cmd->args = list_create();
|
||||
cmd->user = user;
|
||||
|
||||
if (split_string(message, " ", tokens, 0) <= 0)
|
||||
{
|
||||
cmd->status = cmd_status_syntax_error;
|
||||
goto command_parse_cleanup;
|
||||
}
|
||||
|
||||
// Setup hub command.
|
||||
cmd->prefix = strdup(((char*) list_get_first(tokens)) + 1);
|
||||
|
||||
// Find a matching command handler
|
||||
handle = command_get_handler(cbase, list_get_first(tokens), user, cmd);
|
||||
if (cmd->status != cmd_status_ok)
|
||||
goto command_parse_cleanup;
|
||||
|
||||
// Parse arguments
|
||||
cmd->status = command_extract_arguments(cbase, user, handle, tokens, cmd->args);
|
||||
goto command_parse_cleanup;
|
||||
|
||||
command_parse_cleanup:
|
||||
list_clear(tokens, &hub_free);
|
||||
list_destroy(tokens);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
void command_get_syntax(struct command_handle* handler, struct cbuffer* buf)
|
||||
{
|
||||
size_t n, arg_count;
|
||||
@ -494,7 +217,7 @@ int command_check_args(struct hub_command* cmd, struct command_handle* handler)
|
||||
int command_invoke(struct command_base* cbase, struct hub_user* user, const char* message)
|
||||
{
|
||||
int ret = 0;
|
||||
struct hub_command* cmd = command_parse(cbase, user, message);
|
||||
struct hub_command* cmd = command_parse(cbase, cbase->hub, user, message);
|
||||
|
||||
switch (cmd->status)
|
||||
{
|
||||
@ -571,7 +294,7 @@ static int command_help(struct command_base* cbase, struct hub_user* user, struc
|
||||
|
||||
for (command = (struct command_handle*) list_get_first(cbase->handlers); command; command = (struct command_handle*) list_get_next(cbase->handlers))
|
||||
{
|
||||
if (command_is_available(command, user))
|
||||
if (command_is_available(command, user->credentials))
|
||||
{
|
||||
cbuf_append_format(buf, "!%s", command->prefix);
|
||||
for (n = strlen(command->prefix); n < cbase->prefix_length_max; n++)
|
||||
@ -583,7 +306,7 @@ static int command_help(struct command_base* cbase, struct hub_user* user, struc
|
||||
else
|
||||
{
|
||||
command = data->data.command;
|
||||
if (command_is_available(command, user))
|
||||
if (command_is_available(command, user->credentials))
|
||||
{
|
||||
cbuf_append_format(buf, "Usage: ");
|
||||
command_get_syntax(command, buf);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* uhub - A tiny ADC p2p connection hub
|
||||
* Copyright (C) 2007-2011, Jan Vidar Krey
|
||||
* Copyright (C) 2007-2012, Jan Vidar Krey
|
||||
*
|
||||
* 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
|
||||
@ -26,91 +26,6 @@ struct hub_command;
|
||||
|
||||
typedef int (*command_handler)(struct command_base* cbase, struct hub_user* user, struct hub_command* cmd);
|
||||
|
||||
enum command_parse_status
|
||||
{
|
||||
cmd_status_ok, /** <<< "Everything seems to OK" */
|
||||
cmd_status_not_found, /** <<< "Command was not found" */
|
||||
cmd_status_access_error, /** <<< "You don't have access to this command" */
|
||||
cmd_status_syntax_error, /** <<< "Not a valid command." */
|
||||
cmd_status_missing_args, /** <<< "Missing some or all required arguments." */
|
||||
cmd_status_arg_nick, /** <<< "A nick argument does not match an online user. ('n')" */
|
||||
cmd_status_arg_cid, /** <<< "A cid argument does not match an online user. ('i')." */
|
||||
cmd_status_arg_address, /** <<< "A address range argument is not valid ('a')." */
|
||||
cmd_status_arg_number, /** <<< "A number argument is not valid ('N')" */
|
||||
cmd_status_arg_cred, /** <<< "A credentials argument is not valid ('C')" */
|
||||
cmd_status_arg_command, /** <<< "A command argument is not valid ('c')" */
|
||||
};
|
||||
|
||||
enum hub_command_arg_type
|
||||
{
|
||||
type_integer,
|
||||
type_string,
|
||||
type_user,
|
||||
type_address,
|
||||
type_range,
|
||||
type_credentials,
|
||||
type_command
|
||||
};
|
||||
|
||||
struct hub_command_arg_data
|
||||
{
|
||||
enum hub_command_arg_type type;
|
||||
union {
|
||||
int integer;
|
||||
char* string;
|
||||
struct hub_user* user;
|
||||
struct ip_addr_encap* address;
|
||||
struct ip_range* range;
|
||||
enum auth_credentials credentials;
|
||||
struct command_handle* command;
|
||||
} data;
|
||||
};
|
||||
|
||||
void hub_command_args_free(struct hub_command* command);
|
||||
|
||||
/**
|
||||
* This struct contains all information needed to invoke
|
||||
* a command, which includes the whole message, the prefix,
|
||||
* the decoded arguments (according to parameter list), and
|
||||
* the user pointer (ptr) which comes from the command it was matched to.
|
||||
*
|
||||
* The message and prefix is generally always available, but args only
|
||||
* if status == cmd_status_ok.
|
||||
* Handler and ptr are NULL if status == cmd_status_not_found, or status == cmd_status_access_error.
|
||||
* Ptr might also be NULL if cmd_status_ok because the command that handles it was added with a NULL ptr.
|
||||
*/
|
||||
struct hub_command
|
||||
{
|
||||
const char* message; /**<<< "The complete message." */
|
||||
char* prefix; /**<<< "The prefix extracted from the message." */
|
||||
struct linked_list* args; /**<<< "List of arguments of type struct hub_command_arg_data. Parsed from message." */
|
||||
enum command_parse_status status; /**<<< "Status of the hub_command." */
|
||||
command_handler handler; /**<<< "The function handler to call in order to invoke this command." */
|
||||
const struct hub_user* user; /**<<< "The user who invoked this command." */
|
||||
void* ptr; /**<<< "A pointer of data which came from struct command_handler" */
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset the command argument iterator and return the number of arguments
|
||||
* that can be extracted from a parsed command.
|
||||
*
|
||||
* @param cmd the command to start iterating arguments
|
||||
* @return returns the number of arguments provided for the command
|
||||
*/
|
||||
extern size_t hub_command_arg_reset(struct hub_command* cmd);
|
||||
|
||||
/**
|
||||
* Obtain the current argument and place it in data and increments the iterator.
|
||||
* If no argument exists, or the argument is of a different type than \param type, then 0 is returned.
|
||||
*
|
||||
* NOTE: when calling hub_command_arg_next the first time during a command callback it is safe to assume
|
||||
* that the first argument will be extracted. Thus you don't need to call hub_command_arg_reset().
|
||||
*
|
||||
* @param cmd the command used for iterating arguments.
|
||||
* @param type the expected type of this argument
|
||||
* @return NULL if no argument is found or if the argument found does not match the expected type.
|
||||
*/
|
||||
extern struct hub_command_arg_data* hub_command_arg_next(struct hub_command* cmd, enum hub_command_arg_type type);
|
||||
|
||||
/**
|
||||
* Argument codes are used to automatically parse arguments
|
||||
@ -181,22 +96,15 @@ extern int command_del(struct command_base*, struct command_handle*);
|
||||
extern int command_invoke(struct command_base*, struct hub_user* user, const char* message);
|
||||
|
||||
/**
|
||||
* Parse a message as a command and return a status indicating if the command
|
||||
* is valid and that the arguments are sane.
|
||||
*
|
||||
* @param cbase Command base pointer.
|
||||
* @param user User who invoked the command.
|
||||
* @param message The message that is to be interpreted as a command (including the invokation prefix '!' or '+')
|
||||
*
|
||||
* @return a hub_command that must be freed with command_free(). @See struct hub_command.
|
||||
* Returns 1 if the command handle can be used with the given credentials, 0 otherwise.
|
||||
*/
|
||||
extern struct hub_command* command_parse(struct command_base* cbase, const struct hub_user* user, const char* message);
|
||||
int command_is_available(struct command_handle* handle, enum auth_credentials credentials);
|
||||
|
||||
/**
|
||||
* Free a hub_command that was created in command_parse().
|
||||
* Lookup a command handle based on prefix.
|
||||
* If no matching command handle is found then NULL is returned.
|
||||
*/
|
||||
extern void command_free(struct hub_command* command);
|
||||
|
||||
struct command_handle* command_handler_lookup(struct command_base* cbase, const char* prefix);
|
||||
|
||||
extern void commands_builtin_add(struct command_base*);
|
||||
extern void commands_builtin_remove(struct command_base*);
|
||||
|
@ -88,6 +88,7 @@ extern "C" {
|
||||
#include "core/route.h"
|
||||
#include "core/pluginloader.h"
|
||||
#include "core/hub.h"
|
||||
#include "core/command_parser.h"
|
||||
#include "core/commands.h"
|
||||
#include "core/inf.h"
|
||||
#include "core/hubevent.h"
|
||||
|
Loading…
Reference in New Issue
Block a user