summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/asterisk/channel.h1
-rw-r--r--main/channel.c10
-rw-r--r--main/pbx.c69
-rw-r--r--main/presencestate.c187
4 files changed, 209 insertions, 58 deletions
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 45e94ce29..40d87c993 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -631,6 +631,7 @@ struct ast_channel_tech {
struct ast_channel *(* const requester)(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause);
int (* const devicestate)(const char *device_number); /*!< Devicestate call back */
+ int (* const presencestate)(const char *presence_provider, char **subtype, char **message); /*!< Presencestate callback */
/*!
* \brief Start sending a literal DTMF digit
diff --git a/main/channel.c b/main/channel.c
index 4bf2f61ef..aab8e228c 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -275,7 +275,7 @@ static const char *party_number_plan2str(int plan)
/*! \brief Show channel types - CLI command */
static char *handle_cli_core_show_channeltypes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
-#define FORMAT "%-15.15s %-40.40s %-12.12s %-12.12s %-12.12s\n"
+#define FORMAT "%-15.15s %-40.40s %-13.13s %-13.13s %-13.13s %-13.13s\n"
struct chanlist *cl;
int count_chan = 0;
@@ -294,13 +294,14 @@ static char *handle_cli_core_show_channeltypes(struct ast_cli_entry *e, int cmd,
if (a->argc != 3)
return CLI_SHOWUSAGE;
- ast_cli(a->fd, FORMAT, "Type", "Description", "Devicestate", "Indications", "Transfer");
- ast_cli(a->fd, FORMAT, "-----------", "-----------", "-----------", "-----------", "-----------");
+ ast_cli(a->fd, FORMAT, "Type", "Description", "Devicestate", "Presencestate", "Indications", "Transfer");
+ ast_cli(a->fd, FORMAT, "-------------", "-------------", "-------------", "-------------", "-------------", "-------------");
AST_RWLIST_RDLOCK(&backends);
AST_RWLIST_TRAVERSE(&backends, cl, list) {
ast_cli(a->fd, FORMAT, cl->tech->type, cl->tech->description,
(cl->tech->devicestate) ? "yes" : "no",
+ (cl->tech->presencestate) ? "yes" : "no",
(cl->tech->indicate) ? "yes" : "no",
(cl->tech->transfer) ? "yes" : "no");
count_chan++;
@@ -375,6 +376,7 @@ static char *handle_cli_core_show_channeltype(struct ast_cli_entry *e, int cmd,
ast_cli(a->fd,
"-- Info about channel driver: %s --\n"
" Device State: %s\n"
+ "Presence State: %s\n"
" Indication: %s\n"
" Transfer : %s\n"
" Capabilities: %s\n"
@@ -385,6 +387,7 @@ static char *handle_cli_core_show_channeltype(struct ast_cli_entry *e, int cmd,
" Text Support: %s\n",
cl->tech->type,
(cl->tech->devicestate) ? "yes" : "no",
+ (cl->tech->presencestate) ? "yes" : "no",
(cl->tech->indicate) ? "yes" : "no",
(cl->tech->transfer) ? "yes" : "no",
ast_format_cap_get_names(cl->tech->capabilities, &codec_buf),
@@ -7415,6 +7418,7 @@ static int data_channeltypes_provider_handler(const struct ast_data_search *sear
ast_data_add_str(data_type, "name", cl->tech->type);
ast_data_add_str(data_type, "description", cl->tech->description);
ast_data_add_bool(data_type, "devicestate", cl->tech->devicestate ? 1 : 0);
+ ast_data_add_bool(data_type, "presencestate", cl->tech->presencestate ? 1 : 0);
ast_data_add_bool(data_type, "indications", cl->tech->indicate ? 1 : 0);
ast_data_add_bool(data_type, "transfer", cl->tech->transfer ? 1 : 0);
ast_data_add_bool(data_type, "send_digit_begin", cl->tech->send_digit_begin ? 1 : 0);
diff --git a/main/pbx.c b/main/pbx.c
index b605a9143..267717e48 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -1157,10 +1157,14 @@ static int add_hintdevice(struct ast_hint *hint, const char *devicelist)
return -1;
}
ast_str_set(&str, 0, "%s", devicelist);
- parse = parse_hint_device(str);
+ parse = ast_str_buffer(str);
- while ((cur = strsep(&parse, "&"))) {
+ /* Spit on '&' and ',' to handle presence hints as well */
+ while ((cur = strsep(&parse, "&,"))) {
devicelength = strlen(cur);
+ if (!devicelength) {
+ continue;
+ }
device = ao2_t_alloc(sizeof(*device) + devicelength, hintdevice_destroy,
"allocating a hintdevice structure");
if (!device) {
@@ -11828,10 +11832,12 @@ static int pbx_builtin_sayphonetic(struct ast_channel *chan, const char *data)
static void presence_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
{
- struct ast_presence_state_message *presence_state = stasis_message_data(msg);
+ struct ast_presence_state_message *presence_state;
struct ast_hint *hint;
struct ast_str *hint_app = NULL;
- struct ao2_iterator hint_iter;
+ struct ast_hintdevice *device;
+ struct ast_hintdevice *cmpdevice;
+ struct ao2_iterator *dev_iter;
struct ao2_iterator cb_iter;
char context_name[AST_MAX_CONTEXT];
char exten_name[AST_MAX_EXTENSION];
@@ -11840,38 +11846,48 @@ static void presence_state_cb(void *unused, struct stasis_subscription *sub, str
return;
}
+ presence_state = stasis_message_data(msg);
+
+ if (ao2_container_count(hintdevices) == 0) {
+ /* There are no hints monitoring devices. */
+ return;
+ }
+
hint_app = ast_str_create(1024);
if (!hint_app) {
return;
}
ast_mutex_lock(&context_merge_lock);/* Hold off ast_merge_contexts_and_delete */
- hint_iter = ao2_iterator_init(hints, 0);
- for (; (hint = ao2_iterator_next(&hint_iter)); ao2_cleanup(hint)) {
- struct ast_state_cb *state_cb;
- const char *app;
- char *parse;
- SCOPED_AO2LOCK(lock, hint);
- if (!hint->exten) {
- /* The extension has already been destroyed */
- continue;
- }
+ cmpdevice = ast_alloca(sizeof(*cmpdevice) + strlen(presence_state->provider));
+ strcpy(cmpdevice->hintdevice, presence_state->provider);
- /* Does this hint monitor the device that changed state? */
- app = ast_get_extension_app(hint->exten);
- if (ast_strlen_zero(app)) {
- /* The hint does not monitor presence at all. */
- continue;
- }
+ ast_mutex_lock(&context_merge_lock);/* Hold off ast_merge_contexts_and_delete */
+ dev_iter = ao2_t_callback(hintdevices,
+ OBJ_POINTER | OBJ_MULTIPLE,
+ hintdevice_cmp_multiple,
+ cmpdevice,
+ "find devices in container");
+ if (!dev_iter) {
+ ast_mutex_unlock(&context_merge_lock);
+ ast_free(hint_app);
+ return;
+ }
+
+ for (; (device = ao2_iterator_next(dev_iter)); ao2_t_ref(device, -1, "Next device")) {
+ struct ast_state_cb *state_cb;
- ast_str_set(&hint_app, 0, "%s", app);
- parse = parse_hint_presence(hint_app);
- if (ast_strlen_zero(parse)) {
+ if (!device->hint) {
+ /* Should never happen. */
continue;
}
- if (strcasecmp(parse, presence_state->provider)) {
- /* The hint does not monitor the presence provider. */
+ hint = device->hint;
+
+ ao2_lock(hint);
+ if (!hint->exten) {
+ /* The extension has already been destroyed */
+ ao2_unlock(hint);
continue;
}
@@ -11885,6 +11901,7 @@ static void presence_state_cb(void *unused, struct stasis_subscription *sub, str
ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
sizeof(exten_name));
ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(hint->exten));
+ ao2_unlock(hint);
/* Check to see if update is necessary */
if ((hint->last_presence_state == presence_state->state) &&
@@ -11928,7 +11945,7 @@ static void presence_state_cb(void *unused, struct stasis_subscription *sub, str
}
ao2_iterator_destroy(&cb_iter);
}
- ao2_iterator_destroy(&hint_iter);
+ ao2_iterator_destroy(dev_iter);
ast_mutex_unlock(&context_merge_lock);
ast_free(hint_app);
diff --git a/main/presencestate.c b/main/presencestate.c
index 07df7429d..529979bac 100644
--- a/main/presencestate.c
+++ b/main/presencestate.c
@@ -66,6 +66,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/presencestate.h"
#include "asterisk/pbx.h"
#include "asterisk/app.h"
+#include "asterisk/test.h"
/*! \brief Device state strings for printing */
static const struct {
@@ -146,42 +147,74 @@ static enum ast_presence_state presence_state_cached(const char *presence_provid
static enum ast_presence_state ast_presence_state_helper(const char *presence_provider, char **subtype, char **message, int check_cache)
{
- struct presence_state_provider *provider;
- char *address;
- char *label = ast_strdupa(presence_provider);
- int res = AST_PRESENCE_INVALID;
-
- if (check_cache) {
- res = presence_state_cached(presence_provider, subtype, message);
- if (res != AST_PRESENCE_INVALID) {
- return res;
+ char *labels = ast_strdupa(presence_provider);
+ char *label;
+ enum ast_presence_state state = AST_PRESENCE_INVALID;
+ enum ast_presence_state state_order[] = {
+ [AST_PRESENCE_INVALID] = 0,
+ [AST_PRESENCE_NOT_SET] = 1,
+ [AST_PRESENCE_AVAILABLE] = 2,
+ [AST_PRESENCE_UNAVAILABLE] = 3,
+ [AST_PRESENCE_CHAT] = 4,
+ [AST_PRESENCE_AWAY] = 5,
+ [AST_PRESENCE_XA] = 6,
+ [AST_PRESENCE_DND] = 7
+ };
+
+ while ((label = strsep(&labels, "&"))) {
+ enum ast_presence_state next_state = AST_PRESENCE_INVALID;
+ char *next_subtype = NULL;
+ char *next_message = NULL;
+
+ if (check_cache) {
+ next_state = presence_state_cached(label, &next_subtype, &next_message);
}
- }
- if ((address = strchr(label, ':'))) {
- *address = '\0';
- address++;
- } else {
- ast_log(LOG_WARNING, "No label found for presence state provider: %s\n", presence_provider);
- return res;
- }
+ if (next_state == AST_PRESENCE_INVALID) {
+ struct presence_state_provider *provider;
+ const struct ast_channel_tech *chan_tech;
+ char *address;
+
+ if ((address = strchr(label, '/'))) {
+ *address++ = '\0';
+
+ if ((chan_tech = ast_get_channel_tech(label)) && chan_tech->presencestate) {
+ next_state = chan_tech->presencestate(address, &next_subtype, &next_message);
+ }
+ } else if ((address = strchr(label, ':'))) {
+ *address++ = '\0';
+
+ AST_RWLIST_RDLOCK(&presence_state_providers);
+ AST_RWLIST_TRAVERSE(&presence_state_providers, provider, list) {
+ ast_debug(5, "Checking provider %s with %s\n", provider->label, label);
+
+ if (!strcasecmp(provider->label, label)) {
+ next_state = provider->callback(address, &next_subtype, &next_message);
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&presence_state_providers);
+
+ if (!provider) {
+ ast_log(LOG_WARNING, "No provider found for label: %s\n", label);
+ }
+ } else {
+ ast_log(LOG_WARNING, "No label found for presence state provider: %s\n", label);
+ }
+ }
- AST_RWLIST_RDLOCK(&presence_state_providers);
- AST_RWLIST_TRAVERSE(&presence_state_providers, provider, list) {
- ast_debug(5, "Checking provider %s with %s\n", provider->label, label);
+ if (state_order[next_state] > state_order[state]) {
+ state = next_state;
- if (!strcasecmp(provider->label, label)) {
- res = provider->callback(address, subtype, message);
- break;
- }
- }
- AST_RWLIST_UNLOCK(&presence_state_providers);
+ ast_free(*subtype);
+ ast_free(*message);
- if (!provider) {
- ast_log(LOG_WARNING, "No provider found for label %s\n", label);
+ *subtype = next_subtype;
+ *message = next_message;
+ }
}
- return res;
+ return state;
}
enum ast_presence_state ast_presence_state(const char *presence_provider, char **subtype, char **message)
@@ -354,6 +387,99 @@ static const char *presence_state_get_id(struct stasis_message *msg)
return presence_state->provider;
}
+#if defined(TEST_FRAMEWORK)
+
+#define TEST_CATEGORY "/main/presence"
+
+static int presence_test_alice_state = AST_PRESENCE_UNAVAILABLE;
+static int presence_test_bob_state = AST_PRESENCE_UNAVAILABLE;
+
+static int presence_test_presencestate(const char *label, char **subtype, char **message)
+{
+ if (!strcmp(label, "Alice")) {
+ return presence_test_alice_state;
+ } else if (!strcmp(label, "Bob")) {
+ return presence_test_bob_state;
+ } else {
+ return AST_PRESENCE_UNAVAILABLE;
+ }
+}
+
+static struct ast_channel_tech presence_test_tech = {
+ .type = "PresenceTestChannel",
+ .description = "Presence test technology",
+ .presencestate = presence_test_presencestate,
+};
+
+AST_TEST_DEFINE(test_presence_chan)
+{
+ int res = AST_TEST_FAIL;
+ char provider[80];
+ enum ast_presence_state state;
+ char *subtype = NULL, *message = NULL;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "channel_presence";
+ info->category = TEST_CATEGORY;
+ info->summary = "Channel presence state tests";
+ info->description = "Creates test channel technology and then test the presence state callback";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ if (ast_channel_register(&presence_test_tech)) {
+ ast_log(LOG_WARNING, "Unable to register channel type '%s'\n", presence_test_tech.type);
+ goto presence_test_cleanup;
+ }
+
+ /* Check Alice's state */
+ snprintf(provider, sizeof(provider), "%s/Alice", presence_test_tech.type);
+
+ presence_test_alice_state = AST_PRESENCE_AVAILABLE;
+ state = ast_presence_state_nocache(provider, &subtype, &message);
+
+ if (state != presence_test_alice_state) {
+ ast_log(LOG_WARNING, "Presence state of '%s' returned '%s' instead of the expected value '%s'\n",
+ provider, ast_presence_state2str(state), ast_presence_state2str(presence_test_alice_state));
+ goto presence_test_cleanup;
+ }
+
+ /* Check Alice's and Bob's state, Alice's should win as DND > AVAILABLE */
+ snprintf(provider, sizeof(provider), "%s/Alice&%s/Bob", presence_test_tech.type, presence_test_tech.type);
+
+ presence_test_alice_state = AST_PRESENCE_DND;
+ presence_test_bob_state = AST_PRESENCE_UNAVAILABLE;
+ state = ast_presence_state_nocache(provider, &subtype, &message);
+
+ if (state != presence_test_alice_state) {
+ ast_log(LOG_WARNING, "Presence state of '%s' returned '%s' instead of the expected value '%s'\n",
+ provider, ast_presence_state2str(state), ast_presence_state2str(presence_test_alice_state));
+ goto presence_test_cleanup;
+ }
+
+ /* Check Alice's and Bob's state, Bob's should now win as AVAILABLE < UNAVAILABLE */
+ presence_test_alice_state = AST_PRESENCE_AVAILABLE;
+ state = ast_presence_state_nocache(provider, &subtype, &message);
+
+ if (state != presence_test_bob_state) {
+ ast_log(LOG_WARNING, "Presence state of '%s' returned '%s' instead of the expected value '%s'\n",
+ provider, ast_presence_state2str(state), ast_presence_state2str(presence_test_bob_state));
+ goto presence_test_cleanup;
+ }
+
+ res = AST_TEST_PASS;
+
+presence_test_cleanup:
+ ast_channel_unregister(&presence_test_tech);
+ ast_free(subtype);
+ ast_free(message);
+
+ return res;
+}
+#endif
+
static void presence_state_engine_cleanup(void)
{
ao2_cleanup(presence_state_topic_all);
@@ -362,6 +488,7 @@ static void presence_state_engine_cleanup(void)
presence_state_cache = NULL;
presence_state_topic_cached = stasis_caching_unsubscribe_and_join(presence_state_topic_cached);
STASIS_MESSAGE_TYPE_CLEANUP(ast_presence_state_message_type);
+ AST_TEST_UNREGISTER(test_presence_chan);
}
int ast_presence_state_engine_init(void)
@@ -387,6 +514,8 @@ int ast_presence_state_engine_init(void)
return -1;
}
+ AST_TEST_REGISTER(test_presence_chan);
+
return 0;
}