Пример #1
0
/*!
 * \brief Verifies a user event header/value pair
 *
 * \param user_event which user event to check
 * \param header The header to verify
 * \param value The value read from the event
 *
 * \retval -1 on error or evaluation failure
 * \retval 0 if match not needed or success
 */
static int verify_user_event_fields(int user_event, const char *header, const char *value)
{
	struct ast_variable *current;
	struct ast_variable *expected;
	regex_t regexbuf;
	int error;

	if (user_event >= AST_VECTOR_SIZE(&expected_user_event_fields)) {
		return -1;
	}

	expected = AST_VECTOR_GET(&expected_user_event_fields, user_event);
	if (!expected) {
		return -1;
	}

	for (current = expected; current; current = current->next) {
		struct ast_variable *bad_header;

		if (strcmp(current->name, header)) {
			continue;
		}

		error = regcomp(&regexbuf, current->value, REG_EXTENDED | REG_NOSUB);
		if (error) {
			char error_buf[128];
			regerror(error, &regexbuf, error_buf, sizeof(error_buf));
			ast_log(LOG_ERROR, "Failed to compile regex '%s' for header check '%s': %s\n",
				current->value, current->name, error_buf);
			return -1;
		}

		if (!regexec(&regexbuf, value, 0, NULL, 0)) {
			regfree(&regexbuf);
			return 0;
		}

		bad_header = ast_variable_new(header, value, __FILE__);
		if (bad_header) {
			struct ast_variable *bad_headers_head = NULL;

			if (user_event < AST_VECTOR_SIZE(&bad_headers)) {
				bad_headers_head = AST_VECTOR_GET(&bad_headers, user_event);
			}
			ast_variable_list_append(&bad_headers_head, bad_header);
			AST_VECTOR_INSERT(&bad_headers, user_event, bad_headers_head);
		}
		regfree(&regexbuf);
		return -1;
	}

	return 0;
}
Пример #2
0
struct stasis_forward *stasis_forward_cancel(struct stasis_forward *forward)
{
	int idx;
	struct stasis_topic *from;
	struct stasis_topic *to;

	if (!forward) {
		return NULL;
	}

	from = forward->from_topic;
	to = forward->to_topic;

	if (from && to) {
		topic_lock_both(to, from);
		AST_VECTOR_REMOVE_ELEM_UNORDERED(&to->upstream_topics, from,
			AST_VECTOR_ELEM_CLEANUP_NOOP);

		for (idx = 0; idx < AST_VECTOR_SIZE(&to->subscribers); ++idx) {
			topic_remove_subscription(from, AST_VECTOR_GET(&to->subscribers, idx));
		}
		ao2_unlock(from);
		ao2_unlock(to);
	}

	ao2_cleanup(forward);

	return NULL;
}
Пример #3
0
int __ast_string_fields_cmp(struct ast_string_field_vector *left,
	struct ast_string_field_vector *right)
{
	int i;
	int res = 0;

	ast_assert(AST_VECTOR_SIZE(left) == AST_VECTOR_SIZE(right));

	for (i = 0; i < AST_VECTOR_SIZE(left); i++) {
		if ((res = strcmp(*AST_VECTOR_GET(left, i), *AST_VECTOR_GET(right, i)))) {
			return res;
		}
	}

	return res;
}
Пример #4
0
/*!
 * \brief Explicitly shutdown a session.
 *
 * \details An explicit shutdown is necessary, since the \ref stasis_app has a reference
 *          to this session. We also need to be sure to null out the \c ws_session field,
 *          since the websocket is about to go away.
 *
 * \internal
 *
 * \param session  Event session object (\ref event_session).
 */
