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