mirror of https://github.com/asterisk/asterisk
This change adds support for refers that are not session based. It includes a refer implementation for the PJSIP technology which results in out-of-dialog REFERs being sent to a PJSIP endpoint. These can be triggered using the new ARI endpoint `/endpoints/refer`. Resolves: #71 UserNote: There is a new ARI endpoint `/endpoints/refer` for referring an endpoint to some URI or endpoint.pull/238/head
parent
d16046e41f
commit
51a7b18038
@ -0,0 +1,325 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2023, Commend International
|
||||
*
|
||||
* Maximilian Fridrich <m.fridrich@commend.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.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Out-of-call refer support
|
||||
*
|
||||
* \author Maximilian Fridrich <m.fridrich@commend.com>
|
||||
*
|
||||
* The purpose of this API is to provide support for refers that
|
||||
* are not session based. The refers are passed into the Asterisk core
|
||||
* to be routed through the dialplan or another interface and potentially
|
||||
* sent back out through a refer technology that has been registered
|
||||
* through this API.
|
||||
*/
|
||||
|
||||
#ifndef __AST_REFER_H__
|
||||
#define __AST_REFER_H__
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* \brief A refer structure.
|
||||
*
|
||||
* This is an opaque type that represents a refer.
|
||||
*/
|
||||
struct ast_refer;
|
||||
|
||||
/*!
|
||||
* \brief A refer technology
|
||||
*
|
||||
* A refer technology is capable of transmitting text refers.
|
||||
*/
|
||||
struct ast_refer_tech {
|
||||
/*!
|
||||
* \brief Name of this refer technology
|
||||
*
|
||||
* This is the name that comes at the beginning of a URI for refers
|
||||
* that should be sent to this refer technology implementation.
|
||||
* For example, refers sent to "pjsip:m.fridrich@commend.com" would be
|
||||
* passed to the ast_refer_tech with a name of "pjsip".
|
||||
*/
|
||||
const char * const name;
|
||||
/*!
|
||||
* \brief Send a refer.
|
||||
*
|
||||
* \param refer The refer to send
|
||||
*
|
||||
* The fields of the ast_refer are guaranteed not to change during the
|
||||
* duration of this function call.
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval non-zero failure
|
||||
*/
|
||||
int (* const refer_send)(const struct ast_refer *refer);
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Register a refer technology
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval non-zero failure
|
||||
*/
|
||||
int ast_refer_tech_register(const struct ast_refer_tech *tech);
|
||||
|
||||
/*!
|
||||
* \brief Unregister a refer technology.
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval non-zero failure
|
||||
*/
|
||||
int ast_refer_tech_unregister(const struct ast_refer_tech *tech);
|
||||
|
||||
/*!
|
||||
* \brief Allocate a refer.
|
||||
*
|
||||
* Allocate a refer for the purposes of passing it into the Asterisk core
|
||||
* to be routed through the dialplan. This refer must be destroyed using
|
||||
* ast_refer_destroy().
|
||||
*
|
||||
* \return A refer object. This function will return NULL if an allocation
|
||||
* error occurs.
|
||||
*/
|
||||
struct ast_refer *ast_refer_alloc(void);
|
||||
|
||||
/*!
|
||||
* \brief Destroy an ast_refer
|
||||
*
|
||||
* \retval NULL always.
|
||||
*/
|
||||
struct ast_refer *ast_refer_destroy(struct ast_refer *refer);
|
||||
|
||||
/*!
|
||||
* \brief Bump a refer's ref count
|
||||
*/
|
||||
struct ast_refer *ast_refer_ref(struct ast_refer *refer);
|
||||
|
||||
/*!
|
||||
* \brief Set the 'to' URI of a refer
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int __attribute__((format(printf, 2, 3)))
|
||||
ast_refer_set_to(struct ast_refer *refer, const char *fmt, ...);
|
||||
|
||||
/*!
|
||||
* \brief Set the 'from' URI of a refer
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int __attribute__((format(printf, 2, 3)))
|
||||
ast_refer_set_from(struct ast_refer *refer, const char *fmt, ...);
|
||||
|
||||
/*!
|
||||
* \brief Set the 'refer_to' URI of a refer
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int __attribute__((format(printf, 2, 3)))
|
||||
ast_refer_set_refer_to(struct ast_refer *refer, const char *fmt, ...);
|
||||
|
||||
/*!
|
||||
* \brief Set the 'to_self' value of a refer
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int ast_refer_set_to_self(struct ast_refer *refer, int val);
|
||||
|
||||
/*!
|
||||
* \brief Set the technology associated with this refer
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int __attribute__((format(printf, 2, 3)))
|
||||
ast_refer_set_tech(struct ast_refer *refer, const char *fmt, ...);
|
||||
|
||||
/*!
|
||||
* \brief Set the technology's endpoint associated with this refer
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int __attribute__((format(printf, 2, 3)))
|
||||
ast_refer_set_endpoint(struct ast_refer *refer, const char *fmt, ...);
|
||||
|
||||
/*!
|
||||
* \brief Set a variable on the refer being sent to a refer tech directly.
|
||||
* \note Setting a variable that already exists overwrites the existing variable value
|
||||
*
|
||||
* \param refer
|
||||
* \param name Name of variable to set
|
||||
* \param value Value of variable to set
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int ast_refer_set_var_outbound(struct ast_refer *refer, const char *name, const char *value);
|
||||
|
||||
/*!
|
||||
* \brief Get the specified variable on the refer and unlink it from the container of variables
|
||||
* \note The return value must be freed by the caller.
|
||||
*
|
||||
* \param refer
|
||||
* \param name Name of variable to get
|
||||
*
|
||||
* \return The value associated with variable "name". NULL if variable not found.
|
||||
*/
|
||||
char *ast_refer_get_var_and_unlink(struct ast_refer *refer, const char *name);
|
||||
|
||||
/*!
|
||||
* \brief Get the specified variable on the refer
|
||||
* \note The return value is valid only as long as the ast_refer is valid. Hold a reference
|
||||
* to the refer if you plan on storing the return value. It is possible to re-set the
|
||||
* same refer var name (with ast_refer_set_var_outbound passing the variable name)
|
||||
* while holding a pointer to the result of this function.
|
||||
*
|
||||
* \param refer
|
||||
* \param name Name of variable to get
|
||||
*
|
||||
* \return The value associated with variable "name". NULL if variable not found.
|
||||
*/
|
||||
const char *ast_refer_get_var(struct ast_refer *refer, const char *name);
|
||||
|
||||
/*!
|
||||
* \brief Get the "refer-to" value of a refer.
|
||||
* \note The return value is valid only as long as the ast_refer is valid. Hold a reference
|
||||
* to the refer if you plan on storing the return value.
|
||||
*
|
||||
* \param refer The refer to get the "refer-to" value from
|
||||
*
|
||||
* \return The "refer-to" value of the refer, encoded in UTF-8.
|
||||
*/
|
||||
const char *ast_refer_get_refer_to(const struct ast_refer *refer);
|
||||
|
||||
/*!
|
||||
* \brief Retrieve the source of this refer
|
||||
*
|
||||
* \param refer The refer to get the soure from
|
||||
*
|
||||
* \return The source of the refer
|
||||
* \retval NULL or empty string if the refer has no source
|
||||
*/
|
||||
const char *ast_refer_get_from(const struct ast_refer *refer);
|
||||
|
||||
/*!
|
||||
* \brief Retrieve the destination of this refer
|
||||
*
|
||||
* \param refer The refer to get the destination from
|
||||
*
|
||||
* \return The destination of the refer
|
||||
* \retval NULL or empty string if the refer has no destination
|
||||
*/
|
||||
const char *ast_refer_get_to(const struct ast_refer *refer);
|
||||
|
||||
/*!
|
||||
* \brief Retrieve the "to_self" value of this refer
|
||||
*
|
||||
* \param refer The refer to get the destination from
|
||||
*
|
||||
* \return The to_self value of the refer
|
||||
*/
|
||||
int ast_refer_get_to_self(const struct ast_refer *refer);
|
||||
|
||||
/*!
|
||||
* \brief Retrieve the technology associated with this refer
|
||||
*
|
||||
* \param refer The refer to get the technology from
|
||||
*
|
||||
* \return The technology of the refer
|
||||
* \retval NULL or empty string if the refer has no associated technology
|
||||
*/
|
||||
const char *ast_refer_get_tech(const struct ast_refer *refer);
|
||||
|
||||
/*!
|
||||
* \brief Retrieve the endpoint associated with this refer
|
||||
*
|
||||
* \param refer The refer to get the endpoint from
|
||||
*
|
||||
* \return The endpoint associated with the refer
|
||||
* \retval NULL or empty string if the refer has no associated endpoint
|
||||
*/
|
||||
const char *ast_refer_get_endpoint(const struct ast_refer *refer);
|
||||
|
||||
/*!
|
||||
* \brief Send a refer directly to an endpoint.
|
||||
*
|
||||
* Regardless of the return value of this function, this function will take
|
||||
* care of ensuring that the refer object is properly destroyed when needed.
|
||||
*
|
||||
* \retval 0 refer successfully queued to be sent out
|
||||
* \retval non-zero failure, refer not get sent out.
|
||||
*/
|
||||
int ast_refer_send(struct ast_refer *refer);
|
||||
|
||||
/*!
|
||||
* \brief Opaque iterator for refer variables
|
||||
*/
|
||||
struct ast_refer_var_iterator;
|
||||
|
||||
/*!
|
||||
* \brief Create a new refer variable iterator
|
||||
* \param refer A refer whose variables are to be iterated over
|
||||
*
|
||||
* \return An opaque pointer to the new iterator
|
||||
*/
|
||||
struct ast_refer_var_iterator *ast_refer_var_iterator_init(const struct ast_refer *refer);
|
||||
|
||||
/*!
|
||||
* \brief Get the next variable name and value
|
||||
*
|
||||
* \param iter An iterator created with ast_refer_var_iterator_init
|
||||
* \param name A pointer to the name result pointer
|
||||
* \param value A pointer to the value result pointer
|
||||
*
|
||||
* \note The refcount to iter->current_used must be decremented by the caller
|
||||
* by calling ast_refer_var_unref_current.
|
||||
*
|
||||
* \retval 0 No more entries
|
||||
* \retval 1 Valid entry
|
||||
*/
|
||||
int ast_refer_var_iterator_next(struct ast_refer_var_iterator *iter, const char **name, const char **value);
|
||||
|
||||
/*!
|
||||
* \brief Destroy a refer variable iterator
|
||||
* \param iter Iterator to be destroyed
|
||||
*/
|
||||
void ast_refer_var_iterator_destroy(struct ast_refer_var_iterator *iter);
|
||||
|
||||
/*!
|
||||
* \brief Unref a refer var from inside an iterator loop
|
||||
*/
|
||||
void ast_refer_var_unref_current(struct ast_refer_var_iterator *iter);
|
||||
|
||||
/*!
|
||||
* @}
|
||||
*/
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __AST_REFER_H__ */
|
@ -0,0 +1,537 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2023, Commend International
|
||||
*
|
||||
* Maximilian Fridrich <m.fridrich@commend.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.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Out-of-call refer support
|
||||
*
|
||||
* \author Maximilian Fridrich <m.fridrich@commend.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include "asterisk/_private.h"
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/datastore.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/manager.h"
|
||||
#include "asterisk/strings.h"
|
||||
#include "asterisk/astobj2.h"
|
||||
#include "asterisk/vector.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/taskprocessor.h"
|
||||
#include "asterisk/refer.h"
|
||||
|
||||
struct refer_data {
|
||||
/* Stored in stuff[] at struct end */
|
||||
char *name;
|
||||
/* Stored separately */
|
||||
char *value;
|
||||
/* Holds name */
|
||||
char stuff[0];
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief A refer.
|
||||
*/
|
||||
struct ast_refer {
|
||||
AST_DECLARE_STRING_FIELDS(
|
||||
/*! Where the refer is going */
|
||||
AST_STRING_FIELD(to);
|
||||
/*! Where we "say" the refer came from */
|
||||
AST_STRING_FIELD(from);
|
||||
/*! Where to refer to */
|
||||
AST_STRING_FIELD(refer_to);
|
||||
/*! An endpoint associated with this refer */
|
||||
AST_STRING_FIELD(endpoint);
|
||||
/*! The technology of the endpoint associated with this refer */
|
||||
AST_STRING_FIELD(tech);
|
||||
);
|
||||
/* Whether to refer to Asterisk itself, if refer_to is an Asterisk endpoint. */
|
||||
int to_self;
|
||||
/*! Technology/dialplan specific variables associated with the refer */
|
||||
struct ao2_container *vars;
|
||||
};
|
||||
|
||||
/*! \brief Lock for \c refer_techs vector */
|
||||
static ast_rwlock_t refer_techs_lock;
|
||||
|
||||
/*! \brief Vector of refer technologies */
|
||||
AST_VECTOR(, const struct ast_refer_tech *) refer_techs;
|
||||
|
||||
static int refer_data_cmp_fn(void *obj, void *arg, int flags)
|
||||
{
|
||||
const struct refer_data *object_left = obj;
|
||||
const struct refer_data *object_right = arg;
|
||||
const char *right_key = arg;
|
||||
int cmp;
|
||||
|
||||
switch (flags & OBJ_SEARCH_MASK) {
|
||||
case OBJ_SEARCH_OBJECT:
|
||||
right_key = object_right->name;
|
||||
case OBJ_SEARCH_KEY:
|
||||
cmp = strcasecmp(object_left->name, right_key);
|
||||
break;
|
||||
case OBJ_SEARCH_PARTIAL_KEY:
|
||||
cmp = strncasecmp(object_left->name, right_key, strlen(right_key));
|
||||
break;
|
||||
default:
|
||||
cmp = 0;
|
||||
break;
|
||||
}
|
||||
if (cmp) {
|
||||
return 0;
|
||||
}
|
||||
return CMP_MATCH;
|
||||
}
|
||||
|
||||
static void refer_data_destructor(void *obj)
|
||||
{
|
||||
struct refer_data *data = obj;
|
||||
ast_free(data->value);
|
||||
ast_free(data);
|
||||
}
|
||||
|
||||
static void refer_destructor(void *obj)
|
||||
{
|
||||
struct ast_refer *refer = obj;
|
||||
|
||||
ast_string_field_free_memory(refer);
|
||||
ao2_cleanup(refer->vars);
|
||||
}
|
||||
|
||||
struct ast_refer *ast_refer_alloc(void)
|
||||
{
|
||||
struct ast_refer *refer;
|
||||
|
||||
if (!(refer = ao2_alloc_options(sizeof(*refer), refer_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ast_string_field_init(refer, 128)) {
|
||||
ao2_ref(refer, -1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
refer->vars = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
|
||||
NULL, refer_data_cmp_fn);
|
||||
if (!refer->vars) {
|
||||
ao2_ref(refer, -1);
|
||||
return NULL;
|
||||
}
|
||||
refer->to_self = 0;
|
||||
|
||||
return refer;
|
||||
}
|
||||
|
||||
struct ast_refer *ast_refer_ref(struct ast_refer *refer)
|
||||
{
|
||||
ao2_ref(refer, 1);
|
||||
return refer;
|
||||
}
|
||||
|
||||
struct ast_refer *ast_refer_destroy(struct ast_refer *refer)
|
||||
{
|
||||
ao2_ref(refer, -1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ast_refer_set_to(struct ast_refer *refer, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ast_string_field_build_va(refer, to, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ast_refer_set_from(struct ast_refer *refer, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ast_string_field_build_va(refer, from, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ast_refer_set_refer_to(struct ast_refer *refer, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ast_string_field_build_va(refer, refer_to, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ast_refer_set_to_self(struct ast_refer *refer, int val)
|
||||
{
|
||||
refer->to_self = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ast_refer_set_tech(struct ast_refer *refer, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ast_string_field_build_va(refer, tech, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ast_refer_set_endpoint(struct ast_refer *refer, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ast_string_field_build_va(refer, endpoint, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *ast_refer_get_refer_to(const struct ast_refer *refer)
|
||||
{
|
||||
return refer->refer_to;
|
||||
}
|
||||
|
||||
const char *ast_refer_get_from(const struct ast_refer *refer)
|
||||
{
|
||||
return refer->from;
|
||||
}
|
||||
|
||||
const char *ast_refer_get_to(const struct ast_refer *refer)
|
||||
{
|
||||
return refer->to;
|
||||
}
|
||||
|
||||
int ast_refer_get_to_self(const struct ast_refer *refer)
|
||||
{
|
||||
return refer->to_self;
|
||||
}
|
||||
|
||||
const char *ast_refer_get_tech(const struct ast_refer *refer)
|
||||
{
|
||||
return refer->tech;
|
||||
}
|
||||
|
||||
const char *ast_refer_get_endpoint(const struct ast_refer *refer)
|
||||
{
|
||||
return refer->endpoint;
|
||||
}
|
||||
|
||||
static struct refer_data *refer_data_new(const char *name)
|
||||
{
|
||||
struct refer_data *data;
|
||||
int name_len = strlen(name) + 1;
|
||||
|
||||
if ((data = ao2_alloc_options(name_len + sizeof(*data), refer_data_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK))) {
|
||||
data->name = data->stuff;
|
||||
strcpy(data->name, name);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static struct refer_data *refer_data_find(struct ao2_container *vars, const char *name)
|
||||
{
|
||||
return ao2_find(vars, name, OBJ_SEARCH_KEY);
|
||||
}
|
||||
|
||||
char *ast_refer_get_var_and_unlink(struct ast_refer *refer, const char *name)
|
||||
{
|
||||
struct refer_data *data;
|
||||
char *val = NULL;
|
||||
|
||||
if (!(data = ao2_find(refer->vars, name, OBJ_SEARCH_KEY | OBJ_UNLINK))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
val = ast_strdup(data->value);
|
||||
ao2_ref(data, -1);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int refer_set_var_full(struct ast_refer *refer, const char *name, const char *value)
|
||||
{
|
||||
struct refer_data *data;
|
||||
|
||||
if (!(data = refer_data_find(refer->vars, name))) {
|
||||
if (ast_strlen_zero(value)) {
|
||||
return 0;
|
||||
}
|
||||
if (!(data = refer_data_new(name))) {
|
||||
return -1;
|
||||
};
|
||||
data->value = ast_strdup(value);
|
||||
|
||||
ao2_link(refer->vars, data);
|
||||
} else {
|
||||
if (ast_strlen_zero(value)) {
|
||||
ao2_unlink(refer->vars, data);
|
||||
} else {
|
||||
data->value = ast_strdup(value);
|
||||
}
|
||||
}
|
||||
|
||||
ao2_ref(data, -1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ast_refer_set_var_outbound(struct ast_refer *refer, const char *name, const char *value)
|
||||
{
|
||||
return refer_set_var_full(refer, name, value);
|
||||
}
|
||||
|
||||
const char *ast_refer_get_var(struct ast_refer *refer, const char *name)
|
||||
{
|
||||
struct refer_data *data;
|
||||
const char *val = NULL;
|
||||
|
||||
if (!(data = refer_data_find(refer->vars, name))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
val = data->value;
|
||||
ao2_ref(data, -1);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
struct ast_refer_var_iterator {
|
||||
struct ao2_iterator iter;
|
||||
struct refer_data *current_used;
|
||||
};
|
||||
|
||||
struct ast_refer_var_iterator *ast_refer_var_iterator_init(const struct ast_refer *refer)
|
||||
{
|
||||
struct ast_refer_var_iterator *iter;
|
||||
|
||||
iter = ast_calloc(1, sizeof(*iter));
|
||||
if (!iter) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iter->iter = ao2_iterator_init(refer->vars, 0);
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
int ast_refer_var_iterator_next(struct ast_refer_var_iterator *iter, const char **name, const char **value)
|
||||
{
|
||||
struct refer_data *data;
|
||||
|
||||
if (!iter) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
data = ao2_iterator_next(&iter->iter);
|
||||
if (!data) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*name = data->name;
|
||||
*value = data->value;
|
||||
|
||||
iter->current_used = data;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ast_refer_var_unref_current(struct ast_refer_var_iterator *iter)
|
||||
{
|
||||
ao2_cleanup(iter->current_used);
|
||||
iter->current_used = NULL;
|
||||
}
|
||||
|
||||
void ast_refer_var_iterator_destroy(struct ast_refer_var_iterator *iter)
|
||||
{
|
||||
if (iter) {
|
||||
ao2_iterator_destroy(&iter->iter);
|
||||
ast_refer_var_unref_current(iter);
|
||||
ast_free(iter);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal \brief Find a \c ast_refer_tech by its technology name
|
||||
*
|
||||
* \param tech_name The name of the refer technology
|
||||
*
|
||||
* \note \c refer_techs should be locked via \c refer_techs_lock prior to
|
||||
* calling this function
|
||||
*
|
||||
* \retval NULL if no \ref ast_refer_tech has been registered
|
||||
* \return \ref ast_refer_tech if registered
|
||||
*/
|
||||
static const struct ast_refer_tech *refer_find_by_tech_name(const char *tech_name)
|
||||
{
|
||||
const struct ast_refer_tech *current;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < AST_VECTOR_SIZE(&refer_techs); i++) {
|
||||
current = AST_VECTOR_GET(&refer_techs, i);
|
||||
if (!strcmp(current->name, tech_name)) {
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ast_refer_send(struct ast_refer *refer)
|
||||
{
|
||||
char *tech_name = NULL;
|
||||
const struct ast_refer_tech *refer_tech;
|
||||
int res = -1;
|
||||
|
||||
if (ast_strlen_zero(refer->to)) {
|
||||
ao2_ref(refer, -1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tech_name = ast_strdupa(refer->to);
|
||||
tech_name = strsep(&tech_name, ":");
|
||||
|
||||
ast_rwlock_rdlock(&refer_techs_lock);
|
||||
refer_tech = refer_find_by_tech_name(tech_name);
|
||||
|
||||
if (!refer_tech) {
|
||||
ast_log(LOG_ERROR, "Unknown refer tech: %s\n", tech_name);
|
||||
ast_rwlock_unlock(&refer_techs_lock);
|
||||
ao2_ref(refer, -1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ao2_lock(refer);
|
||||
res = refer_tech->refer_send(refer);
|
||||
ao2_unlock(refer);
|
||||
|
||||
ast_rwlock_unlock(&refer_techs_lock);
|
||||
|
||||
ao2_ref(refer, -1);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int ast_refer_tech_register(const struct ast_refer_tech *tech)
|
||||
{
|
||||
const struct ast_refer_tech *match;
|
||||
|
||||
ast_rwlock_wrlock(&refer_techs_lock);
|
||||
|
||||
match = refer_find_by_tech_name(tech->name);
|
||||
if (match) {
|
||||
ast_log(LOG_ERROR, "Refer technology already registered for '%s'\n",
|
||||
tech->name);
|
||||
ast_rwlock_unlock(&refer_techs_lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (AST_VECTOR_APPEND(&refer_techs, tech)) {
|
||||
ast_log(LOG_ERROR, "Failed to register refer technology for '%s'\n",
|
||||
tech->name);
|
||||
ast_rwlock_unlock(&refer_techs_lock);
|
||||
return -1;
|
||||
}
|
||||
ast_verb(3, "Refer technology '%s' registered.\n", tech->name);
|
||||
|
||||
ast_rwlock_unlock(&refer_techs_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Comparison callback for \c ast_refer_tech vector removal
|
||||
*
|
||||
* \param vec_elem The element in the vector being compared
|
||||
* \param srch The element being looked up
|
||||
*
|
||||
* \retval non-zero The items are equal
|
||||
* \retval 0 The items are not equal
|
||||
*/
|
||||
static int refer_tech_cmp(const struct ast_refer_tech *vec_elem, const struct ast_refer_tech *srch)
|
||||
{
|
||||
if (!vec_elem->name || !srch->name) {
|
||||
return (vec_elem->name == srch->name) ? 1 : 0;
|
||||
}
|
||||
return !strcmp(vec_elem->name, srch->name);
|
||||
}
|
||||
|
||||
int ast_refer_tech_unregister(const struct ast_refer_tech *tech)
|
||||
{
|
||||
int match;
|
||||
|
||||
ast_rwlock_wrlock(&refer_techs_lock);
|
||||
match = AST_VECTOR_REMOVE_CMP_UNORDERED(&refer_techs, tech, refer_tech_cmp,
|
||||
AST_VECTOR_ELEM_CLEANUP_NOOP);
|
||||
ast_rwlock_unlock(&refer_techs_lock);
|
||||
|
||||
if (match) {
|
||||
ast_log(LOG_ERROR, "No '%s' refer technology found.\n", tech->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_verb(2, "Refer technology '%s' unregistered.\n", tech->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Clean up other resources on Asterisk shutdown
|
||||
*/
|
||||
static void refer_shutdown(void)
|
||||
{
|
||||
AST_VECTOR_FREE(&refer_techs);
|
||||
ast_rwlock_destroy(&refer_techs_lock);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Initialize stuff during Asterisk startup.
|
||||
*
|
||||
* Cleanup isn't a big deal in this function. If we return non-zero,
|
||||
* Asterisk is going to exit.
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval non-zero failure
|
||||
*/
|
||||
int ast_refer_init(void)
|
||||
{
|
||||
ast_rwlock_init(&refer_techs_lock);
|
||||
if (AST_VECTOR_INIT(&refer_techs, 8)) {
|
||||
return -1;
|
||||
}
|
||||
ast_register_cleanup(refer_shutdown);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in new issue