diff options
-rw-r--r-- | include/asterisk/channel.h | 1 | ||||
-rw-r--r-- | main/channel.c | 10 | ||||
-rw-r--r-- | main/pbx.c | 69 | ||||
-rw-r--r-- | main/presencestate.c | 187 |
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; } |