32 #include <pjsip_simple.h>
35 #include "asterisk/res_pjsip.h"
36 #include "asterisk/res_pjsip_outbound_publish.h"
37 #include "asterisk/res_pjsip_pubsub.h"
38 #include "asterisk/res_pjsip_body_generator_types.h"
46 #define BODY_SIZE 1024
47 #define EVENT_TYPE_SIZE 50
52 #define PUBLISHER_BUCKETS 31
113 #define DEFAULT_PRESENCE_BODY "application/pidf+xml"
114 #define DEFAULT_DIALOG_BODY "application/dialog-info+xml"
117 static int new_subscribe(
struct ast_sip_endpoint *endpoint,
const char *resource);
120 static int get_resource_display_name(
struct ast_sip_endpoint *endpoint,
const char *resource,
char *display_name,
int display_name_size);
145 .body_type = AST_SIP_EXTEN_STATE_DATA,
146 .accept = { DEFAULT_PRESENCE_BODY, },
149 .notifier = &presence_notifier,
154 .start_publishing = publisher_start,
155 .stop_publishing = publisher_stop,
160 .body_type = AST_SIP_EXTEN_STATE_DATA,
161 .accept = { DEFAULT_DIALOG_BODY, },
164 .notifier = &dialog_notifier,
169 .start_publishing = publisher_start,
170 .stop_publishing = publisher_stop,
173 static void exten_state_subscription_destructor(
void *obj)
178 ast_sip_subscription_destroy(sub->
sip_sub);
186 pjsip_user_agent_hdr *user_agent_hdr = ast_sip_subscription_get_header(
187 sip_sub,
"User-Agent");
189 if (!user_agent_hdr) {
193 size = pj_strlen(&user_agent_hdr->hvalue) + 1;
195 ast_copy_pj_str(user_agent, &user_agent_hdr->hvalue, size);
204 #define INITIAL_LAST_EXTEN_STATE -3
218 exten_state_sub = ao2_alloc(
sizeof(*exten_state_sub), exten_state_subscription_destructor);
219 if (!exten_state_sub) {
233 exten_state_sub->
user_agent = get_user_agent(sip_sub);
234 return exten_state_sub;
243 static void notify_task_data_destructor(
void *obj)
247 ao2_ref(task_data->exten_state_sub, -1);
251 ast_free(task_data->exten_state_data.
user_agent);
259 ao2_alloc(
sizeof(*task_data), notify_task_data_destructor);
262 ast_log(LOG_WARNING,
"Unable to create notify task data\n");
266 task_data->exten_state_sub = exten_state_sub;
269 ao2_ref(task_data->exten_state_sub, +1);
271 task_data->exten_state_data.
exten = exten_state_sub->
exten;
272 task_data->exten_state_data.
exten_state = info->exten_state;
273 task_data->exten_state_data.
presence_state = info->presence_state;
278 task_data->exten_state_data.
sub = exten_state_sub->
sip_sub;
279 task_data->exten_state_data.
datastores = ast_sip_subscription_get_datastores(exten_state_sub->
sip_sub);
283 ast_verb(2,
"Watcher for hint %s %s\n", exten, info->exten_state
285 task_data->terminate = 1;
291 static int notify_task(
void *obj)
296 .body_data = &task_data->exten_state_data,
303 if (ast_sip_subscription_is_terminated(task_data->exten_state_sub->
sip_sub)) {
308 ast_sip_subscription_get_local_uri(task_data->exten_state_sub->
sip_sub,
309 task_data->exten_state_data.
local,
sizeof(task_data->exten_state_data.
local));
310 ast_sip_subscription_get_remote_uri(task_data->exten_state_sub->
sip_sub,
311 task_data->exten_state_data.
remote,
sizeof(task_data->exten_state_data.
remote));
314 task_data->exten_state_data.
pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
315 "exten_state", 1024, 1024);
316 if (!task_data->exten_state_data.
pool) {
320 task_data->exten_state_data.
sub = task_data->exten_state_sub->
sip_sub;
321 task_data->exten_state_data.
datastores = ast_sip_subscription_get_datastores(task_data->exten_state_sub->
sip_sub);
323 ast_sip_subscription_notify(task_data->exten_state_sub->
sip_sub, &data,
324 task_data->terminate);
326 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(),
327 task_data->exten_state_data.
pool);
337 static int state_changed(
const char *context,
const char *exten,
346 if (ast_sip_subscription_is_terminated(exten_state_sub->
sip_sub)) {
350 if (!(task_data = alloc_notify_task_data(exten, exten_state_sub, info))) {
358 ao2_cleanup(task_data);
364 static void state_changed_destroy(
int id,
void *data)
367 ao2_cleanup(exten_state_sub);
371 static const char ds_name[] =
"exten state datastore";
383 ast_sip_subscription_alloc_datastore(&ds_info, ds_name), ao2_cleanup);
389 datastore->data = exten_state_sub;
390 ast_sip_subscription_add_datastore(exten_state_sub->
sip_sub, datastore);
404 ast_sip_subscription_get_datastore(sub, ds_name), ao2_cleanup);
406 return datastore ? datastore->data : NULL;
413 if (!exten_state_sub) {
418 ast_sip_subscription_remove_datastore(exten_state_sub->
sip_sub, ds_name);
420 ao2_cleanup(exten_state_sub);
424 const char *resource)
429 ast_log(LOG_NOTICE,
"Endpoint '%s' state subscription failed: "
430 "Extension '%s' does not exist in context '%s' or has no associated hint\n",
439 const char *resource,
char *display_name,
int display_name_size)
443 if (!endpoint || ast_strlen_zero(resource) || !display_name || display_name_size <= 0) {
449 if (!
ast_get_hint(NULL, 0, display_name, display_name_size, NULL, context, resource)) {
450 ast_log(LOG_NOTICE,
"Endpoint '%s': "
451 "Extension '%s' does not exist in context '%s' or has no associated hint\n",
461 struct ast_sip_endpoint *endpoint = ast_sip_subscription_get_endpoint(sip_sub);
462 const char *resource = ast_sip_subscription_get_resource_name(sip_sub);
465 if (!(exten_state_sub = exten_state_subscription_alloc(sip_sub, endpoint))) {
466 ao2_cleanup(endpoint);
472 sizeof(exten_state_sub->
context));
477 state_changed, state_changed_destroy, exten_state_sub)) < 0) {
478 ast_log(LOG_WARNING,
"Unable to subscribe endpoint '%s' to extension '%s@%s'\n",
481 ao2_cleanup(endpoint);
482 ao2_cleanup(exten_state_sub);
487 ao2_cleanup(endpoint);
492 if (add_datastore(exten_state_sub)) {
493 ast_log(LOG_WARNING,
"Unable to add to subscription datastore.\n");
494 ao2_cleanup(exten_state_sub);
498 ao2_cleanup(exten_state_sub);
502 static void exten_state_data_destructor(
void *obj)
509 if (exten_state_data->
pool) {
510 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), exten_state_data->
pool);
518 char *subtype = NULL;
522 exten_state_data = ao2_alloc(
sizeof(*exten_state_data), exten_state_data_destructor);
523 if (!exten_state_data) {
527 exten_state_data->
exten = exten_state_sub->
exten;
529 if (presence_state == -1 || presence_state == AST_PRESENCE_INVALID) {
530 ao2_cleanup(exten_state_data);
537 ast_sip_subscription_get_local_uri(sip_sub, exten_state_data->
local,
538 sizeof(exten_state_data->
local));
539 ast_sip_subscription_get_remote_uri(sip_sub, exten_state_data->
remote,
540 sizeof(exten_state_data->
remote));
541 exten_state_data->
sub = sip_sub;
542 exten_state_data->
datastores = ast_sip_subscription_get_datastores(sip_sub);
548 ao2_cleanup(exten_state_data);
552 exten_state_data->
pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
553 "exten_state", 1024, 1024);
554 if (!exten_state_data->
pool) {
555 ao2_cleanup(exten_state_data);
559 return exten_state_data;
566 exten_state_sub = get_exten_state_sub(sub);
567 if (!exten_state_sub) {
571 return exten_state_data_alloc(sub, exten_state_sub);
578 get_exten_state_sub(sub);
580 if (!exten_state_sub) {
585 "Extension: %s\r\nExtensionStates: %s\r\n",
603 ast_free((
void *) doomed->exten_state_data.exten);
604 ast_free(doomed->exten_state_data.presence_subtype);
605 ast_free(doomed->exten_state_data.presence_message);
606 ao2_cleanup(doomed->exten_state_data.device_state_info);
624 exten_state_pub_data_destroy(pub_data);
638 exten_state_pub_data_destroy(pub_data);
651 static int exten_state_publisher_cb(
void *data)
666 exten_state_pub_data_destroy(pub_data);
671 pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
"pub_state_body",
674 ast_log(LOG_WARNING,
"Exten state publishing unable to create memory pool\n");
675 exten_state_pub_data_destroy(pub_data);
689 if (ast_strlen_zero(uri)) {
690 ast_log(LOG_WARNING,
"PUBLISH client '%s' has no from_uri or server_uri defined.\n",
697 if (ast_strlen_zero(uri)) {
698 ast_log(LOG_WARNING,
"PUBLISH client '%s' has no to_uri or server_uri defined.\n",
705 res = ast_sip_pubsub_generate_body_content(publisher->
body_type,
710 "PUBLISH client '%s' unable to generate %s/%s PUBLISH body.\n",
721 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
724 exten_state_pub_data_destroy(pub_data);
731 static int exten_state_publisher_state_cb(
const char *context,
const char *exten,
struct ast_state_cb_info *info,
void *data)
737 ast_debug(5,
"Exten state publisher: %s@%s Reason:%s State:%s Presence:%s Subtype:'%s' Message:'%s'\n",
746 S_OR(info->presence_subtype,
""),
747 S_OR(info->presence_message,
""));
749 for (; (publisher = ao2_iterator_next(&publisher_iter));
ao2_ref(publisher, -1)) {
756 pub_data = exten_state_pub_data_alloc(exten, info);
767 ast_debug(5,
"'%s' will publish exten state\n", publisher->
name);
775 exten_state_pub_data_destroy(pub_data);
784 static int exten_state_publisher_hash(
const void *obj,
const int flags)
807 static int exten_state_publisher_cmp(
void *obj,
void *arg,
int flags)
811 const char *right_key = arg;
816 right_key = object_right->
name;
819 cmp = strcmp(object_left->
name, right_key);
838 static void exten_state_publisher_destroy(
void *obj)
850 ao2_cleanup(publisher->
client);
854 static int build_regex(regex_t *regex,
const char *text)
858 if ((res = regcomp(regex, text, REG_EXTENDED | REG_ICASE | REG_NOSUB))) {
859 size_t len = regerror(res, regex, NULL, 0);
861 regerror(res, regex, buf, len);
862 ast_log(LOG_ERROR,
"Could not compile regex '%s': %s\n", text, buf);
873 size_t body_type_size;
874 size_t body_subtype_size;
876 const char *body_full;
885 if (ast_strlen_zero(body_full)) {
886 ast_log(LOG_ERROR,
"Outbound extension state publisher '%s': Body not set\n",
892 body_type = strsep(&body_subtype,
"/");
893 if (ast_strlen_zero(body_type) || ast_strlen_zero(body_subtype)) {
894 ast_log(LOG_ERROR,
"Outbound extension state publisher '%s': Body '%s' missing type or subtype\n",
899 if (!ast_sip_pubsub_is_body_generator_registered(body_type, body_subtype)) {
900 ast_log(LOG_ERROR,
"Outbound extension state publisher '%s': '%s' body generator not registered\n",
905 name_size = strlen(name) + 1;
906 body_type_size = strlen(body_type) + 1;
907 body_subtype_size = strlen(body_subtype) + 1;
909 publisher = ao2_alloc_options(
910 sizeof(*publisher) + name_size + body_type_size + body_subtype_size,
923 if (!ast_strlen_zero(context)) {
925 ast_log(LOG_ERROR,
"Outbound extension state publisher '%s': Could not build context filter '%s'\n",
935 if (!ast_strlen_zero(exten)) {
937 ast_log(LOG_ERROR,
"Outbound extension state publisher '%s': Could not build exten filter '%s'\n",
948 ast_log(LOG_ERROR,
"Outbound extension state publisher '%s': Could not create datastores container\n",
956 ao2_lock(publishers);
961 ao2_unlock(publishers);
974 static int unload_module(
void)
988 ast_sip_unregister_event_publisher_handler(&dialog_publisher);
989 ast_sip_unregister_subscription_handler(&dialog_handler);
990 ast_sip_unregister_event_publisher_handler(&presence_publisher);
991 ast_sip_unregister_subscription_handler(&presence_handler);
996 publish_exten_state_serializer = NULL;
998 ao2_cleanup(publishers);
1007 static int load_module(
void)
1010 PUBLISHER_BUCKETS, exten_state_publisher_hash, NULL, exten_state_publisher_cmp);
1012 ast_log(LOG_WARNING,
"Unable to create container to store extension state publishers\n");
1017 if (!publish_exten_state_serializer) {
1022 if (ast_sip_register_subscription_handler(&presence_handler)) {
1023 ast_log(LOG_WARNING,
"Unable to register subscription handler %s\n",
1029 if (ast_sip_register_event_publisher_handler(&presence_publisher)) {
1030 ast_log(LOG_WARNING,
"Unable to register presence publisher %s\n",
1036 if (ast_sip_register_subscription_handler(&dialog_handler)) {
1037 ast_log(LOG_WARNING,
"Unable to register subscription handler %s\n",
1043 if (ast_sip_register_event_publisher_handler(&dialog_publisher)) {
1044 ast_log(LOG_WARNING,
"Unable to register presence publisher %s\n",
1053 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,
"PJSIP Extension State Notifications",
1054 .support_level = AST_MODULE_SUPPORT_CORE,
1055 .load = load_module,
1056 .unload = unload_module,
1058 .requires =
"res_pjsip,res_pjsip_pubsub,res_pjsip_outbound_publish",
char exten[AST_MAX_EXTENSION]
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
enum ast_extension_states last_exten_state
int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten)
If an extension hint exists, return non-zero.
int ast_extension_state_add_destroy_extended(const char *context, const char *exten, ast_state_cb_type change_cb, ast_state_cb_destroy_type destroy_cb, void *data)
Add watcher for extended extension states with destructor.
void *(* get_notify_data)(struct ast_sip_subscription *sub)
Supply data needed to create a NOTIFY body.
A subscription for extension state.
struct ast_sip_outbound_publish_client * client
ast_extension_states
Extension states.
Asterisk main include file. File version handling, generic pbx functions.
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
enum ast_presence_state last_presence_state
The arg parameter is a search key, but is not an object.
Data used to create bodies for NOTIFY/PUBLISH requests.
int ast_extension_state_del(int id, ast_state_cb_type change_cb)
Deletes a state change watcher by ID.
struct ast_sip_exten_state_data exten_state_data
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
static pj_pool_t * pool
Global memory pool for configuration and timers.
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
struct ast_sip_endpoint_subscription_configuration subscription
char remote[PJSIP_MAX_URL_SIZE]
const ast_string_field context
Assume that the ao2_container is already locked.
Structure for a data store type.
enum ast_extension_states exten_state
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
struct ast_taskprocessor * serializer
#define ao2_link_flags(container, obj, flags)
Add an object to a container.
#define ast_strdup(str)
A wrapper for strdup()
Structure for a data store object.
int(* get_resource_display_name)(struct ast_sip_endpoint *endpoint, const char *resource, char *display_name, int display_name_size)
Supply Display Name for resource.
void(* to_ami)(struct ast_sip_subscription *sub, struct ast_str **buf)
Converts the subscriber to AMI.
struct ast_sip_subscription * sub
unsigned int context_filter
int ast_hint_presence_state(struct ast_channel *c, const char *context, const char *exten, char **subtype, char **message)
Uses hint and presence state callback to get the presence state of an extension.
char context[AST_MAX_CONTEXT]
struct ao2_container * datastores
struct ao2_container * ast_datastores_alloc(void)
Allocate a specialized data stores container.
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
An extension state publisher.
const char * ast_sorcery_object_get_extended(const void *object, const char *name)
Get an extended field value from a sorcery object.
The arg parameter is a partial search key similar to OBJ_SEARCH_KEY.
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Outbound publish client state information (persists for lifetime of a publish)
enum ast_presence_state presence_state
#define ast_strdupa(s)
duplicate a string in memory from the stack
Callbacks that event publisher handlers will define.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
#define AST_MAX_EXTENSION
const char * event_name
The name of the event this handler deals with.
const char * ast_extension_state2str(int extension_state)
Return string representation of the state of an extension.
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
int(* subscription_established)(struct ast_sip_subscription *sub)
Called when an inbound subscription has been accepted.
#define ast_malloc(len)
A wrapper for malloc()
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_VECTOR(name, type)
Define a vector structure.
An entity with which Asterisk communicates.
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
static force_inline char * ast_str_to_lower(char *str)
Convert a string to all lower-case.
int ast_extension_state_extended(struct ast_channel *c, const char *context, const char *exten, struct ao2_container **device_state_info)
Uses hint and devicestate callback to get the extended state of an extension.
Structure representing a "virtual" SIP subscription.
struct ao2_container * device_state_info
char local[PJSIP_MAX_URL_SIZE]
int(* new_subscribe)(struct ast_sip_endpoint *endpoint, const char *resource)
Called when a SUBSCRIBE arrives attempting to establish a new subscription.
int ast_shutdown_final(void)
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Support for dynamic strings.
int ast_sip_push_task(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Pushes a task to SIP servants.
struct ast_taskprocessor * ast_sip_create_serializer(const char *name)
Create a new serializer for SIP tasks.
userdata associated with baseline taskprocessor test
Outbound publish information.
unsigned int exten_filter
#define ast_calloc(num, len)
A wrapper for calloc()
Support for logging to various files, console and syslog Configuration in file logger.conf.
Module has failed to load, may be in an inconsistent state.
An API for managing task processing threads that can be shared across modules.
structure used for presence XML bodies
The arg parameter is an object of the same type.
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
A ast_taskprocessor structure is a singleton by name.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
void * ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
Unreference the specified taskprocessor and its reference count will decrement.
Search option field mask.
int ast_extension_state_add(const char *context, const char *exten, ast_state_cb_type change_cb, void *data)
Add watcher for extension states.
struct ao2_container * datastores
#define ASTERISK_GPL_KEY
The text the key() function should return.
const char * default_accept
Default body type defined for the event package this notifier handles.
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
void(* subscription_shutdown)(struct ast_sip_subscription *subscription)
Called when a subscription is to be destroyed.
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
struct ast_sip_subscription * sip_sub
const char * ast_presence_state2str(enum ast_presence_state state)
Convert presence state to text string for output.
#define AST_VECTOR_CALLBACK_VOID(vec, callback,...)
Execute a callback on every element in a vector disregarding callback return.
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Sorcery Data Access Layer API.
static force_inline int attribute_pure ast_str_hash(const char *str)
Compute a hash value on a string.