#include <pjsip.h>
#include <pjsip_ua.h>
+#include "asterisk/app.h"
#include "asterisk/cli.h"
#include "asterisk/config.h"
#include "asterisk/manager.h"
ao2_cleanup(data->info);
}
+static void notify_cli_channel_data_destroy(void *obj)
+{
+ struct notify_channel_data *data = obj;
+
+ ao2_cleanup(data->info);
+ ao2_cleanup(data->session);
+}
+
/*!
* \internal
* \brief Destroy the notify CLI data releasing any resources (URI variant)
return data;
}
+/*!
+ * \internal
+ * \brief Construct a notify URI data object for CLI.
+ */
+static struct notify_channel_data* notify_cli_channel_data_create(
+ struct ast_sip_session *session, void *info)
+{
+ struct notify_channel_data *data = ao2_alloc_options(sizeof(*data),
+ notify_cli_channel_data_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
+
+ if (!data) {
+ return NULL;
+ }
+
+ data->session = session;
+ data->info = info;
+ ao2_ref(data->info, +1);
+
+ data->build_notify = build_cli_notify;
+
+ return data;
+}
+
/*!
* \internal
* \brief Destroy the notify AMI data releasing any resources.
* \internal
* \brief Do completion on the notify CLI command.
*/
-static char *cli_complete_notify(const char *line, const char *word,
- int pos, int state, int using_uri)
+static char *cli_complete_notify(struct ast_cli_args *a)
{
char *c = NULL;
- if (pos == 3) {
+ if (a->pos == 3) {
int which = 0;
- int wordlen = strlen(word);
+ int wordlen = strlen(a->word);
RAII_VAR(struct notify_cfg *, cfg,
ao2_global_obj_ref(globals), ao2_cleanup);
/* do completion for notify type */
struct ao2_iterator i = ao2_iterator_init(cfg->notify_options, 0);
while ((option = ao2_iterator_next(&i))) {
- if (!strncasecmp(word, option->name, wordlen) && ++which > state) {
+ if (!strncasecmp(a->word, option->name, wordlen) && ++which > a->n) {
c = ast_strdup(option->name);
}
return c;
}
- if (pos == 4) {
- int wordlen = strlen(word);
+ if (a->pos == 4) {
+ int wordlen = strlen(a->word);
- if (ast_strlen_zero(word)) {
- if (state == 0) {
+ if (ast_strlen_zero(a->word)) {
+ if (a->n == 0) {
c = ast_strdup("endpoint");
- } else if (state == 1) {
+ } else if (a->n == 1) {
c = ast_strdup("uri");
+ } else if (a->n == 2) {
+ c = ast_strdup("channel");
}
- } else if (state == 0) {
- if (!strncasecmp(word, "endpoint", wordlen)) {
+ } else if (a->n == 0) {
+ if (!strncasecmp(a->word, "endpoint", wordlen)) {
c = ast_strdup("endpoint");
- } else if (!strncasecmp(word, "uri", wordlen)) {
+ } else if (!strncasecmp(a->word, "uri", wordlen)) {
c = ast_strdup("uri");
+ } else if (!strncasecmp(a->word, "channel", wordlen)) {
+ c = ast_strdup("channel");
}
}
return c;
}
- return pos > 4 && !using_uri ? cli_complete_endpoint(word) : NULL;
+ if (a->pos > 4) {
+ if (!strcasecmp(a->argv[4], "endpoint")) {
+ return cli_complete_endpoint(a->word);
+ } else if (!strcasecmp(a->argv[4], "channel")) {
+ return ast_complete_channels(a->line, a->word, a->pos, a->n, 5);
+ }
+ }
+ return NULL;
}
/*!
int i;
int using_uri = 0;
+ int using_channel = 0;
switch (cmd) {
case CLI_INIT:
e->command = "pjsip send notify";
e->usage =
- "Usage: pjsip send notify <type> {endpoint|uri} <peer> [<peer>...]\n"
+ "Usage: pjsip send notify <type> {endpoint|uri|channel} <peer> [<peer>...]\n"
" Send a NOTIFY request to an endpoint\n"
" Message types are defined in pjsip_notify.conf\n";
return NULL;
case CLI_GENERATE:
- if (a->argc > 4 && (!strcasecmp(a->argv[4], "uri"))) {
- using_uri = 1;
- }
-
- return cli_complete_notify(a->line, a->word, a->pos, a->n, using_uri);
+ return cli_complete_notify(a);
}
if (a->argc < 6) {
if (!strcasecmp(a->argv[4], "uri")) {
using_uri = 1;
+ } else if (!strcasecmp(a->argv[4], "channel")) {
+ using_channel = 1;
} else if (strcasecmp(a->argv[4], "endpoint")) {
return CLI_SHOWUSAGE;
}
}
for (i = 5; i < a->argc; ++i) {
+ enum notify_result result;
ast_cli(a->fd, "Sending NOTIFY of type '%s' to '%s'\n",
a->argv[3], a->argv[i]);
- switch (using_uri ? push_notify_uri(a->argv[i], option, notify_cli_uri_data_create) :
- push_notify(a->argv[i], option, notify_cli_data_create)) {
+ if (using_uri) {
+ result = push_notify_uri(a->argv[i], option, notify_cli_uri_data_create);
+ } else if (using_channel) {
+ result = push_notify_channel(a->argv[i], option, notify_cli_channel_data_create);
+ } else {
+ result = push_notify(a->argv[i], option, notify_cli_data_create);
+ }
+ switch(result) {
case INVALID_ENDPOINT:
- ast_cli(a->fd, "Unable to retrieve endpoint %s\n",
- a->argv[i]);
+ ast_cli(a->fd, "Unable to retrieve endpoint %s\n", a->argv[i]);
+ break;
+ case INVALID_CHANNEL:
+ ast_cli(a->fd, "Unable to find channel %s\n", a->argv[i]);
break;
case ALLOC_ERROR:
ast_cli(a->fd, "Unable to allocate NOTIFY task data\n");
return 0;
}
+/*! \brief Convert headers string such as "Event=hold&Event=answer&..." into ast variable list*/
+/* Caller has to call ast_variables_destroy() to free the list*/
+static struct ast_variable *headers_to_variables(const char *headers)
+{
+ struct ast_variable *varlist = NULL;
+ struct ast_variable *var;
+ char *cur;
+ char *header;
+
+ cur = (char *)headers;
+
+ while( (header = strsep(&cur, "&")) ) {
+ char *name;
+ char *value;
+
+ name = value = header;
+ strsep(&value, "=");
+
+ if (!value || ast_strlen_zero(name)) {
+ continue;
+ }
+
+ var = ast_variable_new(name, value, "");
+ var->next = varlist;
+ varlist = var;
+ }
+ return varlist;
+}
+
+/*! \brief Application entry point to send a SIP notify to an endpoint. */
+static int app_notify(struct ast_channel *chan, const char *data)
+{
+ RAII_VAR(struct notify_cfg *, cfg, NULL, ao2_cleanup);
+ RAII_VAR(struct notify_option *, option, NULL, ao2_cleanup);
+
+ struct ast_variable *varlist = NULL;
+ char *tmp;
+ int res;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(to);
+ AST_APP_ARG(headers);
+ );
+
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "PJSIPNotify requires arguments (to, &header=...)\n");
+ return -1;
+ }
+
+ tmp = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, tmp);
+ cfg = ao2_global_obj_ref(globals);
+
+ if (!(option = notify_option_find(cfg->notify_options, args.headers))) {
+ /* If the app is passed a list of headers, use the notify_ami_*_data_create
+ functions as the option data is handled the same way as the ami command. */
+ varlist = headers_to_variables(args.headers);
+ if (ast_strlen_zero(args.to)) {
+ res = push_notify_channel(ast_channel_name(chan), varlist, notify_ami_channel_data_create);
+ } else {
+ res = push_notify_uri(args.to, varlist, notify_ami_uri_data_create);
+ }
+ } else {
+ /* If the app is passed a configured notify option, use the notify_cli_*_data_create
+ functions as the option data is handled the same way as the cli command. */
+ if (ast_strlen_zero(args.to)) {
+ res = push_notify_channel(ast_channel_name(chan), option, notify_cli_channel_data_create);
+ } else {
+ res = push_notify_uri(args.to, option, notify_cli_uri_data_create);
+ }
+ }
+
+ switch (res) {
+ case INVALID_CHANNEL:
+ case INVALID_ENDPOINT:
+ case ALLOC_ERROR:
+ res = -1;
+ ast_variables_destroy(varlist);
+ break;
+ case TASK_PUSH_ERROR:
+ /* Don't need to destroy vars since it is handled by cleanup in push_notify_channel */
+ res = -1;
+ break;
+ case SUCCESS:
+ res = 0;
+ break;
+ }
+
+ return res;
+}
+
static int load_module(void)
{
if (aco_info_init(¬ify_cfg)) {
ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options));
ast_manager_register_xml("PJSIPNotify", EVENT_FLAG_SYSTEM, manager_notify);
+ ast_register_application_xml("PJSIPNotify", app_notify);
return AST_MODULE_LOAD_SUCCESS;
}
static int unload_module(void)
{
ast_manager_unregister("PJSIPNotify");
+ ast_unregister_application("PJSIPNotify");
ast_cli_unregister_multiple(cli_options, ARRAY_LEN(cli_options));
aco_info_destroy(¬ify_cfg);
ao2_global_obj_release(globals);