static void event_session_shutdown(struct event_session *session)
{
	struct ao2_iterator i;
	char *app;
	int j;
	SCOPED_AO2LOCK(lock, session);

	/* Clean up the websocket_apps container */
	if (session->websocket_apps) {
		i = ao2_iterator_init(session->websocket_apps, 0);
		while ((app = ao2_iterator_next(&i))) {
			stasis_app_unregister(app);
			ao2_cleanup(app);
		}
		ao2_iterator_destroy(&i);
		ao2_cleanup(session->websocket_apps);
		session->websocket_apps = NULL;
	}

	/* Clean up the message_queue container */
	for (j = 0; j < AST_VECTOR_SIZE(&session->message_queue); j++) {
		struct ast_json *msg = AST_VECTOR_GET(&session->message_queue, j);
		ast_json_unref(msg);
	}
	AST_VECTOR_FREE(&session->message_queue);

	/* Remove the handle to the underlying websocket session */
	session->ws_session = NULL;
}
Пример #5
0
static int topic_remove_subscription(struct stasis_topic *topic, struct stasis_subscription *sub)
{
	size_t idx;
	SCOPED_AO2LOCK(lock_topic, topic);

	for (idx = 0; idx < AST_VECTOR_SIZE(&topic->upstream_topics); ++idx) {
		topic_remove_subscription(
			AST_VECTOR_GET(&topic->upstream_topics, idx), sub);
	}

	return AST_VECTOR_REMOVE_ELEM_UNORDERED(&topic->subscribers, sub,
		AST_VECTOR_ELEM_CLEANUP_NOOP);
}
Пример #6
0
/*!
 * \internal
 * \brief Destructor for \ref ast_multi_object_blob objects
 */
static void multi_object_blob_dtor(void *obj)
{
	struct ast_multi_object_blob *multi = obj;
	int type;
	int i;

	for (type = 0; type < STASIS_UMOS_MAX; ++type) {
		for (i = 0; i < AST_VECTOR_SIZE(&multi->snapshots[type]); ++i) {
			ao2_cleanup(AST_VECTOR_GET(&multi->snapshots[type], i));
		}
		AST_VECTOR_FREE(&multi->snapshots[type]);
	}
	ast_json_unref(multi->blob);
}
Пример #7
0
int __ast_string_fields_copy(struct ast_string_field_pool *copy_pool,
	struct ast_string_field_mgr *copy_mgr, struct ast_string_field_mgr *orig_mgr)
{
	int i;
	struct ast_string_field_vector *dest = &(copy_mgr->string_fields);
	struct ast_string_field_vector *src = &(orig_mgr->string_fields);

	ast_assert(AST_VECTOR_SIZE(dest) == AST_VECTOR_SIZE(src));

	for (i = 0; i < AST_VECTOR_SIZE(dest); i++) {
		__ast_string_field_release_active(copy_pool, *AST_VECTOR_GET(dest, i));
		*AST_VECTOR_GET(dest, i) = __ast_string_field_empty;
	}

	for (i = 0; i < AST_VECTOR_SIZE(dest); i++) {
		if (ast_string_field_ptr_set_by_fields(copy_pool, *copy_mgr, AST_VECTOR_GET(dest, i),
			*AST_VECTOR_GET(src, i))) {
			return -1;
		}
	}

	return 0;
}
Пример #8
0
/*! \internal \brief convert multi object blob to ami string */
static struct ast_str *multi_object_blob_to_ami(void *obj)
{
	struct ast_str *ami_str=ast_str_create(1024);
	struct ast_str *ami_snapshot;
	const struct ast_multi_object_blob *multi = obj;
	enum stasis_user_multi_object_snapshot_type type;
	int i;

	if (!ami_str) {
		return NULL;
	}
	if (!multi) {
		ast_free(ami_str);
		return NULL;
	}

	for (type = 0; type < STASIS_UMOS_MAX; ++type) {
		for (i = 0; i < AST_VECTOR_SIZE(&multi->snapshots[type]); ++i) {
			char *name = "";
			void *snapshot = AST_VECTOR_GET(&multi->snapshots[type], i);
			ami_snapshot = NULL;

			if (i > 0) {
				ast_asprintf(&name, "%d", i + 1);
			}

			switch (type) {
			case STASIS_UMOS_CHANNEL:
				ami_snapshot = ast_manager_build_channel_state_string_prefix(snapshot, name);
				break;

			case STASIS_UMOS_BRIDGE:
				ami_snapshot = ast_manager_build_bridge_state_string_prefix(snapshot, name);
				break;

			case STASIS_UMOS_ENDPOINT:
				/* currently not sending endpoint snapshots to AMI */
				break;
			}
			if (ami_snapshot) {
				ast_str_append(&ami_str, 0, "%s", ast_str_buffer(ami_snapshot));
				ast_free(ami_snapshot);
			}
		}
	}

