35 #include "asterisk/res_pjsip.h"
36 #include "asterisk/res_pjsip_session.h"
120 #define CONTENT_TYPE_SIZE 64
121 #define CONTENT_SIZE 512
127 static const char notify_config[] =
"pjsip_notify.conf";
142 static int notify_option_hash(
const void *obj,
int flags)
148 static int notify_option_cmp(
void *obj,
void *arg,
int flags)
152 const char *key = flags &
OBJ_KEY ? arg : option2->
name;
157 static void notify_option_destroy(
void *obj)
160 ao2_cleanup(option->
items);
163 static void *notify_option_alloc(
const char *category)
165 int category_size = strlen(category) + 1;
168 sizeof(*option) + category_size, notify_option_destroy);
188 return ao2_find(container, category,
OBJ_KEY);
191 static int notify_option_handler(
const struct aco_option *opt,
196 int name_size = strlen(var->
name) + 1;
197 int value_size = strlen(var->
value) + 1;
200 ao2_alloc(
sizeof(*item) + name_size + value_size,
203 item->name = item->buf;
204 item->value = item->buf + name_size;
220 static void notify_cfg_destroy(
void *obj)
223 ao2_cleanup(cfg->notify_options);
226 static void *notify_cfg_alloc(
void)
230 if (!(cfg = ao2_alloc(
sizeof(*cfg), notify_cfg_destroy))) {
235 20, notify_option_hash, NULL, notify_option_cmp);
236 if (!cfg->notify_options) {
248 .category =
"general",
249 .item_offset = offsetof(
struct notify_cfg, notify_options),
250 .item_alloc = notify_option_alloc,
251 .item_find = notify_option_find
256 static struct aco_file module_conf = {
264 .files = ACO_FILES(&module_conf)
284 static void notify_cli_data_destroy(
void *obj)
289 ao2_cleanup(data->
info);
299 void (*build_notify)(pjsip_tx_data *,
void *);
309 void (*build_notify)(pjsip_tx_data *,
void *);
312 static void notify_cli_uri_data_destroy(
void *obj)
317 ao2_cleanup(data->info);
324 static void build_cli_notify(pjsip_tx_data *tdata,
void *info);
333 struct notify_data *data = ao2_alloc(
sizeof(*data),
334 notify_cli_data_destroy);
355 const char *uri,
void *info)
358 notify_cli_uri_data_destroy);
373 data->build_notify = build_cli_notify;
382 static void notify_ami_data_destroy(
void *obj)
395 static void notify_ami_uri_data_destroy(
void *obj)
408 static void notify_ami_channel_data_destroy(
void *obj)
413 ao2_cleanup(data->session);
417 static void build_ami_notify(pjsip_tx_data *tdata,
void *info);
426 struct notify_data *data = ao2_alloc(
sizeof(*data),
427 notify_ami_data_destroy);
446 const char *uri,
void *info)
449 notify_ami_uri_data_destroy);
461 data->build_notify = build_ami_notify;
475 data = ao2_alloc_options(
sizeof(*data), notify_ami_channel_data_destroy,
481 data->session = session;
483 data->build_notify = build_ami_notify;
496 static int not_allowed(
const char *name)
499 static const char *names[] = {
511 for (i = 0; i < ARRAY_LEN(names); ++i) {
512 if (!strcasecmp(name, names[i])) {
523 static int multiple_headers_allowed(
const char *name)
526 return strcasecmp(
"Event", name);
534 static void build_notify_body(pjsip_tx_data *tdata,
struct ast_str *content_type,
546 if ((p = strchr(body.type,
'/'))) {
550 ast_sip_add_body(tdata, &body);
558 static void build_notify(pjsip_tx_data *tdata,
const char *name,
const char *value,
561 if (not_allowed(name)) {
562 ast_log(LOG_WARNING,
"Cannot specify %s header, "
567 if (!strcasecmp(name,
"Content-type")) {
568 if (!(*content_type)) {
572 }
else if (!strcasecmp(name,
"Content")) {
583 if (!multiple_headers_allowed(name)) {
585 pj_cstr(&hdr_name, name);
587 if (pjsip_msg_find_hdr_by_name(tdata->msg, &hdr_name, NULL)) {
588 ast_log(LOG_ERROR,
"Only one '%s' header can be added to a NOTIFY, "
589 "ignoring \"%s: %s\"\n", name, name, value);
594 ast_sip_add_header(tdata, name, value);
603 static void build_cli_notify(pjsip_tx_data *tdata,
void *info)
605 struct notify_option *option = info;
612 while ((item = ao2_iterator_next(&i))) {
613 build_notify(tdata, item->name, item->value,
614 &content_type, &content);
619 build_notify_body(tdata, content_type, content);
627 static void build_ami_notify(pjsip_tx_data *tdata,
void *info)
634 for (i = vars; i; i = i->
next) {
635 if (!strcasecmp(i->
name,
"Content-Length")) {
636 ast_log(LOG_NOTICE,
"It is not necessary to specify Content-Length, ignoring.\n");
640 &content_type, &content);
643 build_notify_body(tdata, content_type, content);
650 static int notify_contact(
void *obj,
void *arg,
int flags)
654 pjsip_tx_data *tdata;
656 if (ast_sip_create_request(
"NOTIFY", NULL, data->
endpoint,
657 NULL, contact, &tdata)) {
658 ast_log(LOG_WARNING,
"SIP NOTIFY - Unable to create request for "
659 "contact %s\n", contact->
uri);
663 ast_sip_add_header(tdata,
"Subscription-State",
"terminated");
666 if (ast_sip_send_request(tdata, NULL, data->
endpoint, NULL, NULL)) {
667 ast_log(LOG_ERROR,
"SIP NOTIFY - Unable to send request for "
668 "contact %s\n", contact->
uri);
682 static int notify_endpoint(
void *obj)
685 char *aor_name, *aors;
688 ast_log(LOG_WARNING,
"Unable to NOTIFY - "
689 "endpoint has no configured AORs\n");
695 while ((aor_name =
ast_strip(strsep(&aors,
",")))) {
697 ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
700 if (!aor || !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
714 static int notify_uri(
void *obj)
718 ast_sip_default_outbound_endpoint(), ao2_cleanup);
719 pjsip_tx_data *tdata;
722 ast_log(LOG_WARNING,
"No default outbound endpoint set, can not send "
723 "NOTIFY requests to arbitrary URIs.\n");
727 if (ast_strlen_zero(data->uri)) {
728 ast_log(LOG_WARNING,
"Unable to NOTIFY - URI is blank.\n");
732 if (ast_sip_create_request(
"NOTIFY", NULL, endpoint,
733 data->uri, NULL, &tdata)) {
734 ast_log(LOG_WARNING,
"SIP NOTIFY - Unable to create request for "
735 "uri %s\n", data->uri);
739 ast_sip_add_header(tdata,
"Subscription-State",
"terminated");
743 if (ast_sip_send_request(tdata, NULL, endpoint, NULL, NULL)) {
744 ast_log(LOG_ERROR,
"SIP NOTIFY - Unable to send request for "
745 "uri %s\n", data->uri);
756 static int notify_channel(
void *obj)
759 pjsip_tx_data *tdata;
760 struct pjsip_dialog *dlg;
762 if (!data->session->channel
763 || !data->session->inv_session
764 || data->session->inv_session->state < PJSIP_INV_STATE_EARLY
765 || data->session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
769 ast_debug(1,
"Sending notify on channel %s\n", ast_channel_name(data->session->channel));
771 dlg = data->session->inv_session->dlg;
773 if (ast_sip_create_request(
"NOTIFY", dlg, NULL, NULL, NULL, &tdata)) {
777 ast_sip_add_header(tdata,
"Subscription-State",
"terminated");
780 if (ast_sip_send_request(tdata, dlg, NULL, NULL, NULL)) {
799 const char *uri,
void *info);
808 static enum notify_result push_notify(
const char *endpoint_name,
void *info,
815 ast_sip_get_sorcery(),
"endpoint", endpoint_name))) {
816 return INVALID_ENDPOINT;
819 if (!(data = data_create(endpoint, info))) {
825 return TASK_PUSH_ERROR;
835 static enum notify_result push_notify_uri(
const char *uri,
void *info,
836 task_uri_data_create data_create)
840 if (!(data = data_create(uri, info))) {
846 return TASK_PUSH_ERROR;
856 static enum notify_result push_notify_channel(
const char *channel_name,
void *info,
857 task_channel_data_create data_create)
867 ast_debug(1,
"No channel found with name %s", channel_name);
868 return INVALID_CHANNEL;
872 ast_log(LOG_WARNING,
"Channel was a non-PJSIP channel: %s\n", channel_name);
874 return INVALID_CHANNEL;
877 ast_channel_lock(ch);
878 ch_pvt = ast_channel_tech_pvt(ch);
882 || session->
inv_session->state < PJSIP_INV_STATE_EARLY
883 || session->
inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
884 ast_debug(1,
"No active session for channel %s\n", channel_name);
885 ast_channel_unlock(ch);
887 return INVALID_CHANNEL;
891 ast_channel_unlock(ch);
900 data = data_create(session, info);
908 return TASK_PUSH_ERROR;
918 static char *cli_complete_endpoint(
const char *word)
920 int wordlen = strlen(word);
926 "endpoint", word, wordlen);
927 if (endpoints == NULL) {
932 while ((endpoint = ao2_iterator_next(&i))) {
935 ao2_cleanup(endpoint);
948 static char *cli_complete_notify(
const char *line,
const char *word,
949 int pos,
int state,
int using_uri)
955 int wordlen = strlen(word);
959 struct notify_option *option;
963 while ((option = ao2_iterator_next(&i))) {
964 if (!strncasecmp(word, option->
name, wordlen) && ++which > state) {
978 int wordlen = strlen(word);
980 if (ast_strlen_zero(word)) {
983 }
else if (state == 1) {
986 }
else if (state == 0) {
987 if (!strncasecmp(word,
"endpoint", wordlen)) {
989 }
else if (!strncasecmp(word,
"uri", wordlen)) {
997 return pos > 4 && !using_uri ? cli_complete_endpoint(word) : NULL;
1011 RAII_VAR(
struct notify_option *, option, NULL, ao2_cleanup);
1018 e->
command =
"pjsip send notify";
1020 "Usage: pjsip send notify <type> {endpoint|uri} <peer> [<peer>...]\n"
1021 " Send a NOTIFY request to an endpoint\n"
1022 " Message types are defined in pjsip_notify.conf\n";
1025 if (a->argc > 4 && (!strcasecmp(a->argv[4],
"uri"))) {
1029 return cli_complete_notify(a->line, a->word, a->pos, a->n, using_uri);
1033 return CLI_SHOWUSAGE;
1036 if (!strcasecmp(a->argv[4],
"uri")) {
1038 }
else if (strcasecmp(a->argv[4],
"endpoint")) {
1039 return CLI_SHOWUSAGE;
1044 if (!(option = notify_option_find(cfg->notify_options, a->argv[3])))
1046 ast_cli(a->fd,
"Unable to find notify type '%s'\n",
1051 for (i = 5; i < a->argc; ++i) {
1052 ast_cli(a->fd,
"Sending NOTIFY of type '%s' to '%s'\n",
1053 a->argv[3], a->argv[i]);
1055 switch (using_uri ? push_notify_uri(a->argv[i], option, notify_cli_uri_data_create) :
1056 push_notify(a->argv[i], option, notify_cli_data_create)) {
1057 case INVALID_ENDPOINT:
1058 ast_cli(a->fd,
"Unable to retrieve endpoint %s\n",
1062 ast_cli(a->fd,
"Unable to allocate NOTIFY task data\n");
1064 case TASK_PUSH_ERROR:
1065 ast_cli(a->fd,
"Unable to push NOTIFY task\n");
1076 AST_CLI_DEFINE(cli_notify,
"Send a NOTIFY request to a SIP endpoint")
1085 static void manager_send_response(
struct mansession *s,
const struct message *m,
enum notify_type type,
enum notify_result res,
struct ast_variable *vars,
const char *endpoint_name)
1088 case INVALID_CHANNEL:
1089 if (type == NOTIFY_CHANNEL) {
1097 case INVALID_ENDPOINT:
1098 if (type == NOTIFY_ENDPOINT) {
1110 case TASK_PUSH_ERROR:
1124 static void manager_notify_endpoint(
struct mansession *s,
1125 const struct message *m,
const char *endpoint_name)
1128 RAII_VAR(
struct notify_option *, option, NULL, ao2_cleanup);
1130 enum notify_result res;
1133 if (!ast_strlen_zero(option_name) && (cfg =
ao2_global_obj_ref(globals)) && !(option = notify_option_find(cfg->notify_options, option_name))) {
1141 if (!strncasecmp(endpoint_name,
"sip/", 4)) {
1145 if (!strncasecmp(endpoint_name,
"pjsip/", 6)) {
1150 res = push_notify(endpoint_name, option, notify_cli_data_create);
1152 res = push_notify(endpoint_name, vars, notify_ami_data_create);
1155 manager_send_response(s, m, NOTIFY_ENDPOINT, res, vars, endpoint_name);
1162 static void manager_notify_uri(
struct mansession *s,
1163 const struct message *m,
const char *uri)
1166 RAII_VAR(
struct notify_option *, option, NULL, ao2_cleanup);
1167 enum notify_result res;
1171 if (!ast_strlen_zero(option_name) && (cfg =
ao2_global_obj_ref(globals)) && !(option = notify_option_find(cfg->notify_options, option_name))) {
1180 res = push_notify_uri(uri, option, notify_cli_uri_data_create);
1182 res = push_notify_uri(uri, vars, notify_ami_uri_data_create);
1185 manager_send_response(s, m, NOTIFY_URI, res, vars, NULL);
1192 static void manager_notify_channel(
struct mansession *s,
1193 const struct message *m,
const char *channel)
1195 enum notify_result res;
1199 res = push_notify_channel(channel, vars, notify_ami_channel_data_create);
1201 manager_send_response(s, m, NOTIFY_CHANNEL, res, vars, NULL);
1217 if (!ast_strlen_zero(endpoint_name)) {
1220 if (!ast_strlen_zero(uri)) {
1223 if (!ast_strlen_zero(channel)) {
1227 if ((!ast_strlen_zero(option) && !ast_strlen_zero(variables)) || (ast_strlen_zero(option) && ast_strlen_zero(variables))) {
1229 "PJSIPNotify requires either an Option or Variable(s)."
1230 "You must use only one of them.");
1231 }
else if (1 < count) {
1233 "PJSIPNotify requires either an endpoint name, a SIP URI, or a channel. "
1234 "You must use only one of them.");
1235 }
else if (!ast_strlen_zero(endpoint_name)) {
1236 manager_notify_endpoint(s, m, endpoint_name);
1237 }
else if (!ast_strlen_zero(uri)) {
1238 manager_notify_uri(s, m, uri);
1239 }
else if (!ast_strlen_zero(channel)) {
1240 manager_notify_channel(s, m, channel);
1243 "PJSIPNotify requires either an endpoint name, a SIP URI, or a channel.");
1249 static int load_module(
void)
1256 "", notify_option_handler, 0);
1269 static int reload_module(
void)
1278 static int unload_module(
void)
1288 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,
"CLI/AMI PJSIP NOTIFY Support",
1289 .support_level = AST_MODULE_SUPPORT_CORE,
1290 .load = load_module,
1291 .reload = reload_module,
1292 .unload = unload_module,
1294 .requires =
"res_pjsip",
struct ast_variable * next
Main Channel structure associated with a channel.
Asterisk main include file. File version handling, generic pbx functions.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
#define aco_option_register_custom(info, name, matchtype, types, default_val, handler, flags)
Register a config option.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
#define ast_channel_unref(c)
Decrease channel reference count.
descriptor for a cli entry.
struct ao2_container * items
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
#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.
Allow objects with duplicate keys in container.
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a list container.
Structure for variables, used for configurations and for channel variables.
A structure which contains a channel implementation and session.
struct ast_sip_session * session
Pointer to session.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
enum aco_process_status aco_process_config(struct aco_info *info, int reload)
Process a config info via the options registered with an aco_info.
#define ao2_global_obj_ref(holder)
Get a reference to the object stored in the global holder.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
#define ast_strdup(str)
A wrapper for strdup()
struct ao2_container * ast_sorcery_retrieve_by_prefix(const struct ast_sorcery *sorcery, const char *type, const char *prefix, const size_t prefix_len)
Retrieve multiple objects whose id begins with the specified prefix.
void(* build_notify)(pjsip_tx_data *, void *)
The representation of a single configuration file to be processed.
struct pjsip_inv_session * inv_session
#define ACO_TYPES(...)
A helper macro to ensure that aco_info types always have a sentinel.
void astman_send_error_va(struct mansession *s, const struct message *m, const char *fmt,...)
Send error in manager transaction (with va_args support)
A structure describing a SIP session.
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
struct ast_sip_endpoint * endpoint
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Configuration File Parser.
int aco_info_init(struct aco_info *info)
Initialize an aco_info structure.
#define ast_strdupa(s)
duplicate a string in memory from the stack
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
In case you didn't read that giant block of text above the mansession_session struct, the mansession is named this solely to keep the API the same in Asterisk. This structure really represents data that is different from Manager action to Manager action. The mansession_session pointer contained within points to session-specific data.
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
struct ao2_container * container
#define ast_debug(level,...)
Log a DEBUG message.
An entity with which Asterisk communicates.
Structure to describe a channel "technology", ie a channel driver See for examples: ...
Core PBX routines and definitions.
struct ast_taskprocessor * serializer
static struct task_data * task_data_create(void)
Create a task_data object.
Their was an error and no changes were applied.
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
struct ast_sip_endpoint_info_configuration info
#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.
void aco_info_destroy(struct aco_info *info)
Destroy an initialized aco_info struct.
#define ao2_global_obj_release(holder)
Release the ao2 object held in the global holder.
static struct stasis_rest_handlers endpoints
REST handler for /api-docs/endpoints.json.
Module has failed to load, may be in an inconsistent state.
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
struct ast_variable * astman_get_variables_order(const struct message *m, enum variable_orders order)
Get a linked list of the Variable: headers with order specified.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Standard Command Line Interface.
Type information about a category-level configurable object.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
int ast_cli_completion_add(char *value)
Add a result to a request for completion options.
const ast_string_field aors
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
#define AO2_GLOBAL_OBJ_STATIC(name)
Define a global object holder to be used to hold an ao2 object, statically initialized.
#define ASTERISK_GPL_KEY
The text the key() function should return.
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
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.
#define CONFIG_INFO_STANDARD(name, arr, alloc,...)
Declare an aco_info struct with default module and preload values.
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
static force_inline int attribute_pure ast_str_case_hash(const char *str)
Compute a hash value on a case-insensitive string.
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Sorcery Data Access Layer API.
#define ao2_link(container, obj)
Add an object to a container.