Exemple #1
0
/*! \brief Allocate an \ref ast_ari_conf for config parsing */
static void *conf_alloc(void)
{
	RAII_VAR(struct ast_ari_conf *, cfg, NULL, ao2_cleanup);

	cfg = ao2_alloc_options(sizeof(*cfg), conf_destructor,
		AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (!cfg) {
		return NULL;
	}

	cfg->general = ao2_alloc_options(sizeof(*cfg->general), NULL,
		AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (!cfg->general) {
		return NULL;
	}
	aco_set_defaults(&general_option, "general", cfg->general);

	if (ast_string_field_init(cfg->general, 64)) {
		return NULL;
	}

	cfg->users = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
		AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, user_sort_cmp, NULL);

	ao2_ref(cfg, +1);
	return cfg;
}
Exemple #2
0
/*! \brief Allocate an \ref ast_ari_conf for config parsing */
static void *conf_alloc(void)
{
	struct ast_ari_conf *cfg;

	cfg = ao2_alloc_options(sizeof(*cfg), conf_destructor,
		AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (!cfg) {
		return NULL;
	}

	cfg->general = ao2_alloc_options(sizeof(*cfg->general), conf_general_dtor,
		AO2_ALLOC_OPT_LOCK_NOLOCK);

	cfg->users = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
		AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, user_sort_cmp, NULL);

	if (!cfg->users
		|| !cfg->general
		|| ast_string_field_init(cfg->general, 64)
		|| aco_set_defaults(&general_option, "general", cfg->general)) {
		ao2_ref(cfg, -1);
		return NULL;
	}

	return cfg;
}
Exemple #3
0
static struct cache_entry *cache_entry_create(struct stasis_message_type *type, const char *id, struct stasis_message *snapshot)
{
	RAII_VAR(struct cache_entry *, entry, NULL, ao2_cleanup);

	ast_assert(type != NULL);
	ast_assert(id != NULL);

	entry = ao2_alloc_options(sizeof(*entry), cache_entry_dtor,
		AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (!entry) {
		return NULL;
	}

	entry->id = ast_strdup(id);
	if (!entry->id) {
		return NULL;
	}

	ao2_ref(type, +1);
	entry->type = type;
	if (snapshot != NULL) {
		ao2_ref(snapshot, +1);
		entry->snapshot = snapshot;
	}

	ao2_ref(entry, +1);
	return entry;
}
/*! \brief Observer callback for when a contact is created */
static void contact_expiration_observer_created(const void *object)
{
	const struct ast_sip_contact *contact = object;
	struct contact_expiration *expiration;
	int expires = MAX(0, ast_tvdiff_ms(contact->expiration_time, ast_tvnow()));

	if (ast_tvzero(contact->expiration_time)) {
		return;
	}

	expiration = ao2_alloc_options(sizeof(*expiration), contact_expiration_destroy,
		AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (!expiration) {
		return;
	}

	expiration->contact = (struct ast_sip_contact*)contact;
	ao2_ref(expiration->contact, +1);

	ao2_ref(expiration, +1);
	if ((expiration->sched = ast_sched_add(sched, expires, contact_expiration_expire, expiration)) < 0) {
		ao2_ref(expiration, -1);
		ast_log(LOG_ERROR, "Scheduled expiration for contact '%s' could not be performed, contact may persist past life\n",
			ast_sorcery_object_get_id(contact));
	} else {
		ao2_link(contact_autoexpire, expiration);
	}
	ao2_ref(expiration, -1);
}
Exemple #5
0
int __ast_format_interface_register(const char *codec, const struct ast_format_interface *interface, struct ast_module *mod)
{
	SCOPED_AO2WRLOCK(lock, interfaces);
	struct format_interface *format_interface;

	if (!interface->format_clone || !interface->format_destroy) {
		ast_log(LOG_ERROR, "Format interface for codec '%s' does not implement required callbacks\n", codec);
		return -1;
	}

	format_interface = ao2_find(interfaces, codec, OBJ_SEARCH_KEY | OBJ_NOLOCK);
	if (format_interface) {
		ast_log(LOG_ERROR, "A format interface is already present for codec '%s'\n", codec);
		ao2_ref(format_interface, -1);
		return -1;
	}

	format_interface = ao2_alloc_options(sizeof(*format_interface) + strlen(codec) + 1,
		NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (!format_interface) {
		return -1;
	}
	format_interface->interface = interface;
	strcpy(format_interface->codec, codec); /* Safe */

	/* Once registered a format interface cannot be unregistered. */
	ast_module_shutdown_ref(mod);
	ao2_link_flags(interfaces, format_interface, OBJ_NOLOCK);
	ao2_ref(format_interface, -1);

	ast_verb(2, "Registered format interface for codec '%s'\n", codec);

	return 0;
}
Exemple #6
0
struct ast_named_lock *__ast_named_lock_get(const char *filename, int lineno, const char *func,
	enum ast_named_lock_type lock_type, const char *keyspace, const char *key)
{
	struct ast_named_lock *lock = NULL;
	int concat_key_buff_len = strlen(keyspace) + strlen(key) + 2;
	char *concat_key = ast_alloca(concat_key_buff_len);

	sprintf(concat_key, "%s-%s", keyspace, key); /* Safe */

	ao2_lock(named_locks);
	lock = ao2_find(named_locks, concat_key, OBJ_SEARCH_KEY | OBJ_NOLOCK);
	if (lock) {
		ao2_unlock(named_locks);
		ast_assert((ao2_options_get(lock) & AO2_ALLOC_OPT_LOCK_MASK) == lock_type);
		return lock;
	}

	lock = ao2_alloc_options(sizeof(*lock) + concat_key_buff_len, NULL, lock_type);
	if (lock) {
		strcpy(lock->key, concat_key); /* Safe */
		ao2_link_flags(named_locks, lock, OBJ_NOLOCK);
	}
	ao2_unlock(named_locks);

	return lock;
}
static int bridge_builtin_set_limits(struct ast_bridge_features *features,
	struct ast_bridge_features_limits *limits,
	enum ast_bridge_hook_remove_flags remove_flags)
{
	RAII_VAR(struct ast_bridge_features_limits *, feature_limits, NULL, ao2_cleanup);

	if (!limits->duration) {
		return -1;
	}

	/* Create limits hook_pvt data. */
	ast_module_ref(ast_module_info->self);
	feature_limits = ao2_alloc_options(sizeof(*feature_limits),
		bridge_features_limits_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (!feature_limits) {
		ast_module_unref(ast_module_info->self);
		return -1;
	}
	if (ast_bridge_features_limits_construct(feature_limits)) {
		return -1;
	}
	bridge_features_limits_copy(feature_limits, limits);
	feature_limits->quitting_time = ast_tvadd(ast_tvnow(),
		ast_samp2tv(feature_limits->duration, 1000));

	/* Install limit hooks. */
	ao2_ref(feature_limits, +1);
	if (ast_bridge_interval_hook(features, AST_BRIDGE_HOOK_TIMER_OPTION_MEDIA,
		feature_limits->duration,
		bridge_features_duration_callback, feature_limits, __ao2_cleanup, remove_flags)) {
		ast_log(LOG_ERROR, "Failed to schedule the duration limiter to the bridge channel.\n");
		ao2_ref(feature_limits, -1);
		return -1;
	}
	if (!ast_strlen_zero(feature_limits->connect_sound)) {
		ao2_ref(feature_limits, +1);
		if (ast_bridge_interval_hook(features, AST_BRIDGE_HOOK_TIMER_OPTION_MEDIA, 1,
			bridge_features_connect_callback, feature_limits, __ao2_cleanup, remove_flags)) {
			ast_log(LOG_WARNING, "Failed to schedule connect sound to the bridge channel.\n");
			ao2_ref(feature_limits, -1);
		}
	}
	if (feature_limits->warning && feature_limits->warning < feature_limits->duration) {
		ao2_ref(feature_limits, +1);
		if (ast_bridge_interval_hook(features, AST_BRIDGE_HOOK_TIMER_OPTION_MEDIA,
			feature_limits->duration - feature_limits->warning,
			bridge_features_warning_callback, feature_limits, __ao2_cleanup, remove_flags)) {
			ast_log(LOG_WARNING, "Failed to schedule warning sound playback to the bridge channel.\n");
			ao2_ref(feature_limits, -1);
		}
	}

	return 0;
}
Exemple #8
0
/*!
 * \internal
 * \brief fopencookie()/funopen() stream allocation function.
 *
 * \retval stream_cookie on success.
 * \retval NULL on error.
 */
static struct ast_tcptls_stream *tcptls_stream_alloc(void)
{
	struct ast_tcptls_stream *stream;

	stream = ao2_alloc_options(sizeof(*stream), tcptls_stream_dtor,
		AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (stream) {
		stream->fd = -1;
		stream->timeout = -1;
	}
	return stream;
}
Exemple #9
0
static struct serializer *serializer_create(struct ast_threadpool *pool,
	struct ast_serializer_shutdown_group *shutdown_group)
{
	struct serializer *ser;

	ser = ao2_alloc_options(sizeof(*ser), serializer_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (!ser) {
		return NULL;
	}
	ao2_ref(pool, +1);
	ser->pool = pool;
	ser->shutdown_group = ao2_bump(shutdown_group);
	return ser;
}
Exemple #10
0
int ast_str_container_add(struct ao2_container *str_container, const char *add)
{
	char *ao2_add;

	/* The ao2_add object is immutable so it doesn't need a lock of its own. */
	ao2_add = ao2_alloc_options(strlen(add) + 1, NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (!ao2_add) {
		return -1;
	}
	strcpy(ao2_add, add);/* Safe */

	ao2_link(str_container, ao2_add);
	ao2_ref(ao2_add, -1);
	return 0;
}
Exemple #11
0
static struct corosync_node *corosync_node_alloc(struct ast_event *event)
{
	struct corosync_node *node;

	node = ao2_alloc_options(sizeof(*node), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (!node) {
		return NULL;
	}

	memcpy(&node->eid, (struct ast_eid *)ast_event_get_ie_raw(event, AST_EVENT_IE_EID), sizeof(node->eid));
	node->id = ast_event_get_ie_uint(event, AST_EVENT_IE_NODE_ID);
	ast_sockaddr_parse(&node->addr, ast_event_get_ie_str(event, AST_EVENT_IE_LOCAL_ADDR), PARSE_PORT_IGNORE);

	return node;
}
Exemple #12
0
int ast_sip_for_each_contact(const struct ast_sip_aor *aor,
		ao2_callback_fn on_contact, void *arg)
{
	struct ao2_container *contacts;
	struct ao2_iterator i;
	int res = 0;
	void *object = NULL;

	if (!on_contact ||
	    !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
		return 0;
	}

	i = ao2_iterator_init(contacts, 0);
	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);

		wrapper = ao2_alloc_options(sizeof(struct ast_sip_contact_wrapper),
			contact_wrapper_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
		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);

		if ((res = on_contact(wrapper, arg, 0))) {
			break;
		}
	}
	ao2_iterator_destroy(&i);
	ao2_ref(contacts, -1);
	return res;
}
Exemple #13
0
struct stasis_topic_pool *stasis_topic_pool_create(struct stasis_topic *pooled_topic)
{
	struct stasis_topic_pool *pool;

	pool = ao2_alloc_options(sizeof(*pool), topic_pool_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (!pool) {
		return NULL;
	}

	pool->pool_container = ao2_container_alloc(TOPIC_POOL_BUCKETS,
		topic_pool_entry_hash, topic_pool_entry_cmp);
	if (!pool->pool_container) {
		ao2_cleanup(pool);
		return NULL;
	}
	ao2_ref(pooled_topic, +1);
	pool->pool_topic = pooled_topic;

	return pool;
}
Exemple #14
0
struct stasis_caching_topic *stasis_caching_topic_create(struct stasis_topic *original_topic, struct stasis_cache *cache)
{
	RAII_VAR(struct stasis_caching_topic *, caching_topic, NULL, ao2_cleanup);
	struct stasis_subscription *sub;
	RAII_VAR(char *, new_name, NULL, ast_free);
	int ret;

	ret = ast_asprintf(&new_name, "%s-cached", stasis_topic_name(original_topic));
	if (ret < 0) {
		return NULL;
	}

	caching_topic = ao2_alloc_options(sizeof(*caching_topic),
		stasis_caching_topic_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (caching_topic == NULL) {
		return NULL;
	}

	caching_topic->topic = stasis_topic_create(new_name);
	if (caching_topic->topic == NULL) {
		return NULL;
	}

	ao2_ref(cache, +1);
	caching_topic->cache = cache;

	sub = internal_stasis_subscribe(original_topic, caching_topic_exec, caching_topic, 0);
	if (sub == NULL) {
		return NULL;
	}

	ao2_ref(original_topic, +1);
	caching_topic->original_topic = original_topic;

	/* This is for the reference contained in the subscription above */
	ao2_ref(caching_topic, +1);
	caching_topic->sub = sub;

	/* The subscription holds the reference, so no additional ref bump. */
	return caching_topic;
}
Exemple #15
0
struct stasis_cache *stasis_cache_create(snapshot_get_id id_fn)
{
        RAII_VAR(struct stasis_cache *, cache, NULL, ao2_cleanup);

        cache = ao2_alloc_options(sizeof(*cache), cache_dtor,
		AO2_ALLOC_OPT_LOCK_NOLOCK);
        if (!cache) {
                return NULL;
        }

        cache->entries = ao2_container_alloc(NUM_CACHE_BUCKETS, cache_entry_hash,
                cache_entry_cmp);
        if (!cache->entries) {
                return NULL;
        }

        cache->id_fn = id_fn;

        ao2_ref(cache, +1);
        return cache;
}
Exemple #16
0
/*!
 * \internal
 * \since 12.0.0
 * \brief Allocate a new holding bridge wrapper with the given bridge name and bridge ID.
 *
 * \param bridge_name name of the bridge wrapper
 * \param bridge the bridge being wrapped
 *
 * \retval Pointer to the newly allocated holding bridge wrapper
 * \retval NULL if allocation failed. The bridge will be destroyed if this function fails.
 */
static struct wait_bridge_wrapper *wait_bridge_wrapper_alloc(const char *bridge_name, struct ast_bridge *bridge)
{
    struct wait_bridge_wrapper *bridge_wrapper;

    bridge_wrapper = ao2_alloc_options(sizeof(*bridge_wrapper) + strlen(bridge_name) + 1,
                                       wait_bridge_wrapper_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK);
    if (!bridge_wrapper) {
        ast_bridge_destroy(bridge, 0);
        return NULL;
    }

    strcpy(bridge_wrapper->name, bridge_name);
    bridge_wrapper->bridge = bridge;

    if (!ao2_link(wait_bridge_wrappers, bridge_wrapper)) {
        ao2_cleanup(bridge_wrapper);
        return NULL;
    }

    return bridge_wrapper;
}
Exemple #17
0
struct stasis_forward *stasis_forward_all(struct stasis_topic *from_topic,
	struct stasis_topic *to_topic)
{
	int res;
	size_t idx;
	RAII_VAR(struct stasis_forward *, forward, NULL, ao2_cleanup);

	if (!from_topic || !to_topic) {
		return NULL;
	}

	forward = ao2_alloc_options(sizeof(*forward), forward_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (!forward) {
		return NULL;
	}

	/* Forwards to ourselves are implicit. */
	if (to_topic == from_topic) {
		return ao2_bump(forward);
	}

	forward->from_topic = ao2_bump(from_topic);
	forward->to_topic = ao2_bump(to_topic);

	topic_lock_both(to_topic, from_topic);
	res = AST_VECTOR_APPEND(&to_topic->upstream_topics, from_topic);
	if (res != 0) {
		ao2_unlock(from_topic);
		ao2_unlock(to_topic);
		return NULL;
	}

	for (idx = 0; idx < AST_VECTOR_SIZE(&to_topic->subscribers); ++idx) {
		topic_add_subscription(from_topic, AST_VECTOR_GET(&to_topic->subscribers, idx));
	}
	ao2_unlock(from_topic);
	ao2_unlock(to_topic);

	return ao2_bump(forward);
}
Exemple #18
0
static void bridge_stasis_queue_join_action(struct ast_bridge *self,
        struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
{
    struct defer_bridge_add_obj *defer;

    defer = ao2_alloc_options(sizeof(*defer), defer_bridge_add_dtor,
                              AO2_ALLOC_OPT_LOCK_NOLOCK);
    if (!defer) {
        return;
    }
    ao2_ref(self, +1);
    defer->bridge = self;
    if (swap) {
        ast_channel_ref(swap->chan);
        defer->swap = swap->chan;
    }

    ast_channel_lock(bridge_channel->chan);
    command_prestart_queue_command(bridge_channel->chan, defer_bridge_add,
                                   defer, __ao2_cleanup);
    ast_channel_unlock(bridge_channel->chan);
}
Exemple #19
0
/*! \brief Allocate an \ref ast_ari_conf_user for config parsing */
static void *user_alloc(const char *cat)
{
	RAII_VAR(struct ast_ari_conf_user *, user, NULL, ao2_cleanup);

	if (!cat) {
		return NULL;
	}

	ast_debug(3, "Allocating user %s\n", cat);

	user = ao2_alloc_options(sizeof(*user), user_dtor,
		AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (!user) {
		return NULL;
	}

	user->username = ast_strdup(cat);
	if (!user->username) {
		return NULL;
	}

	ao2_ref(user, +1);
	return user;
}
Exemple #20
0
static struct stasis_message *update_create(struct stasis_message *old_snapshot, struct stasis_message *new_snapshot)
{
	RAII_VAR(struct stasis_cache_update *, update, NULL, ao2_cleanup);
	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);

	ast_assert(old_snapshot != NULL || new_snapshot != NULL);

	update = ao2_alloc_options(sizeof(*update), stasis_cache_update_dtor,
		AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (!update) {
		return NULL;
	}

	if (old_snapshot) {
		ao2_ref(old_snapshot, +1);
		update->old_snapshot = old_snapshot;
		if (!new_snapshot) {
			ao2_ref(stasis_message_type(old_snapshot), +1);
			update->type = stasis_message_type(old_snapshot);
		}
	}
	if (new_snapshot) {
		ao2_ref(new_snapshot, +1);
		update->new_snapshot = new_snapshot;
		ao2_ref(stasis_message_type(new_snapshot), +1);
		update->type = stasis_message_type(new_snapshot);
	}

	msg = stasis_message_create(stasis_cache_update_type(), update);
	if (!msg) {
		return NULL;
	}

	ao2_ref(msg, +1);
	return msg;
}
Exemple #21
0
static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata)
{
	struct ast_sip_endpoint *endpoint;
	struct unidentified_request *unid;
	int is_ack = rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD;

	endpoint = rdata->endpt_info.mod_data[endpoint_mod.id];
	if (endpoint) {
		/*
		 * ao2_find with OBJ_UNLINK always write locks the container before even searching
		 * for the object.  Since the majority case is that the object won't be found, do
		 * the find without OBJ_UNLINK to prevent the unnecessary write lock, then unlink
		 * if needed.
		 */
		unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY);
		if (unid) {
			ao2_unlink(unidentified_requests, unid);
			ao2_ref(unid, -1);
		}
		apply_acls(rdata);
		return PJ_FALSE;
	}

	endpoint = ast_sip_identify_endpoint(rdata);
	if (endpoint) {
		unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY);
		if (unid) {
			ao2_unlink(unidentified_requests, unid);
			ao2_ref(unid, -1);
		}
	}

	if (!endpoint) {
		/* always use an artificial endpoint - per discussion no reason
		   to have "alwaysauthreject" as an option.  It is felt using it
		   was a bug fix and it is not needed since we are not worried about
		   breaking old stuff and we really don't want to enable the discovery
		   of SIP accounts */
		endpoint = ast_sip_get_artificial_endpoint();
	}

	/* endpoint ref held by mod_data[] */
	rdata->endpt_info.mod_data[endpoint_mod.id] = endpoint;

	if (endpoint == artificial_endpoint && !is_ack) {
		char name[AST_UUID_STR_LEN] = "";
		pjsip_uri *from = rdata->msg_info.from->uri;

		if (PJSIP_URI_SCHEME_IS_SIP(from) || PJSIP_URI_SCHEME_IS_SIPS(from)) {
			pjsip_sip_uri *sip_from = pjsip_uri_get_uri(from);
			ast_copy_pj_str(name, &sip_from->user, sizeof(name));
		}

		unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY);
		if (unid) {
			check_endpoint(rdata, unid, name);
			ao2_ref(unid, -1);
		} else if (using_auth_username) {
			ao2_wrlock(unidentified_requests);
			/* Checking again with the write lock held allows us to eliminate the DUPS_REPLACE and sort_fn */
			unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name,
				OBJ_SEARCH_KEY | OBJ_NOLOCK);
			if (unid) {
				check_endpoint(rdata, unid, name);
			} else {
				unid = ao2_alloc_options(sizeof(*unid) + strlen(rdata->pkt_info.src_name) + 1,
					NULL, AO2_ALLOC_OPT_LOCK_RWLOCK);
				if (!unid) {
					ao2_unlock(unidentified_requests);
					pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
					return PJ_TRUE;
				}
				strcpy(unid->src_name, rdata->pkt_info.src_name); /* Safe */
				unid->first_seen = ast_tvnow();
				unid->count = 1;
				ao2_link_flags(unidentified_requests, unid, OBJ_NOLOCK);
			}
			ao2_ref(unid, -1);
			ao2_unlock(unidentified_requests);
		} else {
			log_failed_request(rdata, "No matching endpoint found", 0, 0);
			ast_sip_report_invalid_endpoint(name, rdata);
		}
	}

	apply_acls(rdata);
	return PJ_FALSE;
}
Exemple #22
0
struct ast_channel *stasis_app_control_snoop(struct ast_channel *chan,
	enum stasis_app_snoop_direction spy, enum stasis_app_snoop_direction whisper,
	const char *app, const char *app_args, const char *snoop_id)
{
	RAII_VAR(struct stasis_app_snoop *, snoop, NULL, ao2_cleanup);
	struct ast_format_cap *caps;
	pthread_t thread;
	struct ast_assigned_ids assignedids = {
		.uniqueid = snoop_id,
	};

	if (spy == STASIS_SNOOP_DIRECTION_NONE &&
		whisper == STASIS_SNOOP_DIRECTION_NONE) {
		return NULL;
	}

	snoop = ao2_alloc_options(sizeof(*snoop), snoop_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (!snoop) {
		return NULL;
	}

	/* Allocate a buffer to store the Stasis application and arguments in */
	snoop->app = ast_str_create(64);
	if (!snoop->app) {
		return NULL;
	}

	ast_str_set(&snoop->app, 0, "%s", app);
	if (!ast_strlen_zero(app_args)) {
		ast_str_append(&snoop->app, 0, ",%s", app_args);
	}

	/* Set up a timer for the Snoop channel so it wakes up at a specific interval */
	snoop->timer = ast_timer_open();
	if (!snoop->timer) {
		return NULL;
	}
	ast_timer_set_rate(snoop->timer, 1000 / SNOOP_INTERVAL);

	/* Determine which signed linear format should be used */
	snoop_determine_format(chan, snoop);

	/* Allocate a Snoop channel and set up various parameters */
	snoop->chan = ast_channel_alloc(1, AST_STATE_UP, "", "", "", "", "", &assignedids, NULL, 0, "Snoop/%s-%08x", ast_channel_uniqueid(chan),
		(unsigned)ast_atomic_fetchadd_int((int *)&chan_idx, +1));
	if (!snoop->chan) {
		return NULL;
	}

	ast_copy_string(snoop->uniqueid, ast_channel_uniqueid(chan), sizeof(snoop->uniqueid));

	/* To keep the channel valid on the Snoop structure until it is destroyed we bump the ref up here */
	ast_channel_ref(snoop->chan);

	ast_channel_tech_set(snoop->chan, &snoop_tech);
	ao2_ref(snoop, +1);
	ast_channel_tech_pvt_set(snoop->chan, snoop);
	ast_channel_set_fd(snoop->chan, 0, ast_timer_fd(snoop->timer));

	/* The format on the Snoop channel will be this signed linear format, and it will never change */
	caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
	if (!caps) {
		ast_channel_unlock(snoop->chan);
		ast_hangup(snoop->chan);
		return NULL;
	}
	ast_format_cap_append(caps, snoop->spy_format, 0);
	ast_channel_nativeformats_set(snoop->chan, caps);
	ao2_ref(caps, -1);

	ast_channel_set_writeformat(snoop->chan, snoop->spy_format);
	ast_channel_set_rawwriteformat(snoop->chan, snoop->spy_format);
	ast_channel_set_readformat(snoop->chan, snoop->spy_format);
	ast_channel_set_rawreadformat(snoop->chan, snoop->spy_format);

	ast_channel_unlock(snoop->chan);

	if (spy != STASIS_SNOOP_DIRECTION_NONE) {
		if (snoop_setup_audiohook(chan, AST_AUDIOHOOK_TYPE_SPY, spy, &snoop->spy_direction, &snoop->spy)) {
			ast_hangup(snoop->chan);
			return NULL;
		}

		snoop->spy_samples = ast_format_get_sample_rate(snoop->spy_format) / (1000 / SNOOP_INTERVAL);
		snoop->spy_active = 1;
	}

	/* If whispering is enabled set up the audiohook */
	if (whisper != STASIS_SNOOP_DIRECTION_NONE) {
		if (snoop_setup_audiohook(chan, AST_AUDIOHOOK_TYPE_WHISPER, whisper, &snoop->whisper_direction, &snoop->whisper)) {
			ast_hangup(snoop->chan);
			return NULL;
		}

		snoop->whisper_active = 1;
	}

	/* Create the thread which services the Snoop channel */
	ao2_ref(snoop, +1);
	if (ast_pthread_create_detached_background(&thread, NULL, snoop_stasis_thread, snoop)) {
		ao2_cleanup(snoop);

		/* No other thread is servicing this channel so we can immediately hang it up */
		ast_hangup(snoop->chan);
		return NULL;
	}

	publish_chanspy_message(snoop, 1);

	/* The caller of this has a reference as well */
	return ast_channel_ref(snoop->chan);
}
Exemple #23
0
struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data)
{
	RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup);
	size_t size;
	int res = 0;

	ast_assert(name != NULL);
	ast_assert(handler != NULL);

	ast_verb(1, "Creating Stasis app '%s'\n", name);

	size = sizeof(*app) + strlen(name) + 1;
	app = ao2_alloc_options(size, app_dtor, AO2_ALLOC_OPT_LOCK_MUTEX);

	if (!app) {
		return NULL;
	}

	app->forwards = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX,
		AO2_CONTAINER_ALLOC_OPT_DUPS_OBJ_REJECT,
		forwards_sort, NULL);
	if (!app->forwards) {
		return NULL;
	}

	app->topic = stasis_topic_create(name);
	if (!app->topic) {
		return NULL;
	}

	app->bridge_router = stasis_message_router_create(ast_bridge_topic_all());
	if (!app->bridge_router) {
		return NULL;
	}

	res |= stasis_message_router_add(app->bridge_router,
		ast_bridge_merge_message_type(), bridge_merge_handler, app);

	res |= stasis_message_router_add(app->bridge_router,
		ast_blind_transfer_type(), bridge_blind_transfer_handler, app);

	res |= stasis_message_router_add(app->bridge_router,
		ast_attended_transfer_type(), bridge_attended_transfer_handler, app);

	res |= stasis_message_router_set_default(app->bridge_router,
		bridge_default_handler, app);

	if (res != 0) {
		return NULL;
	}
	/* Bridge router holds a reference */
	ao2_ref(app, +1);

	app->router = stasis_message_router_create(app->topic);
	if (!app->router) {
		return NULL;
	}

	res |= stasis_message_router_add_cache_update(app->router,
		ast_bridge_snapshot_type(), sub_bridge_update_handler, app);

	res |= stasis_message_router_add_cache_update(app->router,
		ast_channel_snapshot_type(), sub_channel_update_handler, app);

	res |= stasis_message_router_add_cache_update(app->router,
		ast_endpoint_snapshot_type(), sub_endpoint_update_handler, app);

	res |= stasis_message_router_set_default(app->router,
		sub_default_handler, app);

	if (res != 0) {
		return NULL;
	}
	/* Router holds a reference */
	ao2_ref(app, +1);

	strncpy(app->name, name, size - sizeof(*app));
	app->handler = handler;
	ao2_ref(data, +1);
	app->data = data;

	ao2_ref(app, +1);
	return app;
}
Exemple #24
0
struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data, enum stasis_app_subscription_model subscription_model)
{
	RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup);
	size_t size;
	int res = 0;
	size_t context_size = strlen("stasis-") + strlen(name) + 1;
	char context_name[context_size];

	ast_assert(name != NULL);
	ast_assert(handler != NULL);

	ast_verb(1, "Creating Stasis app '%s'\n", name);

	size = sizeof(*app) + strlen(name) + 1;
	app = ao2_alloc_options(size, app_dtor, AO2_ALLOC_OPT_LOCK_MUTEX);
	if (!app) {
		return NULL;
	}
	app->subscription_model = subscription_model;

	app->forwards = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX,
		AO2_CONTAINER_ALLOC_OPT_DUPS_OBJ_REJECT,
		forwards_sort, NULL);
	if (!app->forwards) {
		return NULL;
	}

	app->topic = stasis_topic_create(name);
	if (!app->topic) {
		return NULL;
	}

	app->bridge_router = stasis_message_router_create(ast_bridge_topic_all());
	if (!app->bridge_router) {
		return NULL;
	}

	res |= stasis_message_router_add(app->bridge_router,
		ast_bridge_merge_message_type(), bridge_merge_handler, app);

	res |= stasis_message_router_add(app->bridge_router,
		ast_blind_transfer_type(), bridge_blind_transfer_handler, app);

	res |= stasis_message_router_add(app->bridge_router,
		ast_attended_transfer_type(), bridge_attended_transfer_handler, app);

	res |= stasis_message_router_add(app->bridge_router,
		stasis_subscription_change_type(), bridge_subscription_change_handler, app);

	if (res != 0) {
		return NULL;
	}
	/* Bridge router holds a reference */
	ao2_ref(app, +1);

	app->router = stasis_message_router_create(app->topic);
	if (!app->router) {
		return NULL;
	}

	res |= stasis_message_router_add(app->router,
		ast_bridge_snapshot_type(), sub_bridge_update_handler, app);

	res |= stasis_message_router_add(app->router,
		ast_channel_snapshot_type(), sub_channel_update_handler, app);

	res |= stasis_message_router_add_cache_update(app->router,
		ast_endpoint_snapshot_type(), sub_endpoint_update_handler, app);

	res |= stasis_message_router_add(app->router,
		stasis_subscription_change_type(), sub_subscription_change_handler, app);

	stasis_message_router_set_formatters_default(app->router,
		sub_default_handler, app, STASIS_SUBSCRIPTION_FORMATTER_JSON);

	if (res != 0) {
		return NULL;
	}
	/* Router holds a reference */
	ao2_ref(app, +1);

	strncpy(app->name, name, size - sizeof(*app));
	app->handler = handler;
	app->data = ao2_bump(data);

	/* Create a context, a match-all extension, and a 'h' extension for this application. Note that
	 * this should only be done if a context does not already exist. */
	strcpy(context_name, "stasis-");
	strcat(context_name, name);
	if (!ast_context_find(context_name)) {
		if (!ast_context_find_or_create(NULL, NULL, context_name, "res_stasis")) {
			ast_log(LOG_WARNING, "Could not create context '%s' for Stasis application '%s'\n", context_name, name);
		} else {
			ast_add_extension(context_name, 0, "_.", 1, NULL, NULL, "Stasis", ast_strdup(name), ast_free_ptr, "res_stasis");
			ast_add_extension(context_name, 0, "h", 1, NULL, NULL, "NoOp", NULL, NULL, "res_stasis");
		}
	} else {
		ast_log(LOG_WARNING, "Not creating context '%s' for Stasis application '%s' because it already exists\n",
			context_name, name);
	}

	ao2_ref(app, +1);
	return app;
}
Exemple #25
0
int ast_sip_initialize_distributor(void)
{
	unidentified_requests = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_RWLOCK, 0,
		DEFAULT_SUSPECTS_BUCKETS, suspects_hash, NULL, suspects_compare);
	if (!unidentified_requests) {
		return -1;
	}

	dialog_associations = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_RWLOCK, 0,
		DIALOG_ASSOCIATIONS_BUCKETS, dialog_associations_hash, NULL,
		dialog_associations_cmp);
	if (!dialog_associations) {
		ast_sip_destroy_distributor();
		return -1;
	}

	if (distributor_pool_setup()) {
		ast_sip_destroy_distributor();
		return -1;
	}

	prune_context = ast_sched_context_create();
	if (!prune_context) {
		ast_sip_destroy_distributor();
		return -1;
	}

	if (ast_sched_start_thread(prune_context)) {
		ast_sip_destroy_distributor();
		return -1;
	}

	ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &global_observer);
	ast_sorcery_reload_object(ast_sip_get_sorcery(), "global");

	if (create_artificial_endpoint() || create_artificial_auth()) {
		ast_sip_destroy_distributor();
		return -1;
	}

	if (ast_sip_register_service(&distributor_mod)) {
		ast_sip_destroy_distributor();
		return -1;
	}
	if (ast_sip_register_service(&endpoint_mod)) {
		ast_sip_destroy_distributor();
		return -1;
	}
	if (ast_sip_register_service(&auth_mod)) {
		ast_sip_destroy_distributor();
		return -1;
	}

	unid_formatter = ao2_alloc_options(sizeof(struct ast_sip_cli_formatter_entry), NULL,
		AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (!unid_formatter) {
		ast_sip_destroy_distributor();
		ast_log(LOG_ERROR, "Unable to allocate memory for unid_formatter\n");
		return -1;
	}
	unid_formatter->name = "unidentified_request";
	unid_formatter->print_header = cli_unid_print_header;
	unid_formatter->print_body = cli_unid_print_body;
	unid_formatter->get_container = cli_unid_get_container;
	unid_formatter->iterate = cli_unid_iterate;
	unid_formatter->get_id = cli_unid_get_id;
	unid_formatter->retrieve_by_id = cli_unid_retrieve_by_id;
	ast_sip_register_cli_formatter(unid_formatter);

	ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));

	return 0;
}
Exemple #26
0
static struct topic_pool_entry *topic_pool_entry_alloc(void)
{
	return ao2_alloc_options(sizeof(struct topic_pool_entry), topic_pool_entry_dtor,
		AO2_ALLOC_OPT_LOCK_NOLOCK);
}
static void register_aor_core(pjsip_rx_data *rdata,
	struct ast_sip_endpoint *endpoint,
	struct ast_sip_aor *aor,
	const char *aor_name,
	struct ao2_container *contacts,
	struct aor_core_response *response)
{
	static const pj_str_t USER_AGENT = { "User-Agent", 10 };

