Cleaned up command handling code, by splitting into multiple files.

This commit is contained in:
Jan Vidar Krey 2012-05-01 20:15:43 +02:00
parent 9efe4bce57
commit 74ca5a0a33
7 changed files with 468 additions and 394 deletions

View File

@ -136,6 +136,7 @@ OLD_REVISION=$(shell grep GIT_REVISION revision.h 2>/dev/null | cut -f 3 -d " "
# Sources # Sources
libuhub_SOURCES := \ libuhub_SOURCES := \
src/core/auth.c \ src/core/auth.c \
src/core/command_parser.c \
src/core/commands.c \ src/core/commands.c \
src/core/config.c \ src/core/config.c \
src/core/eventqueue.c \ src/core/eventqueue.c \

View File

@ -1,6 +1,7 @@
#include <uhub.h> #include <uhub.h>
static struct hub_info* hub = NULL; static struct hub_info* hub = NULL;
static struct hub_command* cmd = NULL;
static struct hub_user user; static struct hub_user user;
static struct command_base* cbase = NULL; static struct command_base* cbase = NULL;
static struct command_handle* c_test1 = NULL; static struct command_handle* c_test1 = NULL;
@ -17,6 +18,7 @@ static int result = 0;
EXO_TEST(setup, { EXO_TEST(setup, {
hub = hub_malloc_zero(sizeof(struct hub_info)); hub = hub_malloc_zero(sizeof(struct hub_info));
cbase = command_initialize(hub); cbase = command_initialize(hub);
hub->commands = cbase;
return cbase && hub && uman_init(hub) == 0; 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) 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; enum command_parse_status status = cmd->status;
command_free(cmd); command_free(cmd);
return status == expected; 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); }); 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, { 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; return verify_argument(cmd, type_integer) == NULL;
}); });
EXO_TEST(command_argument_integer_2, { 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; return verify_arg_integer(cmd, 10) && verify_arg_integer(cmd, 42) && verify_argument(cmd, type_integer) == NULL;
}); });
EXO_TEST(command_argument_integer_3, { 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); return verify_arg_integer(cmd, 10) && verify_arg_integer(cmd, 42) && verify_arg_integer(cmd, 6784);
}); });
EXO_TEST(command_argument_user_1, { 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) ; return verify_arg_user(cmd, &user) ;
}); });
EXO_TEST(command_argument_cid_1, { 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) ; return verify_arg_user(cmd, &user) ;
}); });
EXO_TEST(command_argument_cred_1, { 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);; return verify_arg_cred(cmd, auth_cred_admin);;
}); });
EXO_TEST(command_argument_cred_2, { 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);; return verify_arg_cred(cmd, auth_cred_operator);;
}); });
EXO_TEST(command_argument_cred_3, { 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); return verify_arg_cred(cmd, auth_cred_operator);
}); });
EXO_TEST(command_argument_cred_4, { 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); return verify_arg_cred(cmd, auth_cred_super);
}); });
EXO_TEST(command_argument_cred_5, { 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); return verify_arg_cred(cmd, auth_cred_guest);
}); });
EXO_TEST(command_argument_cred_6, { 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); 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_user_destroy, { return uman_remove(hub, &user) == 0; });
EXO_TEST(command_destroy, { EXO_TEST(command_destroy, {
command_free(cmd);
cmd = NULL;
DEL_TEST(c_test1); DEL_TEST(c_test1);
DEL_TEST(c_test2); DEL_TEST(c_test2);
DEL_TEST(c_test3); DEL_TEST(c_test3);

298
src/core/command_parser.c Normal file
View 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
View 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 */

View File

@ -38,7 +38,6 @@ struct command_base
struct hub_info* hub; struct hub_info* hub;
struct linked_list* handlers; struct linked_list* handlers;
size_t prefix_length_max; 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) 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; 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(handle != NULL);
uhub_assert(user != NULL); return handle->cred <= credentials;
return handle->cred <= user->credentials;
} }
void hub_command_args_free(struct hub_command* cmd)
{
struct hub_command_arg_data* data = NULL;
if (!cmd->args) struct command_handle* command_handler_lookup(struct command_base* cbase, const char* prefix)
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* handler = NULL; struct command_handle* handler = NULL;
size_t prefix_len = strlen(prefix); 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) void command_get_syntax(struct command_handle* handler, struct cbuffer* buf)
{ {
size_t n, arg_count; 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 command_invoke(struct command_base* cbase, struct hub_user* user, const char* message)
{ {
int ret = 0; 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) 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)) 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); cbuf_append_format(buf, "!%s", command->prefix);
for (n = strlen(command->prefix); n < cbase->prefix_length_max; n++) 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 else
{ {
command = data->data.command; command = data->data.command;
if (command_is_available(command, user)) if (command_is_available(command, user->credentials))
{ {
cbuf_append_format(buf, "Usage: "); cbuf_append_format(buf, "Usage: ");
command_get_syntax(command, buf); command_get_syntax(command, buf);

View File

@ -1,6 +1,6 @@
/* /*
* uhub - A tiny ADC p2p connection hub * 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 * 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 * 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); 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 * 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); 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 * Returns 1 if the command handle can be used with the given credentials, 0 otherwise.
* 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, 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_add(struct command_base*);
extern void commands_builtin_remove(struct command_base*); extern void commands_builtin_remove(struct command_base*);

View File

@ -88,6 +88,7 @@ extern "C" {
#include "core/route.h" #include "core/route.h"
#include "core/pluginloader.h" #include "core/pluginloader.h"
#include "core/hub.h" #include "core/hub.h"
#include "core/command_parser.h"
#include "core/commands.h" #include "core/commands.h"
#include "core/inf.h" #include "core/inf.h"
#include "core/hubevent.h" #include "core/hubevent.h"