From 1dba731cc3a5ab4a89d03445d741499837c5ddf1 Mon Sep 17 00:00:00 2001 From: Jan Vidar Krey Date: Thu, 19 Apr 2012 00:21:49 +0200 Subject: [PATCH] Fix bug #158 - Added plugin for setting topic (hub description). Load plugin mod_topic, and it will provide 3 new user commands: !topic - set new topic !cleartopic - reset the topic (use default hub description) !showtopic - show the current topic --- GNUmakefile | 9 +- src/core/commands.c | 29 +++++- src/core/commands.h | 8 +- src/core/plugincallback.c | 56 ++++++++++++ src/plugin_api/handle.h | 9 ++ src/plugins/mod_topic.c | 101 +++++++++++++++++++++ vs2010/plugins/mod_topic/mod_topic.vcxproj | 85 +++++++++++++++++ vs2010/uhub.sln | 10 ++ 8 files changed, 303 insertions(+), 4 deletions(-) create mode 100644 src/plugins/mod_topic.c create mode 100644 vs2010/plugins/mod_topic/mod_topic.vcxproj diff --git a/GNUmakefile b/GNUmakefile index 62216b9..ec66fee 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -232,6 +232,9 @@ plugin_chat_history_TARGET := mod_chat_history.so plugin_chat_only_SOURCE := src/plugins/mod_chat_only.c plugin_chat_only_TARGET := mod_chat_only.so +plugin_topic_SOURCES := src/plugins/mod_topic.c +plugin_topic_TARGET := mod_topic.so + # Source to objects libuhub_OBJECTS := $(libuhub_SOURCES:.c=.o) libutils_OBJECTS := $(libutils_SOURCES:.c=.o) @@ -243,7 +246,7 @@ uhub-passwd_OBJECTS := $(uhub-passwd_SOURCES:.c=.o) adcrush_OBJECTS := $(adcrush_SOURCES:.c=.o) admin_OBJECTS := $(admin_SOURCES:.c=.o) -all_plugins := $(plugin_example_TARGET) $(plugin_logging_TARGET) $(plugin_auth_TARGET) $(plugin_auth_sqlite_TARGET) $(plugin_welcome_TARGET) $(plugin_chat_history_TARGET) $(plugin_chat_only_TARGET) +all_plugins := $(plugin_example_TARGET) $(plugin_logging_TARGET) $(plugin_auth_TARGET) $(plugin_auth_sqlite_TARGET) $(plugin_welcome_TARGET) $(plugin_chat_history_TARGET) $(plugin_chat_only_TARGET) $(plugin_topic_TARGET) all_OBJECTS := $(libuhub_OBJECTS) $(uhub_OBJECTS) $(libutils_OBJECTS) $(adcrush_OBJECTS) $(autotest_OBJECTS) $(admin_OBJECTS) $(libadc_common_OBJECTS) $(libadc_client_OBJECTS) all_OBJECTS += $(all_plugins) @@ -289,6 +292,10 @@ $(plugin_chat_only_TARGET): $(plugin_chat_only_SOURCE) $(libutils_OBJECTS) $(plugin_welcome_TARGET): $(plugin_welcome_SOURCES) $(libutils_OBJECTS) $(MSG_CC) $(CC) -shared -fPIC -o $@ $^ $(CFLAGS) +$(plugin_topic_TARGET): $(plugin_topic_SOURCES) $(libutils_OBJECTS) + $(MSG_CC) $(CC) -shared -fPIC -o $@ $^ $(CFLAGS) + + $(adcrush_BINARY): $(adcrush_OBJECTS) $(libuhub_OBJECTS) $(libutils_OBJECTS) $(libadc_common_OBJECTS) $(libadc_client_OBJECTS) $(MSG_LD) $(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS) diff --git a/src/core/commands.c b/src/core/commands.c index 113e218..420edcd 100644 --- a/src/core/commands.c +++ b/src/core/commands.c @@ -148,8 +148,11 @@ static enum command_parse_status command_extract_arguments(struct command_base* { 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; @@ -160,7 +163,27 @@ static enum command_parse_status command_extract_arguments(struct command_base* while (status == cmd_status_ok && (arg_code = command->args[arg++])) { - token = list_get_first(tokens); + 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) { status = (arg_code == '?' ? cmd_status_ok : cmd_status_missing_args); @@ -173,6 +196,10 @@ static enum command_parse_status command_extract_arguments(struct command_base* opt = 1; continue; + case '+': + greedy = 1; + continue; + case 'u': data = hub_malloc(sizeof(*data)); data->type = type_user; diff --git a/src/core/commands.h b/src/core/commands.h index 6688d05..6ba84da 100644 --- a/src/core/commands.h +++ b/src/core/commands.h @@ -128,12 +128,16 @@ extern struct hub_command_arg_data* hub_command_arg_next(struct hub_command* cmd * N = number (integer) * * Prefix an argument with ? to make it optional. - * NOTE; if an argument is optional then all following arguments must also be optional. + * Prefix with + to make the argument greedy, which causes it to grab the rest of the line ignoring boundaries (only supported for string types). + * + * NOTE: if an argument is optional then all following arguments must also be optional. + * NOTE: You can combine optional and greedy, example: "?+m" would match "", "a", "a b c", etc. * * Example: * "nia" means "nick cid ip" * "n?p" means "nick [password]" where password is optional. - * + * "?N?N" means two optional integers, this can also be expressed as "?NN". + * "?+m" means an optional string which may contain spaces that would otherwise be split into separate arguments. */ struct command_handle { diff --git a/src/core/plugincallback.c b/src/core/plugincallback.c index 7701241..0dd1e50 100644 --- a/src/core/plugincallback.c +++ b/src/core/plugincallback.c @@ -159,6 +159,58 @@ struct plugin_command_arg_data* cbfunc_command_arg_next(struct plugin_handle* pl return (struct plugin_command_arg_data*) hub_command_arg_next((struct hub_command*) cmd, (enum hub_command_arg_type) t); } +static char* cbfunc_get_hub_name(struct plugin_handle* plugin) +{ + struct hub_info* hub = plugin_get_hub(plugin); + char* str_encoded = adc_msg_get_named_argument(hub->command_info, ADC_INF_FLAG_NICK); + char* str = adc_msg_unescape(str_encoded); + hub_free(str_encoded); + return str; +} + +static char* cbfunc_get_hub_description(struct plugin_handle* plugin) +{ + struct hub_info* hub = plugin_get_hub(plugin); + char* str_encoded = adc_msg_get_named_argument(hub->command_info, ADC_INF_FLAG_DESCRIPTION); + char* str = adc_msg_unescape(str_encoded); + hub_free(str_encoded); + return str; +} + +static void cbfunc_set_hub_name(struct plugin_handle* plugin, const char* str) +{ + struct hub_info* hub = plugin_get_hub(plugin); + struct adc_message* command; + char* new_str = adc_msg_escape(str ? str : hub->config->hub_name); + + adc_msg_replace_named_argument(hub->command_info, ADC_INF_FLAG_NICK, new_str); + + // Broadcast hub name + command = adc_msg_construct(ADC_CMD_IINF, (strlen(new_str) + 8)); + adc_msg_add_named_argument(command, ADC_INF_FLAG_NICK, new_str); + route_to_all(hub, command); + + adc_msg_free(command); + hub_free(new_str); +} + +static void cbfunc_set_hub_description(struct plugin_handle* plugin, const char* str) +{ + struct hub_info* hub = plugin_get_hub(plugin); + struct adc_message* command; + char* new_str = adc_msg_escape(str ? str : hub->config->hub_description); + + adc_msg_replace_named_argument(hub->command_info, ADC_INF_FLAG_DESCRIPTION, new_str); + + // Broadcast hub description + command = adc_msg_construct(ADC_CMD_IINF, (strlen(new_str) + 8)); + adc_msg_add_named_argument(command, ADC_INF_FLAG_DESCRIPTION, new_str); + route_to_all(hub, command); + + adc_msg_free(command); + hub_free(new_str); +} + void plugin_register_callback_functions(struct plugin_handle* handle) { handle->hub.send_message = cbfunc_send_message; @@ -168,6 +220,10 @@ void plugin_register_callback_functions(struct plugin_handle* handle) handle->hub.command_del = cbfunc_command_del; handle->hub.command_arg_reset = cbfunc_command_arg_reset; handle->hub.command_arg_next = cbfunc_command_arg_next; + handle->hub.get_name = cbfunc_get_hub_name; + handle->hub.set_name = cbfunc_set_hub_name; + handle->hub.get_description = cbfunc_get_hub_description; + handle->hub.set_description = cbfunc_set_hub_description; } void plugin_unregister_callback_functions(struct plugin_handle* handle) diff --git a/src/plugin_api/handle.h b/src/plugin_api/handle.h index f714fcb..2baeeee 100644 --- a/src/plugin_api/handle.h +++ b/src/plugin_api/handle.h @@ -118,6 +118,11 @@ typedef int (*hfunc_command_del)(struct plugin_handle*, struct plugin_command_ha typedef size_t (*hfunc_command_arg_reset)(struct plugin_handle*, struct plugin_command*); typedef struct plugin_command_arg_data* (*hfunc_command_arg_next)(struct plugin_handle*, struct plugin_command*, enum plugin_command_arg_type); +typedef char* (*hfunc_get_hub_name)(struct plugin_handle*); +typedef char* (*hfunc_get_hub_description)(struct plugin_handle*); +typedef void (*hfunc_set_hub_name)(struct plugin_handle*, const char*); +typedef void (*hfunc_set_hub_description)(struct plugin_handle*, const char*); + /** * These are functions created and initialized by the hub and which can be used * by plugins to access functionality internal to the hub. @@ -131,6 +136,10 @@ struct plugin_hub_funcs hfunc_command_del command_del; hfunc_command_arg_reset command_arg_reset; hfunc_command_arg_next command_arg_next; + hfunc_get_hub_name get_name; + hfunc_get_hub_description get_description; + hfunc_set_hub_name set_name; + hfunc_set_hub_description set_description; }; struct plugin_handle diff --git a/src/plugins/mod_topic.c b/src/plugins/mod_topic.c new file mode 100644 index 0000000..555b596 --- /dev/null +++ b/src/plugins/mod_topic.c @@ -0,0 +1,101 @@ +/* + * 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 "plugin_api/handle.h" +#include "plugin_api/command_api.h" +#include "util/memory.h" +#include "util/cbuffer.h" + +struct topic_plugin_data +{ + struct plugin_command_handle* topic; + struct plugin_command_handle* cleartopic; + struct plugin_command_handle* showtopic; +}; + +static int command_topic_handler(struct plugin_handle* plugin, struct plugin_user* user, struct plugin_command* cmd) +{ + struct cbuffer* buf = cbuf_create(128); + struct plugin_command_arg_data* arg = plugin->hub.command_arg_next(plugin, cmd, plugin_cmd_arg_type_string); + + plugin->hub.set_description(plugin, arg ? arg->data.string : NULL); + cbuf_append_format(buf, "*** %s: Topic set to \"%s\"", cmd->prefix, arg->data.string); + plugin->hub.send_message(plugin, user, cbuf_get(buf)); + cbuf_destroy(buf); + return 0; +} + +static int command_cleartopic_handler(struct plugin_handle* plugin, struct plugin_user* user, struct plugin_command* cmd) +{ + struct cbuffer* buf = cbuf_create(128); + plugin->hub.set_description(plugin, NULL); + cbuf_append_format(buf, "*** %s: Topic cleared.", cmd->prefix); + plugin->hub.send_message(plugin, user, cbuf_get(buf)); + cbuf_destroy(buf); + return 0; +} + +static int command_showtopic_handler(struct plugin_handle* plugin, struct plugin_user* user, struct plugin_command* cmd) +{ + struct cbuffer* buf = cbuf_create(128); + char* topic = plugin->hub.get_description(plugin); + cbuf_append_format(buf, "*** %s: Current topic is: \"%s\"", cmd->prefix, topic); + plugin->hub.send_message(plugin, user, cbuf_get(buf)); + cbuf_destroy(buf); + hub_free(topic); + return 0; +} + +int plugin_register(struct plugin_handle* plugin, const char* config) +{ + struct topic_plugin_data* data = (struct topic_plugin_data*) hub_malloc(sizeof(struct topic_plugin_data)); + + data->topic = (struct plugin_command_handle*) hub_malloc_zero(sizeof(struct plugin_command_handle)); + data->cleartopic = (struct plugin_command_handle*) hub_malloc_zero(sizeof(struct plugin_command_handle)); + data->showtopic = (struct plugin_command_handle*) hub_malloc_zero(sizeof(struct plugin_command_handle)); + + PLUGIN_INITIALIZE(plugin, "Topic plugin", "1.0", "Add commands for changing the hub topic (description)"); + + PLUGIN_COMMAND_INITIALIZE(data->topic, (void*) data, "topic", "+m", auth_cred_operator, command_topic_handler, "Set new topic"); + PLUGIN_COMMAND_INITIALIZE(data->cleartopic, (void*) data, "cleartopic", "", auth_cred_operator, command_cleartopic_handler, "Clear the current topic"); + PLUGIN_COMMAND_INITIALIZE(data->showtopic, (void*) data, "showtopic", "", auth_cred_guest, command_showtopic_handler, "Shows the current topic"); + + plugin->hub.command_add(plugin, data->topic); + plugin->hub.command_add(plugin, data->cleartopic); + plugin->hub.command_add(plugin, data->showtopic); + plugin->ptr = data; + + + return 0; +} + +int plugin_unregister(struct plugin_handle* plugin) +{ + struct topic_plugin_data* data = (struct topic_plugin_data*) plugin->ptr; + + plugin->hub.command_del(plugin, data->topic); + plugin->hub.command_del(plugin, data->cleartopic); + plugin->hub.command_del(plugin, data->showtopic); + hub_free(data->topic); + hub_free(data->showtopic); + hub_free(data); + plugin->ptr = NULL; + return 0; +} + diff --git a/vs2010/plugins/mod_topic/mod_topic.vcxproj b/vs2010/plugins/mod_topic/mod_topic.vcxproj new file mode 100644 index 0000000..ca8b592 --- /dev/null +++ b/vs2010/plugins/mod_topic/mod_topic.vcxproj @@ -0,0 +1,85 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {9BBAEFDA-8E0E-4E7F-B8B6-00A3432FC45B} + Win32Proj + mod_topic + + + + DynamicLibrary + true + Unicode + + + DynamicLibrary + false + true + Unicode + + + + + + + + + + + + + true + $(SolutionDir)..\src;$(IncludePath) + $(SolutionDir)vs2010\$(Configuration);$(LibraryPath) + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXAMPLE_EXPORTS;%(PreprocessorDefinitions) + + + Windows + true + $(SolutionDir)\$(Configuration)\uhub_utils.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXAMPLE_EXPORTS;%(PreprocessorDefinitions) + + + Windows + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/vs2010/uhub.sln b/vs2010/uhub.sln index 8ebdc1e..dfe6770 100644 --- a/vs2010/uhub.sln +++ b/vs2010/uhub.sln @@ -32,6 +32,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "uhub-passwd", "uhub-passwd\ {A4FC9D62-D544-4D2A-88D7-A58529C3460B} = {A4FC9D62-D544-4D2A-88D7-A58529C3460B} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mod_topic", "plugins\mod_topic\mod_topic.vcxproj", "{9BBAEFDA-8E0E-4E7F-B8B6-00A3432FC45B}" + ProjectSection(ProjectDependencies) = postProject + {0A3C1519-D877-47D9-A82E-40AC1BC79D75} = {0A3C1519-D877-47D9-A82E-40AC1BC79D75} + {98D8BFE9-DACD-40E1-B7BE-C9079ABF832E} = {98D8BFE9-DACD-40E1-B7BE-C9079ABF832E} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -66,6 +72,10 @@ Global {E92EB860-AE9E-4532-A75F-0929A7BCF6D0}.Debug|Win32.Build.0 = Debug|Win32 {E92EB860-AE9E-4532-A75F-0929A7BCF6D0}.Release|Win32.ActiveCfg = Release|Win32 {E92EB860-AE9E-4532-A75F-0929A7BCF6D0}.Release|Win32.Build.0 = Release|Win32 + {9BBAEFDA-8E0E-4E7F-B8B6-00A3432FC45B}.Debug|Win32.ActiveCfg = Debug|Win32 + {9BBAEFDA-8E0E-4E7F-B8B6-00A3432FC45B}.Debug|Win32.Build.0 = Debug|Win32 + {9BBAEFDA-8E0E-4E7F-B8B6-00A3432FC45B}.Release|Win32.ActiveCfg = Release|Win32 + {9BBAEFDA-8E0E-4E7F-B8B6-00A3432FC45B}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE