summaryrefslogtreecommitdiff
path: root/res
diff options
context:
space:
mode:
Diffstat (limited to 'res')
-rw-r--r--res/res_pjsip.c15
-rw-r--r--res/res_pjsip/include/res_pjsip_private.h5
-rw-r--r--res/res_pjsip/pjsip_options.c65
-rw-r--r--res/res_pjsip_notify.c44
-rw-r--r--res/res_pjsip_outbound_registration.c167
5 files changed, 279 insertions, 17 deletions
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 2326c9add..81816a5fa 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -951,6 +951,20 @@
</configObject>
</configFile>
</configInfo>
+ <manager name="PJSIPQualify" language="en_US">
+ <synopsis>
+ Qualify a chan_pjsip endpoint.
+ </synopsis>
+ <syntax>
+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+ <parameter name="Endpoint" required="true">
+ <para>The endpoint you want to qualify.</para>
+ </parameter>
+ </syntax>
+ <description>
+ <para>Qualify a chan_pjsip endpoint.</para>
+ </description>
+ </manager>
***/
@@ -1876,6 +1890,7 @@ static int unload_pjsip(void *data)
static int unload_module(void)
{
+ ast_res_pjsip_cleanup_options_handling();
ast_sip_destroy_distributor();
ast_res_pjsip_destroy_configuration();
ast_sip_destroy_global_headers();
diff --git a/res/res_pjsip/include/res_pjsip_private.h b/res/res_pjsip/include/res_pjsip_private.h
index 20586b708..6011b25bf 100644
--- a/res/res_pjsip/include/res_pjsip_private.h
+++ b/res/res_pjsip/include/res_pjsip_private.h
@@ -71,4 +71,9 @@ int ast_sip_initialize_system(void);
*/
int ast_sip_initialize_global(void);
+/*!
+ * \brief Clean up res_pjsip options handling
+ */
+void ast_res_pjsip_cleanup_options_handling(void);
+
#endif /* RES_PJSIP_PRIVATE_H_ */
diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c
index e833a5056..ce0eeb2b7 100644
--- a/res/res_pjsip/pjsip_options.c
+++ b/res/res_pjsip/pjsip_options.c
@@ -652,6 +652,64 @@ static char *cli_qualify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *
return CLI_SUCCESS;
}
+/*!
+ * \internal
+ * \brief Send qualify request to the given contact.
+ */
+static int ami_contact_cb(void *obj, void *arg, int flags)
+{
+ struct ast_sip_contact *contact = obj;
+ ao2_ref(contact, +1);
+ if (ast_sip_push_task(NULL, qualify_contact_task, contact)) {
+ ao2_cleanup(contact);
+ }
+ return 0;
+}
+
+static int ami_sip_qualify(struct mansession *s, const struct message *m)
+{
+ const char *endpoint_name = astman_get_header(m, "Endpoint");
+ RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
+ char *aor_name, *aors;
+
+ if (ast_strlen_zero(endpoint_name)) {
+ astman_send_error(s, m, "Endpoint parameter missing.");
+ return 0;
+ }
+
+ endpoint = ast_sorcery_retrieve_by_id(
+ ast_sip_get_sorcery(),
+ "endpoint",
+ endpoint_name);
+ if (!endpoint) {
+ astman_send_error(s, m, "Unable to retrieve endpoint\n");
+ return 0;
+ }
+
+ /* send a qualify for all contacts registered with the endpoint */
+ if (ast_strlen_zero(endpoint->aors)) {
+ astman_send_error(s, m, "No AoRs configured for endpoint\n");
+ return 0;
+ }
+
+ aors = ast_strdupa(endpoint->aors);
+
+ while ((aor_name = strsep(&aors, ","))) {
+ RAII_VAR(struct ast_sip_aor *, aor,
+ ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
+ RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
+
+ if (!aor || !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
+ continue;
+ }
+
+ ao2_callback(contacts, OBJ_NODATA, ami_contact_cb, NULL);
+ }
+
+ astman_send_ack(s, m, "Endpoint found, will qualify");
+ return 0;
+}
+
static struct ast_cli_entry cli_options[] = {
AST_CLI_DEFINE(cli_qualify, "Send an OPTIONS request to a PJSIP endpoint")
};
@@ -778,6 +836,13 @@ int ast_res_pjsip_init_options_handling(int reload)
qualify_and_schedule_permanent();
ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options));
+ ast_manager_register2("PJSIPQualify", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_sip_qualify, NULL, NULL, NULL);
return 0;
}
+
+void ast_res_pjsip_cleanup_options_handling(void)
+{
+ ast_cli_unregister_multiple(cli_options, ARRAY_LEN(cli_options));
+ ast_manager_unregister("PJSIPQualify");
+}
diff --git a/res/res_pjsip_notify.c b/res/res_pjsip_notify.c
index 8bda962cb..2f0cfd31a 100644
--- a/res/res_pjsip_notify.c
+++ b/res/res_pjsip_notify.c
@@ -34,6 +34,24 @@
#include "asterisk/res_pjsip.h"
#include "asterisk/sorcery.h"
+/*** DOCUMENTATION
+ <manager name="PJSIPNotify" language="en_US">
+ <synopsis>
+ Send a NOTIFY to an endpoint.
+ </synopsis>
+ <syntax>
+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+ <parameter name="Endpoint" required="true">
+ <para>The endpoint to which to send the NOTIFY.</para>
+ </parameter>
+ </syntax>
+ <description>
+ <para>Send a NOTIFY to an endpoint.</para>
+ <para>Parameters will be placed into the notify as SIP headers.</para>
+ </description>
+ </manager>
+ ***/
+
#define CONTENT_TYPE_SIZE 64
#define CONTENT_SIZE 512
@@ -541,7 +559,7 @@ static char *cli_complete_notify(const char *line, const char *word,
{
char *c = NULL;
- if (pos == 2) {
+ if (pos == 3) {
int which = 0;
int wordlen = strlen(word);
@@ -564,7 +582,7 @@ static char *cli_complete_notify(const char *line, const char *word,
ao2_iterator_destroy(&i);
return c;
}
- return pos > 2 ? cli_complete_endpoint(word, state) : NULL;
+ return pos > 3 ? cli_complete_endpoint(word, state) : NULL;
}
/*!
@@ -584,9 +602,9 @@ static char *cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a
switch (cmd) {
case CLI_INIT:
- e->command = "pjsip notify";
+ e->command = "pjsip send notify";
e->usage =
- "Usage: pjsip notify <type> <peer> [<peer>...]\n"
+ "Usage: pjsip send notify <type> <peer> [<peer>...]\n"
" Send a NOTIFY request to an endpoint\n"
" Message types are defined in sip_notify.conf\n";
return NULL;
@@ -594,22 +612,22 @@ static char *cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a
return cli_complete_notify(a->line, a->word, a->pos, a->n);
}
- if (a->argc < 4) {
+ if (a->argc < 5) {
return CLI_SHOWUSAGE;
}
cfg = ao2_global_obj_ref(globals);
- if (!(option = notify_option_find(cfg->notify_options, a->argv[2])))
+ if (!(option = notify_option_find(cfg->notify_options, a->argv[3])))
{
ast_cli(a->fd, "Unable to find notify type '%s'\n",
- a->argv[2]);
+ a->argv[3]);
return CLI_FAILURE;
}
- for (i = 3; i < a->argc; ++i) {
+ for (i = 4; i < a->argc; ++i) {
ast_cli(a->fd, "Sending NOTIFY of type '%s' to '%s'\n",
- a->argv[2], a->argv[i]);
+ a->argv[3], a->argv[i]);
switch (push_notify(a->argv[i], option,
notify_cli_data_create)) {
@@ -641,11 +659,11 @@ static struct ast_cli_entry cli_options[] = {
*/
static int manager_notify(struct mansession *s, const struct message *m)
{
- const char *endpoint_name = astman_get_header(m, "Channel");
+ const char *endpoint_name = astman_get_header(m, "Endpoint");
struct ast_variable *vars = astman_get_variables(m);
if (ast_strlen_zero(endpoint_name)) {
- astman_send_error(s, m, "PJSIPNotify requires a channel name");
+ astman_send_error(s, m, "PJSIPNotify requires an endpoint name");
return 0;
}
@@ -668,7 +686,7 @@ static int manager_notify(struct mansession *s, const struct message *m)
break;
}
- astman_send_ack(s, m, "Notify Sent");
+ astman_send_ack(s, m, "NOTIFY sent");
return 0;
}
@@ -706,7 +724,7 @@ static int unload_module(void)
return 0;
}
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "CLI/AMI PJSIP Notify Support",
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "CLI/AMI PJSIP NOTIFY Support",
.load = load_module,
.reload = reload_module,
.unload = unload_module,
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index 6d9368ec1..c104b3edf 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -30,6 +30,7 @@
#include "asterisk/res_pjsip.h"
#include "asterisk/module.h"
#include "asterisk/taskprocessor.h"
+#include "asterisk/cli.h"
/*** DOCUMENTATION
<configInfo name="res_pjsip_outbound_registration" language="en_US">
@@ -90,6 +91,17 @@
</configObject>
</configFile>
</configInfo>
+ <manager name="PJSIPUnregister" language="en_US">
+ <synopsis>
+ Unregister an outbound registration.
+ </synopsis>
+ <syntax>
+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+ <parameter name="Registration" required="true">
+ <para>The outbound registration to unregister.</para>
+ </parameter>
+ </syntax>
+ </manager>
***/
/*! \brief Amount of buffer time (in seconds) before expiration that we re-register at */
@@ -209,7 +221,7 @@ static int handle_client_registration(void *data)
/*! \brief Timer callback function, used just for registrations */
static void sip_outbound_registration_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
{
- RAII_VAR(struct sip_outbound_registration_client_state *, client_state, entry->user_data, ao2_cleanup);
+ struct sip_outbound_registration_client_state *client_state = entry->user_data;
ao2_ref(client_state, +1);
if (ast_sip_push_task(client_state->serializer, handle_client_registration, client_state)) {
@@ -333,7 +345,10 @@ static int handle_registration_response(void *data)
pjsip_tx_data *tdata;
if (!ast_sip_create_request_with_auth(&response->client_state->outbound_auths,
response->rdata, response->tsx, &tdata)) {
- pjsip_regc_send(response->client_state->client, tdata);
+ ao2_ref(response->client_state, +1);
+ if (pjsip_regc_send(response->client_state->client, tdata) != PJ_SUCCESS) {
+ ao2_cleanup(response->client_state);
+ }
return 0;
}
/* Otherwise, fall through so the failure is processed appropriately */
@@ -388,6 +403,7 @@ static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *par
response->code = param->code;
response->expiration = param->expiration;
response->client_state = client_state;
+ ao2_ref(response->client_state, +1);
if (param->rdata) {
struct pjsip_retry_after_hdr *retry_after = pjsip_msg_find_hdr(param->rdata->msg_info.msg, PJSIP_H_RETRY_AFTER, NULL);
@@ -397,8 +413,6 @@ static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *par
pjsip_rx_data_clone(param->rdata, 0, &response->rdata);
}
- ao2_ref(response->client_state, +1);
-
if (ast_sip_push_task(client_state->serializer, handle_registration_response, response)) {
ast_log(LOG_WARNING, "Failed to pass incoming registration response to threadpool\n");
ao2_cleanup(response);
@@ -704,6 +718,147 @@ static int outbound_auth_handler(const struct aco_option *opt, struct ast_variab
return ast_sip_auth_array_init(&registration->outbound_auths, var->value);
}
+static struct sip_outbound_registration *retrieve_registration(const char *registration_name)
+{
+ return ast_sorcery_retrieve_by_id(
+ ast_sip_get_sorcery(),
+ "registration",
+ registration_name);
+}
+
+static int unregister_task(void *obj)
+{
+ RAII_VAR(struct sip_outbound_registration*, registration, obj, ao2_cleanup);
+ struct pjsip_regc *client = registration->state->client_state->client;
+ pjsip_tx_data *tdata;
+
+ if (pjsip_regc_unregister(client, &tdata) != PJ_SUCCESS) {
+ return 0;
+ }
+
+ ao2_ref(registration->state->client_state, +1);
+ if (pjsip_regc_send(client, tdata) != PJ_SUCCESS) {
+ ao2_cleanup(registration->state->client_state);
+ }
+
+ return 0;
+}
+
+static int queue_unregister(struct sip_outbound_registration *registration)
+{
+ ao2_ref(registration, +1);
+ if (ast_sip_push_task(registration->state->client_state->serializer, unregister_task, registration)) {
+ ao2_cleanup(registration);
+ return -1;
+ }
+ return 0;
+}
+
+static char *cli_complete_registration(const char *line, const char *word,
+int pos, int state)
+{
+ char *result = NULL;
+ int wordlen;
+ int which = 0;
+ struct sip_outbound_registration *registration;
+ RAII_VAR(struct ao2_container *, registrations, NULL, ao2_cleanup);
+ struct ao2_iterator i;
+
+ if (pos != 3) {
+ return NULL;
+ }
+
+ wordlen = strlen(word);
+ registrations = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "registration",
+ AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+ if (!registrations) {
+ return NULL;
+ }
+
+ i = ao2_iterator_init(registrations, 0);
+ while ((registration = ao2_iterator_next(&i))) {
+ const char *name = ast_sorcery_object_get_id(registration);
+ if (!strncasecmp(word, name, wordlen) && ++which > state) {
+ result = ast_strdup(name);
+ }
+
+ ao2_cleanup(registration);
+ if (result) {
+ break;
+ }
+ }
+ ao2_iterator_destroy(&i);
+ return result;
+}
+
+static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ RAII_VAR(struct sip_outbound_registration *, registration, NULL, ao2_cleanup);
+ const char *registration_name;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "pjsip send unregister";
+ e->usage =
+ "Usage: pjsip send unregister <registration>\n"
+ " Send a SIP REGISTER request to the specified outbound "
+ "registration with an expiration of 0. This will cause the contact "
+ "added by this registration to be removed on the remote system.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return cli_complete_registration(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc != 4) {
+ return CLI_SHOWUSAGE;
+ }
+
+ registration_name = a->argv[3];
+
+ registration = retrieve_registration(registration_name);
+ if (!registration) {
+ ast_cli(a->fd, "Unable to retrieve registration %s\n", registration_name);
+ return CLI_FAILURE;
+ }
+
+ if (queue_unregister(registration)) {
+ ast_cli(a->fd, "Failed to queue unregistration");
+ return 0;
+ }
+
+ return CLI_SUCCESS;
+}
+
+static int ami_unregister(struct mansession *s, const struct message *m)
+{
+ const char *registration_name = astman_get_header(m, "Registration");
+ RAII_VAR(struct sip_outbound_registration *, registration, NULL, ao2_cleanup);
+
+ if (ast_strlen_zero(registration_name)) {
+ astman_send_error(s, m, "Registration parameter missing.");
+ return 0;
+ }
+
+ registration = retrieve_registration(registration_name);
+ if (!registration) {
+ astman_send_error(s, m, "Unable to retrieve registration entry\n");
+ return 0;
+ }
+
+
+ if (queue_unregister(registration)) {
+ astman_send_ack(s, m, "Failed to queue unregistration");
+ return 0;
+ }
+
+ astman_send_ack(s, m, "Unregistration sent");
+ return 0;
+}
+
+static struct ast_cli_entry cli_outbound_registration[] = {
+ AST_CLI_DEFINE(cli_unregister, "Send a REGISTER request to an outbound registration target with a expiration of 0")
+};
+
static int load_module(void)
{
ast_sorcery_apply_default(ast_sip_get_sorcery(), "registration", "config", "pjsip.conf,criteria=type=registration");
@@ -726,6 +881,8 @@ static int load_module(void)
ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration");
sip_outbound_registration_perform_all();
+ ast_cli_register_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
+ ast_manager_register_xml("PJSIPUnregister", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_unregister);
return AST_MODULE_LOAD_SUCCESS;
}
@@ -738,6 +895,8 @@ static int reload_module(void)
static int unload_module(void)
{
+ ast_cli_unregister_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
+ ast_manager_unregister("PJSIPUnregister");
return 0;
}