32 #include <pjsip_simple.h>
34 #include "asterisk/res_pjsip.h"
35 #include "asterisk/res_pjsip_outbound_publish.h"
36 #include "asterisk/res_pjsip_pubsub.h"
132 static void asterisk_devicestate_publisher_state_destroy(
void *obj)
136 ao2_cleanup(publisher_state->
client);
144 static const struct ast_datastore_info asterisk_devicestate_publisher_state_datastore = {
145 .
type =
"asterisk-devicestate-publisher",
146 .destroy = asterisk_devicestate_publisher_state_destroy,
150 static void asterisk_mwi_publisher_state_destroy(
void *obj)
154 ao2_cleanup(publisher_state->
client);
163 .
type =
"asterisk-mwi-publisher",
164 .destroy = asterisk_mwi_publisher_state_destroy,
181 .
type =
"application",
202 "{ s: s, s: s, s: s, s: i, s:s }",
203 "type",
"devicestate",
204 "device", dev_state->
device,
219 ast_sip_publish_client_send(publisher_state->
client, &body);
239 .
type =
"application",
260 "{ s: s, s: s, s: i, s: i, s:s }",
261 "type",
"mailboxstate",
277 ast_sip_publish_client_send(publisher_state->
client, &body);
283 static int cached_devstate_cb(
void *obj,
void *arg,
int flags)
294 static int cached_mwistate_cb(
void *obj,
void *arg,
int flags)
305 static int build_regex(regex_t *regex,
const char *text)
309 if ((res = regcomp(regex, text, REG_EXTENDED | REG_ICASE | REG_NOSUB))) {
310 size_t len = regerror(res, regex, NULL, 0);
312 regerror(res, regex, buf, len);
313 ast_log(LOG_ERROR,
"Could not compile regex '%s': %s\n", text, buf);
328 datastore = ast_sip_publish_client_alloc_datastore(&asterisk_devicestate_publisher_state_datastore,
329 "asterisk-devicestate-publisher");
335 if (!publisher_state) {
338 datastore->
data = publisher_state;
341 if (!ast_strlen_zero(value)) {
350 if (ast_sip_publish_client_add_datastore(client, datastore)) {
355 asterisk_publisher_devstate_cb,
ao2_bump(datastore));
357 ast_sip_publish_client_remove_datastore(client,
"asterisk-devicestate-publisher");
374 RAII_VAR(
struct ast_datastore *, datastore, ast_sip_publish_client_get_datastore(client,
"asterisk-devicestate-publisher"),
382 publisher_state = datastore->
data;
388 ast_sip_publish_client_remove_datastore(client,
"asterisk-devicestate-publisher");
395 .start_publishing = asterisk_start_devicestate_publishing,
396 .stop_publishing = asterisk_stop_devicestate_publishing,
407 datastore = ast_sip_publish_client_alloc_datastore(&asterisk_mwi_publisher_state_datastore,
"asterisk-mwi-publisher");
413 if (!publisher_state) {
416 datastore->
data = publisher_state;
419 if (!ast_strlen_zero(value)) {
428 if (ast_sip_publish_client_add_datastore(client, datastore)) {
433 asterisk_publisher_mwistate_cb,
ao2_bump(datastore));
435 ast_sip_publish_client_remove_datastore(client,
"asterisk-mwi-publisher");
452 RAII_VAR(
struct ast_datastore *, datastore, ast_sip_publish_client_get_datastore(client,
"asterisk-mwi-publisher"),
460 publisher_state = datastore->
data;
466 ast_sip_publish_client_remove_datastore(client,
"asterisk-mwi-publisher");
473 .start_publishing = asterisk_start_mwi_publishing,
474 .stop_publishing = asterisk_stop_mwi_publishing,
477 static int asterisk_publication_new(
struct ast_sip_endpoint *endpoint,
const char *resource,
const char *event_configuration)
480 event_configuration), ao2_cleanup);
498 ast_debug(2,
"Received device state event for resource '%s' but it is not configured to accept them\n",
503 if (ast_strlen_zero(device) || ast_strlen_zero(state)) {
504 ast_debug(1,
"Received incomplete device state event for resource '%s'\n",
510 ast_debug(2,
"Received device state on resource '%s' for device '%s' but it has been filtered out\n",
532 ast_debug(2,
"Received mailbox state event for resource '%s' but it is not configured to accept them\n",
537 if (ast_strlen_zero(uniqueid)) {
538 ast_debug(1,
"Received incomplete mailbox state event for resource '%s'\n",
544 ast_debug(2,
"Received mailbox state on resource '%s' for uniqueid '%s' but it has been filtered out\n",
550 mailbox = strsep(&item_id,
"@");
570 ast_log(LOG_ERROR,
"Received refresh request for devicestate on publication '%s' but publish '%s' is not available\n",
575 datastore = ast_sip_publish_client_get_datastore(client,
"asterisk-devicestate-publisher");
592 static int asterisk_publication_devicestate_state_change(
struct ast_sip_publication *pub, pjsip_msg_body *body,
593 enum ast_sip_publish_state state)
596 ast_sip_publication_get_event_configuration(pub)), ao2_cleanup);
598 const char *eid, *type;
613 if (!ast_sip_is_content_type(&body->content_type,
"application",
"json")) {
614 ast_debug(2,
"Received unsupported content type for Asterisk event on resource '%s'\n",
621 ast_debug(1,
"Received unparseable JSON event for resource '%s'\n",
628 ast_debug(1,
"Received event without eid for resource '%s'\n",
636 ast_debug(1,
"Received event without type for resource '%s'\n",
639 }
else if (!strcmp(type,
"devicestate")) {
640 res = asterisk_publication_devicestate(pub, config, &pubsub_eid, json);
641 }
else if (!strcmp(type,
"refresh")) {
642 res = asterisk_publication_devicestate_refresh(pub, config, &pubsub_eid, json);
661 ast_log(LOG_ERROR,
"Received refresh request for mwi state on publication '%s' but publish '%s' is not available\n",
666 datastore = ast_sip_publish_client_get_datastore(client,
"asterisk-mwi-publisher");
683 static int asterisk_publication_mwi_state_change(
struct ast_sip_publication *pub, pjsip_msg_body *body,
684 enum ast_sip_publish_state state)
687 ast_sip_publication_get_event_configuration(pub)), ao2_cleanup);
689 const char *eid, *type;
704 if (!ast_sip_is_content_type(&body->content_type,
"application",
"json")) {
705 ast_debug(2,
"Received unsupported content type for Asterisk event on resource '%s'\n",
712 ast_debug(1,
"Received unparseable JSON event for resource '%s'\n",
719 ast_debug(1,
"Received event without eid for resource '%s'\n",
727 ast_debug(1,
"Received event without type for resource '%s'\n",
730 }
else if (!strcmp(type,
"mailboxstate")) {
731 res = asterisk_publication_mailboxstate(pub, config, &pubsub_eid, json);
732 }
else if (!strcmp(type,
"refresh")) {
733 res = asterisk_publication_mwi_refresh(pub, config, &pubsub_eid, json);
739 static int send_refresh_cb(
void *obj,
void *arg,
int flags)
747 ast_sip_publish_client_send(client, arg);
755 ast_sip_publish_client_send(client, arg);
764 static void asterisk_publication_send_refresh(
void)
771 .
type =
"application",
806 .new_publication = asterisk_publication_new,
807 .publication_state_change = asterisk_publication_devicestate_state_change,
812 .new_publication = asterisk_publication_new,
813 .publication_state_change = asterisk_publication_mwi_state_change,
817 static void asterisk_publication_config_destroy(
void *obj)
825 static void *asterisk_publication_config_alloc(
const char *name)
828 asterisk_publication_config_destroy);
843 if (ast_strlen_zero(var->
value)) {
847 if (!strcmp(var->
name,
"device_state_filter")) {
851 }
else if (!strcmp(var->
name,
"mailbox_state_filter")) {
860 static int load_module(
void)
863 ast_log(LOG_ERROR,
"Entity ID is not set.\n");
867 ast_sorcery_apply_config(ast_sip_get_sorcery(),
"res_pjsip_publish_asterisk");
868 ast_sorcery_apply_default(ast_sip_get_sorcery(),
"asterisk-publication",
"config",
"pjsip.conf,criteria=type=asterisk-publication");
871 ast_log(LOG_ERROR,
"Unable to register 'asterisk-publication' type with sorcery\n");
884 if (ast_sip_register_publish_handler(&asterisk_devicestate_publication_handler)) {
885 ast_log(LOG_WARNING,
"Unable to register event publication handler %s\n",
886 asterisk_devicestate_publication_handler.
event_name);
889 if (ast_sip_register_publish_handler(&asterisk_mwi_publication_handler)) {
890 ast_log(LOG_WARNING,
"Unable to register event publication handler %s\n",
892 ast_sip_unregister_publish_handler(&asterisk_devicestate_publication_handler);
895 if (ast_sip_register_event_publisher_handler(&asterisk_devicestate_publisher_handler)) {
896 ast_log(LOG_WARNING,
"Unable to register event publisher handler %s\n",
897 asterisk_devicestate_publisher_handler.
event_name);
898 ast_sip_unregister_publish_handler(&asterisk_devicestate_publication_handler);
899 ast_sip_unregister_publish_handler(&asterisk_mwi_publication_handler);
902 if (ast_sip_register_event_publisher_handler(&asterisk_mwi_publisher_handler)) {
903 ast_log(LOG_WARNING,
"Unable to register event publisher handler %s\n",
905 ast_sip_unregister_event_publisher_handler(&asterisk_mwi_publisher_handler);
906 ast_sip_unregister_publish_handler(&asterisk_devicestate_publication_handler);
907 ast_sip_unregister_publish_handler(&asterisk_mwi_publication_handler);
911 asterisk_publication_send_refresh();
916 static int reload_module(
void)
919 asterisk_publication_send_refresh();
923 static int unload_module(
void)
925 ast_sip_unregister_publish_handler(&asterisk_devicestate_publication_handler);
926 ast_sip_unregister_publish_handler(&asterisk_mwi_publication_handler);
927 ast_sip_unregister_event_publisher_handler(&asterisk_devicestate_publisher_handler);
928 ast_sip_unregister_event_publisher_handler(&asterisk_mwi_publisher_handler);
933 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,
"PJSIP Asterisk Event PUBLISH Support",
934 .support_level = AST_MODULE_SUPPORT_CORE,
936 .reload = reload_module,
937 .unload = unload_module,
939 .requires =
"res_pjsip,res_pjsip_outbound_publish,res_pjsip_pubsub",
const char * event_name
The name of the event this handler deals with.
Structure which contains Asterisk mailbox publisher state information.
Asterisk main include file. File version handling, generic pbx functions.
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
struct ast_json * ast_json_load_buf(const char *buffer, size_t buflen, struct ast_json_error *error)
Parse buffer with known length into a JSON object or array.
unsigned int mailbox_state
Accept inbound mailbox state events.
const ast_string_field mailboxstate_publish
Optional name of a mailbox state publish item, used to request the remote side update us...
char * ast_eid_to_str(char *s, int maxlen, struct ast_eid *eid)
Convert an EID to a string.
unsigned int mailbox_state_filter
Mailbox state should be filtered.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
regex_t device_state_regex
Regex used for filtering outbound device state.
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
void ast_json_free(void *p)
Asterisk's custom JSON allocator. Exposed for use by unit tests.
enum ast_device_state state
int stasis_subscription_is_subscribed(const struct stasis_subscription *sub)
Returns whether a subscription is currently subscribed.
Structure for variables, used for configurations and for channel variables.
#define ast_json_dump_string(root)
Encode a JSON value to a compact string.
Perform no matching, return all objects.
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
Type for a default handler that should do nothing.
int stasis_subscription_set_filter(struct stasis_subscription *subscription, enum stasis_subscription_message_filter filter)
Set the message type filtering level on a subscription.
Structure for a data store type.
enum ast_devstate_cache cachable
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Return all matching objects.
Structure for a data store object.
struct ao2_container * stasis_cache_dump(struct stasis_cache *cache, struct stasis_message_type *type)
Dump cached items to a subscription for the ast_eid_default entity.
An Entity ID is essentially a MAC address, brief and unique.
int ast_eid_cmp(const struct ast_eid *eid1, const struct ast_eid *eid2)
Compare two EIDs.
regex_t mailbox_state_regex
Regex used for filtering inbound mailbox state.
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
const char * ast_sorcery_object_get_extended(const void *object, const char *name)
Get an extended field value from a sorcery object.
struct stasis_subscription * device_state_subscription
Device state subscription.
struct stasis_message_type * ast_device_state_message_type(void)
Get the Stasis message type for device state messages.
#define ast_sorcery_object_field_register_custom(sorcery, type, name, default_val, config_handler, sorcery_handler, multiple_handler, flags,...)
Register a field within an object with custom handlers.
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
enum ast_device_state ast_devstate_val(const char *val)
Convert device state from text to integer value.
Outbound publish client state information (persists for lifetime of a publish)
int ast_sorcery_object_unregister(struct ast_sorcery *sorcery, const char *type)
Unregister an object type.
const struct ast_eid * eid
The EID of the server where this message originated.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
#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.
const char * event_name
The name of the event this handler deals with.
#define AST_STRING_FIELD(name)
Declare a string field.
struct stasis_cache * ast_mwi_state_cache(void)
Backend cache for ast_mwi_topic_cached().
unsigned int device_state_filter
Device state should be filtered.
int ast_str_to_eid(struct ast_eid *eid, const char *s)
Convert a string into an EID.
struct ast_sip_outbound_publish_client * client
The publish client to send PUBLISH messages on.
unsigned int mailbox_state_filter
Mailbox state should be filtered.
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
#define ast_debug(level,...)
Log a DEBUG message.
An entity with which Asterisk communicates.
#define ast_sorcery_object_register(sorcery, type, alloc, transform, apply)
Register an object type.
int ast_publish_mwi_state_full(const char *mailbox, const char *context, int new_msgs, int old_msgs, const char *channel_id, struct ast_eid *eid)
Publish a MWI state update via stasis with all parameters.
const char * ast_devstate_str(enum ast_device_state devstate) attribute_pure
Convert device state to text string that is easier to parse.
struct ast_sip_outbound_publish_client * client
The publish client to send PUBLISH messages on.
int ast_publish_device_state_full(const char *device, enum ast_device_state state, enum ast_devstate_cache cachable, struct ast_eid *eid)
Publish a device state update with EID.
struct stasis_cache * ast_device_state_cache(void)
Backend cache for ast_device_state_topic_cached()
SORCERY_OBJECT(details)
Sorcery object details.
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
Type for default option handler for bools (ast_true/ast_false)
struct stasis_message_type * ast_mwi_state_type(void)
Get the Stasis Message Bus API message type for MWI messages.
Outbound publish information.
Callbacks that publication handlers will define.
struct stasis_topic * ast_device_state_topic_all(void)
Get the Stasis topic for device state messages.
unsigned int device_state_filter
Device state should be filtered.
#define ast_calloc(num, len)
A wrapper for calloc()
Structure which contains Asterisk publication information.
#define STRFLDSET(type,...)
Convert a struct and a list of stringfield fields to an argument list of field offsets.
Support for logging to various files, console and syslog Configuration in file logger.conf.
struct stasis_subscription * stasis_unsubscribe_and_join(struct stasis_subscription *subscription)
Cancel a subscription, blocking until the last message is processed.
Module has failed to load, may be in an inconsistent state.
struct stasis_subscription * mailbox_state_subscription
Mailbox state subscription.
const ast_string_field uniqueid
Structure which contains Asterisk device state publisher state information.
void * ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, struct ast_variable *fields)
Retrieve an object or multiple objects using specific fields.
#define ast_sorcery_object_field_register(sorcery, type, name, default_val, opt_type, flags,...)
Register a field within an object.
struct ast_eid ast_eid_default
Global EID.
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
int stasis_subscription_accept_message_type(struct stasis_subscription *subscription, const struct stasis_message_type *type)
Indicate to a subscription that we are interested in a message type.
Abstract JSON element (object, array, string, int, ...).
Type for default option handler for stringfields.
Structure representing a SIP publication.
The structure that contains device state.
struct stasis_message_type * stasis_subscription_change_type(void)
Gets the message type for subscription change notices.
const ast_string_field devicestate_publish
Optional name of a device state publish item struct ast_string_field_mgr __field_mgr used to request ...
int ast_eid_is_empty(const struct ast_eid *eid)
Check if EID is empty.
struct stasis_topic * ast_mwi_topic_all(void)
Get the Stasis Message Bus API topic for MWI messages.
void * ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
The structure that contains MWI state.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
intmax_t ast_json_integer_get(const struct ast_json *integer)
Get the value from a JSON integer.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
unsigned int device_state
Accept inbound device state events.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
regex_t device_state_regex
Regex used for filtering inbound device state.
void ast_sorcery_reload_object(const struct ast_sorcery *sorcery, const char *type)
Inform any wizards of a specific object type to reload persistent objects.
regex_t mailbox_state_regex
Regex used for filtering outbound mailbox state.