	return ami_str;
}
Пример #9
0
/*! \internal \brief convert multi object blob to ari json */
static struct ast_json *multi_user_event_to_json(
	struct stasis_message *message,
	const struct stasis_message_sanitizer *sanitize)
{
	RAII_VAR(struct ast_json *, out, NULL, ast_json_unref);
	struct ast_multi_object_blob *multi = stasis_message_data(message);
	struct ast_json *blob = multi->blob;
	const struct timeval *tv = stasis_message_timestamp(message);
	enum stasis_user_multi_object_snapshot_type type;
	int i;

	out = ast_json_object_create();
	if (!out) {
		return NULL;
	}

	ast_json_object_set(out, "type", ast_json_string_create("ChannelUserevent"));
	ast_json_object_set(out, "timestamp", ast_json_timeval(*tv, NULL));
	ast_json_object_set(out, "eventname", ast_json_string_create(ast_json_string_get((ast_json_object_get(blob, "eventname")))));
	ast_json_object_set(out, "userevent", ast_json_deep_copy(blob));

	for (type = 0; type < STASIS_UMOS_MAX; ++type) {
		for (i = 0; i < AST_VECTOR_SIZE(&multi->snapshots[type]); ++i) {
			struct ast_json *json_object = NULL;
			char *name = NULL;
			void *snapshot = AST_VECTOR_GET(&multi->snapshots[type], i);

			switch (type) {
			case STASIS_UMOS_CHANNEL:
				json_object = ast_channel_snapshot_to_json(snapshot, sanitize);
				name = "channel";
				break;
			case STASIS_UMOS_BRIDGE:
				json_object = ast_bridge_snapshot_to_json(snapshot, sanitize);
				name = "bridge";
				break;
			case STASIS_UMOS_ENDPOINT:
				json_object = ast_endpoint_snapshot_to_json(snapshot, sanitize);
				name = "endpoint";
				break;
			}
			if (json_object) {
				ast_json_object_set(out, name, json_object);
			}
		}
	}
	return ast_json_ref(out);
}
Пример #10
0
int stasis_subscription_is_subscribed(const struct stasis_subscription *sub)
{
	if (sub) {
		size_t i;
		struct stasis_topic *topic = sub->topic;
		SCOPED_AO2LOCK(lock_topic, topic);

		for (i = 0; i < AST_VECTOR_SIZE(&topic->subscribers); ++i) {
			if (AST_VECTOR_GET(&topic->subscribers, i) == sub) {
				return 1;
			}
		}
	}

	return 0;
}
Пример #11
0
/*!
 * \internal
 * \brief Remove excess existing contacts that expire the soonest.
 * \since 13.18.0
 *
 * \param contacts Container of unmodified contacts that could remove.
 * \param to_remove Maximum number of contacts to remove.
 *
 * \return Nothing
 */
static void remove_excess_contacts(struct ao2_container *contacts, struct ao2_container *response_contacts,
	unsigned int to_remove)
{
	struct excess_contact_vector contact_vec;

	/*
	 * Create a sorted vector to hold the to_remove soonest to
	 * expire contacts.  The vector has an extra space to
	 * temporarily hold the longest to expire contact that we
	 * won't remove.
	 */
	if (AST_VECTOR_INIT(&contact_vec, to_remove + 1)) {
		return;
	}
	ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, vec_contact_add, &contact_vec);

	/*
	 * The vector should always be populated with the number
	 * of contacts we need to remove.  Just in case, we will
	 * remove all contacts in the vector even if the contacts
	 * container had fewer contacts than there should be.
	 */
	ast_assert(AST_VECTOR_SIZE(&contact_vec) == to_remove);
	to_remove = AST_VECTOR_SIZE(&contact_vec);

	/* Remove the excess contacts that expire the soonest */
	while (to_remove--) {
		struct ast_sip_contact *contact;

		contact = AST_VECTOR_GET(&contact_vec, to_remove);

		ast_sip_location_delete_contact(contact);
		ast_verb(3, "Removed contact '%s' from AOR '%s' due to remove_existing\n",
			contact->uri, contact->aor);
		ast_test_suite_event_notify("AOR_CONTACT_REMOVED",
			"Contact: %s\r\n"
			"AOR: %s\r\n"
			"UserAgent: %s",
			contact->uri,
			contact->aor,
			contact->user_agent);

		ao2_unlink(response_contacts, contact);
	}

	AST_VECTOR_FREE(&contact_vec);
}
Пример #12
0
/*!
 * \brief Updates the websocket session for an \ref event_session.
 *
 * \details The websocket for the given \ref event_session will be updated to the value
 *          of the \c ws_session argument.
 *
 *          If the value of the \c ws_session is not \c NULL and there are messages in the
 *          event session's \c message_queue, the messages are dispatched and removed from
 *          the queue.
 *
 * \internal
 *
 * \param session     The event session object to update (\ref event_session).
 * \param ws_session  Handle to the underlying websocket session
 *                    (\ref ast_ari_websocket_session).
 */
