From 74ca5a0a33df3db7d5987100549e1a97e234cff7 Mon Sep 17 00:00:00 2001 From: Jan Vidar Krey Date: Tue, 1 May 2012 20:15:43 +0200 Subject: [PATCH] Cleaned up command handling code, by splitting into multiple files. --- GNUmakefile | 1 + autotest/test_commands.tcc | 38 +++-- src/core/command_parser.c | 298 +++++++++++++++++++++++++++++++++++++ src/core/command_parser.h | 131 ++++++++++++++++ src/core/commands.c | 289 +---------------------------------- src/core/commands.h | 104 +------------ src/uhub.h | 1 + 7 files changed, 468 insertions(+), 394 deletions(-) create mode 100644 src/core/command_parser.c create mode 100644 src/core/command_parser.h diff --git a/GNUmakefile b/GNUmakefile index b3e4c3c..36364ae 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -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 \ diff --git a/autotest/test_commands.tcc b/autotest/test_commands.tcc index f7f0104..1790d05 100644 --- a/autotest/test_commands.tcc +++ b/autotest/test_commands.tcc @@ -1,6 +1,7 @@ #include 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); diff --git a/src/core/command_parser.c b/src/core/command_parser.c new file mode 100644 index 0000000..93ee5e2 --- /dev/null +++ b/src/core/command_parser.c @@ -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 . + * + */ + +#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; +} + diff --git a/src/core/command_parser.h b/src/core/command_parser.h new file mode 100644 index 0000000..34547e8 --- /dev/null +++ b/src/core/command_parser.h @@ -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 . + * + */ + +#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 */ diff --git a/src/core/commands.c b/src/core/commands.c index 9366024..b764186 100644 --- a/src/core/commands.c +++ b/src/core/commands.c @@ -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); diff --git a/src/core/commands.h b/src/core/commands.h index 8e85423..6f77534 100644 --- a/src/core/commands.h +++ b/src/core/commands.h @@ -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*); diff --git a/src/uhub.h b/src/uhub.h index b0dff42..d5fc0f7 100644 --- a/src/uhub.h +++ b/src/uhub.h @@ -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"