summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/asterisk/res_pjsip_outbound_publish.h22
-rw-r--r--res/res_pjsip_exten_state.c182
-rw-r--r--res/res_pjsip_outbound_publish.c14
3 files changed, 216 insertions, 2 deletions
diff --git a/include/asterisk/res_pjsip_outbound_publish.h b/include/asterisk/res_pjsip_outbound_publish.h
index debec9413..b2038f58b 100644
--- a/include/asterisk/res_pjsip_outbound_publish.h
+++ b/include/asterisk/res_pjsip_outbound_publish.h
@@ -93,6 +93,28 @@ void ast_sip_unregister_event_publisher_handler(struct ast_sip_event_publisher_h
struct ast_sip_outbound_publish_client *ast_sip_publish_client_get(const char *name);
/*!
+ * \brief Get the From URI the client will use.
+ * \since 14.0.0
+ *
+ * \param client The publication client to get the From URI
+ *
+ * \retval From-uri on success
+ * \retval Empty-string on failure
+ */
+const char *ast_sip_publish_client_get_from_uri(struct ast_sip_outbound_publish_client *client);
+
+/*!
+ * \brief Get the To URI the client will use.
+ * \since 14.0.0
+ *
+ * \param client The publication client to get the To URI
+ *
+ * \retval From-uri on success
+ * \retval Empty-string on failure
+ */
+const char *ast_sip_publish_client_get_to_uri(struct ast_sip_outbound_publish_client *client);
+
+/*!
* \brief Alternative for ast_datastore_alloc()
*
* There are two major differences between this and ast_datastore_alloc()
diff --git a/res/res_pjsip_exten_state.c b/res/res_pjsip_exten_state.c
index 6b4e758d3..76a88a29c 100644
--- a/res/res_pjsip_exten_state.c
+++ b/res/res_pjsip_exten_state.c
@@ -56,6 +56,9 @@
*/
static struct ao2_container *publishers;
+/*! Serializer for outbound extension state publishing. */
+static struct ast_taskprocessor *publish_exten_state_serializer;
+
/*!
* \brief A subscription for extension state
*
@@ -542,6 +545,142 @@ static void to_ami(struct ast_sip_subscription *sub,
exten_state_sub->last_exten_state));
}
+struct exten_state_pub_data {
+ /*! Publishers needing state update */
+ AST_VECTOR(name, struct exten_state_publisher *) pubs;
+ /*! Body generator state data */
+ struct ast_sip_exten_state_data exten_state_data;
+};
+
+static void exten_state_pub_data_destroy(struct exten_state_pub_data *doomed)
+{
+ if (!doomed) {
+ return;
+ }
+
+ ast_free((void *) doomed->exten_state_data.exten);
+ ast_free(doomed->exten_state_data.presence_subtype);
+ ast_free(doomed->exten_state_data.presence_message);
+ ao2_cleanup(doomed->exten_state_data.device_state_info);
+
+ AST_VECTOR_CALLBACK_VOID(&doomed->pubs, ao2_ref, -1);
+ AST_VECTOR_FREE(&doomed->pubs);
+
+ ast_free(doomed);
+}
+
+static struct exten_state_pub_data *exten_state_pub_data_alloc(const char *exten, struct ast_state_cb_info *info)
+{
+ struct exten_state_pub_data *pub_data;
+
+ pub_data = ast_calloc(1, sizeof(*pub_data));
+ if (!pub_data) {
+ return NULL;
+ }
+
+ if (AST_VECTOR_INIT(&pub_data->pubs, ao2_container_count(publishers))) {
+ exten_state_pub_data_destroy(pub_data);
+ return NULL;
+ }
+
+ /* Save off currently known information for the body generators. */
+ pub_data->exten_state_data.exten = ast_strdup(exten);
+ pub_data->exten_state_data.exten_state = info->exten_state;
+ pub_data->exten_state_data.presence_state = info->presence_state;
+ pub_data->exten_state_data.presence_subtype = ast_strdup(info->presence_subtype);
+ pub_data->exten_state_data.presence_message = ast_strdup(info->presence_message);
+ pub_data->exten_state_data.device_state_info = ao2_bump(info->device_state_info);
+ if (!pub_data->exten_state_data.exten
+ || !pub_data->exten_state_data.presence_subtype
+ || !pub_data->exten_state_data.presence_message) {
+ exten_state_pub_data_destroy(pub_data);
+ return NULL;
+ }
+ return pub_data;
+}
+
+/*!
+ * \internal
+ * \brief Create exten state PUBLISH messages under PJSIP thread.
+ * \since 14.0.0
+ *
+ * \return 0
+ */
+static int exten_state_publisher_cb(void *data)
+{
+ struct exten_state_pub_data *pub_data = data;
+ struct exten_state_publisher *publisher;
+ size_t idx;
+ struct ast_str *body_text;
+ pj_pool_t *pool;
+ struct ast_sip_body_data gen_data = {
+ .body_type = AST_SIP_EXTEN_STATE_DATA,
+ .body_data = &pub_data->exten_state_data,
+ };
+ struct ast_sip_body body;
+
+ body_text = ast_str_create(64);
+ if (!body_text) {
+ exten_state_pub_data_destroy(pub_data);
+ return 0;
+ }
+
+ /* Need a PJSIP memory pool to generate the bodies. */
+ pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "pub_state_body",
+ 1024, 1024);
+ if (!pool) {
+ ast_log(LOG_WARNING, "Exten state publishing unable to create memory pool\n");
+ exten_state_pub_data_destroy(pub_data);
+ ast_free(body_text);
+ return 0;
+ }
+ pub_data->exten_state_data.pool = pool;
+
+ for (idx = 0; idx < AST_VECTOR_SIZE(&pub_data->pubs); ++idx) {
+ const char *uri;
+ int res;
+
+ publisher = AST_VECTOR_GET(&pub_data->pubs, idx);
+
+ uri = ast_sip_publish_client_get_from_uri(publisher->client);
+ if (ast_strlen_zero(uri)) {
+ ast_log(LOG_WARNING, "PUBLISH client '%s' has no from_uri or server_uri defined.\n",
+ publisher->name);
+ continue;
+ }
+ ast_copy_string(pub_data->exten_state_data.local, uri, sizeof(pub_data->exten_state_data.local));
+
+ uri = ast_sip_publish_client_get_to_uri(publisher->client);
+ if (ast_strlen_zero(uri)) {
+ ast_log(LOG_WARNING, "PUBLISH client '%s' has no to_uri or server_uri defined.\n",
+ publisher->name);
+ continue;
+ }
+ ast_copy_string(pub_data->exten_state_data.remote, uri, sizeof(pub_data->exten_state_data.remote));
+
+ res = ast_sip_pubsub_generate_body_content(publisher->body_type,
+ publisher->body_subtype, &gen_data, &body_text);
+ pj_pool_reset(pool);
+ if (res) {
+ ast_log(LOG_WARNING,
+ "PUBLISH client '%s' unable to generate %s/%s PUBLISH body.\n",
+ publisher->name, publisher->body_type, publisher->body_subtype);
+ continue;
+ }
+
+ body.type = publisher->body_type;
+ body.subtype = publisher->body_subtype;
+ body.body_text = ast_str_buffer(body_text);
+ ast_sip_publish_client_send(publisher->client, &body);
+ }
+
+ pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
+
+ ast_free(body_text);
+ exten_state_pub_data_destroy(pub_data);
+ return 0;
+}
+
/*!
* \brief Global extension state callback function
*/
@@ -549,17 +688,46 @@ static int exten_state_publisher_state_cb(const char *context, const char *exten
{
struct ao2_iterator publisher_iter;
struct exten_state_publisher *publisher;
-
+ struct exten_state_pub_data *pub_data = NULL;
+
+ ast_debug(5, "Exten state publisher: %s@%s Reason:%s State:%s Presence:%s Subtype:'%s' Message:'%s'\n",
+ exten, context,
+ info->reason == AST_HINT_UPDATE_DEVICE
+ ? "Device"
+ : info->reason == AST_HINT_UPDATE_PRESENCE
+ ? "Presence"
+ : "Unknown",
+ ast_extension_state2str(info->exten_state),
+ ast_presence_state2str(info->presence_state),
+ S_OR(info->presence_subtype, ""),
+ S_OR(info->presence_message, ""));
publisher_iter = ao2_iterator_init(publishers, 0);
for (; (publisher = ao2_iterator_next(&publisher_iter)); ao2_ref(publisher, -1)) {
if ((publisher->context_filter && regexec(&publisher->context_regex, context, 0, NULL, 0)) ||
(publisher->exten_filter && regexec(&publisher->exten_regex, exten, 0, NULL, 0))) {
continue;
}
- /* This is a placeholder for additional code to come */
+
+ if (!pub_data) {
+ pub_data = exten_state_pub_data_alloc(exten, info);
+ if (!pub_data) {
+ ao2_ref(publisher, -1);
+ break;
+ }
+ }
+
+ ao2_ref(publisher, +1);
+ AST_VECTOR_APPEND(&pub_data->pubs, publisher);
+ ast_debug(5, "'%s' will publish exten state\n", publisher->name);
}
ao2_iterator_destroy(&publisher_iter);
+ if (pub_data
+ && ast_sip_push_task(publish_exten_state_serializer, exten_state_publisher_cb,
+ pub_data)) {
+ exten_state_pub_data_destroy(pub_data);
+ }
+
return 0;
}
@@ -755,6 +923,10 @@ static int unload_module(void)
ast_sip_unregister_subscription_handler(&presence_handler);
ast_extension_state_del(0, exten_state_publisher_state_cb);
+
+ ast_taskprocessor_unreference(publish_exten_state_serializer);
+ publish_exten_state_serializer = NULL;
+
ao2_cleanup(publishers);
publishers = NULL;
@@ -777,6 +949,12 @@ static int load_module(void)
return AST_MODULE_LOAD_DECLINE;
}
+ publish_exten_state_serializer = ast_sip_create_serializer("pjsip/exten_state");
+ if (!publish_exten_state_serializer) {
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
if (ast_sip_register_subscription_handler(&presence_handler)) {
ast_log(LOG_WARNING, "Unable to register subscription handler %s\n",
presence_handler.event_name);
diff --git a/res/res_pjsip_outbound_publish.c b/res/res_pjsip_outbound_publish.c
index 0fe2e3e5d..0265c424e 100644
--- a/res/res_pjsip_outbound_publish.c
+++ b/res/res_pjsip_outbound_publish.c
@@ -425,6 +425,20 @@ struct ast_sip_outbound_publish_client *ast_sip_publish_client_get(const char *n
return state->client;
}
+const char *ast_sip_publish_client_get_from_uri(struct ast_sip_outbound_publish_client *client)
+{
+ struct ast_sip_outbound_publish *publish = client->publish;
+
+ return S_OR(publish->from_uri, S_OR(publish->server_uri, ""));
+}
+
+const char *ast_sip_publish_client_get_to_uri(struct ast_sip_outbound_publish_client *client)
+{
+ struct ast_sip_outbound_publish *publish = client->publish;
+
+ return S_OR(publish->to_uri, S_OR(publish->server_uri, ""));
+}
+
int ast_sip_register_event_publisher_handler(struct ast_sip_event_publisher_handler *handler)
{
struct ast_sip_event_publisher_handler *existing;