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" */
};
/**