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...)
This commit is contained in:
Blair Bonnett 2012-08-09 15:23:33 +12:00
parent 7a440211dd
commit dc94754488
3 changed files with 129 additions and 7 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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