Skip to content
Snippets Groups Projects
Commit 057ed940 authored by zuul's avatar zuul Committed by Gerrit Code Review
Browse files

Merge "res_pjsip_exten_state: Add config support for exten state publishers."

parents 9309a96a 81ea80b7
No related branches found
No related tags found
No related merge requests found
......@@ -20,16 +20,20 @@
<depend>pjproject</depend>
<depend>res_pjsip</depend>
<depend>res_pjsip_pubsub</depend>
<depend>res_pjsip_outbound_publish</depend>
<support_level>core</support_level>
***/
#include "asterisk.h"
#include <regex.h>
#include <pjsip.h>
#include <pjsip_simple.h>
#include <pjlib.h>
#include "asterisk/res_pjsip.h"
#include "asterisk/res_pjsip_outbound_publish.h"
#include "asterisk/res_pjsip_pubsub.h"
#include "asterisk/res_pjsip_body_generator_types.h"
#include "asterisk/module.h"
......@@ -42,6 +46,16 @@
#define BODY_SIZE 1024
#define EVENT_TYPE_SIZE 50
/*!
* \brief The number of buckets to use for storing publishers
*/
#define PUBLISHER_BUCKETS 31
/*!
* \brief Container of active outbound extension state publishers
*/
static struct ao2_container *publishers;
/*!
* \brief A subscription for extension state
*
......@@ -68,6 +82,29 @@ struct exten_state_subscription {
enum ast_presence_state last_presence_state;
};
/*!
* \brief An extension state publisher
*
*/
struct exten_state_publisher {
/*! Regular expression for context filtering */
regex_t context_regex;
/*! Regular expression for extension filtering */
regex_t exten_regex;
/*! Publish client to use for sending publish messages */
struct ast_sip_outbound_publish_client *client;
/*! Whether context filtering is active */
unsigned int context_filter;
/*! Whether extension filtering is active */
unsigned int exten_filter;
/*! The body type to use for this publisher - stored after the name */
char *body_type;
/*! The body subtype to use for this publisher - stored after the body type */
char *body_subtype;
/*! The name of this publisher */
char name[0];
};
#define DEFAULT_PRESENCE_BODY "application/pidf+xml"
#define DEFAULT_DIALOG_BODY "application/dialog-info+xml"
......@@ -77,6 +114,9 @@ static int subscription_established(struct ast_sip_subscription *sub);
static void *get_notify_data(struct ast_sip_subscription *sub);
static void to_ami(struct ast_sip_subscription *sub,
struct ast_str **buf);
static int publisher_start(struct ast_sip_outbound_publish *configuration,
struct ast_sip_outbound_publish_client *client);
static int publisher_stop(struct ast_sip_outbound_publish_client *client);
struct ast_sip_notifier presence_notifier = {
.default_accept = DEFAULT_PRESENCE_BODY,
......@@ -101,6 +141,12 @@ struct ast_sip_subscription_handler presence_handler = {
.notifier = &presence_notifier,
};
struct ast_sip_event_publisher_handler presence_publisher = {
.event_name = "presence",
.start_publishing = publisher_start,
.stop_publishing = publisher_stop,
};
struct ast_sip_subscription_handler dialog_handler = {
.event_name = "dialog",
.body_type = AST_SIP_EXTEN_STATE_DATA,
......@@ -110,6 +156,12 @@ struct ast_sip_subscription_handler dialog_handler = {
.notifier = &dialog_notifier,
};
struct ast_sip_event_publisher_handler dialog_publisher = {
.event_name = "dialog",
.start_publishing = publisher_start,
.stop_publishing = publisher_stop,
};
static void exten_state_subscription_destructor(void *obj)
{
struct exten_state_subscription *sub = obj;
......@@ -490,31 +542,264 @@ static void to_ami(struct ast_sip_subscription *sub,
exten_state_sub->last_exten_state));
}
/*!
* \brief Global extension state callback function
*/
static int exten_state_publisher_state_cb(const char *context, const char *exten, struct ast_state_cb_info *info, void *data)
{
struct ao2_iterator publisher_iter;
struct exten_state_publisher *publisher;
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 */
}
ao2_iterator_destroy(&publisher_iter);
return 0;
}
/*!
* \brief Hashing function for extension state publisher
*/
static int exten_state_publisher_hash(const void *obj, const int flags)
{
const struct exten_state_publisher *object;
const char *key;
switch (flags & OBJ_SEARCH_MASK) {
case OBJ_SEARCH_KEY:
key = obj;
break;
case OBJ_SEARCH_OBJECT:
object = obj;
key = object->name;
break;
default:
ast_assert(0);
return 0;
}
return ast_str_hash(key);
}
/*!
* \brief Comparator function for extension state publisher
*/
static int exten_state_publisher_cmp(void *obj, void *arg, int flags)
{
const struct exten_state_publisher *object_left = obj;
const struct exten_state_publisher *object_right = arg;
const char *right_key = arg;
int cmp;
switch (flags & OBJ_SEARCH_MASK) {
case OBJ_SEARCH_OBJECT:
right_key = object_right->name;
/* Fall through */
case OBJ_SEARCH_KEY:
cmp = strcmp(object_left->name, right_key);
break;
case OBJ_SEARCH_PARTIAL_KEY:
/* Not supported by container. */
ast_assert(0);
return 0;
default:
cmp = 0;
break;
}
if (cmp) {
return 0;
}
return CMP_MATCH;
}
/*!
* \brief Destructor for extension state publisher
*/
static void exten_state_publisher_destroy(void *obj)
{
struct exten_state_publisher *publisher = obj;
if (publisher->context_filter) {
regfree(&publisher->context_regex);
}
if (publisher->exten_filter) {
regfree(&publisher->exten_regex);
}
ao2_cleanup(publisher->client);
}
static int build_regex(regex_t *regex, const char *text)
{
int res;
if ((res = regcomp(regex, text, REG_EXTENDED | REG_ICASE | REG_NOSUB))) {
size_t len = regerror(res, regex, NULL, 0);
char buf[len];
regerror(res, regex, buf, len);
ast_log(LOG_ERROR, "Could not compile regex '%s': %s\n", text, buf);
return -1;
}
return 0;
}
static int publisher_start(struct ast_sip_outbound_publish *configuration, struct ast_sip_outbound_publish_client *client)
{
struct exten_state_publisher *publisher;
size_t name_size;
size_t body_type_size;
size_t body_subtype_size;
char *body_subtype;
const char *body_full;
const char *body_type;
const char *name;
const char *context;
const char *exten;
name = ast_sorcery_object_get_id(configuration);
body_full = ast_sorcery_object_get_extended(configuration, "body");
if (ast_strlen_zero(body_full)) {
ast_log(LOG_ERROR, "Outbound extension state publisher '%s': Body not set\n",
name);
return -1;
}
body_subtype = ast_strdupa(body_full);
body_type = strsep(&body_subtype, "/");
if (ast_strlen_zero(body_type) || ast_strlen_zero(body_subtype)) {
ast_log(LOG_ERROR, "Outbound extension state publisher '%s': Body '%s' missing type or subtype\n",
name, body_full);
return -1;
}
name_size = strlen(name) + 1;
body_type_size = strlen(body_type) + 1;
body_subtype_size = strlen(body_subtype) + 1;
publisher = ao2_alloc_options(
sizeof(*publisher) + name_size + body_type_size + body_subtype_size,
exten_state_publisher_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
if (!publisher) {
return -1;
}
ast_copy_string(publisher->name, name, name_size);
publisher->body_type = publisher->name + name_size;
ast_copy_string(publisher->body_type, body_type, body_type_size);
publisher->body_subtype = publisher->body_type + body_type_size;
ast_copy_string(publisher->body_subtype, body_subtype, body_subtype_size);
context = ast_sorcery_object_get_extended(configuration, "context");
if (!ast_strlen_zero(context)) {
if (build_regex(&publisher->context_regex, context)) {
ast_log(LOG_ERROR, "Outbound extension state publisher '%s': Could not build context filter '%s'\n",
name, context);
ao2_ref(publisher, -1);
return -1;
}
publisher->context_filter = 1;
}
exten = ast_sorcery_object_get_extended(configuration, "exten");
if (!ast_strlen_zero(exten)) {
if (build_regex(&publisher->exten_regex, exten)) {
ast_log(LOG_ERROR, "Outbound extension state publisher '%s': Could not build exten filter '%s'\n",
name, exten);
ao2_ref(publisher, -1);
return -1;
}
publisher->exten_filter = 1;
}
publisher->client = ao2_bump(client);
ao2_lock(publishers);
if (!ao2_container_count(publishers)) {
ast_extension_state_add(NULL, NULL, exten_state_publisher_state_cb, NULL);
}
ao2_link_flags(publishers, publisher, OBJ_NOLOCK);
ao2_unlock(publishers);
ao2_ref(publisher, -1);
return 0;
}
static int publisher_stop(struct ast_sip_outbound_publish_client *client)
{
ao2_find(publishers, ast_sorcery_object_get_id(client), OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA);
return 0;
}
static int unload_module(void)
{
ast_sip_unregister_event_publisher_handler(&dialog_publisher);
ast_sip_unregister_subscription_handler(&dialog_handler);
ast_sip_unregister_event_publisher_handler(&presence_publisher);
ast_sip_unregister_subscription_handler(&presence_handler);
ast_extension_state_del(0, exten_state_publisher_state_cb);
ao2_cleanup(publishers);
publishers = NULL;
return 0;
}
static int load_module(void)
{
CHECK_PJSIP_MODULE_LOADED();
CHECK_PJSIP_PUBSUB_MODULE_LOADED();
if (!ast_module_check("res_pjsip_outbound_publish.so")) {
ast_log(LOG_WARNING, "This module requires the 'res_pjsip_outbound_publish.so' module to be loaded\n");
return AST_MODULE_LOAD_DECLINE;
}
publishers = ao2_container_alloc(PUBLISHER_BUCKETS, exten_state_publisher_hash,
exten_state_publisher_cmp);
if (!publishers) {
ast_log(LOG_WARNING, "Unable to create container to store extension state publishers\n");
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);
unload_module();
return AST_MODULE_LOAD_DECLINE;
}
if (ast_sip_register_event_publisher_handler(&presence_publisher)) {
ast_log(LOG_WARNING, "Unable to register presence publisher %s\n",
presence_publisher.event_name);
unload_module();
return AST_MODULE_LOAD_DECLINE;
}
if (ast_sip_register_subscription_handler(&dialog_handler)) {
ast_log(LOG_WARNING, "Unable to register subscription handler %s\n",
dialog_handler.event_name);
ast_sip_unregister_subscription_handler(&presence_handler);
unload_module();
return AST_MODULE_LOAD_DECLINE;
}
return AST_MODULE_LOAD_SUCCESS;
}
if (ast_sip_register_event_publisher_handler(&dialog_publisher)) {
ast_log(LOG_WARNING, "Unable to register presence publisher %s\n",
dialog_publisher.event_name);
unload_module();
return AST_MODULE_LOAD_DECLINE;
}
static int unload_module(void)
{
ast_sip_unregister_subscription_handler(&dialog_handler);
ast_sip_unregister_subscription_handler(&presence_handler);
return 0;
return AST_MODULE_LOAD_SUCCESS;
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Extension State Notifications",
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment