diff --git a/res/res_pjsip.c b/res/res_pjsip.c index 2326c9add7d1180733d4220e7393a6a9181c7e57..81816a5fa06513b361e51f4508ecc2cd9d4b1e06 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 20586b708819a36ff0a39e5f1a5241ed8f3644b6..6011b25bf5bde2fa5e79f9c5a3ccca0d1dd5e711 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 e833a505612492cfd6d38bc4667c98bac370299a..ce0eeb2b7b5a2c7ee09b8cec6305cea5957b8cbc 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 8bda962cb2a6e8a7558f00e06c8dd10804c3417b..2f0cfd31afaacddb9baf017aa6bc5bffb91adf06 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 6d9368ec167edfddd7af194e0d6ddc1762ae7f77..c104b3edf5dc316bfc7ca09c93565d581bc62746 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(®istration->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; }