static void event_session_update_websocket(
		struct event_session *session, struct ast_ari_websocket_session *ws_session)
{
	int i;

	ast_assert(session != NULL);

	ao2_lock(session);

	session->ws_session = ws_session;

	for (i = 0; i < AST_VECTOR_SIZE(&session->message_queue); i++) {
		struct ast_json *msg = AST_VECTOR_GET(&session->message_queue, i);
		ast_ari_websocket_session_write(session->ws_session, msg);
		ast_json_unref(msg);
	}

	AST_VECTOR_RESET(&session->message_queue, AST_VECTOR_ELEM_CLEANUP_NOOP);
	ao2_unlock(session);
}
Пример #13
0
/*!
 * \brief Add a subscriber to a topic.
 * \param topic Topic
 * \param sub Subscriber
 * \return 0 on success
 * \return Non-zero on error
 */
static int topic_add_subscription(struct stasis_topic *topic, struct stasis_subscription *sub)
{
	size_t idx;
	SCOPED_AO2LOCK(lock, topic);

	/* The reference from the topic to the subscription is shared with
	 * the owner of the subscription, which will explicitly unsubscribe
	 * to release it.
	 *
	 * If we bumped the refcount here, the owner would have to unsubscribe
	 * and cleanup, which is a bit awkward. */
	AST_VECTOR_APPEND(&topic->subscribers, sub);

	for (idx = 0; idx < AST_VECTOR_SIZE(&topic->upstream_topics); ++idx) {
		topic_add_subscription(
			AST_VECTOR_GET(&topic->upstream_topics, idx), sub);
	}

	return 0;
}
Пример #14
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);
}
Пример #15
0
/*!
 * \internal \brief Publish a message to a topic's subscribers
 * \brief topic The topic to publish to
 * \brief message The message to publish
 * \brief sync_sub An optional subscriber of the topic to publish synchronously
 * to
 */
static void publish_msg(struct stasis_topic *topic,
	struct stasis_message *message, struct stasis_subscription *sync_sub)
{
	size_t i;

	ast_assert(topic != NULL);
	ast_assert(message != NULL);

	/*
	 * The topic may be unref'ed by the subscription invocation.
	 * Make sure we hold onto a reference while dispatching.
	 */
	ao2_ref(topic, +1);
	ao2_lock(topic);
	for (i = 0; i < AST_VECTOR_SIZE(&topic->subscribers); ++i) {
		struct stasis_subscription *sub = AST_VECTOR_GET(&topic->subscribers, i);

		ast_assert(sub != NULL);

		dispatch_message(sub, message, (sub == sync_sub));
	}
	ao2_unlock(topic);
	ao2_ref(topic, -1);
}
Пример #16
0
/*! \brief Wait for the \ref test_msg_handler to receive the message */
static int handler_wait_for_message(struct ast_test *test)
{
	int error = 0;
	struct timeval wait_now = ast_tvnow();
	struct timespec wait_time = { .tv_sec = wait_now.tv_sec + 1, .tv_nsec = wait_now.tv_usec * 1000 };

	ast_mutex_lock(&handler_lock);
	while (!handler_received_message) {
		error = ast_cond_timedwait(&handler_cond, &handler_lock, &wait_time);
		if (error == ETIMEDOUT) {
			ast_test_status_update(test, "Test timed out while waiting for handler to get message\n");
			ast_test_set_result(test, AST_TEST_FAIL);
			break;
		}
	}
	ast_mutex_unlock(&handler_lock);

	return (error != ETIMEDOUT);
}

