From c3b368a68ab2929206776f6a1dcc1edcbbda2e38 Mon Sep 17 00:00:00 2001
From: Blair Bonnett
Date: Mon, 6 Aug 2012 23:58:57 +1200
Subject: [PATCH] Add ability to reserve SIDs for certain users.
If you have a bot connected to the hub, and you want to add a user
command to interact with the bot via PM, you need to know its session ID
(SID). However, SIDs are assigned when the client first connects, prior
to the nickname being sent, and so we cannot just assign a certain SID
based on the nickname as part of the connection routine.
To overcome this, this commit adds the ability to reserve the first few
SIDs (at hub start time, when the SIDs are known) for certain nicknames.
The user manager then checks each time a user logs in to see if the
nickname matches a reserved one, and if so sets up an alias from the
reserved SID to the SID the user was given. This alias is only checked
for private messages (the ADC DMSG or EMSG commands) which are routed to
the real user. Any other commands are ignored as there should be no need
for such aliasing.
The list of nicknames to reserve SIDs for is read from a space-separated
list in the reserved_sids parameter of the config file. The reserved
users must also be registered users (i.e., given a password) -- if they
are not, the alias is not set up for them.
---
src/core/config.xml | 18 +++++++
src/core/gen_config.c | 16 ++++++
src/core/gen_config.h | 1 +
src/core/hub.c | 7 +++
src/core/usermanager.c | 109 +++++++++++++++++++++++++++++++++++++++++
src/core/usermanager.h | 10 ++++
6 files changed, 161 insertions(+)
diff --git a/src/core/config.xml b/src/core/config.xml
index b5e949d..da0528b 100644
--- a/src/core/config.xml
+++ b/src/core/config.xml
@@ -117,6 +117,24 @@
0.4.0
+
+ Alias reserved SIDs to certain users.
+
+
+ When setting up user commands, you may want to be able to use them to private message a certain account (e.g., a bot connected to the hub). Since a session ID (SID) is allocated to a client prior to it sending its nickname, this is not directly possible. Instead, you can reserve fixed SIDs which are aliased to a particular user when they connect. As they are reserved when the hub starts up, these have predictable values.
+
+
+ Each nickname in the list must be separated by a space. The SID AAAA is reserved for the hub itself. The first nickname in the list will be aliased to AAAB, the second to AAAC and so on. Fixed SIDs can only be allocated to registered users - if somebody without a password logs in under a reserved nickname, the alias will not be applied (but they can still log in). Also, they are only checked when a private message is sent, so any other messages to/from the aliased SID will be dropped as an unknown user per the ADC protocol.
+
+ ]]>
+
+ 0.5.0
+
+
Support obsolete clients using a ADC protocol prior to 1.0
max_users = 500;
config->registered_users_only = 0;
config->register_self = 0;
+ config->reserved_sids = hub_strdup("");
config->obsolete_clients = 0;
config->chat_is_privileged = 0;
config->hub_name = hub_strdup("uhub");
@@ -195,6 +196,16 @@ static int apply_config(struct hub_config* config, char* key, char* data, int li
return 0;
}
+ if (!strcmp(key, "reserved_sids"))
+ {
+ if (!apply_string(key, data, &config->reserved_sids, (char*) ""))
+ {
+ LOG_ERROR("Configuration parse error on line %d", line_count);
+ return -1;
+ }
+ return 0;
+ }
+
if (!strcmp(key, "obsolete_clients"))
{
if (!apply_boolean(key, data, &config->obsolete_clients))
@@ -937,6 +948,8 @@ void free_config(struct hub_config* config)
hub_free(config->server_alt_ports);
+ hub_free(config->reserved_sids);
+
hub_free(config->hub_name);
hub_free(config->hub_description);
@@ -1059,6 +1072,9 @@ void dump_config(struct hub_config* config, int ignore_defaults)
if (!ignore_defaults || config->register_self != 0)
fprintf(stdout, "register_self = %s\n", config->register_self ? "yes" : "no");
+ if (!ignore_defaults || strcmp(config->reserved_sids, "") != 0)
+ fprintf(stdout, "reserved_sids = \"%s\"\n", config->reserved_sids);
+
if (!ignore_defaults || config->obsolete_clients != 0)
fprintf(stdout, "obsolete_clients = %s\n", config->obsolete_clients ? "yes" : "no");
diff --git a/src/core/gen_config.h b/src/core/gen_config.h
index a6effe1..8f3cac0 100644
--- a/src/core/gen_config.h
+++ b/src/core/gen_config.h
@@ -12,6 +12,7 @@ struct hub_config
int max_users; /*<<< Maximum number of users allowed on the hub (default: 500) */
int registered_users_only; /*<<< Allow registered users only (default: 0) */
int register_self; /*<<< Allow users to register themselves on the hub. (default: 0) */
+ char* reserved_sids; /*<<< Alias reserved SIDs to certain users. (default: "") */
int obsolete_clients; /*<<< Support obsolete clients using a ADC protocol prior to 1.0 (default: 0) */
int chat_is_privileged; /*<<< Allow chat for operators and above only (default: 0) */
char* hub_name; /*<<< Name of hub (default: "uhub") */
diff --git a/src/core/hub.c b/src/core/hub.c
index 8697f96..d0160a8 100644
--- a/src/core/hub.c
+++ b/src/core/hub.c
@@ -342,7 +342,14 @@ int hub_handle_chat_message(struct hub_info* hub, struct hub_user* u, struct adc
{
struct hub_user* target = uman_get_user_by_sid(hub, cmd->target);
if (target)
+ {
+ /* The SID the PM was sent to is reserved for some user. Hence
+ * we need to update the target SID in the message so it is
+ * routed properly. */
+ if(cmd->target != target->id.sid) strncpy(cmd->cache + 10, sid_to_string(target->id.sid), 4);
+
status = plugin_handle_private_message(hub, u, target, message_decoded, 0);
+ }
else
relay = 0;
}
diff --git a/src/core/usermanager.c b/src/core/usermanager.c
index 452eff4..7fe9aec 100644
--- a/src/core/usermanager.c
+++ b/src/core/usermanager.c
@@ -73,6 +73,60 @@ static void timer_statistics(struct timeout_evt* t)
timeout_queue_reschedule(net_backend_get_timeout_queue(), hub->users->timeout, TIMEOUT_STATS);
}
+int add_reserved_sid(char* nick, int splitcount, void* data)
+{
+ struct hub_user_manager* users = (struct hub_user_manager*)data;
+
+ /* Safety check: make sure the nickname can fit. */
+ size_t nicklen = strlen(nick);
+ if(nicklen > MAX_NICK_LEN)
+ {
+ LOG_ERROR("Nickname %s for reserved SID is too long (length %d, max %d)", nick, nicklen, MAX_NICK_LEN);
+ return -1;
+ }
+
+ /* Try to create a structure for the new reserved SID. */
+ struct reserved_sid* newresv = (struct reserved_sid*)malloc(sizeof(struct reserved_sid));
+ if(newresv == NULL)
+ {
+ LOG_ERROR("Could not allocate memory for reserved SID for %s", nick);
+ return -1;
+ }
+
+ /* Try to create a dummy user for the reserved SID. */
+ newresv->dummy_user = (struct hub_user*)malloc(sizeof(struct hub_user));
+ if(newresv->dummy_user == NULL)
+ {
+ LOG_ERROR("Could not allocate memory for reserved SID for %s", nick);
+ free(newresv);
+ return -1;
+ }
+ strncpy(newresv->dummy_user->id.nick, nick, nicklen+1);
+
+ /* No users logged in at this point. */
+ newresv->real_user = NULL;
+
+ /* Allocate the SID. */
+ newresv->pool = users->sids;
+ newresv->sid = sid_alloc(users->sids, newresv->dummy_user);
+
+ /* Add to the list and keep track of how many we've allocated. */
+ list_append(users->reserved, newresv);
+ users->reserved_end = newresv->sid;
+
+ /* Done. */
+ LOG_INFO("Reserved SID %s for %s", sid_to_string(newresv->sid), newresv->dummy_user->id.nick);
+ return 1;
+}
+
+void remove_reserved_sid(void *node)
+{
+ struct reserved_sid* resv = (struct reserved_sid*)node;
+ LOG_INFO("Removing reserved SID %s for %s", sid_to_string(resv->sid), resv->dummy_user->id.nick);
+ sid_free(resv->pool, resv->sid);
+ free(resv->dummy_user);
+}
+
int uman_init(struct hub_info* hub)
{
struct hub_user_manager* users = NULL;
@@ -100,6 +154,11 @@ int uman_init(struct hub_info* hub)
timeout_queue_insert(net_backend_get_timeout_queue(), users->timeout, TIMEOUT_STATS);
}
+ /* Process any reserved SIDs. */
+ users->reserved = list_create();
+ users->reserved_end = 0;
+ string_split(hub->config->reserved_sids, " ", (void*)users, &add_reserved_sid);
+
hub->users = users;
return 0;
}
@@ -116,6 +175,12 @@ int uman_shutdown(struct hub_info* hub)
hub_free(hub->users->timeout);
}
+ if (hub->users->reserved)
+ {
+ list_clear(hub->users->reserved, &remove_reserved_sid);
+ list_destroy(hub->users->reserved);
+ }
+
if (hub->users->list)
{
list_clear(hub->users->list, &clear_user_list_callback);
@@ -138,6 +203,23 @@ int uman_add(struct hub_info* hub, struct hub_user* user)
if (user->hub)
return -1;
+ /* Check if a SID has been reserved for this user. NB. user must be
+ * registered for reserved SIDs to be used. */
+ if(hub->users->reserved_end && user->credentials >= auth_cred_user)
+ {
+ struct reserved_sid* resv = (struct reserved_sid*)list_get_first(hub->users->reserved);
+ while(resv)
+ {
+ if(strcmp(resv->dummy_user->id.nick, user->id.nick) == 0)
+ {
+ resv->real_user = user;
+ LOG_INFO("Reserved user %s logged in.", user->id.nick);
+ break;
+ }
+ resv = (struct reserved_sid*)list_get_next(hub->users->reserved);
+ }
+ }
+
list_append(hub->users->list, user);
hub->users->count++;
hub->users->count_peak = MAX(hub->users->count, hub->users->count_peak);
@@ -154,6 +236,22 @@ int uman_remove(struct hub_info* hub, struct hub_user* user)
if (!hub || !user)
return -1;
+ /* Check if a SID has been reserved for this user. */
+ if(hub->users->reserved_end)
+ {
+ struct reserved_sid* resv = (struct reserved_sid*)list_get_first(hub->users->reserved);
+ while(resv)
+ {
+ if(resv->real_user == user)
+ {
+ resv->real_user = NULL;
+ LOG_INFO("Reserved user %s has left the building.", user->id.nick);
+ break;
+ }
+ resv = (struct reserved_sid*)list_get_next(hub->users->reserved);
+ }
+ }
+
list_remove(hub->users->list, user);
if (hub->users->count > 0)
@@ -176,6 +274,17 @@ int uman_remove(struct hub_info* hub, struct hub_user* user)
struct hub_user* uman_get_user_by_sid(struct hub_info* hub, sid_t sid)
{
+ /* This is a reserved SID. */
+ if(sid && sid <= hub->users->reserved_end)
+ {
+ struct reserved_sid* resv = (struct reserved_sid*)list_get_index(hub->users->reserved, sid-1);
+
+ /* See if the real user is currently logged on and return accordingly. */
+ if(resv->real_user != NULL) return resv->real_user;
+ return 0;
+ }
+
+ /* Use the SID lookup code. */
return sid_lookup(hub->users->sids, sid);
}
diff --git a/src/core/usermanager.h b/src/core/usermanager.h
index 7cc5190..65e252e 100644
--- a/src/core/usermanager.h
+++ b/src/core/usermanager.h
@@ -20,6 +20,14 @@
#ifndef HAVE_UHUB_USER_MANAGER_H
#define HAVE_UHUB_USER_MANAGER_H
+struct reserved_sid
+{
+ sid_t sid; /**<< "Reserved SID" */
+ struct sid_pool* pool; /**<< "Pool the SID was reserved from" */
+ struct hub_user* dummy_user; /**<< "Dummy user account for the reserved SID" */
+ struct hub_user* real_user; /**<< "Real user account the SID links to, or NULL if not logged in" */
+};
+
struct hub_user_manager
{
size_t count; /**<< "Number of all fully connected and logged in users" */
@@ -29,6 +37,8 @@ struct hub_user_manager
uint64_t shared_files; /**<< "The total number of shared files among fully connected users." */
struct linked_list* list; /**<< "Contains all logged in users" */
struct timeout_evt* timeout; /**<< "Timeout handler for statistics" */
+ struct linked_list* reserved; /**<< "Contains reserved SID information" */
+ sid_t reserved_end; /**<< "Value of last reserved SID" */
};
/**