From dc947544880ea2d05f5953edf5503a0ce78774ce Mon Sep 17 00:00:00 2001 From: Blair Bonnett Date: Thu, 9 Aug 2012 15:23:33 +1200 Subject: [PATCH] Add hub function for plugins to get list of currently connected users. Can be filtered to include only users of a certain credential level, or users of a certain credential level or greater. Results are returned as a linked list of plugin_user objects. Hub function to free the memory used by the list also added. As plugins may store the list, users may disconnect before the list is used. Hence a copy of each user is made for the list, so that if a plugin tries to send a message to a user who has left the hub it will be ignored, rather than the hub trying to access memory that was free()d when the user disconnected. The lookup function to turn a plugin_user into a hub_user for some of the other hub functions is changed accordingly. (I think this description is possibly more confusing than just looking at the code, but oh well...) --- src/core/plugincallback.c | 120 ++++++++++++++++++++++++++++++++++++-- src/core/pluginucmd.c | 11 +++- src/plugin_api/handle.h | 5 ++ 3 files changed, 129 insertions(+), 7 deletions(-) diff --git a/src/core/plugincallback.c b/src/core/plugincallback.c index 09902b8..e1b8daa 100644 --- a/src/core/plugincallback.c +++ b/src/core/plugincallback.c @@ -51,18 +51,24 @@ static int plugin_command_dispatch(struct command_base* cbase, struct hub_user* return 0; } -static struct hub_user* convert_user_type(struct plugin_user* user) +static struct hub_user* convert_user_type(struct plugin_handle* plugin, struct plugin_user* user) { - struct hub_user* huser = (struct hub_user*) user; - return huser; + /* The plugin_user is not guaranteed to point at the same memory as the + * corresponding hub_user - for example, get_user_list() makes a copy of + * the data in case the user quits before the plugin uses the list. Hence + * we need to look it up by SID. */ + struct hub_info* hub = plugin_get_hub(plugin); + return uman_get_user_by_sid(hub, user->sid); } static int cbfunc_send_message(struct plugin_handle* plugin, struct plugin_user* user, const char* message) { + struct hub_user* huser = convert_user_type(plugin, user); + if(huser == NULL) return 0; char* buffer = adc_msg_escape(message); struct adc_message* command = adc_msg_construct(ADC_CMD_IMSG, strlen(buffer) + 6); adc_msg_add_argument(command, buffer); - route_to_user(plugin_get_hub(plugin), convert_user_type(user), command); + route_to_user(plugin_get_hub(plugin), huser, command); adc_msg_free(command); hub_free(buffer); return 1; @@ -70,13 +76,15 @@ static int cbfunc_send_message(struct plugin_handle* plugin, struct plugin_user* static int cbfunc_send_status(struct plugin_handle* plugin, struct plugin_user* user, int code, const char* message) { + struct hub_user* huser = convert_user_type(plugin, user); + if(huser == NULL) return 0; char code_str[4]; char* buffer = adc_msg_escape(message); struct adc_message* command = adc_msg_construct(ADC_CMD_ISTA, strlen(buffer) + 10); snprintf(code_str, sizeof(code_str), "%03d", code); adc_msg_add_argument(command, code_str); adc_msg_add_argument(command, buffer); - route_to_user(plugin_get_hub(plugin), convert_user_type(user), command); + route_to_user(plugin_get_hub(plugin), huser, command); adc_msg_free(command); hub_free(buffer); return 1; @@ -84,7 +92,8 @@ static int cbfunc_send_status(struct plugin_handle* plugin, struct plugin_user* static int cbfunc_user_disconnect(struct plugin_handle* plugin, struct plugin_user* user) { - hub_disconnect_user(plugin_get_hub(plugin), convert_user_type(user), quit_kicked); + struct hub_user* huser = convert_user_type(plugin, user); + if(huser != NULL) hub_disconnect_user(plugin_get_hub(plugin), huser, quit_kicked); return 0; } @@ -185,6 +194,103 @@ static void cbfunc_set_hub_description(struct plugin_handle* plugin, const char* hub_free(new_str); } +/* Get a list of users currently connected to the hub. The list can be filtered + * with the credentials parameter: + * - auth_cred_none means no filtering i.e., everybody returned. + * - Any of the other auth_cred_xxx values means only users of that credential + * level are returned. + * - The negative of an auth_cred_xxx value means only users of at least that + * credential level are returned. For example, -auth_cred_operators returns + * any operators or admins. + * + * NULL is returned on error, and an empty list is returned if no users match + * the requested credentials. + */ +static struct linked_list* cbfunc_get_user_list(struct plugin_handle* plugin, enum auth_credentials credentials) +{ + /* Determine the comparison mode. */ + int atleast = 0; + if((int)credentials < 0) + { + credentials = -credentials; + atleast = 1; + } + + /* Check the credential level is valid. */ + if(credentials > auth_cred_admin) + { + plugin->error_msg = "Invalid credential level in get_user_list"; + return NULL; + } + + /* Get the master user list and prepare our copy. */ + struct hub_info* hub = plugin_get_hub(plugin); + struct linked_list* orig_list = hub->users->list; + struct linked_list* new_list = list_create(); + if(new_list == NULL) + { + plugin->error_msg = "Unable to allocate memory for user list"; + return NULL; + } + + /* Go through each connected user. */ + struct hub_user* user = (struct hub_user*)list_get_first(orig_list); + while(user != NULL) + { + /* Check if we should be including them in the output. */ + int include = 0; + if(credentials == 0) include = 1; + else + { + if(atleast) + { + if(user->credentials >= credentials) include = 1; + } + else + { + if(user->credentials == credentials) include = 1; + } + } + + /* Do we need to include this user? */ + if(include) + { + /* Try to allocate space. We are going to make a copy of the user + * data in case the user disconnects before the plugin uses the + * list. This way, any hub functions the plugin tries to call will + * fail, but at least it won't be trying to access free'd memory. */ + struct plugin_user* puser = (struct plugin_user*)malloc(sizeof(struct plugin_user)); + if(puser == NULL) + { + plugin->error_msg = "Unable to allocate memory for list entry in get_user_list."; + list_clear(new_list, &free); + list_destroy(new_list); + return NULL; + } + + /* Copy the pertinent information across and add it to the list. */ + memcpy(puser, user, sizeof(struct plugin_user)); + list_append(new_list, puser); + } + + /* Next user please. */ + user = (struct hub_user*)list_get_next(orig_list); + } + + /* Done. */ + return new_list; +} + +/* Clean up the memory used by a user list. */ +static void cbfunc_free_user_list(struct plugin_handle* handle, struct linked_list* list) +{ + if(list != NULL) + { + list_clear(list, &free); + list_destroy(list); + } +} + void plugin_register_callback_functions(struct plugin_handle* handle) { handle->hub.send_message = cbfunc_send_message; @@ -203,6 +309,8 @@ void plugin_register_callback_functions(struct plugin_handle* handle) handle->hub.ucmd_add_pm = cbfunc_ucmd_add_pm; handle->hub.ucmd_send = cbfunc_ucmd_send; handle->hub.ucmd_free = cbfunc_ucmd_free; + handle->hub.get_user_list = cbfunc_get_user_list; + handle->hub.free_user_list = cbfunc_free_user_list; } void plugin_unregister_callback_functions(struct plugin_handle* handle) diff --git a/src/core/pluginucmd.c b/src/core/pluginucmd.c index af72924..29f51ed 100644 --- a/src/core/pluginucmd.c +++ b/src/core/pluginucmd.c @@ -305,6 +305,15 @@ int cbfunc_ucmd_add_pm(struct plugin_handle* plugin, struct plugin_ucmd* ucmd, c int cbfunc_ucmd_send(struct plugin_handle* plugin, struct plugin_user* user, struct plugin_ucmd* ucmd) { + /* Check the user is still connected. */ + struct hub_info* hub = plugin_get_hub(plugin); + struct hub_user* huser = uman_get_user_by_sid(hub, user->sid); + if(huser == NULL) + { + plugin->error_msg = "User is not connected to the hub."; + return 0; + } + /* Make sure we have a command. */ if(!ucmd->length && !(ucmd->separator || ucmd->remove)) { @@ -359,7 +368,7 @@ int cbfunc_ucmd_send(struct plugin_handle* plugin, struct plugin_user* user, str } /* Send it. */ - route_to_user(plugin_get_hub(plugin), (struct hub_user*)user, message); + route_to_user(hub, huser, message); /* Success. */ adc_msg_free(message); diff --git a/src/plugin_api/handle.h b/src/plugin_api/handle.h index 8456118..4644ac2 100644 --- a/src/plugin_api/handle.h +++ b/src/plugin_api/handle.h @@ -129,6 +129,9 @@ typedef int (*hfunc_ucmd_add_pm)(struct plugin_handle*, struct p typedef int (*hfunc_ucmd_send)(struct plugin_handle*, struct plugin_user*, struct plugin_ucmd*); typedef void (*hfunc_ucmd_free)(struct plugin_handle*, struct plugin_ucmd*); +typedef struct linked_list* (*hfunc_get_user_list)(struct plugin_handle*, enum auth_credentials); +typedef void (*hfunc_free_user_list)(struct plugin_handle*, struct linked_list*); + /** * These are functions created and initialized by the hub and which can be used * by plugins to access functionality internal to the hub. @@ -151,6 +154,8 @@ struct plugin_hub_funcs hfunc_ucmd_add_pm ucmd_add_pm; hfunc_ucmd_send ucmd_send; hfunc_ucmd_free ucmd_free; + hfunc_get_user_list get_user_list; + hfunc_free_user_list free_user_list; }; struct plugin_handle