/*! \brief Wait for the expected number of user events to be received */
static int user_event_wait_for_events(struct ast_test *test, int expected_events)
{
	int error;
	struct timeval wait_now = ast_tvnow();
	struct timespec wait_time = { .tv_sec = wait_now.tv_sec + 1, .tv_nsec = wait_now.tv_usec * 1000 };

	expected_user_events = expected_events;

	ast_mutex_lock(&user_event_lock);
	while (received_user_events != expected_user_events) {
		error = ast_cond_timedwait(&user_event_cond, &user_event_lock, &wait_time);
		if (error == ETIMEDOUT) {
			ast_test_status_update(test, "Test timed out while waiting for %d expected user events\n", expected_events);
			ast_test_set_result(test, AST_TEST_FAIL);
			break;
		}
	}
	ast_mutex_unlock(&user_event_lock);

	ast_test_status_update(test, "Received %d of %d user events\n", received_user_events, expected_events);
	return !(received_user_events == expected_events);
}

static int verify_bad_headers(struct ast_test *test)
{
	int res = 0;
	int i;

	for (i = 0; i < AST_VECTOR_SIZE(&bad_headers); i++) {
		struct ast_variable *headers;
		struct ast_variable *current;

		headers = AST_VECTOR_GET(&bad_headers, i);
		if (!headers) {
			continue;
		}

		res = -1;
		for (current = headers; current; current = current->next) {
			ast_test_status_update(test, "Expected UserEvent %d: Failed to match %s: %s\n",
				i, current->name, current->value);
			ast_test_set_result(test, AST_TEST_FAIL);
		}
	}

	return res;
}
Пример #17
0
static struct ast_sip_aor *find_registrar_aor(struct pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint)
{
	struct ast_sip_aor *aor = NULL;
	char *aor_name = NULL;
	char *domain_name;
	char *username = NULL;
	int i;

	for (i = 0; i < AST_VECTOR_SIZE(&endpoint->ident_method_order); ++i) {
		pjsip_sip_uri *uri;
		pjsip_authorization_hdr *header = NULL;

		switch (AST_VECTOR_GET(&endpoint->ident_method_order, i)) {
		case AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME:
			uri = pjsip_uri_get_uri(rdata->msg_info.to->uri);

			domain_name = ast_alloca(uri->host.slen + 1);
			ast_copy_pj_str(domain_name, &uri->host, uri->host.slen + 1);
			username = ast_alloca(uri->user.slen + 1);
			ast_copy_pj_str(username, &uri->user, uri->user.slen + 1);

			/*
			 * We may want to match without any user options getting
			 * in the way.
			 */
			AST_SIP_USER_OPTIONS_TRUNCATE_CHECK(username);

			aor_name = find_aor_name(username, domain_name, endpoint->aors);
			if (aor_name) {
				ast_debug(3, "Matched aor '%s' by To username\n", aor_name);
			}
			break;
		case AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME:
			while ((header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION,
				header ? header->next : NULL))) {
				if (header && !pj_stricmp2(&header->scheme, "digest")) {
					username = ast_alloca(header->credential.digest.username.slen + 1);
					ast_copy_pj_str(username, &header->credential.digest.username, header->credential.digest.username.slen + 1);
					domain_name = ast_alloca(header->credential.digest.realm.slen + 1);
					ast_copy_pj_str(domain_name, &header->credential.digest.realm, header->credential.digest.realm.slen + 1);

					aor_name = find_aor_name(username, domain_name, endpoint->aors);
					if (aor_name) {
						ast_debug(3, "Matched aor '%s' by Authentication username\n", aor_name);
						break;
					}
				}
			}
			break;
		default:
			continue;
		}

		if (aor_name) {
			break;
		}
	}

	if (ast_strlen_zero(aor_name) || !(aor = ast_sip_location_retrieve_aor(aor_name))) {
		/* The provided AOR name was not found (be it within the configuration or sorcery itself) */
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL);
		ast_sip_report_req_no_support(endpoint, rdata, "registrar_requested_aor_not_found");
		ast_log(LOG_WARNING, "AOR '%s' not found for endpoint '%s'\n",
			username ?: "", ast_sorcery_object_get_id(endpoint));
	}