2013-04-25 18:25:31 +00:00
|
|
|
/*
|
|
|
|
* Asterisk -- An open source telephony toolkit.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2013, Digium, Inc.
|
|
|
|
*
|
|
|
|
* Joshua Colp <jcolp@digium.com>
|
|
|
|
*
|
|
|
|
* See http://www.asterisk.org for more information about
|
|
|
|
* the Asterisk project. Please do not directly contact
|
|
|
|
* any of the maintainers of this project for assistance;
|
|
|
|
* the project provides a web site, mailing lists and IRC
|
|
|
|
* channels for your use.
|
|
|
|
*
|
|
|
|
* This program is free software, distributed under the terms of
|
|
|
|
* the GNU General Public License Version 2. See the LICENSE file
|
|
|
|
* at the top of the source tree.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "asterisk.h"
|
2018-02-27 21:40:18 +00:00
|
|
|
#include <pjsip.h>
|
|
|
|
#include <pjlib.h>
|
2013-04-25 18:25:31 +00:00
|
|
|
|
2013-07-30 18:14:50 +00:00
|
|
|
#include "asterisk/res_pjsip.h"
|
2013-04-25 18:25:31 +00:00
|
|
|
#include "asterisk/logger.h"
|
|
|
|
#include "asterisk/astobj2.h"
|
2016-04-15 19:26:15 +00:00
|
|
|
#include "asterisk/paths.h"
|
2013-04-25 18:25:31 +00:00
|
|
|
#include "asterisk/sorcery.h"
|
2016-06-02 23:19:13 +00:00
|
|
|
#include "asterisk/taskprocessor.h"
|
2013-07-30 18:14:50 +00:00
|
|
|
#include "include/res_pjsip_private.h"
|
2013-12-20 21:32:13 +00:00
|
|
|
#include "asterisk/res_pjsip_cli.h"
|
2015-11-18 16:07:09 +00:00
|
|
|
#include "asterisk/statsd.h"
|
2016-04-01 18:30:56 +00:00
|
|
|
#include "asterisk/named_locks.h"
|
2013-06-22 14:03:22 +00:00
|
|
|
|
2016-04-14 12:23:54 +00:00
|
|
|
#include "asterisk/res_pjproject.h"
|
|
|
|
|
|
|
|
static int pj_max_hostname = PJ_MAX_HOSTNAME;
|
|
|
|
static int pjsip_max_url_size = PJSIP_MAX_URL_SIZE;
|
|
|
|
|
2013-04-25 18:25:31 +00:00
|
|
|
/*! \brief Destructor for AOR */
|
|
|
|
static void aor_destroy(void *obj)
|
|
|
|
{
|
|
|
|
struct ast_sip_aor *aor = obj;
|
|
|
|
|
|
|
|
ao2_cleanup(aor->permanent_contacts);
|
|
|
|
ast_string_field_free_memory(aor);
|
res_pjsip_mwi: Add voicemail extension and mwi_subscribe_replaces_unsolicited
res_pjsip_mwi was missing the chan_sip "vmexten" functionality which adds
the Message-Account header to the MWI NOTIFY. Also, specifying mailboxes
on endpoints for unsolicited mwi and on aors for subscriptions required
that the admin know in advance which the client wanted. If you specified
mailboxes on the endpoint, subscriptions were rejected even if you also
specified mailboxes on the aor.
Voicemail extension:
* Added a global default_voicemail_extension which defaults to "".
* Added voicemail_extension to both endpoint and aor.
* Added ast_sip_subscription_get_dialog for support.
* Added ast_sip_subscription_get_sip_uri for support.
When an unsolicited NOTIFY is constructed, the From header is parsed, the
voicemail extension from the endpoint is substituted for the user, and the
result placed in the Message-Account field in the body.
When a subscribed NOTIFY is constructed, the subscription dialog local uri
is parsed, the voicemail_extension from the aor (looked up from the
subscription resource name) is substituted for the user, and the result
placed in the Message-Account field in the body.
If no voicemail extension was defined, the Message-Account field is not added
to the NOTIFY body.
mwi_subscribe_replaces_unsolicited:
* Added mwi_subscribe_replaces_unsolicited to endpoint.
The previous behavior was to reject a subscribe if a previous internal
subscription for unsolicited MWI was found for the mailbox. That remains the
default. However, if there are mailboxes also set on the aor and the client
subscribes and mwi_subscribe_replaces_unsolicited is set, the existing internal
subscription is removed and replaced with the external subscription. This
allows an admin to configure mailboxes on both the endpoint and aor and allows
the client to select which to use.
ASTERISK-25865 #close
Reported-by: Ross Beer
Change-Id: Ic15a9415091760539c7134a5ba3dc4a6a1217cea
2016-03-25 03:55:03 +00:00
|
|
|
ast_free(aor->voicemail_extension);
|
2013-04-25 18:25:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Allocator for AOR */
|
|
|
|
static void *aor_alloc(const char *name)
|
|
|
|
{
|
2016-08-26 22:22:51 +00:00
|
|
|
void *lock;
|
|
|
|
struct ast_sip_aor *aor;
|
|
|
|
|
|
|
|
lock = ast_named_lock_get(AST_NAMED_LOCK_TYPE_MUTEX, "aor", name);
|
|
|
|
if (!lock) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
aor = ast_sorcery_lockable_alloc(sizeof(struct ast_sip_aor), aor_destroy, lock);
|
|
|
|
ao2_ref(lock, -1);
|
|
|
|
|
2013-04-25 18:25:31 +00:00
|
|
|
if (!aor) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
ast_string_field_init(aor, 128);
|
2016-08-26 22:22:51 +00:00
|
|
|
|
2013-04-25 18:25:31 +00:00
|
|
|
return aor;
|
|
|
|
}
|
|
|
|
|
2015-09-07 16:15:59 +00:00
|
|
|
/*! \brief Internal callback function which destroys the specified contact */
|
|
|
|
static int destroy_contact(void *obj, void *arg, int flags)
|
|
|
|
{
|
|
|
|
struct ast_sip_contact *contact = obj;
|
|
|
|
|
|
|
|
ast_sip_location_delete_contact(contact);
|
|
|
|
|
|
|
|
return CMP_MATCH;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void aor_deleted_observer(const void *object)
|
|
|
|
{
|
2015-11-03 17:15:09 +00:00
|
|
|
const struct ast_sip_aor *aor = object;
|
2015-09-07 16:15:59 +00:00
|
|
|
const char *aor_id = ast_sorcery_object_get_id(object);
|
2017-11-16 21:00:20 +00:00
|
|
|
/* Give enough space for ;@ at the end, since that is our object naming scheme */
|
|
|
|
size_t prefix_len = strlen(aor_id) + sizeof(";@") - 1;
|
|
|
|
char prefix[prefix_len + 1];
|
2015-09-07 16:15:59 +00:00
|
|
|
struct ao2_container *contacts;
|
|
|
|
|
2015-11-03 17:15:09 +00:00
|
|
|
if (aor->permanent_contacts) {
|
|
|
|
ao2_callback(aor->permanent_contacts, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, destroy_contact, NULL);
|
|
|
|
}
|
2015-09-07 16:15:59 +00:00
|
|
|
|
2017-11-16 21:00:20 +00:00
|
|
|
sprintf(prefix, "%s;@", aor_id); /* Safe */
|
|
|
|
if (!(contacts = ast_sorcery_retrieve_by_prefix(ast_sip_get_sorcery(), "contact", prefix, prefix_len))) {
|
2015-09-07 16:15:59 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Destroy any contacts that may still exist that were made for this AoR */
|
|
|
|
ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, destroy_contact, NULL);
|
|
|
|
|
|
|
|
ao2_ref(contacts, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Observer for contacts so state can be updated on respective endpoints */
|
|
|
|
static const struct ast_sorcery_observer aor_observer = {
|
|
|
|
.deleted = aor_deleted_observer,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2013-04-25 18:25:31 +00:00
|
|
|
/*! \brief Destructor for contact */
|
|
|
|
static void contact_destroy(void *obj)
|
|
|
|
{
|
|
|
|
struct ast_sip_contact *contact = obj;
|
|
|
|
|
|
|
|
ast_string_field_free_memory(contact);
|
2015-06-17 10:04:39 +00:00
|
|
|
ao2_cleanup(contact->endpoint);
|
2013-04-25 18:25:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Allocator for contact */
|
|
|
|
static void *contact_alloc(const char *name)
|
|
|
|
{
|
2013-06-22 14:26:25 +00:00
|
|
|
struct ast_sip_contact *contact = ast_sorcery_generic_alloc(sizeof(*contact), contact_destroy);
|
2015-12-01 04:19:18 +00:00
|
|
|
char *id = ast_strdupa(name);
|
|
|
|
char *aor = id;
|
|
|
|
char *aor_separator = NULL;
|
2013-04-25 18:25:31 +00:00
|
|
|
|
|
|
|
if (!contact) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ast_string_field_init(contact, 256)) {
|
|
|
|
ao2_cleanup(contact);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-12-01 04:19:18 +00:00
|
|
|
/* Dynamic contacts are delimited with ";@" and static ones with "@@" */
|
|
|
|
if ((aor_separator = strstr(id, ";@")) || (aor_separator = strstr(id, "@@"))) {
|
|
|
|
*aor_separator = '\0';
|
|
|
|
}
|
|
|
|
ast_assert(aor_separator != NULL);
|
|
|
|
|
|
|
|
ast_string_field_set(contact, aor, aor);
|
|
|
|
|
2013-04-25 18:25:31 +00:00
|
|
|
return contact;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ast_sip_aor *ast_sip_location_retrieve_aor(const char *aor_name)
|
|
|
|
{
|
|
|
|
return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "aor", aor_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Internal callback function which deletes and unlinks any expired contacts */
|
|
|
|
static int contact_expire(void *obj, void *arg, int flags)
|
|
|
|
{
|
|
|
|
struct ast_sip_contact *contact = obj;
|
|
|
|
|
|
|
|
/* If the contact has not yet expired it is valid */
|
|
|
|
if (ast_tvdiff_ms(contact->expiration_time, ast_tvnow()) > 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_sip_location_delete_contact(contact);
|
|
|
|
|
|
|
|
return CMP_MATCH;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Internal callback function which links static contacts into another container */
|
|
|
|
static int contact_link_static(void *obj, void *arg, int flags)
|
|
|
|
{
|
|
|
|
struct ao2_container *dest = arg;
|
|
|
|
|
2014-02-06 17:55:45 +00:00
|
|
|
ao2_link(dest, obj);
|
2013-04-25 18:25:31 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-05-31 15:41:45 +00:00
|
|
|
/*! \brief Internal callback function which removes any contact which is unreachable */
|
|
|
|
static int contact_remove_unreachable(void *obj, void *arg, int flags)
|
|
|
|
{
|
|
|
|
struct ast_sip_contact *contact = obj;
|
|
|
|
struct ast_sip_contact_status *status;
|
|
|
|
int unreachable;
|
|
|
|
|
2017-12-11 18:34:53 +00:00
|
|
|
status = ast_sip_get_contact_status(contact);
|
2017-05-31 15:41:45 +00:00
|
|
|
if (!status) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
unreachable = (status->status == UNAVAILABLE);
|
|
|
|
ao2_ref(status, -1);
|
|
|
|
|
|
|
|
return unreachable ? CMP_MATCH : 0;
|
|
|
|
}
|
|
|
|
|
2013-04-25 18:25:31 +00:00
|
|
|
struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact(const struct ast_sip_aor *aor)
|
2017-05-31 15:41:45 +00:00
|
|
|
{
|
|
|
|
return ast_sip_location_retrieve_first_aor_contact_filtered(aor, AST_SIP_CONTACT_FILTER_DEFAULT);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact_filtered(const struct ast_sip_aor *aor,
|
|
|
|
unsigned int flags)
|
2013-04-25 18:25:31 +00:00
|
|
|
{
|
2016-08-04 23:03:56 +00:00
|
|
|
struct ao2_container *contacts;
|
|
|
|
struct ast_sip_contact *contact = NULL;
|
2013-04-25 18:25:31 +00:00
|
|
|
|
2017-05-31 15:41:45 +00:00
|
|
|
contacts = ast_sip_location_retrieve_aor_contacts_filtered(aor, flags);
|
2016-08-04 23:03:56 +00:00
|
|
|
if (contacts && ao2_container_count(contacts)) {
|
|
|
|
/* Get the first AOR contact in the container. */
|
|
|
|
contact = ao2_callback(contacts, 0, NULL, NULL);
|
2013-04-25 18:25:31 +00:00
|
|
|
}
|
2016-08-04 23:03:56 +00:00
|
|
|
ao2_cleanup(contacts);
|
2013-04-25 18:25:31 +00:00
|
|
|
return contact;
|
|
|
|
}
|
|
|
|
|
2016-04-01 18:30:56 +00:00
|
|
|
struct ao2_container *ast_sip_location_retrieve_aor_contacts_nolock(const struct ast_sip_aor *aor)
|
2017-05-31 15:41:45 +00:00
|
|
|
{
|
|
|
|
return ast_sip_location_retrieve_aor_contacts_nolock_filtered(aor, AST_SIP_CONTACT_FILTER_DEFAULT);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ao2_container *ast_sip_location_retrieve_aor_contacts_nolock_filtered(const struct ast_sip_aor *aor,
|
|
|
|
unsigned int flags)
|
2013-04-25 18:25:31 +00:00
|
|
|
{
|
2017-11-16 21:00:20 +00:00
|
|
|
/* Give enough space for ;@ at the end, since that is our object naming scheme */
|
|
|
|
size_t prefix_len = strlen(ast_sorcery_object_get_id(aor)) + sizeof(";@") - 1;
|
|
|
|
char prefix[prefix_len + 1];
|
2013-04-25 18:25:31 +00:00
|
|
|
struct ao2_container *contacts;
|
|
|
|
|
2017-11-16 21:00:20 +00:00
|
|
|
sprintf(prefix, "%s;@", ast_sorcery_object_get_id(aor)); /* Safe */
|
|
|
|
if (!(contacts = ast_sorcery_retrieve_by_prefix(ast_sip_get_sorcery(), "contact", prefix, prefix_len))) {
|
2013-04-25 18:25:31 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Prune any expired contacts and delete them, we do this first because static contacts can never expire */
|
2014-02-06 17:55:45 +00:00
|
|
|
ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, contact_expire, NULL);
|
2013-04-25 18:25:31 +00:00
|
|
|
|
|
|
|
/* Add any permanent contacts from the AOR */
|
|
|
|
if (aor->permanent_contacts) {
|
2014-02-06 17:55:45 +00:00
|
|
|
ao2_callback(aor->permanent_contacts, OBJ_NODATA, contact_link_static, contacts);
|
2013-04-25 18:25:31 +00:00
|
|
|
}
|
|
|
|
|
2017-05-31 15:41:45 +00:00
|
|
|
if (flags & AST_SIP_CONTACT_FILTER_REACHABLE) {
|
|
|
|
ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, contact_remove_unreachable, NULL);
|
|
|
|
}
|
|
|
|
|
2013-04-25 18:25:31 +00:00
|
|
|
return contacts;
|
|
|
|
}
|
|
|
|
|
2016-04-01 18:30:56 +00:00
|
|
|
struct ao2_container *ast_sip_location_retrieve_aor_contacts(const struct ast_sip_aor *aor)
|
2017-05-31 15:41:45 +00:00
|
|
|
{
|
|
|
|
return ast_sip_location_retrieve_aor_contacts_filtered(aor, AST_SIP_CONTACT_FILTER_DEFAULT);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ao2_container *ast_sip_location_retrieve_aor_contacts_filtered(const struct ast_sip_aor *aor,
|
|
|
|
unsigned int flags)
|
2016-04-01 18:30:56 +00:00
|
|
|
{
|
|
|
|
struct ao2_container *contacts;
|
|
|
|
|
2016-08-26 22:22:51 +00:00
|
|
|
/* ao2_lock / ao2_unlock do not actually write aor since it has an ao2 lockobj. */
|
|
|
|
ao2_lock((void*)aor);
|
2017-05-31 15:41:45 +00:00
|
|
|
contacts = ast_sip_location_retrieve_aor_contacts_nolock_filtered(aor, flags);
|
2016-08-26 22:22:51 +00:00
|
|
|
ao2_unlock((void*)aor);
|
2016-04-01 18:30:56 +00:00
|
|
|
|
|
|
|
return contacts;
|
|
|
|
}
|
|
|
|
|
2017-05-31 15:41:45 +00:00
|
|
|
|
2015-01-05 17:53:42 +00:00
|
|
|
void ast_sip_location_retrieve_contact_and_aor_from_list(const char *aor_list, struct ast_sip_aor **aor,
|
|
|
|
struct ast_sip_contact **contact)
|
2017-05-31 15:41:45 +00:00
|
|
|
{
|
|
|
|
ast_sip_location_retrieve_contact_and_aor_from_list_filtered(aor_list, AST_SIP_CONTACT_FILTER_DEFAULT, aor, contact);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ast_sip_location_retrieve_contact_and_aor_from_list_filtered(const char *aor_list, unsigned int flags,
|
|
|
|
struct ast_sip_aor **aor, struct ast_sip_contact **contact)
|
2013-04-25 18:25:31 +00:00
|
|
|
{
|
|
|
|
char *aor_name;
|
|
|
|
char *rest;
|
|
|
|
|
|
|
|
/* If the location is still empty we have nowhere to go */
|
|
|
|
if (ast_strlen_zero(aor_list) || !(rest = ast_strdupa(aor_list))) {
|
|
|
|
ast_log(LOG_WARNING, "Unable to determine contacts from empty aor list\n");
|
2015-01-05 17:53:42 +00:00
|
|
|
return;
|
2013-04-25 18:25:31 +00:00
|
|
|
}
|
|
|
|
|
2015-01-05 17:53:42 +00:00
|
|
|
*aor = NULL;
|
|
|
|
*contact = NULL;
|
|
|
|
|
res_pjsip: Strip spaces from items parsed from comma-separated lists
Configurations like "aors = a, b, c" were either ignoring everything after "a"
or trying to look up " b". Same for mailboxes, ciphers, contacts and a few
others.
To fix, all the strsep(©, ",") calls have been wrapped in ast_strip. To
facilitate this, ast_strip, ast_skip_blanks and ast_skip_nonblanks were
updated to handle null pointers.
In some cases, an ast_strlen_zero() test was added to skip consecutive commas.
There was also an attempt to ast_free an ast_strdupa'd string in
ast_sip_for_each_aor which was causing a SEGV. I removed it.
Although this issue was reported for realtime, the issue was in the res_pjsip
modules so all config mechanisms were affected.
ASTERISK-25829 #close
Reported-by: Mateusz Kowalski
Change-Id: I0b22a2cf22a7c1c50d4ecacbfa540155bec0e7a2
2016-03-06 20:38:41 +00:00
|
|
|
while ((aor_name = ast_strip(strsep(&rest, ",")))) {
|
2015-01-05 17:53:42 +00:00
|
|
|
*aor = ast_sip_location_retrieve_aor(aor_name);
|
2013-04-25 18:25:31 +00:00
|
|
|
|
2015-01-05 17:53:42 +00:00
|
|
|
if (!(*aor)) {
|
2013-04-25 18:25:31 +00:00
|
|
|
continue;
|
|
|
|
}
|
2017-05-31 15:41:45 +00:00
|
|
|
*contact = ast_sip_location_retrieve_first_aor_contact_filtered(*aor, flags);
|
2013-04-25 18:25:31 +00:00
|
|
|
/* If a valid contact is available use its URI for dialing */
|
2015-01-05 17:53:42 +00:00
|
|
|
if (*contact) {
|
2013-04-25 18:25:31 +00:00
|
|
|
break;
|
|
|
|
}
|
2015-01-05 17:53:42 +00:00
|
|
|
|
|
|
|
ao2_ref(*aor, -1);
|
|
|
|
*aor = NULL;
|
2013-04-25 18:25:31 +00:00
|
|
|
}
|
2015-01-05 17:53:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct ast_sip_contact *ast_sip_location_retrieve_contact_from_aor_list(const char *aor_list)
|
|
|
|
{
|
|
|
|
struct ast_sip_aor *aor;
|
|
|
|
struct ast_sip_contact *contact;
|
|
|
|
|
|
|
|
ast_sip_location_retrieve_contact_and_aor_from_list(aor_list, &aor, &contact);
|
|
|
|
|
|
|
|
ao2_cleanup(aor);
|
2013-04-25 18:25:31 +00:00
|
|
|
|
|
|
|
return contact;
|
|
|
|
}
|
|
|
|
|
2015-04-11 21:56:52 +00:00
|
|
|
static int permanent_uri_sort_fn(const void *obj_left, const void *obj_right, int flags);
|
|
|
|
static int cli_contact_populate_container(void *obj, void *arg, int flags);
|
|
|
|
|
|
|
|
static int gather_contacts_for_aor(void *obj, void *arg, int flags)
|
|
|
|
{
|
|
|
|
struct ao2_container *aor_contacts;
|
|
|
|
struct ast_sip_aor *aor = obj;
|
|
|
|
struct ao2_container *container = arg;
|
|
|
|
|
|
|
|
aor_contacts = ast_sip_location_retrieve_aor_contacts(aor);
|
|
|
|
if (!aor_contacts) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
ao2_callback(aor_contacts, OBJ_MULTIPLE | OBJ_NODATA, cli_contact_populate_container,
|
|
|
|
container);
|
|
|
|
ao2_ref(aor_contacts, -1);
|
|
|
|
return CMP_MATCH;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ao2_container *ast_sip_location_retrieve_contacts_from_aor_list(const char *aor_list)
|
|
|
|
{
|
|
|
|
struct ao2_container *contacts;
|
|
|
|
|
|
|
|
contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
|
|
|
|
AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, permanent_uri_sort_fn, NULL);
|
|
|
|
if (!contacts) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_sip_for_each_aor(aor_list, gather_contacts_for_aor, contacts);
|
|
|
|
|
|
|
|
return contacts;
|
|
|
|
}
|
|
|
|
|
2013-04-25 18:25:31 +00:00
|
|
|
struct ast_sip_contact *ast_sip_location_retrieve_contact(const char *contact_name)
|
|
|
|
{
|
|
|
|
return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "contact", contact_name);
|
|
|
|
}
|
|
|
|
|
2017-07-31 19:21:06 +00:00
|
|
|
struct ast_sip_contact *ast_sip_location_create_contact(struct ast_sip_aor *aor,
|
|
|
|
const char *uri, struct timeval expiration_time, const char *path_info,
|
|
|
|
const char *user_agent, const char *via_addr, int via_port, const char *call_id,
|
|
|
|
int prune_on_boot, struct ast_sip_endpoint *endpoint)
|
2013-04-25 18:25:31 +00:00
|
|
|
{
|
2016-08-04 23:03:56 +00:00
|
|
|
struct ast_sip_contact *contact;
|
2014-03-08 16:50:36 +00:00
|
|
|
char name[MAX_OBJECT_FIELD * 2 + 3];
|
2015-09-08 12:21:11 +00:00
|
|
|
char hash[33];
|
2013-04-25 18:25:31 +00:00
|
|
|
|
2015-09-08 12:21:11 +00:00
|
|
|
ast_md5_hash(hash, uri);
|
|
|
|
snprintf(name, sizeof(name), "%s;@%s", ast_sorcery_object_get_id(aor), hash);
|
2013-04-25 18:25:31 +00:00
|
|
|
|
2016-08-04 23:03:56 +00:00
|
|
|
contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", name);
|
|
|
|
if (!contact) {
|
2017-07-31 19:21:06 +00:00
|
|
|
return NULL;
|
2013-04-25 18:25:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ast_string_field_set(contact, uri, uri);
|
|
|
|
contact->expiration_time = expiration_time;
|
2013-06-22 14:03:22 +00:00
|
|
|
contact->qualify_frequency = aor->qualify_frequency;
|
2015-04-11 21:56:52 +00:00
|
|
|
contact->qualify_timeout = aor->qualify_timeout;
|
2013-06-22 14:03:22 +00:00
|
|
|
contact->authenticate_qualify = aor->authenticate_qualify;
|
2014-01-15 13:16:10 +00:00
|
|
|
if (path_info && aor->support_path) {
|
|
|
|
ast_string_field_set(contact, path, path_info);
|
|
|
|
}
|
2013-04-25 18:25:31 +00:00
|
|
|
|
2013-12-14 17:28:21 +00:00
|
|
|
if (!ast_strlen_zero(aor->outbound_proxy)) {
|
|
|
|
ast_string_field_set(contact, outbound_proxy, aor->outbound_proxy);
|
|
|
|
}
|
|
|
|
|
2014-02-17 15:36:45 +00:00
|
|
|
if (!ast_strlen_zero(user_agent)) {
|
|
|
|
ast_string_field_set(contact, user_agent, user_agent);
|
|
|
|
}
|
|
|
|
|
2016-04-15 19:26:15 +00:00
|
|
|
if (!ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
|
|
|
|
ast_string_field_set(contact, reg_server, ast_config_AST_SYSTEM_NAME);
|
|
|
|
}
|
|
|
|
|
2016-05-19 19:56:26 +00:00
|
|
|
if (!ast_strlen_zero(via_addr)) {
|
|
|
|
ast_string_field_set(contact, via_addr, via_addr);
|
|
|
|
}
|
|
|
|
contact->via_port = via_port;
|
|
|
|
|
|
|
|
if (!ast_strlen_zero(call_id)) {
|
|
|
|
ast_string_field_set(contact, call_id, call_id);
|
|
|
|
}
|
|
|
|
|
2015-06-17 10:04:39 +00:00
|
|
|
contact->endpoint = ao2_bump(endpoint);
|
2016-06-22 19:25:23 +00:00
|
|
|
if (endpoint) {
|
|
|
|
ast_string_field_set(contact, endpoint_name, ast_sorcery_object_get_id(endpoint));
|
|
|
|
}
|
|
|
|
|
2017-07-31 19:21:06 +00:00
|
|
|
contact->prune_on_boot = prune_on_boot;
|
|
|
|
|
|
|
|
if (ast_sorcery_create(ast_sip_get_sorcery(), contact)) {
|
|
|
|
ao2_ref(contact, -1);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return contact;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri,
|
|
|
|
struct timeval expiration_time, const char *path_info, const char *user_agent,
|
|
|
|
const char *via_addr, int via_port, const char *call_id,
|
|
|
|
struct ast_sip_endpoint *endpoint)
|
|
|
|
{
|
|
|
|
struct ast_sip_contact *contact;
|
|
|
|
|
|
|
|
contact = ast_sip_location_create_contact(aor, uri, expiration_time, path_info,
|
|
|
|
user_agent, via_addr, via_port, call_id, 0, endpoint);
|
|
|
|
ao2_cleanup(contact);
|
|
|
|
return contact ? 0 : -1;
|
2013-04-25 18:25:31 +00:00
|
|
|
}
|
|
|
|
|
2016-04-01 18:30:56 +00:00
|
|
|
int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
|
|
|
|
struct timeval expiration_time, const char *path_info, const char *user_agent,
|
2016-05-19 19:56:26 +00:00
|
|
|
const char *via_addr, int via_port, const char *call_id,
|
2016-04-01 18:30:56 +00:00
|
|
|
struct ast_sip_endpoint *endpoint)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
|
2016-08-26 22:22:51 +00:00
|
|
|
ao2_lock(aor);
|
2016-04-01 18:30:56 +00:00
|
|
|
res = ast_sip_location_add_contact_nolock(aor, uri, expiration_time, path_info, user_agent,
|
2016-05-19 19:56:26 +00:00
|
|
|
via_addr, via_port, call_id,
|
2016-04-01 18:30:56 +00:00
|
|
|
endpoint);
|
2016-08-26 22:22:51 +00:00
|
|
|
ao2_unlock(aor);
|
2016-04-01 18:30:56 +00:00
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2013-04-25 18:25:31 +00:00
|
|
|
int ast_sip_location_update_contact(struct ast_sip_contact *contact)
|
|
|
|
{
|
|
|
|
return ast_sorcery_update(ast_sip_get_sorcery(), contact);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ast_sip_location_delete_contact(struct ast_sip_contact *contact)
|
|
|
|
{
|
|
|
|
return ast_sorcery_delete(ast_sip_get_sorcery(), contact);
|
|
|
|
}
|
|
|
|
|
2017-07-31 19:21:06 +00:00
|
|
|
static int prune_boot_contacts_cb(void *obj, void *arg, int flags)
|
|
|
|
{
|
|
|
|
struct ast_sip_contact *contact = obj;
|
|
|
|
|
2017-08-15 16:14:20 +00:00
|
|
|
if (contact->prune_on_boot
|
|
|
|
&& !strcmp(contact->reg_server, ast_config_AST_SYSTEM_NAME ?: "")) {
|
|
|
|
ast_verb(3, "Removed contact '%s' from AOR '%s' due to system boot\n",
|
|
|
|
contact->uri, contact->aor);
|
2017-07-31 19:21:06 +00:00
|
|
|
ast_sip_location_delete_contact(contact);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ast_sip_location_prune_boot_contacts(void)
|
|
|
|
{
|
|
|
|
struct ao2_container *contacts;
|
|
|
|
|
|
|
|
contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "contact",
|
|
|
|
AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
|
|
|
|
if (contacts) {
|
|
|
|
ao2_callback(contacts, 0, prune_boot_contacts_cb, NULL);
|
|
|
|
ao2_ref(contacts, -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-25 18:25:31 +00:00
|
|
|
/*! \brief Custom handler for translating from a string timeval to actual structure */
|
|
|
|
static int expiration_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
|
|
|
{
|
|
|
|
struct ast_sip_contact *contact = obj;
|
|
|
|
return ast_get_timeval(var->value, &contact->expiration_time, ast_tv(0, 0), NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Custom handler for translating from an actual structure timeval to string */
|
|
|
|
static int expiration_struct2str(const void *obj, const intptr_t *args, char **buf)
|
|
|
|
{
|
|
|
|
const struct ast_sip_contact *contact = obj;
|
2014-05-09 22:49:26 +00:00
|
|
|
return (ast_asprintf(buf, "%ld", contact->expiration_time.tv_sec) < 0) ? -1 : 0;
|
2013-04-25 18:25:31 +00:00
|
|
|
}
|
|
|
|
|
2014-04-17 22:50:23 +00:00
|
|
|
static int permanent_uri_sort_fn(const void *obj_left, const void *obj_right, int flags)
|
|
|
|
{
|
|
|
|
const struct ast_sip_contact *object_left = obj_left;
|
|
|
|
const struct ast_sip_contact *object_right = obj_right;
|
|
|
|
const char *right_key = obj_right;
|
|
|
|
int cmp;
|
|
|
|
|
|
|
|
switch (flags & OBJ_SEARCH_MASK) {
|
|
|
|
case OBJ_SEARCH_OBJECT:
|
|
|
|
right_key = ast_sorcery_object_get_id(object_right);
|
|
|
|
/* Fall through */
|
|
|
|
case OBJ_SEARCH_KEY:
|
|
|
|
cmp = strcmp(ast_sorcery_object_get_id(object_left), right_key);
|
|
|
|
break;
|
|
|
|
case OBJ_SEARCH_PARTIAL_KEY:
|
|
|
|
/*
|
|
|
|
* We could also use a partial key struct containing a length
|
|
|
|
* so strlen() does not get called for every comparison instead.
|
|
|
|
*/
|
|
|
|
cmp = strncmp(ast_sorcery_object_get_id(object_left), right_key, strlen(right_key));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Sort can only work on something with a full or partial key. */
|
|
|
|
ast_assert(0);
|
|
|
|
cmp = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return cmp;
|
|
|
|
}
|
|
|
|
|
2016-04-14 12:23:54 +00:00
|
|
|
int ast_sip_validate_uri_length(const char *contact_uri)
|
|
|
|
{
|
|
|
|
int max_length = pj_max_hostname - 1;
|
2016-04-18 17:12:37 +00:00
|
|
|
char *contact = ast_strdupa(contact_uri);
|
|
|
|
char *host;
|
|
|
|
char *at;
|
|
|
|
int theres_a_port = 0;
|
2016-04-14 12:23:54 +00:00
|
|
|
|
|
|
|
if (strlen(contact_uri) > pjsip_max_url_size - 1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-04-18 17:12:37 +00:00
|
|
|
contact = ast_strip_quoted(contact, "<", ">");
|
|
|
|
|
|
|
|
if (!strncasecmp(contact, "sip:", 4)) {
|
|
|
|
host = contact + 4;
|
|
|
|
} else if (!strncasecmp(contact, "sips:", 5)) {
|
|
|
|
host = contact + 5;
|
|
|
|
} else {
|
|
|
|
/* Not a SIP URI */
|
2016-04-14 12:23:54 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-04-18 17:12:37 +00:00
|
|
|
at = strchr(contact, '@');
|
|
|
|
if (at) {
|
|
|
|
/* sip[s]:user@host */
|
|
|
|
host = at + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (host[0] == '[') {
|
|
|
|
/* Host is an IPv6 address. Just get up to the matching bracket */
|
|
|
|
char *close_bracket;
|
|
|
|
|
|
|
|
close_bracket = strchr(host, ']');
|
|
|
|
if (!close_bracket) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
close_bracket++;
|
|
|
|
if (*close_bracket == ':') {
|
|
|
|
theres_a_port = 1;
|
|
|
|
}
|
|
|
|
*close_bracket = '\0';
|
|
|
|
} else {
|
|
|
|
/* uri parameters could contain ';' so trim them off first */
|
|
|
|
host = strsep(&host, ";?");
|
|
|
|
/* Host is FQDN or IPv4 address. Need to find closing delimiter */
|
|
|
|
if (strchr(host, ':')) {
|
|
|
|
theres_a_port = 1;
|
|
|
|
host = strsep(&host, ":");
|
|
|
|
}
|
2016-04-14 12:23:54 +00:00
|
|
|
}
|
|
|
|
|
2016-04-18 17:12:37 +00:00
|
|
|
if (!theres_a_port) {
|
2016-04-14 12:23:54 +00:00
|
|
|
max_length -= strlen("_sips.tcp.");
|
|
|
|
}
|
|
|
|
|
2016-04-18 17:12:37 +00:00
|
|
|
if (strlen(host) > max_length) {
|
2016-04-14 12:23:54 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-01-03 17:27:08 +00:00
|
|
|
/*! \brief Custom handler for permanent URIs */
|
|
|
|
static int permanent_uri_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
|
|
|
{
|
|
|
|
struct ast_sip_aor *aor = obj;
|
2014-04-17 22:50:23 +00:00
|
|
|
const char *aor_id = ast_sorcery_object_get_id(aor);
|
2014-10-01 12:28:05 +00:00
|
|
|
char *contacts;
|
2014-04-18 17:02:24 +00:00
|
|
|
char *contact_uri;
|
2014-01-03 17:27:08 +00:00
|
|
|
|
2014-10-01 12:28:05 +00:00
|
|
|
if (ast_strlen_zero(var->value)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
contacts = ast_strdupa(var->value);
|
res_pjsip: Strip spaces from items parsed from comma-separated lists
Configurations like "aors = a, b, c" were either ignoring everything after "a"
or trying to look up " b". Same for mailboxes, ciphers, contacts and a few
others.
To fix, all the strsep(©, ",") calls have been wrapped in ast_strip. To
facilitate this, ast_strip, ast_skip_blanks and ast_skip_nonblanks were
updated to handle null pointers.
In some cases, an ast_strlen_zero() test was added to skip consecutive commas.
There was also an attempt to ast_free an ast_strdupa'd string in
ast_sip_for_each_aor which was causing a SEGV. I removed it.
Although this issue was reported for realtime, the issue was in the res_pjsip
modules so all config mechanisms were affected.
ASTERISK-25829 #close
Reported-by: Mateusz Kowalski
Change-Id: I0b22a2cf22a7c1c50d4ecacbfa540155bec0e7a2
2016-03-06 20:38:41 +00:00
|
|
|
while ((contact_uri = ast_strip(strsep(&contacts, ",")))) {
|
2014-04-18 17:02:24 +00:00
|
|
|
struct ast_sip_contact *contact;
|
2015-05-29 21:19:26 +00:00
|
|
|
struct ast_sip_contact_status *status;
|
res_pjsip: Use a MD5 hash for static Contact IDs
When 90d9a70789 was merged, it mostly tested dynamic contacts created as
a result of registering a PJSIP endpoint. Contacts generated in this
fashion typically have a long alphanumeric string as their object identifier,
which maps reasonably well for StatsD. Unfortunately, this doesn't work in the
general case. StatsD treats both '.' and ':' characters as special characters.
In particular, having a ':' appear in the middle of a StatsD metric will
result in the metric being rejected.
This causes some obvious issues with SIP URIs.
The StatsD API should not be responsible for escaping the metric name passed
to it. The metric is treated as a single long string, and it would be
challenging to know what to escape in the string passed to the function.
Likewise, we don't want to escape the metric in PJSIP, as that involves
overhead that is wasted when either res_statsd isn't loaded or enabled.
This patch takes an alternative approach. The Contact ID has been changed
to be "aor@@uri_hash" instead of "aor@@uri". This (a) won't contain any of the
aforementioned special characters, (b) can be done on Contact creation,
which has minimal impact on run-time performance, and (c) also conforms to an
earlier commit that changed the ID for dynamic contacts.
The downside of this is that StatsD users will have to map SHA1 hashes back to
the Contacts that are emitting the statistics. To that end, the CLI commands
have been updated to include the first 10 characters of the MD5 hash, which
should be enough to match what is shown in Graphite (or some other StatsD
backend).
ASTERISK-25595 #close
Change-Id: Ic674a3307280365b4a45864a3571c295b48a01e2
Reported-by: Matt Jordan
Tested-by: George Joseph
2015-12-03 18:07:49 +00:00
|
|
|
char hash[33];
|
2015-12-04 22:23:21 +00:00
|
|
|
char contact_id[strlen(aor_id) + sizeof(hash) + 2];
|
2014-04-18 17:02:24 +00:00
|
|
|
|
res_pjsip: Strip spaces from items parsed from comma-separated lists
Configurations like "aors = a, b, c" were either ignoring everything after "a"
or trying to look up " b". Same for mailboxes, ciphers, contacts and a few
others.
To fix, all the strsep(©, ",") calls have been wrapped in ast_strip. To
facilitate this, ast_strip, ast_skip_blanks and ast_skip_nonblanks were
updated to handle null pointers.
In some cases, an ast_strlen_zero() test was added to skip consecutive commas.
There was also an attempt to ast_free an ast_strdupa'd string in
ast_sip_for_each_aor which was causing a SEGV. I removed it.
Although this issue was reported for realtime, the issue was in the res_pjsip
modules so all config mechanisms were affected.
ASTERISK-25829 #close
Reported-by: Mateusz Kowalski
Change-Id: I0b22a2cf22a7c1c50d4ecacbfa540155bec0e7a2
2016-03-06 20:38:41 +00:00
|
|
|
if (ast_strlen_zero(contact_uri)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-04-14 12:23:54 +00:00
|
|
|
if (ast_sip_validate_uri_length(contact_uri)) {
|
2017-09-13 21:23:54 +00:00
|
|
|
ast_log(LOG_ERROR, "Contact uri or hostname length exceeds pjproject limit or is not a sip(s) uri: %s\n", contact_uri);
|
2016-04-14 12:23:54 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-04-17 22:50:23 +00:00
|
|
|
if (!aor->permanent_contacts) {
|
2014-04-18 17:02:24 +00:00
|
|
|
aor->permanent_contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
|
|
|
|
AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, permanent_uri_sort_fn, NULL);
|
|
|
|
if (!aor->permanent_contacts) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
res_pjsip: Use a MD5 hash for static Contact IDs
When 90d9a70789 was merged, it mostly tested dynamic contacts created as
a result of registering a PJSIP endpoint. Contacts generated in this
fashion typically have a long alphanumeric string as their object identifier,
which maps reasonably well for StatsD. Unfortunately, this doesn't work in the
general case. StatsD treats both '.' and ':' characters as special characters.
In particular, having a ':' appear in the middle of a StatsD metric will
result in the metric being rejected.
This causes some obvious issues with SIP URIs.
The StatsD API should not be responsible for escaping the metric name passed
to it. The metric is treated as a single long string, and it would be
challenging to know what to escape in the string passed to the function.
Likewise, we don't want to escape the metric in PJSIP, as that involves
overhead that is wasted when either res_statsd isn't loaded or enabled.
This patch takes an alternative approach. The Contact ID has been changed
to be "aor@@uri_hash" instead of "aor@@uri". This (a) won't contain any of the
aforementioned special characters, (b) can be done on Contact creation,
which has minimal impact on run-time performance, and (c) also conforms to an
earlier commit that changed the ID for dynamic contacts.
The downside of this is that StatsD users will have to map SHA1 hashes back to
the Contacts that are emitting the statistics. To that end, the CLI commands
have been updated to include the first 10 characters of the MD5 hash, which
should be enough to match what is shown in Graphite (or some other StatsD
backend).
ASTERISK-25595 #close
Change-Id: Ic674a3307280365b4a45864a3571c295b48a01e2
Reported-by: Matt Jordan
Tested-by: George Joseph
2015-12-03 18:07:49 +00:00
|
|
|
ast_md5_hash(hash, contact_uri);
|
|
|
|
snprintf(contact_id, sizeof(contact_id), "%s@@%s", aor_id, hash);
|
2014-04-18 17:02:24 +00:00
|
|
|
contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", contact_id);
|
|
|
|
if (!contact) {
|
2014-04-17 22:50:23 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2013-04-25 18:25:31 +00:00
|
|
|
|
2015-12-04 22:23:21 +00:00
|
|
|
ast_string_field_set(contact, uri, contact_uri);
|
|
|
|
|
2015-05-29 21:19:26 +00:00
|
|
|
status = ast_res_pjsip_find_or_create_contact_status(contact);
|
|
|
|
if (!status) {
|
2015-04-27 17:11:40 +00:00
|
|
|
ao2_ref(contact, -1);
|
|
|
|
return -1;
|
|
|
|
}
|
2015-05-29 21:19:26 +00:00
|
|
|
ao2_ref(status, -1);
|
2015-04-27 17:11:40 +00:00
|
|
|
|
2014-04-18 17:02:24 +00:00
|
|
|
ao2_link(aor->permanent_contacts, contact);
|
|
|
|
ao2_ref(contact, -1);
|
2014-04-17 22:50:23 +00:00
|
|
|
}
|
2013-04-25 18:25:31 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
static int contact_to_var_list(void *object, void *arg, int flags)
|
2014-03-06 22:39:54 +00:00
|
|
|
{
|
2014-03-08 16:50:36 +00:00
|
|
|
struct ast_sip_contact_wrapper *wrapper = object;
|
2014-03-06 22:39:54 +00:00
|
|
|
struct ast_variable **var = arg;
|
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
ast_variable_list_append(&*var, ast_variable_new("contact", wrapper->contact->uri, ""));
|
2014-03-06 22:39:54 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
static int contacts_to_var_list(const void *obj, struct ast_variable **fields)
|
2014-03-06 22:39:54 +00:00
|
|
|
{
|
|
|
|
const struct ast_sip_aor *aor = obj;
|
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
ast_sip_for_each_contact(aor, contact_to_var_list, fields);
|
2014-03-06 22:39:54 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
res_pjsip_mwi: Add voicemail extension and mwi_subscribe_replaces_unsolicited
res_pjsip_mwi was missing the chan_sip "vmexten" functionality which adds
the Message-Account header to the MWI NOTIFY. Also, specifying mailboxes
on endpoints for unsolicited mwi and on aors for subscriptions required
that the admin know in advance which the client wanted. If you specified
mailboxes on the endpoint, subscriptions were rejected even if you also
specified mailboxes on the aor.
Voicemail extension:
* Added a global default_voicemail_extension which defaults to "".
* Added voicemail_extension to both endpoint and aor.
* Added ast_sip_subscription_get_dialog for support.
* Added ast_sip_subscription_get_sip_uri for support.
When an unsolicited NOTIFY is constructed, the From header is parsed, the
voicemail extension from the endpoint is substituted for the user, and the
result placed in the Message-Account field in the body.
When a subscribed NOTIFY is constructed, the subscription dialog local uri
is parsed, the voicemail_extension from the aor (looked up from the
subscription resource name) is substituted for the user, and the result
placed in the Message-Account field in the body.
If no voicemail extension was defined, the Message-Account field is not added
to the NOTIFY body.
mwi_subscribe_replaces_unsolicited:
* Added mwi_subscribe_replaces_unsolicited to endpoint.
The previous behavior was to reject a subscribe if a previous internal
subscription for unsolicited MWI was found for the mailbox. That remains the
default. However, if there are mailboxes also set on the aor and the client
subscribes and mwi_subscribe_replaces_unsolicited is set, the existing internal
subscription is removed and replaced with the external subscription. This
allows an admin to configure mailboxes on both the endpoint and aor and allows
the client to select which to use.
ASTERISK-25865 #close
Reported-by: Ross Beer
Change-Id: Ic15a9415091760539c7134a5ba3dc4a6a1217cea
2016-03-25 03:55:03 +00:00
|
|
|
static int voicemail_extension_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
|
|
|
{
|
|
|
|
struct ast_sip_aor *aor = obj;
|
|
|
|
|
|
|
|
aor->voicemail_extension = ast_strdup(var->value);
|
|
|
|
|
|
|
|
return aor->voicemail_extension ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int voicemail_extension_to_str(const void *obj, const intptr_t *args, char **buf)
|
|
|
|
{
|
|
|
|
const struct ast_sip_aor *aor = obj;
|
|
|
|
|
|
|
|
*buf = ast_strdup(aor->voicemail_extension);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-11-23 17:26:57 +00:00
|
|
|
int ast_sip_for_each_aor(const char *aors, ao2_callback_fn on_aor, void *arg)
|
|
|
|
{
|
2016-08-04 23:03:56 +00:00
|
|
|
char *copy;
|
|
|
|
char *name;
|
|
|
|
int res;
|
2013-11-23 17:26:57 +00:00
|
|
|
|
|
|
|
if (!on_aor || ast_strlen_zero(aors)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
copy = ast_strdupa(aors);
|
res_pjsip: Strip spaces from items parsed from comma-separated lists
Configurations like "aors = a, b, c" were either ignoring everything after "a"
or trying to look up " b". Same for mailboxes, ciphers, contacts and a few
others.
To fix, all the strsep(©, ",") calls have been wrapped in ast_strip. To
facilitate this, ast_strip, ast_skip_blanks and ast_skip_nonblanks were
updated to handle null pointers.
In some cases, an ast_strlen_zero() test was added to skip consecutive commas.
There was also an attempt to ast_free an ast_strdupa'd string in
ast_sip_for_each_aor which was causing a SEGV. I removed it.
Although this issue was reported for realtime, the issue was in the res_pjsip
modules so all config mechanisms were affected.
ASTERISK-25829 #close
Reported-by: Mateusz Kowalski
Change-Id: I0b22a2cf22a7c1c50d4ecacbfa540155bec0e7a2
2016-03-06 20:38:41 +00:00
|
|
|
while ((name = ast_strip(strsep(©, ",")))) {
|
2016-08-04 23:03:56 +00:00
|
|
|
struct ast_sip_aor *aor;
|
2013-11-23 17:26:57 +00:00
|
|
|
|
2016-08-04 23:03:56 +00:00
|
|
|
aor = ast_sip_location_retrieve_aor(name);
|
|
|
|
if (aor) {
|
|
|
|
res = on_aor(aor, arg, 0);
|
|
|
|
ao2_ref(aor, -1);
|
|
|
|
if (res) {
|
|
|
|
return -1;
|
|
|
|
}
|
2013-11-23 17:26:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
static void contact_wrapper_destroy(void *obj)
|
|
|
|
{
|
|
|
|
struct ast_sip_contact_wrapper *wrapper = obj;
|
2016-08-04 23:03:56 +00:00
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
ast_free(wrapper->aor_id);
|
|
|
|
ast_free(wrapper->contact_id);
|
2016-08-04 23:03:56 +00:00
|
|
|
ao2_cleanup(wrapper->contact);
|
2014-03-08 16:50:36 +00:00
|
|
|
}
|
|
|
|
|
2014-02-06 17:55:45 +00:00
|
|
|
int ast_sip_for_each_contact(const struct ast_sip_aor *aor,
|
2013-12-20 21:32:13 +00:00
|
|
|
ao2_callback_fn on_contact, void *arg)
|
2013-11-23 17:26:57 +00:00
|
|
|
{
|
2016-08-04 23:03:56 +00:00
|
|
|
struct ao2_container *contacts;
|
2013-11-23 17:26:57 +00:00
|
|
|
struct ao2_iterator i;
|
2014-03-08 16:50:36 +00:00
|
|
|
int res = 0;
|
|
|
|
void *object = NULL;
|
2013-11-23 17:26:57 +00:00
|
|
|
|
|
|
|
if (!on_contact ||
|
|
|
|
!(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = ao2_iterator_init(contacts, 0);
|
2014-03-08 16:50:36 +00:00
|
|
|
while ((object = ao2_iterator_next(&i))) {
|
|
|
|
RAII_VAR(struct ast_sip_contact *, contact, object, ao2_cleanup);
|
|
|
|
RAII_VAR(struct ast_sip_contact_wrapper *, wrapper, NULL, ao2_cleanup);
|
|
|
|
const char *aor_id = ast_sorcery_object_get_id(aor);
|
|
|
|
|
2016-08-04 23:03:56 +00:00
|
|
|
wrapper = ao2_alloc_options(sizeof(struct ast_sip_contact_wrapper),
|
|
|
|
contact_wrapper_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
|
2014-03-08 16:50:36 +00:00
|
|
|
if (!wrapper) {
|
|
|
|
res = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
wrapper->contact_id = ast_malloc(strlen(aor_id) + strlen(contact->uri) + 2);
|
|
|
|
if (!wrapper->contact_id) {
|
|
|
|
res = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
sprintf(wrapper->contact_id, "%s/%s", aor_id, contact->uri);
|
|
|
|
wrapper->aor_id = ast_strdup(aor_id);
|
|
|
|
if (!wrapper->aor_id) {
|
|
|
|
res = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
wrapper->contact = contact;
|
|
|
|
ao2_bump(wrapper->contact);
|
2013-11-23 17:26:57 +00:00
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
if ((res = on_contact(wrapper, arg, 0))) {
|
|
|
|
break;
|
2013-11-23 17:26:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ao2_iterator_destroy(&i);
|
2016-08-04 23:03:56 +00:00
|
|
|
ao2_ref(contacts, -1);
|
2014-03-08 16:50:36 +00:00
|
|
|
return res;
|
2013-11-23 17:26:57 +00:00
|
|
|
}
|
|
|
|
|
2013-12-20 21:32:13 +00:00
|
|
|
int ast_sip_contact_to_str(void *object, void *arg, int flags)
|
2013-11-23 17:26:57 +00:00
|
|
|
{
|
2014-03-08 16:50:36 +00:00
|
|
|
struct ast_sip_contact_wrapper *wrapper = object;
|
2013-11-23 17:26:57 +00:00
|
|
|
struct ast_str **buf = arg;
|
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
ast_str_append(buf, 0, "%s,", wrapper->contact_id);
|
2013-11-23 17:26:57 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sip_aor_to_ami(const struct ast_sip_aor *aor, struct ast_str **buf)
|
|
|
|
{
|
2016-08-04 23:03:56 +00:00
|
|
|
struct ast_variable *objset;
|
2014-03-06 22:39:54 +00:00
|
|
|
struct ast_variable *i;
|
|
|
|
|
2016-08-04 23:03:56 +00:00
|
|
|
objset = ast_sorcery_objectset_create2(ast_sip_get_sorcery(), aor,
|
|
|
|
AST_HANDLER_ONLY_STRING);
|
2014-03-06 22:39:54 +00:00
|
|
|
if (!objset) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_str_append(buf, 0, "ObjectType: %s\r\n",
|
|
|
|
ast_sorcery_object_get_type(aor));
|
|
|
|
ast_str_append(buf, 0, "ObjectName: %s\r\n",
|
|
|
|
ast_sorcery_object_get_id(aor));
|
|
|
|
|
|
|
|
for (i = objset; i; i = i->next) {
|
|
|
|
char *camel = ast_to_camel_case(i->name);
|
2016-08-04 23:03:56 +00:00
|
|
|
|
2014-03-06 22:39:54 +00:00
|
|
|
if (strcmp(camel, "Contact") == 0) {
|
|
|
|
ast_free(camel);
|
|
|
|
camel = NULL;
|
|
|
|
}
|
|
|
|
ast_str_append(buf, 0, "%s: %s\r\n", S_OR(camel, "Contacts"), i->value);
|
|
|
|
ast_free(camel);
|
|
|
|
}
|
|
|
|
|
2016-08-04 23:03:56 +00:00
|
|
|
ast_variables_destroy(objset);
|
2014-03-06 22:39:54 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int contacts_to_str(const void *obj, const intptr_t *args, char **buf)
|
|
|
|
{
|
|
|
|
const struct ast_sip_aor *aor = obj;
|
2016-08-04 23:03:56 +00:00
|
|
|
struct ast_str *str;
|
|
|
|
|
|
|
|
str = ast_str_create(MAX_OBJECT_FIELD);
|
|
|
|
if (!str) {
|
|
|
|
*buf = NULL;
|
|
|
|
return -1;
|
|
|
|
}
|
2014-03-06 22:39:54 +00:00
|
|
|
|
|
|
|
ast_sip_for_each_contact(aor, ast_sip_contact_to_str, &str);
|
|
|
|
ast_str_truncate(str, -1);
|
|
|
|
|
|
|
|
*buf = ast_strdup(ast_str_buffer(str));
|
2016-08-04 23:03:56 +00:00
|
|
|
ast_free(str);
|
2014-03-06 22:39:54 +00:00
|
|
|
|
2016-08-04 23:03:56 +00:00
|
|
|
return *buf ? 0 : -1;
|
2013-11-23 17:26:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int format_ami_aor_handler(void *obj, void *arg, int flags)
|
|
|
|
{
|
2013-12-20 21:32:13 +00:00
|
|
|
struct ast_sip_aor *aor = obj;
|
2013-11-23 17:26:57 +00:00
|
|
|
struct ast_sip_ami *ami = arg;
|
|
|
|
const struct ast_sip_endpoint *endpoint = ami->arg;
|
2016-08-04 23:03:56 +00:00
|
|
|
struct ast_str *buf;
|
|
|
|
struct ao2_container *contacts;
|
2014-02-13 15:57:21 +00:00
|
|
|
int total_contacts;
|
|
|
|
int num_permanent;
|
2013-11-23 17:26:57 +00:00
|
|
|
|
2016-08-04 23:03:56 +00:00
|
|
|
buf = ast_sip_create_ami_event("AorDetail", ami);
|
2013-11-23 17:26:57 +00:00
|
|
|
if (!buf) {
|
|
|
|
return -1;
|
|
|
|
}
|
2016-08-04 23:03:56 +00:00
|
|
|
contacts = ast_sip_location_retrieve_aor_contacts(aor);
|
|
|
|
if (!contacts) {
|
|
|
|
ast_free(buf);
|
|
|
|
return -1;
|
|
|
|
}
|
2013-11-23 17:26:57 +00:00
|
|
|
|
|
|
|
sip_aor_to_ami(aor, &buf);
|
2014-02-13 15:57:21 +00:00
|
|
|
total_contacts = ao2_container_count(contacts);
|
|
|
|
num_permanent = aor->permanent_contacts ?
|
|
|
|
ao2_container_count(aor->permanent_contacts) : 0;
|
|
|
|
|
|
|
|
ast_str_append(&buf, 0, "TotalContacts: %d\r\n", total_contacts);
|
2013-11-23 17:26:57 +00:00
|
|
|
ast_str_append(&buf, 0, "ContactsRegistered: %d\r\n",
|
2014-02-13 15:57:21 +00:00
|
|
|
total_contacts - num_permanent);
|
2013-11-23 17:26:57 +00:00
|
|
|
ast_str_append(&buf, 0, "EndpointName: %s\r\n",
|
|
|
|
ast_sorcery_object_get_id(endpoint));
|
|
|
|
|
|
|
|
astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
|
2014-09-18 15:14:38 +00:00
|
|
|
ami->count++;
|
|
|
|
|
2016-08-04 23:03:56 +00:00
|
|
|
ast_free(buf);
|
|
|
|
ao2_ref(contacts, -1);
|
2013-11-23 17:26:57 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int format_ami_endpoint_aor(const struct ast_sip_endpoint *endpoint,
|
|
|
|
struct ast_sip_ami *ami)
|
|
|
|
{
|
|
|
|
ami->arg = (void *)endpoint;
|
|
|
|
return ast_sip_for_each_aor(endpoint->aors,
|
|
|
|
format_ami_aor_handler, ami);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ast_sip_endpoint_formatter endpoint_aor_formatter = {
|
|
|
|
.format_ami = format_ami_endpoint_aor
|
|
|
|
};
|
|
|
|
|
2015-10-20 21:02:30 +00:00
|
|
|
static struct ao2_container *cli_aor_get_container(const char *regex)
|
2014-02-06 17:55:45 +00:00
|
|
|
{
|
2016-08-04 23:03:56 +00:00
|
|
|
struct ao2_container *container;
|
2014-03-08 16:50:36 +00:00
|
|
|
struct ao2_container *s_container;
|
2014-02-06 17:55:45 +00:00
|
|
|
|
2015-10-20 21:02:30 +00:00
|
|
|
container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "aor", regex);
|
2014-02-06 17:55:45 +00:00
|
|
|
if (!container) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-08-04 23:03:56 +00:00
|
|
|
/* Create a sorted container of aors. */
|
2014-02-06 17:55:45 +00:00
|
|
|
s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
|
2014-03-08 16:50:36 +00:00
|
|
|
ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);
|
2016-08-04 23:03:56 +00:00
|
|
|
if (s_container
|
|
|
|
&& ao2_container_dup(s_container, container, 0)) {
|
2014-03-08 16:50:36 +00:00
|
|
|
ao2_ref(s_container, -1);
|
2016-08-04 23:03:56 +00:00
|
|
|
s_container = NULL;
|
2014-02-06 17:55:45 +00:00
|
|
|
}
|
2016-08-04 23:03:56 +00:00
|
|
|
ao2_ref(container, -1);
|
2014-03-08 16:50:36 +00:00
|
|
|
|
2014-02-06 17:55:45 +00:00
|
|
|
return s_container;
|
|
|
|
}
|
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
static int cli_contact_populate_container(void *obj, void *arg, int flags)
|
2013-12-20 21:32:13 +00:00
|
|
|
{
|
2014-03-08 16:50:36 +00:00
|
|
|
ao2_link(arg, obj);
|
2014-02-06 17:55:45 +00:00
|
|
|
|
2013-12-20 21:32:13 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
static int cli_aor_gather_contacts(void *obj, void *arg, int flags)
|
2013-12-20 21:32:13 +00:00
|
|
|
{
|
|
|
|
struct ast_sip_aor *aor = obj;
|
2014-03-08 16:50:36 +00:00
|
|
|
|
|
|
|
return ast_sip_for_each_contact(aor, cli_contact_populate_container, arg);
|
2013-12-20 21:32:13 +00:00
|
|
|
}
|
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
static const char *cli_contact_get_id(const void *obj)
|
2014-02-06 17:55:45 +00:00
|
|
|
{
|
2014-03-08 16:50:36 +00:00
|
|
|
const struct ast_sip_contact_wrapper *wrapper = obj;
|
|
|
|
return wrapper->contact_id;
|
|
|
|
}
|
2014-02-06 17:55:45 +00:00
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
static int cli_contact_sort(const void *obj, const void *arg, int flags)
|
|
|
|
{
|
|
|
|
const struct ast_sip_contact_wrapper *left_wrapper = obj;
|
|
|
|
const struct ast_sip_contact_wrapper *right_wrapper = arg;
|
|
|
|
const char *right_key = arg;
|
|
|
|
int cmp = 0;
|
|
|
|
|
|
|
|
switch (flags & OBJ_SEARCH_MASK) {
|
|
|
|
case OBJ_SEARCH_OBJECT:
|
|
|
|
right_key = right_wrapper->contact_id;
|
|
|
|
/* Fall through */
|
|
|
|
case OBJ_SEARCH_KEY:
|
|
|
|
cmp = strcmp(left_wrapper->contact_id, right_key);
|
|
|
|
break;
|
|
|
|
case OBJ_SEARCH_PARTIAL_KEY:
|
|
|
|
cmp = strncmp(left_wrapper->contact_id, right_key, strlen(right_key));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
cmp = 0;
|
|
|
|
break;
|
2014-02-06 17:55:45 +00:00
|
|
|
}
|
2014-03-08 16:50:36 +00:00
|
|
|
|
|
|
|
return cmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cli_contact_compare(void *obj, void *arg, int flags)
|
|
|
|
{
|
|
|
|
const struct ast_sip_contact_wrapper *left_wrapper = obj;
|
|
|
|
const struct ast_sip_contact_wrapper *right_wrapper = arg;
|
|
|
|
const char *right_key = arg;
|
|
|
|
int cmp = 0;
|
|
|
|
|
|
|
|
switch (flags & OBJ_SEARCH_MASK) {
|
|
|
|
case OBJ_SEARCH_OBJECT:
|
|
|
|
right_key = right_wrapper->contact_id;
|
|
|
|
/* Fall through */
|
|
|
|
case OBJ_SEARCH_KEY:
|
|
|
|
if (strcmp(left_wrapper->contact_id, right_key) == 0) {;
|
|
|
|
cmp = CMP_MATCH | CMP_STOP;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case OBJ_SEARCH_PARTIAL_KEY:
|
|
|
|
if (strncmp(left_wrapper->contact_id, right_key, strlen(right_key)) == 0) {
|
|
|
|
cmp = CMP_MATCH;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
cmp = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cli_contact_iterate(void *container, ao2_callback_fn callback, void *args)
|
|
|
|
{
|
|
|
|
return ast_sip_for_each_contact(container, callback, args);
|
2014-02-06 17:55:45 +00:00
|
|
|
}
|
|
|
|
|
2015-10-20 21:02:30 +00:00
|
|
|
static int cli_filter_contacts(void *obj, void *arg, int flags)
|
|
|
|
{
|
|
|
|
struct ast_sip_contact_wrapper *wrapper = obj;
|
|
|
|
regex_t *regexbuf = arg;
|
|
|
|
|
|
|
|
if (!regexec(regexbuf, wrapper->contact_id, 0, NULL, 0)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CMP_MATCH;
|
|
|
|
}
|
|
|
|
|
2018-09-25 22:33:32 +00:00
|
|
|
static int cli_gather_contact(void *obj, void *arg, int flags)
|
|
|
|
{
|
|
|
|
struct ast_sip_contact *contact = obj;
|
|
|
|
RAII_VAR(struct ast_sip_contact_wrapper *, wrapper, NULL, ao2_cleanup);
|
|
|
|
|
|
|
|
if (strcmp(contact->reg_server, ast_config_AST_SYSTEM_NAME ?: "")) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
wrapper = ao2_alloc_options(sizeof(struct ast_sip_contact_wrapper),
|
|
|
|
contact_wrapper_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
|
|
|
|
if (!wrapper) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
wrapper->contact_id = ast_malloc(strlen(contact->aor) + strlen(contact->uri) + 2);
|
|
|
|
if (!wrapper->contact_id) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
sprintf(wrapper->contact_id, "%s/%s", contact->aor, contact->uri);
|
|
|
|
|
|
|
|
wrapper->aor_id = ast_strdup(contact->aor);
|
|
|
|
if (!wrapper->aor_id) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
wrapper->contact = ao2_bump(contact);
|
|
|
|
|
|
|
|
ao2_link(arg, wrapper);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-20 21:02:30 +00:00
|
|
|
static struct ao2_container *cli_contact_get_container(const char *regex)
|
2013-12-20 21:32:13 +00:00
|
|
|
{
|
2018-09-25 22:33:32 +00:00
|
|
|
RAII_VAR(struct ao2_container *, aors, NULL, ao2_cleanup);
|
|
|
|
RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
|
|
|
|
RAII_VAR(struct ast_variable *, var_aor, NULL, ast_variables_destroy);
|
|
|
|
struct ao2_container *contacts_container;
|
2015-10-20 21:02:30 +00:00
|
|
|
regex_t regexbuf;
|
2013-12-20 21:32:13 +00:00
|
|
|
|
2018-09-25 22:33:32 +00:00
|
|
|
if (!(var_aor = ast_variable_new("contact !=", "", ""))) {
|
2013-12-20 21:32:13 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-08-01 10:07:45 +00:00
|
|
|
/* Retrieving all the contacts may result in finding the same contact multiple
|
|
|
|
* times. So that they don't get displayed multiple times we only allow a
|
|
|
|
* single one to be placed into the container.
|
|
|
|
*/
|
|
|
|
contacts_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT,
|
2015-01-06 17:29:33 +00:00
|
|
|
cli_contact_sort, cli_contact_compare);
|
2018-09-25 22:33:32 +00:00
|
|
|
if (!contacts_container) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
contacts = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "contact", regex);
|
|
|
|
if (!contacts) {
|
|
|
|
ao2_ref(contacts_container, -1);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
ao2_callback(contacts, OBJ_NODATA, cli_gather_contact, contacts_container);
|
|
|
|
|
|
|
|
aors = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),
|
|
|
|
"aor", AST_RETRIEVE_FLAG_MULTIPLE, var_aor);
|
|
|
|
if (!aors) {
|
|
|
|
ao2_ref(contacts_container, -1);
|
2013-12-20 21:32:13 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-09-25 22:33:32 +00:00
|
|
|
ao2_callback(aors, OBJ_NODATA, cli_aor_gather_contacts, contacts_container);
|
2013-12-20 21:32:13 +00:00
|
|
|
|
2015-10-20 21:02:30 +00:00
|
|
|
if (!ast_strlen_zero(regex)) {
|
|
|
|
if (regcomp(®exbuf, regex, REG_EXTENDED | REG_NOSUB)) {
|
2018-09-25 22:33:32 +00:00
|
|
|
ao2_ref(contacts_container, -1);
|
2015-10-20 21:02:30 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2018-09-25 22:33:32 +00:00
|
|
|
ao2_callback(contacts_container, OBJ_UNLINK | OBJ_MULTIPLE | OBJ_NODATA, cli_filter_contacts, ®exbuf);
|
2015-10-20 21:02:30 +00:00
|
|
|
regfree(®exbuf);
|
|
|
|
}
|
|
|
|
|
2018-09-25 22:33:32 +00:00
|
|
|
return contacts_container;
|
2013-12-20 21:32:13 +00:00
|
|
|
}
|
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
static void *cli_contact_retrieve_by_id(const char *id)
|
2014-02-06 17:55:45 +00:00
|
|
|
{
|
2016-08-04 23:03:56 +00:00
|
|
|
struct ao2_container *container;
|
|
|
|
void *obj;
|
|
|
|
|
|
|
|
container = cli_contact_get_container("");
|
|
|
|
if (!container) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2015-10-20 21:02:30 +00:00
|
|
|
|
2016-08-04 23:03:56 +00:00
|
|
|
obj = ao2_find(container, id, OBJ_SEARCH_KEY);
|
|
|
|
ao2_ref(container, -1);
|
|
|
|
return obj;
|
2014-02-06 17:55:45 +00:00
|
|
|
}
|
2013-12-20 21:32:13 +00:00
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
static int cli_contact_print_header(void *obj, void *arg, int flags)
|
2013-12-20 21:32:13 +00:00
|
|
|
{
|
|
|
|
struct ast_sip_cli_context *context = arg;
|
|
|
|
int indent = CLI_INDENT_TO_SPACES(context->indent_level);
|
res_pjsip: Use a MD5 hash for static Contact IDs
When 90d9a70789 was merged, it mostly tested dynamic contacts created as
a result of registering a PJSIP endpoint. Contacts generated in this
fashion typically have a long alphanumeric string as their object identifier,
which maps reasonably well for StatsD. Unfortunately, this doesn't work in the
general case. StatsD treats both '.' and ':' characters as special characters.
In particular, having a ':' appear in the middle of a StatsD metric will
result in the metric being rejected.
This causes some obvious issues with SIP URIs.
The StatsD API should not be responsible for escaping the metric name passed
to it. The metric is treated as a single long string, and it would be
challenging to know what to escape in the string passed to the function.
Likewise, we don't want to escape the metric in PJSIP, as that involves
overhead that is wasted when either res_statsd isn't loaded or enabled.
This patch takes an alternative approach. The Contact ID has been changed
to be "aor@@uri_hash" instead of "aor@@uri". This (a) won't contain any of the
aforementioned special characters, (b) can be done on Contact creation,
which has minimal impact on run-time performance, and (c) also conforms to an
earlier commit that changed the ID for dynamic contacts.
The downside of this is that StatsD users will have to map SHA1 hashes back to
the Contacts that are emitting the statistics. To that end, the CLI commands
have been updated to include the first 10 characters of the MD5 hash, which
should be enough to match what is shown in Graphite (or some other StatsD
backend).
ASTERISK-25595 #close
Change-Id: Ic674a3307280365b4a45864a3571c295b48a01e2
Reported-by: Matt Jordan
Tested-by: George Joseph
2015-12-03 18:07:49 +00:00
|
|
|
int filler = CLI_LAST_TABSTOP - indent - 23;
|
2013-12-20 21:32:13 +00:00
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
ast_assert(context->output_buffer != NULL);
|
|
|
|
|
2013-12-20 21:32:13 +00:00
|
|
|
ast_str_append(&context->output_buffer, 0,
|
res_pjsip: Use a MD5 hash for static Contact IDs
When 90d9a70789 was merged, it mostly tested dynamic contacts created as
a result of registering a PJSIP endpoint. Contacts generated in this
fashion typically have a long alphanumeric string as their object identifier,
which maps reasonably well for StatsD. Unfortunately, this doesn't work in the
general case. StatsD treats both '.' and ':' characters as special characters.
In particular, having a ':' appear in the middle of a StatsD metric will
result in the metric being rejected.
This causes some obvious issues with SIP URIs.
The StatsD API should not be responsible for escaping the metric name passed
to it. The metric is treated as a single long string, and it would be
challenging to know what to escape in the string passed to the function.
Likewise, we don't want to escape the metric in PJSIP, as that involves
overhead that is wasted when either res_statsd isn't loaded or enabled.
This patch takes an alternative approach. The Contact ID has been changed
to be "aor@@uri_hash" instead of "aor@@uri". This (a) won't contain any of the
aforementioned special characters, (b) can be done on Contact creation,
which has minimal impact on run-time performance, and (c) also conforms to an
earlier commit that changed the ID for dynamic contacts.
The downside of this is that StatsD users will have to map SHA1 hashes back to
the Contacts that are emitting the statistics. To that end, the CLI commands
have been updated to include the first 10 characters of the MD5 hash, which
should be enough to match what is shown in Graphite (or some other StatsD
backend).
ASTERISK-25595 #close
Change-Id: Ic674a3307280365b4a45864a3571c295b48a01e2
Reported-by: Matt Jordan
Tested-by: George Joseph
2015-12-03 18:07:49 +00:00
|
|
|
"%*s: <Aor/ContactUri%*.*s> <Hash....> <Status> <RTT(ms)..>\n",
|
2013-12-20 21:32:13 +00:00
|
|
|
indent, "Contact", filler, filler, CLI_HEADER_FILLER);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
static int cli_contact_print_body(void *obj, void *arg, int flags)
|
2013-12-20 21:32:13 +00:00
|
|
|
{
|
2014-03-08 16:50:36 +00:00
|
|
|
struct ast_sip_contact_wrapper *wrapper = obj;
|
|
|
|
struct ast_sip_contact *contact = wrapper->contact;
|
2013-12-20 21:32:13 +00:00
|
|
|
struct ast_sip_cli_context *context = arg;
|
|
|
|
int indent;
|
|
|
|
int flexwidth;
|
res_pjsip: Use a MD5 hash for static Contact IDs
When 90d9a70789 was merged, it mostly tested dynamic contacts created as
a result of registering a PJSIP endpoint. Contacts generated in this
fashion typically have a long alphanumeric string as their object identifier,
which maps reasonably well for StatsD. Unfortunately, this doesn't work in the
general case. StatsD treats both '.' and ':' characters as special characters.
In particular, having a ':' appear in the middle of a StatsD metric will
result in the metric being rejected.
This causes some obvious issues with SIP URIs.
The StatsD API should not be responsible for escaping the metric name passed
to it. The metric is treated as a single long string, and it would be
challenging to know what to escape in the string passed to the function.
Likewise, we don't want to escape the metric in PJSIP, as that involves
overhead that is wasted when either res_statsd isn't loaded or enabled.
This patch takes an alternative approach. The Contact ID has been changed
to be "aor@@uri_hash" instead of "aor@@uri". This (a) won't contain any of the
aforementioned special characters, (b) can be done on Contact creation,
which has minimal impact on run-time performance, and (c) also conforms to an
earlier commit that changed the ID for dynamic contacts.
The downside of this is that StatsD users will have to map SHA1 hashes back to
the Contacts that are emitting the statistics. To that end, the CLI commands
have been updated to include the first 10 characters of the MD5 hash, which
should be enough to match what is shown in Graphite (or some other StatsD
backend).
ASTERISK-25595 #close
Change-Id: Ic674a3307280365b4a45864a3571c295b48a01e2
Reported-by: Matt Jordan
Tested-by: George Joseph
2015-12-03 18:07:49 +00:00
|
|
|
const char *contact_id = ast_sorcery_object_get_id(contact);
|
|
|
|
const char *hash_start = contact_id + strlen(contact->aor) + 2;
|
2016-08-04 23:03:56 +00:00
|
|
|
struct ast_sip_contact_status *status;
|
2013-12-20 21:32:13 +00:00
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
ast_assert(contact->uri != NULL);
|
|
|
|
ast_assert(context->output_buffer != NULL);
|
2013-12-20 21:32:13 +00:00
|
|
|
|
2017-12-11 18:34:53 +00:00
|
|
|
status = ast_sip_get_contact_status(contact);
|
2016-08-04 23:03:56 +00:00
|
|
|
|
2013-12-20 21:32:13 +00:00
|
|
|
indent = CLI_INDENT_TO_SPACES(context->indent_level);
|
res_pjsip: Use a MD5 hash for static Contact IDs
When 90d9a70789 was merged, it mostly tested dynamic contacts created as
a result of registering a PJSIP endpoint. Contacts generated in this
fashion typically have a long alphanumeric string as their object identifier,
which maps reasonably well for StatsD. Unfortunately, this doesn't work in the
general case. StatsD treats both '.' and ':' characters as special characters.
In particular, having a ':' appear in the middle of a StatsD metric will
result in the metric being rejected.
This causes some obvious issues with SIP URIs.
The StatsD API should not be responsible for escaping the metric name passed
to it. The metric is treated as a single long string, and it would be
challenging to know what to escape in the string passed to the function.
Likewise, we don't want to escape the metric in PJSIP, as that involves
overhead that is wasted when either res_statsd isn't loaded or enabled.
This patch takes an alternative approach. The Contact ID has been changed
to be "aor@@uri_hash" instead of "aor@@uri". This (a) won't contain any of the
aforementioned special characters, (b) can be done on Contact creation,
which has minimal impact on run-time performance, and (c) also conforms to an
earlier commit that changed the ID for dynamic contacts.
The downside of this is that StatsD users will have to map SHA1 hashes back to
the Contacts that are emitting the statistics. To that end, the CLI commands
have been updated to include the first 10 characters of the MD5 hash, which
should be enough to match what is shown in Graphite (or some other StatsD
backend).
ASTERISK-25595 #close
Change-Id: Ic674a3307280365b4a45864a3571c295b48a01e2
Reported-by: Matt Jordan
Tested-by: George Joseph
2015-12-03 18:07:49 +00:00
|
|
|
flexwidth = CLI_LAST_TABSTOP - indent - 9 - strlen(contact->aor) + 1;
|
2013-12-20 21:32:13 +00:00
|
|
|
|
res_pjsip: Use a MD5 hash for static Contact IDs
When 90d9a70789 was merged, it mostly tested dynamic contacts created as
a result of registering a PJSIP endpoint. Contacts generated in this
fashion typically have a long alphanumeric string as their object identifier,
which maps reasonably well for StatsD. Unfortunately, this doesn't work in the
general case. StatsD treats both '.' and ':' characters as special characters.
In particular, having a ':' appear in the middle of a StatsD metric will
result in the metric being rejected.
This causes some obvious issues with SIP URIs.
The StatsD API should not be responsible for escaping the metric name passed
to it. The metric is treated as a single long string, and it would be
challenging to know what to escape in the string passed to the function.
Likewise, we don't want to escape the metric in PJSIP, as that involves
overhead that is wasted when either res_statsd isn't loaded or enabled.
This patch takes an alternative approach. The Contact ID has been changed
to be "aor@@uri_hash" instead of "aor@@uri". This (a) won't contain any of the
aforementioned special characters, (b) can be done on Contact creation,
which has minimal impact on run-time performance, and (c) also conforms to an
earlier commit that changed the ID for dynamic contacts.
The downside of this is that StatsD users will have to map SHA1 hashes back to
the Contacts that are emitting the statistics. To that end, the CLI commands
have been updated to include the first 10 characters of the MD5 hash, which
should be enough to match what is shown in Graphite (or some other StatsD
backend).
ASTERISK-25595 #close
Change-Id: Ic674a3307280365b4a45864a3571c295b48a01e2
Reported-by: Matt Jordan
Tested-by: George Joseph
2015-12-03 18:07:49 +00:00
|
|
|
ast_str_append(&context->output_buffer, 0, "%*s: %s/%-*.*s %-10.10s %-7.7s %11.3f\n",
|
2013-12-20 21:32:13 +00:00
|
|
|
indent,
|
|
|
|
"Contact",
|
2015-12-01 04:19:18 +00:00
|
|
|
contact->aor,
|
2013-12-20 21:32:13 +00:00
|
|
|
flexwidth, flexwidth,
|
2015-12-01 04:19:18 +00:00
|
|
|
contact->uri,
|
res_pjsip: Use a MD5 hash for static Contact IDs
When 90d9a70789 was merged, it mostly tested dynamic contacts created as
a result of registering a PJSIP endpoint. Contacts generated in this
fashion typically have a long alphanumeric string as their object identifier,
which maps reasonably well for StatsD. Unfortunately, this doesn't work in the
general case. StatsD treats both '.' and ':' characters as special characters.
In particular, having a ':' appear in the middle of a StatsD metric will
result in the metric being rejected.
This causes some obvious issues with SIP URIs.
The StatsD API should not be responsible for escaping the metric name passed
to it. The metric is treated as a single long string, and it would be
challenging to know what to escape in the string passed to the function.
Likewise, we don't want to escape the metric in PJSIP, as that involves
overhead that is wasted when either res_statsd isn't loaded or enabled.
This patch takes an alternative approach. The Contact ID has been changed
to be "aor@@uri_hash" instead of "aor@@uri". This (a) won't contain any of the
aforementioned special characters, (b) can be done on Contact creation,
which has minimal impact on run-time performance, and (c) also conforms to an
earlier commit that changed the ID for dynamic contacts.
The downside of this is that StatsD users will have to map SHA1 hashes back to
the Contacts that are emitting the statistics. To that end, the CLI commands
have been updated to include the first 10 characters of the MD5 hash, which
should be enough to match what is shown in Graphite (or some other StatsD
backend).
ASTERISK-25595 #close
Change-Id: Ic674a3307280365b4a45864a3571c295b48a01e2
Reported-by: Matt Jordan
Tested-by: George Joseph
2015-12-03 18:07:49 +00:00
|
|
|
hash_start,
|
2015-04-27 17:11:40 +00:00
|
|
|
ast_sip_get_contact_short_status_label(status ? status->status : UNKNOWN),
|
2017-12-11 18:34:53 +00:00
|
|
|
(status && (status->status == AVAILABLE)) ? ((long long) status->rtt) / 1000.0 : NAN);
|
2013-12-20 21:32:13 +00:00
|
|
|
|
2016-08-04 23:03:56 +00:00
|
|
|
ao2_cleanup(status);
|
2013-12-20 21:32:13 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
static int cli_aor_iterate(void *container, ao2_callback_fn callback, void *args)
|
2013-12-20 21:32:13 +00:00
|
|
|
{
|
2014-02-06 17:55:45 +00:00
|
|
|
const char *aor_list = container;
|
|
|
|
|
|
|
|
return ast_sip_for_each_aor(aor_list, callback, args);
|
2013-12-20 21:32:13 +00:00
|
|
|
}
|
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
static void *cli_aor_retrieve_by_id(const char *id)
|
|
|
|
{
|
|
|
|
return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "aor", id);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *cli_aor_get_id(const void *obj)
|
|
|
|
{
|
|
|
|
return ast_sorcery_object_get_id(obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cli_aor_print_header(void *obj, void *arg, int flags)
|
2013-12-20 21:32:13 +00:00
|
|
|
{
|
|
|
|
struct ast_sip_cli_context *context = arg;
|
|
|
|
int indent = CLI_INDENT_TO_SPACES(context->indent_level);
|
|
|
|
int filler = CLI_LAST_TABSTOP - indent - 7;
|
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
ast_assert(context->output_buffer != NULL);
|
|
|
|
|
2013-12-20 21:32:13 +00:00
|
|
|
ast_str_append(&context->output_buffer, 0,
|
|
|
|
"%*s: <Aor%*.*s> <MaxContact>\n",
|
|
|
|
indent, "Aor", filler, filler, CLI_HEADER_FILLER);
|
|
|
|
|
|
|
|
if (context->recurse) {
|
2016-08-04 23:03:56 +00:00
|
|
|
struct ast_sip_cli_formatter_entry *formatter_entry;
|
|
|
|
|
2013-12-20 21:32:13 +00:00
|
|
|
context->indent_level++;
|
|
|
|
formatter_entry = ast_sip_lookup_cli_formatter("contact");
|
2014-03-08 16:50:36 +00:00
|
|
|
if (formatter_entry) {
|
2013-12-20 21:32:13 +00:00
|
|
|
formatter_entry->print_header(NULL, context, 0);
|
2016-08-04 23:03:56 +00:00
|
|
|
ao2_ref(formatter_entry, -1);
|
2013-12-20 21:32:13 +00:00
|
|
|
}
|
|
|
|
context->indent_level--;
|
|
|
|
}
|
2014-03-08 16:50:36 +00:00
|
|
|
|
2013-12-20 21:32:13 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
static int cli_aor_print_body(void *obj, void *arg, int flags)
|
2013-12-20 21:32:13 +00:00
|
|
|
{
|
|
|
|
struct ast_sip_aor *aor = obj;
|
|
|
|
struct ast_sip_cli_context *context = arg;
|
|
|
|
int indent;
|
|
|
|
int flexwidth;
|
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
ast_assert(context->output_buffer != NULL);
|
2013-12-20 21:32:13 +00:00
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
// context->current_aor = aor;
|
2013-12-20 21:32:13 +00:00
|
|
|
|
|
|
|
indent = CLI_INDENT_TO_SPACES(context->indent_level);
|
|
|
|
flexwidth = CLI_LAST_TABSTOP - indent - 12;
|
|
|
|
|
2014-05-09 22:49:26 +00:00
|
|
|
ast_str_append(&context->output_buffer, 0, "%*s: %-*.*s %12u\n",
|
2013-12-20 21:32:13 +00:00
|
|
|
indent,
|
|
|
|
"Aor",
|
|
|
|
flexwidth, flexwidth,
|
|
|
|
ast_sorcery_object_get_id(aor), aor->max_contacts);
|
|
|
|
|
|
|
|
if (context->recurse) {
|
2016-08-04 23:03:56 +00:00
|
|
|
struct ast_sip_cli_formatter_entry *formatter_entry;
|
|
|
|
|
2013-12-20 21:32:13 +00:00
|
|
|
context->indent_level++;
|
2014-02-06 17:55:45 +00:00
|
|
|
|
2013-12-20 21:32:13 +00:00
|
|
|
formatter_entry = ast_sip_lookup_cli_formatter("contact");
|
2014-03-08 16:50:36 +00:00
|
|
|
if (formatter_entry) {
|
|
|
|
formatter_entry->iterate(aor, formatter_entry->print_body, context);
|
2016-08-04 23:03:56 +00:00
|
|
|
ao2_ref(formatter_entry, -1);
|
2013-12-20 21:32:13 +00:00
|
|
|
}
|
2014-02-06 17:55:45 +00:00
|
|
|
|
2013-12-20 21:32:13 +00:00
|
|
|
context->indent_level--;
|
2014-02-06 17:55:45 +00:00
|
|
|
|
|
|
|
if (context->indent_level == 0) {
|
|
|
|
ast_str_append(&context->output_buffer, 0, "\n");
|
|
|
|
}
|
2013-12-20 21:32:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (context->show_details || (context->show_details_only_level_0 && context->indent_level == 0)) {
|
|
|
|
ast_str_append(&context->output_buffer, 0, "\n");
|
|
|
|
ast_sip_cli_print_sorcery_objectset(aor, context, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-30 05:14:00 +00:00
|
|
|
static struct ao2_container *cli_get_aors(void)
|
|
|
|
{
|
|
|
|
struct ao2_container *aors;
|
|
|
|
|
|
|
|
aors = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "aor",
|
|
|
|
AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
|
|
|
|
|
|
|
|
return aors;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int format_ami_aorlist_handler(void *obj, void *arg, int flags)
|
|
|
|
{
|
|
|
|
struct ast_sip_aor *aor = obj;
|
|
|
|
struct ast_sip_ami *ami = arg;
|
|
|
|
struct ast_str *buf;
|
|
|
|
|
|
|
|
buf = ast_sip_create_ami_event("AorList", ami);
|
|
|
|
if (!buf) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
sip_aor_to_ami(aor, &buf);
|
|
|
|
|
|
|
|
astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
|
|
|
|
ami->count++;
|
|
|
|
|
|
|
|
ast_free(buf);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ami_show_aors(struct mansession *s, const struct message *m)
|
|
|
|
{
|
|
|
|
struct ast_sip_ami ami = { .s = s, .m = m, .action_id = astman_get_header(m, "ActionID"), };
|
|
|
|
struct ao2_container *aors;
|
|
|
|
|
|
|
|
aors = cli_get_aors();
|
|
|
|
if (!aors) {
|
|
|
|
astman_send_error(s, m, "Could not get AORs\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ao2_container_count(aors)) {
|
|
|
|
astman_send_error(s, m, "No AORs found\n");
|
|
|
|
ao2_ref(aors, -1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
astman_send_listack(s, m, "A listing of AORs follows, presented as AorList events",
|
|
|
|
"start");
|
|
|
|
|
|
|
|
ao2_callback(aors, OBJ_NODATA, format_ami_aorlist_handler, &ami);
|
|
|
|
|
|
|
|
astman_send_list_complete_start(s, m, "AorListComplete", ami.count);
|
|
|
|
astman_send_list_complete_end(s);
|
|
|
|
|
|
|
|
ao2_ref(aors, -1);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-02-06 17:55:45 +00:00
|
|
|
static struct ast_cli_entry cli_commands[] = {
|
|
|
|
AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Aors",
|
|
|
|
.command = "pjsip list aors",
|
2015-10-20 21:02:30 +00:00
|
|
|
.usage = "Usage: pjsip list aors [ like <pattern> ]\n"
|
|
|
|
" List the configured PJSIP Aors\n"
|
|
|
|
" Optional regular expression pattern is used to filter the list.\n"),
|
2014-02-06 17:55:45 +00:00
|
|
|
AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Aors",
|
|
|
|
.command = "pjsip show aors",
|
2015-10-20 21:02:30 +00:00
|
|
|
.usage = "Usage: pjsip show aors [ like <pattern> ]\n"
|
|
|
|
" Show the configured PJSIP Aors\n"
|
|
|
|
" Optional regular expression pattern is used to filter the list.\n"),
|
2014-02-06 17:55:45 +00:00
|
|
|
AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Aor",
|
|
|
|
.command = "pjsip show aor",
|
|
|
|
.usage = "Usage: pjsip show aor <id>\n"
|
|
|
|
" Show the configured PJSIP Aor\n"),
|
|
|
|
|
|
|
|
AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Contacts",
|
|
|
|
.command = "pjsip list contacts",
|
2015-10-20 21:02:30 +00:00
|
|
|
.usage = "Usage: pjsip list contacts [ like <pattern> ]\n"
|
|
|
|
" List the configured PJSIP contacts\n"
|
|
|
|
" Optional regular expression pattern is used to filter the list.\n"),
|
2014-02-20 21:12:02 +00:00
|
|
|
AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Contacts",
|
|
|
|
.command = "pjsip show contacts",
|
2015-10-20 21:02:30 +00:00
|
|
|
.usage = "Usage: pjsip show contacts [ like <pattern> ]\n"
|
|
|
|
" Show the configured PJSIP contacts\n"
|
|
|
|
" Optional regular expression pattern is used to filter the list.\n"),
|
2014-03-08 16:50:36 +00:00
|
|
|
AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Contact",
|
|
|
|
.command = "pjsip show contact",
|
|
|
|
.usage = "Usage: pjsip show contact\n"
|
|
|
|
" Show the configured PJSIP contact\n"),
|
2013-12-20 21:32:13 +00:00
|
|
|
};
|
|
|
|
|
2014-03-08 16:50:36 +00:00
|
|
|
struct ast_sip_cli_formatter_entry *contact_formatter;
|
|
|
|
struct ast_sip_cli_formatter_entry *aor_formatter;
|
|
|
|
|
2015-04-27 17:11:40 +00:00
|
|
|
/*! \brief Always create a contact_status for each contact */
|
|
|
|
static int contact_apply_handler(const struct ast_sorcery *sorcery, void *object)
|
|
|
|
{
|
|
|
|
struct ast_sip_contact_status *status;
|
|
|
|
struct ast_sip_contact *contact = object;
|
|
|
|
|
2017-11-06 04:06:15 +00:00
|
|
|
if (ast_strlen_zero(contact->uri)) {
|
|
|
|
ast_log(LOG_ERROR, "A URI on dynamic contact '%s' is empty\n",
|
|
|
|
ast_sorcery_object_get_id(contact));
|
|
|
|
return -1;
|
|
|
|
}
|
2015-04-27 17:11:40 +00:00
|
|
|
status = ast_res_pjsip_find_or_create_contact_status(contact);
|
2015-06-03 18:17:58 +00:00
|
|
|
ao2_cleanup(status);
|
2015-04-27 17:11:40 +00:00
|
|
|
|
|
|
|
return status ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
2020-06-26 10:18:55 +00:00
|
|
|
static int aor_apply_outbound_proxy(void *obj, void *arg, int flags)
|
|
|
|
{
|
|
|
|
struct ast_sip_contact *contact = obj;
|
|
|
|
struct ast_sip_aor *aor = arg;
|
|
|
|
|
|
|
|
ast_string_field_set(contact, outbound_proxy, aor->outbound_proxy);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int aor_apply_handler(const struct ast_sorcery *sorcery, void *object)
|
|
|
|
{
|
|
|
|
struct ast_sip_aor *aor = object;
|
|
|
|
|
|
|
|
if (!aor->permanent_contacts || ast_strlen_zero(aor->outbound_proxy)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ao2_callback(aor->permanent_contacts, OBJ_NODATA | OBJ_MULTIPLE, aor_apply_outbound_proxy, aor);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-04-25 18:25:31 +00:00
|
|
|
/*! \brief Initialize sorcery with location support */
|
2014-02-06 17:55:45 +00:00
|
|
|
int ast_sip_initialize_sorcery_location(void)
|
2013-04-25 18:25:31 +00:00
|
|
|
{
|
2014-02-06 17:55:45 +00:00
|
|
|
struct ast_sorcery *sorcery = ast_sip_get_sorcery();
|
2015-11-18 16:07:09 +00:00
|
|
|
int i;
|
|
|
|
|
2016-04-14 12:23:54 +00:00
|
|
|
ast_pjproject_get_buildopt("PJ_MAX_HOSTNAME", "%d", &pj_max_hostname);
|
|
|
|
/* As of pjproject 2.4.5, PJSIP_MAX_URL_SIZE isn't exposed yet but we try anyway. */
|
|
|
|
ast_pjproject_get_buildopt("PJSIP_MAX_URL_SIZE", "%d", &pjsip_max_url_size);
|
|
|
|
|
2013-04-25 18:25:31 +00:00
|
|
|
ast_sorcery_apply_default(sorcery, "contact", "astdb", "registrar");
|
2016-06-02 23:19:13 +00:00
|
|
|
ast_sorcery_object_set_congestion_levels(sorcery, "contact", -1,
|
|
|
|
3 * AST_TASKPROCESSOR_HIGH_WATER_LEVEL);
|
2013-07-30 18:14:50 +00:00
|
|
|
ast_sorcery_apply_default(sorcery, "aor", "config", "pjsip.conf,criteria=type=aor");
|
2013-04-25 18:25:31 +00:00
|
|
|
|
2015-04-27 17:11:40 +00:00
|
|
|
if (ast_sorcery_object_register(sorcery, "contact", contact_alloc, NULL, contact_apply_handler) ||
|
2020-06-26 10:18:55 +00:00
|
|
|
ast_sorcery_object_register(sorcery, "aor", aor_alloc, NULL, aor_apply_handler)) {
|
2013-04-25 18:25:31 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-09-07 16:15:59 +00:00
|
|
|
ast_sorcery_observer_add(sorcery, "aor", &aor_observer);
|
|
|
|
|
2013-04-25 18:25:31 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "contact", "type", "", OPT_NOOP_T, 0, 0);
|
|
|
|
ast_sorcery_object_field_register(sorcery, "contact", "uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, uri));
|
2014-01-15 13:16:10 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "contact", "path", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, path));
|
2014-03-06 22:39:54 +00:00
|
|
|
ast_sorcery_object_field_register_custom(sorcery, "contact", "expiration_time", "", expiration_str2struct, expiration_struct2str, NULL, 0, 0);
|
2013-06-22 14:03:22 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "contact", "qualify_frequency", 0, OPT_UINT_T,
|
2015-04-11 21:56:52 +00:00
|
|
|
PARSE_IN_RANGE, FLDSET(struct ast_sip_contact, qualify_frequency), 0, 86400);
|
|
|
|
ast_sorcery_object_field_register(sorcery, "contact", "qualify_timeout", "3.0", OPT_DOUBLE_T, 0, FLDSET(struct ast_sip_contact, qualify_timeout));
|
2016-06-20 18:21:52 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "contact", "authenticate_qualify", "no", OPT_YESNO_T, 1, FLDSET(struct ast_sip_contact, authenticate_qualify));
|
2013-12-14 17:28:21 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "contact", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, outbound_proxy));
|
2014-02-17 15:36:45 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "contact", "user_agent", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, user_agent));
|
2016-06-22 19:25:23 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "contact", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, endpoint_name));
|
2016-04-15 19:26:15 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "contact", "reg_server", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, reg_server));
|
2016-05-19 19:56:26 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "contact", "via_addr", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, via_addr));
|
|
|
|
ast_sorcery_object_field_register(sorcery, "contact", "via_port", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_contact, via_port));
|
|
|
|
ast_sorcery_object_field_register(sorcery, "contact", "call_id", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, call_id));
|
2017-07-31 19:21:06 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "contact", "prune_on_boot", "no", OPT_YESNO_T, 1, FLDSET(struct ast_sip_contact, prune_on_boot));
|
2013-04-25 18:25:31 +00:00
|
|
|
|
|
|
|
ast_sorcery_object_field_register(sorcery, "aor", "type", "", OPT_NOOP_T, 0, 0);
|
|
|
|
ast_sorcery_object_field_register(sorcery, "aor", "minimum_expiration", "60", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, minimum_expiration));
|
|
|
|
ast_sorcery_object_field_register(sorcery, "aor", "maximum_expiration", "7200", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, maximum_expiration));
|
|
|
|
ast_sorcery_object_field_register(sorcery, "aor", "default_expiration", "3600", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, default_expiration));
|
2013-06-22 14:03:22 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "aor", "qualify_frequency", 0, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_aor, qualify_frequency), 0, 86400);
|
2015-04-11 21:56:52 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "aor", "qualify_timeout", "3.0", OPT_DOUBLE_T, 0, FLDSET(struct ast_sip_aor, qualify_timeout));
|
2013-06-22 14:03:22 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "aor", "authenticate_qualify", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, authenticate_qualify));
|
2013-04-25 18:25:31 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "aor", "max_contacts", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, max_contacts));
|
|
|
|
ast_sorcery_object_field_register(sorcery, "aor", "remove_existing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, remove_existing));
|
2014-03-08 16:50:36 +00:00
|
|
|
ast_sorcery_object_field_register_custom(sorcery, "aor", "contact", "", permanent_uri_handler, contacts_to_str, contacts_to_var_list, 0, 0);
|
2013-04-25 18:25:31 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "aor", "mailboxes", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, mailboxes));
|
res_pjsip_mwi: Add voicemail extension and mwi_subscribe_replaces_unsolicited
res_pjsip_mwi was missing the chan_sip "vmexten" functionality which adds
the Message-Account header to the MWI NOTIFY. Also, specifying mailboxes
on endpoints for unsolicited mwi and on aors for subscriptions required
that the admin know in advance which the client wanted. If you specified
mailboxes on the endpoint, subscriptions were rejected even if you also
specified mailboxes on the aor.
Voicemail extension:
* Added a global default_voicemail_extension which defaults to "".
* Added voicemail_extension to both endpoint and aor.
* Added ast_sip_subscription_get_dialog for support.
* Added ast_sip_subscription_get_sip_uri for support.
When an unsolicited NOTIFY is constructed, the From header is parsed, the
voicemail extension from the endpoint is substituted for the user, and the
result placed in the Message-Account field in the body.
When a subscribed NOTIFY is constructed, the subscription dialog local uri
is parsed, the voicemail_extension from the aor (looked up from the
subscription resource name) is substituted for the user, and the result
placed in the Message-Account field in the body.
If no voicemail extension was defined, the Message-Account field is not added
to the NOTIFY body.
mwi_subscribe_replaces_unsolicited:
* Added mwi_subscribe_replaces_unsolicited to endpoint.
The previous behavior was to reject a subscribe if a previous internal
subscription for unsolicited MWI was found for the mailbox. That remains the
default. However, if there are mailboxes also set on the aor and the client
subscribes and mwi_subscribe_replaces_unsolicited is set, the existing internal
subscription is removed and replaced with the external subscription. This
allows an admin to configure mailboxes on both the endpoint and aor and allows
the client to select which to use.
ASTERISK-25865 #close
Reported-by: Ross Beer
Change-Id: Ic15a9415091760539c7134a5ba3dc4a6a1217cea
2016-03-25 03:55:03 +00:00
|
|
|
ast_sorcery_object_field_register_custom(sorcery, "aor", "voicemail_extension", "", voicemail_extension_handler, voicemail_extension_to_str, NULL, 0, 0);
|
2013-12-14 17:28:21 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "aor", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, outbound_proxy));
|
2014-01-15 13:16:10 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "aor", "support_path", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, support_path));
|
2013-04-25 18:25:31 +00:00
|
|
|
|
2017-12-29 08:57:17 +00:00
|
|
|
ast_sip_register_endpoint_formatter(&endpoint_aor_formatter);
|
2014-03-08 16:50:36 +00:00
|
|
|
|
|
|
|
contact_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
|
|
|
|
if (!contact_formatter) {
|
|
|
|
ast_log(LOG_ERROR, "Unable to allocate memory for contact_formatter\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
contact_formatter->name = "contact";
|
|
|
|
contact_formatter->print_header = cli_contact_print_header;
|
|
|
|
contact_formatter->print_body = cli_contact_print_body;
|
|
|
|
contact_formatter->get_container = cli_contact_get_container;
|
|
|
|
contact_formatter->iterate = cli_contact_iterate;
|
|
|
|
contact_formatter->get_id = cli_contact_get_id;
|
|
|
|
contact_formatter->retrieve_by_id = cli_contact_retrieve_by_id;
|
|
|
|
|
|
|
|
aor_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
|
|
|
|
if (!aor_formatter) {
|
|
|
|
ast_log(LOG_ERROR, "Unable to allocate memory for aor_formatter\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
aor_formatter->name = "aor";
|
|
|
|
aor_formatter->print_header = cli_aor_print_header;
|
|
|
|
aor_formatter->print_body = cli_aor_print_body;
|
|
|
|
aor_formatter->get_container = cli_aor_get_container;
|
|
|
|
aor_formatter->iterate = cli_aor_iterate;
|
|
|
|
aor_formatter->get_id = cli_aor_get_id;
|
|
|
|
aor_formatter->retrieve_by_id = cli_aor_retrieve_by_id;
|
|
|
|
|
|
|
|
ast_sip_register_cli_formatter(contact_formatter);
|
|
|
|
ast_sip_register_cli_formatter(aor_formatter);
|
2014-02-06 17:55:45 +00:00
|
|
|
ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
|
2014-03-08 16:50:36 +00:00
|
|
|
|
2017-12-30 05:14:00 +00:00
|
|
|
if (ast_manager_register_xml("PJSIPShowAors", EVENT_FLAG_SYSTEM, ami_show_aors)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-11-18 16:07:09 +00:00
|
|
|
/*
|
|
|
|
* Reset StatsD gauges in case we didn't shut down cleanly.
|
|
|
|
* Note that this must done here, as contacts will create the contact_status
|
|
|
|
* object before PJSIP options handling is initialized.
|
|
|
|
*/
|
2015-12-04 22:23:21 +00:00
|
|
|
for (i = 0; i <= REMOVED; i++) {
|
2015-11-24 19:54:54 +00:00
|
|
|
ast_statsd_log_full_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE, 0, 1.0, ast_sip_get_contact_status_label(i));
|
2015-11-18 16:07:09 +00:00
|
|
|
}
|
|
|
|
|
2014-02-06 17:55:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ast_sip_destroy_sorcery_location(void)
|
|
|
|
{
|
2015-09-07 16:15:59 +00:00
|
|
|
ast_sorcery_observer_remove(ast_sip_get_sorcery(), "aor", &aor_observer);
|
2014-02-06 17:55:45 +00:00
|
|
|
ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
|
2014-03-08 16:50:36 +00:00
|
|
|
ast_sip_unregister_cli_formatter(contact_formatter);
|
|
|
|
ast_sip_unregister_cli_formatter(aor_formatter);
|
2017-12-30 05:14:00 +00:00
|
|
|
ast_manager_unregister("PJSIPShowAors");
|
2014-03-08 16:50:36 +00:00
|
|
|
|
2017-12-29 08:57:17 +00:00
|
|
|
ast_sip_unregister_endpoint_formatter(&endpoint_aor_formatter);
|
2015-01-27 19:12:56 +00:00
|
|
|
|
2013-04-25 18:25:31 +00:00
|
|
|
return 0;
|
|
|
|
}
|