	int added = 0;
	int updated = 0;
	int deleted = 0;
	int permanent = 0;
	int contact_count;
	struct ao2_container *existing_contacts = NULL;
	pjsip_contact_hdr *contact_hdr = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr;
	struct registrar_contact_details details = { 0, };
	pjsip_tx_data *tdata;
	RAII_VAR(struct ast_str *, path_str, NULL, ast_free);
	struct ast_sip_contact *response_contact;
	char *user_agent = NULL;
	pjsip_user_agent_hdr *user_agent_hdr;
	pjsip_expires_hdr *expires_hdr;
	pjsip_via_hdr *via_hdr;
	pjsip_via_hdr *via_hdr_last;
	char *via_addr = NULL;
	int via_port = 0;
	pjsip_cid_hdr *call_id_hdr;
	char *call_id = NULL;
	size_t alloc_size;

	/* We create a single pool and use it throughout this function where we need one */
	details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
		"Contact Comparison", 1024, 256);
	if (!details.pool) {
		response->code = 500;
		return;
	}

	/* If there are any permanent contacts configured on the AOR we need to take them
	 * into account when counting contacts.
	 */
	if (aor->permanent_contacts) {
		permanent = ao2_container_count(aor->permanent_contacts);
	}

	if (registrar_validate_contacts(rdata, details.pool, contacts, aor, permanent, &added, &updated, &deleted)) {
		/* The provided Contact headers do not conform to the specification */
		ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_contacts_provided");
		ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n",
				ast_sorcery_object_get_id(endpoint));
		response->code = 400;
		pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
		return;
	}

	if (registrar_validate_path(rdata, aor, &path_str)) {
		/* Ensure that intervening proxies did not make invalid modifications to the request */
		ast_log(LOG_WARNING, "Invalid modifications made to REGISTER request from '%s' by intervening proxy\n",
				ast_sorcery_object_get_id(endpoint));
		response->code = 420;
		pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
		return;
	}

	if (aor->remove_existing) {
		/* Cumulative number of contacts affected by this registration */
		contact_count = MAX(updated + added - deleted,  0);

		/* We need to keep track of only existing contacts so we can later
		 * remove them if need be.
		 */
		existing_contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
			NULL, ast_sorcery_object_id_compare);
		if (!existing_contacts) {
			response->code = 500;
			pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
			return;
		}

		ao2_callback(contacts, OBJ_NODATA, registrar_add_non_permanent, existing_contacts);
	} else {
		/* Total contacts after this registration */
		contact_count = ao2_container_count(contacts) - permanent + added - deleted;
	}
	if (contact_count > aor->max_contacts) {
		/* Enforce the maximum number of contacts */
		ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_exceeds_maximum_configured_contacts");
		ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %u\n",
				ast_sorcery_object_get_id(endpoint), aor_name, aor->max_contacts);
		response->code = 403;
		pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
		ao2_cleanup(existing_contacts);
		return;
	}

	user_agent_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &USER_AGENT, NULL);
	if (user_agent_hdr) {
		alloc_size = pj_strlen(&user_agent_hdr->hvalue) + 1;
		user_agent = ast_alloca(alloc_size);
		ast_copy_pj_str(user_agent, &user_agent_hdr->hvalue, alloc_size);
	}

	/* Find the first Via header */
	via_hdr = via_hdr_last = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_VIA, NULL);
	if (via_hdr) {
		/* Find the last Via header */
		while ( (via_hdr = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg,
				PJSIP_H_VIA, via_hdr->next)) != NULL) {
			via_hdr_last = via_hdr;
		}
		alloc_size = pj_strlen(&via_hdr_last->sent_by.host) + 1;
		via_addr = ast_alloca(alloc_size);
		ast_copy_pj_str(via_addr, &via_hdr_last->sent_by.host, alloc_size);
		via_port=via_hdr_last->sent_by.port;
	}

	call_id_hdr = (pjsip_cid_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CALL_ID, NULL);
	if (call_id_hdr) {
		alloc_size = pj_strlen(&call_id_hdr->id) + 1;
		call_id = ast_alloca(alloc_size);
		ast_copy_pj_str(call_id, &call_id_hdr->id, alloc_size);
	}

	/* Iterate each provided Contact header and add, update, or delete */
	for (; (contact_hdr = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr->next)); pj_pool_reset(details.pool)) {
		int expiration;
		char contact_uri[pjsip_max_url_size];
		RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);

		if (contact_hdr->star) {
			/* A star means to unregister everything, so do so for the possible contacts */
			ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE,
				registrar_delete_contact, (void *)aor_name);
			/* If we are keeping track of existing contacts for removal then, well, there is
			 * absolutely nothing left so no need to try to remove any.
			 */
			if (existing_contacts) {
				ao2_ref(existing_contacts, -1);
				existing_contacts = NULL;
			}
			break;
		}

		if (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri)) {
			/* This registrar only currently supports sip: and sips: URI schemes */
			continue;
		}

		expiration = registrar_get_expiration(aor, contact_hdr, rdata);
		details.uri = pjsip_uri_get_uri(contact_hdr->uri);
		pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri));

		contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details);

		/* If a contact was returned and we need to keep track of existing contacts then it
		 * should be removed.
		 */
		if (contact && existing_contacts) {
			ao2_unlink(existing_contacts, contact);
		}

		if (!contact) {
			int prune_on_boot;

			/* If they are actually trying to delete a contact that does not exist... be forgiving */
			if (!expiration) {
				ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n",
					contact_uri, aor_name);
				continue;
			}

			prune_on_boot = !ast_sip_will_uri_survive_restart(details.uri, endpoint, rdata);

			contact = ast_sip_location_create_contact(aor, contact_uri,
				ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)),
				path_str ? ast_str_buffer(path_str) : NULL,
				user_agent, via_addr, via_port, call_id, prune_on_boot, endpoint);
			if (!contact) {
				ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n",
					contact_uri, aor_name);
				continue;
			}

			if (prune_on_boot) {
				const char *contact_name;
				struct contact_transport_monitor *monitor;

				/*
				 * Monitor the transport in case it gets disconnected because
				 * the contact won't be valid anymore if that happens.
				 */
				contact_name = ast_sorcery_object_get_id(contact);
				monitor = ao2_alloc_options(sizeof(*monitor) + 2 + strlen(aor_name)
					+ strlen(contact_name), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
				if (monitor) {
					strcpy(monitor->aor_name, aor_name);/* Safe */
					monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1;
					strcpy(monitor->contact_name, contact_name);/* Safe */

					ast_sip_transport_monitor_register(rdata->tp_info.transport,
						register_contact_transport_shutdown_cb, monitor);
					ao2_ref(monitor, -1);
				}
			}

			ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n",
				contact_uri, aor_name, expiration);
			ast_test_suite_event_notify("AOR_CONTACT_ADDED",
					"Contact: %s\r\n"
					"AOR: %s\r\n"
					"Expiration: %d\r\n"
					"UserAgent: %s",
					contact_uri,
					aor_name,
					expiration,
					user_agent);

			ao2_link(contacts, contact);
		} else if (expiration) {
			struct ast_sip_contact *contact_update;

			contact_update = ast_sorcery_copy(ast_sip_get_sorcery(), contact);
			if (!contact_update) {
				ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n",
					contact->uri, expiration);
				continue;
			}

			contact_update->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1));
			contact_update->qualify_frequency = aor->qualify_frequency;
			contact_update->authenticate_qualify = aor->authenticate_qualify;
			if (path_str) {
				ast_string_field_set(contact_update, path, ast_str_buffer(path_str));
			}
			if (user_agent) {
				ast_string_field_set(contact_update, user_agent, user_agent);
			}
			if (!ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
				ast_string_field_set(contact_update, reg_server, ast_config_AST_SYSTEM_NAME);
			}

			if (ast_sip_location_update_contact(contact_update)) {
				ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n",
					contact->uri, expiration);
				ast_sip_location_delete_contact(contact);
				continue;
			}
			ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n",
				contact_uri, aor_name, expiration);
			ast_test_suite_event_notify("AOR_CONTACT_REFRESHED",
					"Contact: %s\r\n"
					"AOR: %s\r\n"
					"Expiration: %d\r\n"
					"UserAgent: %s",
					contact_uri,
					aor_name,
					expiration,
					contact_update->user_agent);
			ao2_link(contacts, contact_update);
			ao2_cleanup(contact_update);
		} else {
			if (contact->prune_on_boot) {
				struct contact_transport_monitor *monitor;
				const char *contact_name =
					ast_sorcery_object_get_id(contact);

				monitor = ast_alloca(sizeof(*monitor) + 2 + strlen(aor_name)
					+ strlen(contact_name));
				strcpy(monitor->aor_name, aor_name);/* Safe */
				monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1;
				strcpy(monitor->contact_name, contact_name);/* Safe */

				ast_sip_transport_monitor_unregister(rdata->tp_info.transport,
					register_contact_transport_shutdown_cb, monitor, contact_transport_monitor_matcher);
			}

			/* We want to report the user agent that was actually in the removed contact */
			ast_sip_location_delete_contact(contact);
			ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name);
			ast_test_suite_event_notify("AOR_CONTACT_REMOVED",
					"Contact: %s\r\n"
					"AOR: %s\r\n"
					"UserAgent: %s",
					contact_uri,
					aor_name,
					contact->user_agent);
		}
	}

	pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);

	/*
	 * If the AOR is configured to remove any contacts over max_contacts
	 * that have not been updated/added/deleted as a result of this
	 * REGISTER do so.
	 *
	 * The existing contacts container holds all contacts that were not
	 * involved in this REGISTER.
	 * The contacts container holds the current contacts of the AOR.
	 */
	if (aor->remove_existing && existing_contacts) {
		/* Total contacts after this registration */
		contact_count = ao2_container_count(existing_contacts) + updated + added;
		if (contact_count > aor->max_contacts) {
			/* Remove excess existing contacts that expire the soonest */
			remove_excess_contacts(existing_contacts, contacts, contact_count - aor->max_contacts);
		}
		ao2_ref(existing_contacts, -1);
	}

	response_contact = ao2_callback(contacts, 0, NULL, NULL);

	/* Send a response containing all of the contacts (including static) that are present on this AOR */
	if (ast_sip_create_response(rdata, 200, response_contact, &tdata) != PJ_SUCCESS) {
		ao2_cleanup(response_contact);
		ao2_cleanup(contacts);
		response->code = 500;
		return;
	}
	ao2_cleanup(response_contact);

	/* Add the date header to the response, some UAs use this to set their date and time */
	registrar_add_date_header(tdata);

	ao2_callback(contacts, 0, registrar_add_contact, tdata);

	if ((expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) {
		expires_hdr = pjsip_expires_hdr_create(tdata->pool, registrar_get_expiration(aor, NULL, rdata));
		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr);
	}

	response->tdata = tdata;
}