summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES11
-rw-r--r--include/asterisk/dial.h12
-rw-r--r--include/asterisk/stasis_app.h31
-rw-r--r--include/asterisk/stringfields.h295
-rw-r--r--main/config.c33
-rw-r--r--main/dial.c107
-rw-r--r--main/frame.c5
-rw-r--r--main/stringfields.c508
-rw-r--r--main/utils.c371
-rw-r--r--res/ari/resource_channels.c177
-rw-r--r--res/ari/resource_channels.h62
-rw-r--r--res/res_ari_channels.c256
-rw-r--r--res/res_http_websocket.c2
-rw-r--r--res/res_pjsip/config_global.c4
-rw-r--r--res/res_pjsip_registrar_expire.c4
-rw-r--r--res/res_pjsip_session.c47
-rw-r--r--res/res_stasis.c6
-rw-r--r--res/stasis/control.c168
-rw-r--r--rest-api/api-docs/channels.json115
-rw-r--r--tests/test_stringfields.c205
20 files changed, 1794 insertions, 625 deletions
diff --git a/CHANGES b/CHANGES
index 594995057..98df1f756 100644
--- a/CHANGES
+++ b/CHANGES
@@ -12,6 +12,17 @@
--- Functionality changes from Asterisk 13 to Asterisk 14 --------------------
------------------------------------------------------------------------------
+ARI
+-----------------
+ * A new ARI method has been added to the channels resource. "create" allows for
+ you to create a new channel and place that channel into a Stasis application. This
+ is similar to origination except that the specified channel is not dialed. This
+ allows for an application writer to create a channel, perform manipulations on it,
+ and then delay dialing the channel until later.
+
+ * To complement the "create" method, a "dial" method has been added to the channels
+ resource in order to place a call to a created channel.
+
Applications
------------------
diff --git a/include/asterisk/dial.h b/include/asterisk/dial.h
index c59257ce5..168c5091a 100644
--- a/include/asterisk/dial.h
+++ b/include/asterisk/dial.h
@@ -76,6 +76,18 @@ struct ast_dial *ast_dial_create(void);
*/
int ast_dial_append(struct ast_dial *dial, const char *tech, const char *device, const struct ast_assigned_ids *assignedids);
+/*!
+ * \brief Append a channel using an actual channel object
+ *
+ * \param dial The ast_dial to add the channel to
+ * \param chan The channel to add to the dial
+ * \retval -1 Failure
+ * \retval non-zero The position of the channel in the list of dialed channels
+ *
+ * \note The chan ref is stolen with a successful return.
+ */
+int ast_dial_append_channel(struct ast_dial *dial, struct ast_channel *chan);
+
/*! \brief Request all appended channels, but do not dial
* \param dial Dialing structure
* \param chan Optional dialing channel
diff --git a/include/asterisk/stasis_app.h b/include/asterisk/stasis_app.h
index 90ef82ebf..0863f9f98 100644
--- a/include/asterisk/stasis_app.h
+++ b/include/asterisk/stasis_app.h
@@ -461,23 +461,6 @@ const char *stasis_app_control_get_channel_id(
const struct stasis_app_control *control);
/*!
- * \brief Dial an endpoint and bridge it to a channel in \c res_stasis
- *
- * If the channel is no longer in \c res_stasis, this function does nothing.
- *
- * \param control Control for \c res_stasis
- * \param endpoint The endpoint to dial.
- * \param exten Extension to dial if no endpoint specified.
- * \param context Context to use with extension.
- * \param timeout The amount of time to wait for answer, before giving up.
- *
- * \return 0 for success
- * \return -1 for error.
- */
-int stasis_app_control_dial(struct stasis_app_control *control, const char *endpoint, const char *exten,
- const char *context, int timeout);
-
-/*!
* \brief Apply a bridge role to a channel controlled by a stasis app control
*
* \param control Control for \c res_stasis
@@ -872,6 +855,20 @@ int stasis_app_channel_unreal_set_internal(struct ast_channel *chan);
*/
int stasis_app_channel_set_internal(struct ast_channel *chan);
+struct ast_dial;
+
+/*!
+ * \brief Dial a channel
+ * \param control Control for \c res_stasis.
+ * \param dial The ast_dial for the outbound channel
+ */
+int stasis_app_control_dial(struct stasis_app_control *control, struct ast_dial *dial);
+
+/*!
+ * \brief Get dial structure on a control
+ */
+struct ast_dial *stasis_app_get_dial(struct stasis_app_control *control);
+
/*! @} */
#endif /* _ASTERISK_STASIS_APP_H */
diff --git a/include/asterisk/stringfields.h b/include/asterisk/stringfields.h
index d0879b2ba..c24424b0a 100644
--- a/include/asterisk/stringfields.h
+++ b/include/asterisk/stringfields.h
@@ -17,6 +17,7 @@
*/
/*! \file
+ \page Stringfields String Fields
\brief String fields in structures
This file contains objects and macros used to manage string
@@ -93,6 +94,80 @@
ast_free(x);
\endcode
+ A new feature "Extended String Fields" has been added in 13.9.0.
+
+ An extended field is one that is declared outside the AST_DECLARE_STRING_FIELDS
+ block but still inside the parent structure. It's most useful for extending
+ structures where adding a new string field to an existing AST_DECLARE_STRING_FIELDS
+ block would break ABI compatibility.
+
+ Example:
+
+ \code
+ struct original_structure_version {
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(foo);
+ AST_STRING_FIELD(bar);
+ );
+ int x1;
+ int x2;
+ };
+ \endcode
+
+ Adding "blah" to the existing string fields breaks ABI compatibility because it changes
+ the offsets of x1 and x2.
+
+ \code
+ struct new_structure_version {
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(foo);
+ AST_STRING_FIELD(bar);
+ AST_STRING_FIELD(blah);
+ );
+ int x1;
+ int x2;
+ };
+ \endcode
+
+ However, adding "blah" as an extended string field to the end of the structure doesn't break
+ ABI compatibility but still allows the use of the existing pool.
+
+ \code
+ struct new_structure_version {
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(foo);
+ AST_STRING_FIELD(bar);
+ );
+ int x1;
+ int x2;
+ AST_STRING_FIELD_EXTENDED(blah);
+ };
+ \endcode
+
+ The only additional step required is to call ast_string_field_init_extended so the
+ pool knows about the new field. It must be called AFTER ast_string_field_init or
+ ast_calloc_with_stringfields. Although ast_calloc_with_stringfields is used in the
+ sample below, it's not necessary for extended string fields.
+
+ \code
+
+ struct new_structure_version *x = ast_calloc_with_stringfields(1, struct new_structure_version, 252);
+ if (!x) {
+ return;
+ }
+
+ ast_string_field_init_extended(x, blah);
+ \endcode
+
+ The new field can now be treated just like any other string field and it's storage will
+ be released with the rest of the string fields.
+
+ \code
+ ast_string_field_set(x, foo, "infinite loop");
+ ast_stringfield_free_memory(x);
+ ast_free(x);
+ \endcode
+
This completes the API description.
*/
@@ -100,6 +175,7 @@
#define _ASTERISK_STRINGFIELDS_H
#include "asterisk/inline_api.h"
+#include "asterisk/vector.h"
/*!
\internal
@@ -139,11 +215,28 @@ struct ast_string_field_pool {
/*!
\internal
+ \brief The definition for the string field vector used for compare and copy
+ \since 13.9.0
+*/
+AST_VECTOR(ast_string_field_vector, const char **);
+
+/*!
+ \internal
+ \brief Structure used to hold a pointer to the embedded pool and the field vector
+ \since 13.9.0
+*/
+struct ast_string_field_header {
+ struct ast_string_field_pool *embedded_pool; /*!< pointer to the embedded pool, if any */
+ struct ast_string_field_vector string_fields; /*!< field vector for compare and copy */
+};
+
+/*!
+ \internal
\brief Structure used to manage the storage for a set of string fields.
*/
struct ast_string_field_mgr {
ast_string_field last_alloc; /*!< the last field allocated */
- struct ast_string_field_pool *embedded_pool; /*!< pointer to the embedded pool, if any */
+ struct ast_string_field_header *header; /*!< pointer to the header */
#if defined(__AST_DEBUG_MALLOC)
const char *owner_file; /*!< filename of owner */
const char *owner_func; /*!< function name of owner */
@@ -218,6 +311,29 @@ void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
#define AST_STRING_FIELD(name) const ast_string_field name
/*!
+ \brief Declare an extended string field
+ \since 13.9.0
+
+ \param name The field name
+*/
+#define AST_STRING_FIELD_EXTENDED(name) AST_STRING_FIELD(name)
+
+enum ast_stringfield_cleanup_type {
+ /*!
+ * Reset all string fields and free all extra pools that may have been created
+ * The allocation or structure can be reused as is.
+ */
+ AST_STRINGFIELD_RESET = 0,
+ /*!
+ * Reset all string fields and free all pools.
+ * If the pointer was returned by ast_calloc_with_stringfields, it can NOT be reused
+ * and should be immediately freed. Otherwise, you must call ast_string_field_init
+ * again if you want to reuse it.
+ */
+ AST_STRINGFIELD_DESTROY = -1,
+};
+
+/*!
\brief Declare the fields needed in a structure
\param field_list The list of fields to declare, using AST_STRING_FIELD() for each one.
Internally, string fields are stored as a pointer to the head of the pool,
@@ -239,17 +355,65 @@ void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
\brief Initialize a field pool and fields
\param x Pointer to a structure containing fields
\param size Amount of storage to allocate.
- Use 0 to reset fields to the default value,
+ Use AST_STRINGFIELD_RESET to reset fields to the default value,
and release all but the most recent pool.
- size<0 (used internally) means free all pools.
+ AST_STRINGFIELD_DESTROY (used internally) means free all pools which is
+ equivalent to calling ast_string_field_free_memory.
+
\return 0 on success, non-zero on failure
*/
#define ast_string_field_init(x, size) \
- __ast_string_field_init(&(x)->__field_mgr, &(x)->__field_mgr_pool, size, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+({ \
+ int __res__ = -1; \
+ if (((void *)(x)) != NULL) { \
+ __res__ = __ast_string_field_init(&(x)->__field_mgr, &(x)->__field_mgr_pool, size, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ } \
+ __res__ ; \
+})
+
+/*!
+ * \brief free all memory - to be called before destroying the object
+ *
+ * \param x
+ *
+ */
+#define ast_string_field_free_memory(x) \
+({ \
+ int __res__ = -1; \
+ if (((void *)(x)) != NULL) { \
+ __res__ = __ast_string_field_free_memory(&(x)->__field_mgr, &(x)->__field_mgr_pool, \
+ AST_STRINGFIELD_DESTROY, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ } \
+ __res__; \
+})
+
+int __ast_string_field_free_memory(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, enum ast_stringfield_cleanup_type cleanup_type,
+ const char *file, int lineno, const char *func);
-/*! \brief free all memory - to be called before destroying the object */
-#define ast_string_field_free_memory(x) \
- __ast_string_field_init(&(x)->__field_mgr, &(x)->__field_mgr_pool, -1, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+/*!
+ * \brief Initialize an extended string field
+ * \since 13.9.0
+ *
+ * \param x Pointer to a structure containing the field
+ * \param field The extended field to initialize
+ * \retval zero on success
+ * \retval non-zero on error
+ *
+ * \note
+ * This macro must be called on ALL fields defined with AST_STRING_FIELD_EXTENDED after
+ * ast_string_field_init has been called.
+ */
+#define ast_string_field_init_extended(x, field) \
+({ \
+ int __res__ = -1; \
+ if (((void *)(x)) != NULL && (x)->__field_mgr.header != NULL) { \
+ ast_string_field *non_const = (ast_string_field *)&(x)->field; \
+ *non_const = __ast_string_field_empty; \
+ __res__ = AST_VECTOR_APPEND(&(x)->__field_mgr.header->string_fields, non_const); \
+ } \
+ __res__; \
+})
/*!
* \internal
@@ -260,9 +424,10 @@ int __ast_string_field_init(struct ast_string_field_mgr *mgr, struct ast_string_
/*!
* \brief Allocate a structure with embedded stringfields in a single allocation
- * \param n Number of structures to allocate (see ast_calloc)
+ * \param n Current imlementation only allows 1 structure to be allocated
* \param type The type of structure to allocate
* \param size The number of bytes of space (minimum) to allocate for stringfields to use
+ * in each structure
*
* This function will allocate memory for one or more structures that use stringfields, and
* also allocate space for the stringfields and initialize the stringfield management
@@ -271,16 +436,16 @@ int __ast_string_field_init(struct ast_string_field_mgr *mgr, struct ast_string_
* \since 1.8
*/
#define ast_calloc_with_stringfields(n, type, size) \
- __ast_calloc_with_stringfields(n, sizeof(type), offsetof(type, __field_mgr), offsetof(type, __field_mgr_pool), \
- size, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+ __ast_calloc_with_stringfields(n, sizeof(type), offsetof(type, __field_mgr), \
+ offsetof(type, __field_mgr_pool), size, __FILE__, __LINE__, __PRETTY_FUNCTION__)
/*!
* \internal
* \brief internal version of ast_calloc_with_stringfields
*/
-void * attribute_malloc __ast_calloc_with_stringfields(unsigned int num_structs, size_t struct_size, size_t field_mgr_offset,
- size_t field_mgr_pool_offset, size_t pool_size, const char *file,
- int lineno, const char *func);
+void * attribute_malloc __ast_calloc_with_stringfields(unsigned int num_structs,
+ size_t struct_size, size_t field_mgr_offset, size_t field_mgr_pool_offset, size_t pool_size,
+ const char *file, int lineno, const char *func);
/*!
\internal
@@ -314,7 +479,14 @@ void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
\retval zero on success
\retval non-zero on error
*/
-#define ast_string_field_ptr_set(x, ptr, data) ast_string_field_ptr_set_by_fields((x)->__field_mgr_pool, (x)->__field_mgr, ptr, data)
+#define ast_string_field_ptr_set(x, ptr, data) \
+({ \
+ int __res__ = -1; \
+ if (((void *)(x)) != NULL) { \
+ __res__ = ast_string_field_ptr_set_by_fields((x)->__field_mgr_pool, (x)->__field_mgr, ptr, data); \
+ } \
+ __res__; \
+})
#define ast_string_field_ptr_set_by_fields(field_mgr_pool, field_mgr, ptr, data) \
({ \
@@ -348,7 +520,14 @@ void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
\retval zero on success
\retval non-zero on error
*/
-#define ast_string_field_set(x, field, data) ast_string_field_ptr_set(x, &(x)->field, data)
+#define ast_string_field_set(x, field, data) \
+({ \
+ int __res__ = -1; \
+ if (((void *)(x)) != NULL) { \
+ __res__ = ast_string_field_ptr_set(x, &(x)->field, data); \
+ } \
+ __res__; \
+})
/*!
\brief Set a field to a complex (built) value
@@ -359,7 +538,14 @@ void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
\return nothing
*/
#define ast_string_field_ptr_build(x, ptr, fmt, args...) \
- __ast_string_field_ptr_build(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) ptr, fmt, args)
+({ \
+ int __res__ = -1; \
+ if (((void *)(x)) != NULL) { \
+ __ast_string_field_ptr_build(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) ptr, fmt, args); \
+ __res__ = 0; \
+ } \
+ __res__; \
+})
/*!
\brief Set a field to a complex (built) value
@@ -370,7 +556,14 @@ void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
\return nothing
*/
#define ast_string_field_build(x, field, fmt, args...) \
- __ast_string_field_ptr_build(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) &(x)->field, fmt, args)
+({ \
+ int __res__ = -1; \
+ if (((void *)(x)) != NULL) { \
+ __ast_string_field_ptr_build(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) &(x)->field, fmt, args); \
+ __res__ = 0; \
+ } \
+ __res__; \
+})
/*!
\brief Set a field to a complex (built) value with prebuilt va_lists.
@@ -381,7 +574,14 @@ void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
\return nothing
*/
#define ast_string_field_ptr_build_va(x, ptr, fmt, args) \
- __ast_string_field_ptr_build_va(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) ptr, fmt, args)
+({ \
+ int __res__ = -1; \
+ if (((void *)(x)) != NULL) { \
+ __ast_string_field_ptr_build_va(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) ptr, fmt, args); \
+ __res__ = 0; \
+ } \
+ __res__; \
+})
/*!
\brief Set a field to a complex (built) value
@@ -392,7 +592,14 @@ void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
\return nothing
*/
#define ast_string_field_build_va(x, field, fmt, args) \
- __ast_string_field_ptr_build_va(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) &(x)->field, fmt, args)
+({ \
+ int __res__ = -1; \
+ if (((void *)(x)) != NULL) { \
+ __ast_string_field_ptr_build_va(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) &(x)->field, fmt, args); \
+ __res__ = 0; \
+ } \
+ __res__; \
+})
/*!
\brief Compare the string fields in two instances of the same structure
@@ -404,25 +611,16 @@ void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
*/
#define ast_string_fields_cmp(instance1, instance2) \
({ \
- int __res__ = 0; \
- size_t __ptr_size__ = sizeof(char *); \
- int __len__ = ((void *)&(instance1)->__field_mgr - (void *)&(instance1)->__field_mgr_pool)/__ptr_size__ - 1; \
- int __len2__ = ((void *)&(instance2)->__field_mgr - (void *)&(instance2)->__field_mgr_pool)/__ptr_size__ - 1; \
- if (__len__ == __len2__) { \
- char **__head1__ = (void *)&(instance1)->__field_mgr_pool + __ptr_size__; \
- char **__head2__ = (void *)&(instance2)->__field_mgr_pool + __ptr_size__; \
- for (__len__ -= 1; __len__ >= 0; __len__--) { \
- __res__ = strcmp(__head1__[__len__], __head2__[__len__]); \
- if (__res__) { \
- break; \
- } \
- } \
- } else { \
- __res__ = -1; \
+ int __res__ = -1; \
+ if (((void *)(instance1)) != NULL && ((void *)(instance2)) != NULL) { \
+ __res__ = __ast_string_fields_cmp(&(instance1)->__field_mgr.header->string_fields, \
+ &(instance2)->__field_mgr.header->string_fields); \
} \
__res__; \
})
+int __ast_string_fields_cmp(struct ast_string_field_vector *left, struct ast_string_field_vector *right);
+
/*!
\brief Copy all string fields from one instance to another of the same structure
\since 12
@@ -433,27 +631,16 @@ void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
*/
#define ast_string_fields_copy(copy, orig) \
({ \
- int __outer_res__ = 0; \
- size_t __ptr_size__ = sizeof(char *); \
- int __len__ = ((void *)&(copy)->__field_mgr - (void *)&(copy)->__field_mgr_pool)/__ptr_size__ - 1; \
- int __len2__ = ((void *)&(orig)->__field_mgr - (void *)&(orig)->__field_mgr_pool)/__ptr_size__ - 1; \
- if (__len__ == __len2__) { \
- ast_string_field *__copy_head__ = (void *)&(copy)->__field_mgr_pool + __ptr_size__; \
- ast_string_field *__orig_head__ = (void *)&(orig)->__field_mgr_pool + __ptr_size__; \
- for (__len2__ -= 1; __len2__ >= 0; __len2__--) { \
- __ast_string_field_release_active((copy)->__field_mgr_pool, __copy_head__[__len2__]); \
- __copy_head__[__len2__] = __ast_string_field_empty; \
- } \
- for (__len__ -= 1; __len__ >= 0; __len__--) { \
- if (ast_string_field_ptr_set((copy), &__copy_head__[__len__], __orig_head__[__len__])) { \
- __outer_res__ = -1; \
- break; \
- } \
- } \
- } else { \
- __outer_res__ = -1; \
+ int __res__ = -1; \
+ if (((void *)(copy)) != NULL && ((void *)(orig)) != NULL) { \
+ __res__ = __ast_string_fields_copy(((copy)->__field_mgr_pool), \
+ (struct ast_string_field_mgr *)&((copy)->__field_mgr), \
+ (struct ast_string_field_mgr *)&((orig)->__field_mgr)); \
} \
- __outer_res__; \
+ __res__; \
})
+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);
+
#endif /* _ASTERISK_STRINGFIELDS_H */
diff --git a/main/config.c b/main/config.c
index a9ea01a8b..dad3b668b 100644
--- a/main/config.c
+++ b/main/config.c
@@ -72,7 +72,8 @@ static char *extconfig_conf = "extconfig.conf";
static struct ao2_container *cfg_hooks;
static void config_hook_exec(const char *filename, const char *module, const struct ast_config *cfg);
static inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2);
-static int does_category_match(struct ast_category *cat, const char *category_name, const char *match);
+static int does_category_match(struct ast_category *cat, const char *category_name,
+ const char *match, char sep);
/*! \brief Structure to keep comments for rewriting configuration files */
struct ast_comment {
@@ -864,7 +865,8 @@ static void move_variables(struct ast_category *old, struct ast_category *new)
/*! \brief Returns true if ALL of the regex expressions and category name match.
* Both can be NULL (I.E. no predicate) which results in a true return;
*/
-static int does_category_match(struct ast_category *cat, const char *category_name, const char *match)
+static int does_category_match(struct ast_category *cat, const char *category_name,
+ const char *match, char sep)
{
char *dupmatch;
char *nvp = NULL;
@@ -883,7 +885,7 @@ static int does_category_match(struct ast_category *cat, const char *category_na
dupmatch = ast_strdupa(match);
- while ((nvp = ast_strsep(&dupmatch, ',', AST_STRSEP_STRIP))) {
+ while ((nvp = ast_strsep(&dupmatch, sep, AST_STRSEP_STRIP))) {
struct ast_variable *v;
char *match_name;
char *match_value = NULL;
@@ -982,19 +984,19 @@ struct ast_category *ast_category_new_template(const char *name, const char *in_
return new_category(name, in_file, lineno, 1);
}
-struct ast_category *ast_category_get(const struct ast_config *config,
- const char *category_name, const char *filter)
+static struct ast_category *category_get_sep(const struct ast_config *config,
+ const char *category_name, const char *filter, char sep)
{
struct ast_category *cat;
for (cat = config->root; cat; cat = cat->next) {
- if (cat->name == category_name && does_category_match(cat, category_name, filter)) {
+ if (cat->name == category_name && does_category_match(cat, category_name, filter, sep)) {
return cat;
}
}
for (cat = config->root; cat; cat = cat->next) {
- if (does_category_match(cat, category_name, filter)) {
+ if (does_category_match(cat, category_name, filter, sep)) {
return cat;
}
}
@@ -1002,6 +1004,12 @@ struct ast_category *ast_category_get(const struct ast_config *config,
return NULL;
}
+struct ast_category *ast_category_get(const struct ast_config *config,
+ const char *category_name, const char *filter)
+{
+ return category_get_sep(config, category_name, filter, ',');
+}
+
const char *ast_category_get_name(const struct ast_category *category)
{
return category->name;
@@ -1125,7 +1133,7 @@ static void ast_includes_destroy(struct ast_config_include *incls)
static struct ast_category *next_available_category(struct ast_category *cat,
const char *name, const char *filter)
{
- for (; cat && !does_category_match(cat, name, filter); cat = cat->next);
+ for (; cat && !does_category_match(cat, name, filter, ','); cat = cat->next);
return cat;
}
@@ -1777,8 +1785,13 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
while ((cur = strsep(&c, ","))) {
if (!strcasecmp(cur, "!")) {
(*cat)->ignored = 1;
- } else if (!strcasecmp(cur, "+")) {
- *cat = ast_category_get(cfg, catname, NULL);
+ } else if (cur[0] == '+') {
+ char *filter = NULL;
+
+ if (cur[1] != ',') {
+ filter = &cur[1];
+ }
+ *cat = category_get_sep(cfg, catname, filter, '&');
if (!(*cat)) {
if (newcat) {
ast_category_destroy(newcat);
diff --git a/main/dial.c b/main/dial.c
index 127f327d1..80247588d 100644
--- a/main/dial.c
+++ b/main/dial.c
@@ -248,22 +248,9 @@ struct ast_dial *ast_dial_create(void)
return dial;
}
-/*! \brief Append a channel
- * \note Appends a channel to a dialing structure
- * \return Returns channel reference number on success, -1 on failure
- */
-int ast_dial_append(struct ast_dial *dial, const char *tech, const char *device, const struct ast_assigned_ids *assignedids)
+static int dial_append_common(struct ast_dial *dial, struct ast_dial_channel *channel,
+ const char *tech, const char *device, const struct ast_assigned_ids *assignedids)
{
- struct ast_dial_channel *channel = NULL;
-
- /* Make sure we have required arguments */
- if (!dial || !tech || !device)
- return -1;
-
- /* Allocate new memory for dialed channel structure */
- if (!(channel = ast_calloc(1, sizeof(*channel))))
- return -1;
-
/* Record technology and device for when we actually dial */
channel->tech = ast_strdup(tech);
channel->device = ast_strdup(device);
@@ -287,6 +274,60 @@ int ast_dial_append(struct ast_dial *dial, const char *tech, const char *device,
AST_LIST_INSERT_TAIL(&dial->channels, channel, list);
return channel->num;
+
+}
+
+/*! \brief Append a channel
+ * \note Appends a channel to a dialing structure
+ * \return Returns channel reference number on success, -1 on failure
+ */
+int ast_dial_append(struct ast_dial *dial, const char *tech, const char *device, const struct ast_assigned_ids *assignedids)
+{
+ struct ast_dial_channel *channel = NULL;
+
+ /* Make sure we have required arguments */
+ if (!dial || !tech || !device)
+ return -1;
+
+ /* Allocate new memory for dialed channel structure */
+ if (!(channel = ast_calloc(1, sizeof(*channel))))
+ return -1;
+
+ return dial_append_common(dial, channel, tech, device, assignedids);
+}
+
+int ast_dial_append_channel(struct ast_dial *dial, struct ast_channel *chan)
+{
+ struct ast_dial_channel *channel;
+ char *tech;
+ char *device;
+ char *dash;
+
+ if (!dial || !chan) {
+ return -1;
+ }
+
+ channel = ast_calloc(1, sizeof(*channel));
+ if (!channel) {
+ return -1;
+ }
+ channel->owner = chan;
+
+ tech = ast_strdupa(ast_channel_name(chan));
+
+ device = strchr(tech, '/');
+ if (!device) {
+ ast_free(channel);
+ return -1;
+ }
+ *device++ = '\0';
+
+ dash = strrchr(device, '-');
+ if (dash) {
+ *dash = '\0';
+ }
+
+ return dial_append_common(dial, channel, tech, device, NULL);
}
/*! \brief Helper function that requests all channels */
@@ -315,27 +356,29 @@ static int begin_dial_prerun(struct ast_dial_channel *channel, struct ast_channe
}
}
- /* Copy device string over */
- ast_copy_string(numsubst, channel->device, sizeof(numsubst));
+ if (!channel->owner) {
+ /* Copy device string over */
+ ast_copy_string(numsubst, channel->device, sizeof(numsubst));
- if (cap && ast_format_cap_count(cap)) {
- cap_request = cap;
- } else if (requester_cap) {
- cap_request = requester_cap;
- } else {
- cap_all_audio = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- ast_format_cap_append_by_type(cap_all_audio, AST_MEDIA_TYPE_AUDIO);
- cap_request = cap_all_audio;
- }
+ if (cap && ast_format_cap_count(cap)) {
+ cap_request = cap;
+ } else if (requester_cap) {
+ cap_request = requester_cap;
+ } else {
+ cap_all_audio = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+ ast_format_cap_append_by_type(cap_all_audio, AST_MEDIA_TYPE_AUDIO);
+ cap_request = cap_all_audio;
+ }
- /* If we fail to create our owner channel bail out */
- if (!(channel->owner = ast_request(channel->tech, cap_request, &assignedids, chan, numsubst, &channel->cause))) {
+ /* If we fail to create our owner channel bail out */
+ if (!(channel->owner = ast_request(channel->tech, cap_request, &assignedids, chan, numsubst, &channel->cause))) {
+ ao2_cleanup(cap_all_audio);
+ return -1;
+ }
+ cap_request = NULL;
+ ao2_cleanup(requester_cap);
ao2_cleanup(cap_all_audio);
- return -1;
}
- cap_request = NULL;
- ao2_cleanup(requester_cap);
- ao2_cleanup(cap_all_audio);
if (chan) {
ast_channel_lock_both(chan, channel->owner);
diff --git a/main/frame.c b/main/frame.c
index ae2120a5a..92b92b6e2 100644
--- a/main/frame.c
+++ b/main/frame.c
@@ -320,11 +320,10 @@ struct ast_frame *ast_frdup(const struct ast_frame *f)
}
out->frametype = f->frametype;
+ out->subclass = f->subclass;
if ((f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_VIDEO) ||
(f->frametype == AST_FRAME_IMAGE)) {
- out->subclass.format = ao2_bump(f->subclass.format);
- } else {
- memcpy(&out->subclass, &f->subclass, sizeof(out->subclass));
+ ao2_bump(out->subclass.format);
}
out->datalen = f->datalen;
out->samples = f->samples;
diff --git a/main/stringfields.c b/main/stringfields.c
new file mode 100644
index 000000000..67dd06c9b
--- /dev/null
+++ b/main/stringfields.c
@@ -0,0 +1,508 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Kevin P. Fleming <kpfleming@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.
+ */
+
+/*! \file
+ *
+ * \brief String fields
+ *
+ * \author Kevin P. Fleming <kpfleming@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_REGISTER_FILE()
+
+#include "asterisk/stringfields.h"
+#include "asterisk/utils.h"
+
+/* this is a little complex... string fields are stored with their
+ allocated size in the bytes preceding the string; even the
+ constant 'empty' string has to be this way, so the code that
+ checks to see if there is enough room for a new string doesn't
+ have to have any special case checks
+*/
+
+static const struct {
+ ast_string_field_allocation allocation;
+ char string[1];
+} __ast_string_field_empty_buffer;
+
+ast_string_field __ast_string_field_empty = __ast_string_field_empty_buffer.string;
+
+#define ALLOCATOR_OVERHEAD 48
+
+static size_t optimal_alloc_size(size_t size)
+{
+ unsigned int count;
+
+ size += ALLOCATOR_OVERHEAD;
+
+ for (count = 1; size; size >>= 1, count++);
+
+ return (1 << count) - ALLOCATOR_OVERHEAD;
+}
+
+static void *calloc_wrapper(unsigned int num_structs, size_t struct_size,
+ const char *file, int lineno, const char *func)
+{
+#if defined(__AST_DEBUG_MALLOC)
+ return __ast_calloc(num_structs, struct_size, file, lineno, func);
+#else
+ return ast_calloc(num_structs, struct_size);
+#endif
+}
+
+/*! \brief add a new block to the pool.
+ * We can only allocate from the topmost pool, so the
+ * fields in *mgr reflect the size of that only.
+ */
+static int add_string_pool(struct ast_string_field_mgr *mgr, struct ast_string_field_pool **pool_head,
+ size_t size, const char *file, int lineno, const char *func)
+{
+ struct ast_string_field_pool *pool;
+ size_t alloc_size = optimal_alloc_size(sizeof(*pool) + size);
+
+ if (!(pool = calloc_wrapper(1, alloc_size, file, lineno, func))) {
+ return -1;
+ }
+
+ pool->prev = *pool_head;
+ pool->size = alloc_size - sizeof(*pool);
+ *pool_head = pool;
+ mgr->last_alloc = NULL;
+
+ return 0;
+}
+
+static void reset_field(const char **p)
+{
+ *p = __ast_string_field_empty;
+}
+
+/*!
+ * \brief Internal cleanup function
+ * \internal
+ * \param mgr
+ * \param pool_head
+ * \param cleanup_type
+ * 0: Reset all string fields and free all pools except the last or embedded pool.
+ * Keep the internal management structures so the structure can be reused.
+ * -1: Reset all string fields and free all pools except the embedded pool.
+ * Free the internal management structures.
+ * \param file
+ * \param lineno
+ * \param func
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int __ast_string_field_free_memory(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, enum ast_stringfield_cleanup_type cleanup_type,
+ const char *file, int lineno, const char *func)
+{
+ struct ast_string_field_pool *cur = NULL;
+ struct ast_string_field_pool *preserve = NULL;
+
+ if (!mgr->header) {
+ return -1;
+ }
+
+ /* reset all the fields regardless of cleanup type */
+ AST_VECTOR_CALLBACK_VOID(&mgr->header->string_fields, reset_field);
+
+ switch (cleanup_type) {
+ case AST_STRINGFIELD_DESTROY:
+ AST_VECTOR_FREE(&mgr->header->string_fields);
+
+ if (mgr->header->embedded_pool) { /* ALWAYS preserve the embedded pool if there is one */
+ preserve = mgr->header->embedded_pool;
+ preserve->used = preserve->active = 0;
+ }
+
+ ast_free(mgr->header);
+ mgr->header = NULL;
+ break;
+ case AST_STRINGFIELD_RESET:
+ /* Preserve the embedded pool if there is one, otherwise the last pool */
+ if (mgr->header->embedded_pool) {
+ preserve = mgr->header->embedded_pool;
+ } else {
+ if (*pool_head == NULL) {
+ ast_log(LOG_WARNING, "trying to reset empty pool\n");
+ return -1;
+ }
+ preserve = *pool_head;
+ }
+ preserve->used = preserve->active = 0;
+ break;
+ default:
+ return -1;
+ }
+
+ cur = *pool_head;
+ while (cur) {
+ struct ast_string_field_pool *prev = cur->prev;
+
+ if (cur != preserve) {
+ ast_free(cur);
+ }
+ cur = prev;
+ }
+
+ *pool_head = preserve;
+ if (preserve) {
+ preserve->prev = NULL;
+ }
+
+ return 0;
+}
+
+/*!
+ * \brief Internal initialization function
+ * \internal
+ * \param mgr
+ * \param pool_head
+ * \param needed
+ * \param file
+ * \param lineno
+ * \param func
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int __ast_string_field_init(struct ast_string_field_mgr *mgr, struct ast_string_field_pool **pool_head,
+ int needed, const char *file, int lineno, const char *func)
+{
+ const char **p = (const char **) pool_head + 1;
+ size_t initial_vector_size = ((size_t) (((char *)mgr) - ((char *)p))) / sizeof(*p);
+
+ if (needed <= 0) {
+ return __ast_string_field_free_memory(mgr, pool_head, needed, file, lineno, func);
+ }
+
+ mgr->last_alloc = NULL;
+#if defined(__AST_DEBUG_MALLOC)
+ mgr->owner_file = file;
+ mgr->owner_func = func;
+ mgr->owner_line = lineno;
+#endif
+
+ if (!(mgr->header = calloc_wrapper(1, sizeof(*mgr->header), file, lineno, func))) {
+ return -1;
+ }
+
+ if (AST_VECTOR_INIT(&mgr->header->string_fields, initial_vector_size)) {
+ ast_free(mgr->header);
+ mgr->header = NULL;
+ return -1;
+ }
+
+ while ((struct ast_string_field_mgr *) p != mgr) {
+ AST_VECTOR_APPEND(&mgr->header->string_fields, p);
+ *p++ = __ast_string_field_empty;
+ }
+
+ *pool_head = NULL;
+ mgr->header->embedded_pool = NULL;
+ if (add_string_pool(mgr, pool_head, needed, file, lineno, func)) {
+ AST_VECTOR_FREE(&mgr->header->string_fields);
+ ast_free(mgr->header);
+ mgr->header = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, size_t needed)
+{
+ char *result = NULL;
+ size_t space = (*pool_head)->size - (*pool_head)->used;
+ size_t to_alloc;
+
+ /* Make room for ast_string_field_allocation and make it a multiple of that. */
+ to_alloc = ast_make_room_for(needed, ast_string_field_allocation);
+ ast_assert(to_alloc % ast_alignof(ast_string_field_allocation) == 0);
+
+ if (__builtin_expect(to_alloc > space, 0)) {
+ size_t new_size = (*pool_head)->size;
+
+ while (new_size < to_alloc) {
+ new_size *= 2;
+ }
+
+#if defined(__AST_DEBUG_MALLOC)
+ if (add_string_pool(mgr, pool_head, new_size, mgr->owner_file, mgr->owner_line, mgr->owner_func))
+ return NULL;
+#else
+ if (add_string_pool(mgr, pool_head, new_size, __FILE__, __LINE__, __FUNCTION__))
+ return NULL;
+#endif
+ }
+
+ /* pool->base is always aligned (gcc aligned attribute). We ensure that
+ * to_alloc is also a multiple of ast_alignof(ast_string_field_allocation)
+ * causing result to always be aligned as well; which in turn fixes that
+ * AST_STRING_FIELD_ALLOCATION(result) is aligned. */
+ result = (*pool_head)->base + (*pool_head)->used;
+ (*pool_head)->used += to_alloc;
+ (*pool_head)->active += needed;
+ result += ast_alignof(ast_string_field_allocation);
+ AST_STRING_FIELD_ALLOCATION(result) = needed;
+ mgr->last_alloc = result;
+
+ return result;
+}
+
+int __ast_string_field_ptr_grow(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, size_t needed, const ast_string_field *ptr)
+{
+ ssize_t grow = needed - AST_STRING_FIELD_ALLOCATION(*ptr);
+ size_t space = (*pool_head)->size - (*pool_head)->used;
+
+ if (*ptr != mgr->last_alloc) {
+ return 1;
+ }
+
+ if (space < grow) {
+ return 1;
+ }
+
+ (*pool_head)->used += grow;
+ (*pool_head)->active += grow;
+ AST_STRING_FIELD_ALLOCATION(*ptr) += grow;
+
+ return 0;
+}
+
+void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
+ const ast_string_field ptr)
+{
+ struct ast_string_field_pool *pool, *prev;
+
+ if (ptr == __ast_string_field_empty) {
+ return;
+ }
+
+ for (pool = pool_head, prev = NULL; pool; prev = pool, pool = pool->prev) {
+ if ((ptr >= pool->base) && (ptr <= (pool->base + pool->size))) {
+ pool->active -= AST_STRING_FIELD_ALLOCATION(ptr);
+ if (pool->active == 0) {
+ if (prev) {
+ prev->prev = pool->prev;
+ ast_free(pool);
+ } else {
+ pool->used = 0;
+ }
+ }
+ break;
+ }
+ }
+}
+
+void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, ast_string_field *ptr,
+ const char *format, va_list ap)
+{
+ size_t needed;
+ size_t available;
+ size_t space = (*pool_head)->size - (*pool_head)->used;
+ int res;
+ ssize_t grow;
+ char *target;
+ va_list ap2;
+
+ /* if the field already has space allocated, try to reuse it;
+ otherwise, try to use the empty space at the end of the current
+ pool
+ */
+ if (*ptr != __ast_string_field_empty) {
+ target = (char *) *ptr;
+ available = AST_STRING_FIELD_ALLOCATION(*ptr);
+ if (*ptr == mgr->last_alloc) {
+ available += space;
+ }
+ } else {
+ /* pool->used is always a multiple of ast_alignof(ast_string_field_allocation)
+ * so we don't need to re-align anything here.
+ */
+ target = (*pool_head)->base + (*pool_head)->used + ast_alignof(ast_string_field_allocation);
+ if (space > ast_alignof(ast_string_field_allocation)) {
+ available = space - ast_alignof(ast_string_field_allocation);
+ } else {
+ available = 0;
+ }
+ }
+
+ va_copy(ap2, ap);
+ res = vsnprintf(target, available, format, ap2);
+ va_end(ap2);
+
+ if (res < 0) {
+ /* Are we out of memory? */
+ return;
+ }
+ if (res == 0) {
+ __ast_string_field_release_active(*pool_head, *ptr);
+ *ptr = __ast_string_field_empty;
+ return;
+ }
+ needed = (size_t)res + 1; /* NUL byte */
+
+ if (needed > available) {
+ /* the allocation could not be satisfied using the field's current allocation
+ (if it has one), or the space available in the pool (if it does not). allocate
+ space for it, adding a new string pool if necessary.
+ */
+ if (!(target = (char *) __ast_string_field_alloc_space(mgr, pool_head, needed))) {
+ return;
+ }
+ vsprintf(target, format, ap);
+ va_end(ap); /* XXX va_end without va_start? */
+ __ast_string_field_release_active(*pool_head, *ptr);
+ *ptr = target;
+ } else if (*ptr != target) {
+ /* the allocation was satisfied using available space in the pool, but not
+ using the space already allocated to the field
+ */
+ __ast_string_field_release_active(*pool_head, *ptr);
+ mgr->last_alloc = *ptr = target;
+ ast_assert(needed < (ast_string_field_allocation)-1);
+ AST_STRING_FIELD_ALLOCATION(target) = (ast_string_field_allocation)needed;
+ (*pool_head)->used += ast_make_room_for(needed, ast_string_field_allocation);
+ (*pool_head)->active += needed;
+ } else if ((grow = (needed - AST_STRING_FIELD_ALLOCATION(*ptr))) > 0) {
+ /* the allocation was satisfied by using available space in the pool *and*
+ the field was the last allocated field from the pool, so it grew
+ */
+ AST_STRING_FIELD_ALLOCATION(*ptr) += grow;
+ (*pool_head)->used += ast_align_for(grow, ast_string_field_allocation);
+ (*pool_head)->active += grow;
+ }
+}
+
+void __ast_string_field_ptr_build(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, ast_string_field *ptr, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ __ast_string_field_ptr_build_va(mgr, pool_head, ptr, format, ap);
+ va_end(ap);
+}
+
+void *__ast_calloc_with_stringfields(unsigned int num_structs, size_t struct_size,
+ size_t field_mgr_offset, size_t field_mgr_pool_offset, size_t pool_size, const char *file,
+ int lineno, const char *func)
+{
+ struct ast_string_field_mgr *mgr;
+ struct ast_string_field_pool *pool;
+ struct ast_string_field_pool **pool_head;
+ size_t pool_size_needed = sizeof(*pool) + pool_size;
+ size_t size_to_alloc = optimal_alloc_size(struct_size + pool_size_needed);
+ void *allocation;
+ const char **p;
+ size_t initial_vector_size;
+
+ ast_assert(num_structs == 1);
+
+ if (!(allocation = calloc_wrapper(num_structs, size_to_alloc, file, lineno, func))) {
+ return NULL;
+ }
+
+ mgr = allocation + field_mgr_offset;
+
+ /*
+ * The header is calloced in __ast_string_field_init so it also gets calloced here
+ * so __ast_string_fields_free_memory can always just free mgr->header.
+ */
+ mgr->header = calloc_wrapper(1, sizeof(*mgr->header), file, lineno, func);
+ if (!mgr->header) {
+ ast_free(allocation);
+ return NULL;
+ }
+
+ pool = allocation + struct_size;
+ pool_head = allocation + field_mgr_pool_offset;
+ p = (const char **) pool_head + 1;
+ initial_vector_size = ((size_t) (((char *)mgr) - ((char *)p))) / sizeof(*p);
+
+ if (AST_VECTOR_INIT(&mgr->header->string_fields, initial_vector_size)) {
+ ast_free(mgr->header);
+ ast_free(allocation);
+ return NULL;
+ }
+
+ while ((struct ast_string_field_mgr *) p != mgr) {
+ AST_VECTOR_APPEND(&mgr->header->string_fields, p);
+ *p++ = __ast_string_field_empty;
+ }
+
+ mgr->header->embedded_pool = pool;
+ *pool_head = pool;
+ pool->size = size_to_alloc - struct_size - sizeof(*pool);
+#if defined(__AST_DEBUG_MALLOC)
+ mgr->owner_file = file;
+ mgr->owner_func = func;
+ mgr->owner_line = lineno;
+#endif
+
+ return allocation;
+}
+
+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;
+}
+
+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->header->string_fields);
+ struct ast_string_field_vector *src = &(orig_mgr->header->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;
+}
diff --git a/main/utils.c b/main/utils.c
index e92f5c3d9..09839752b 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -61,9 +61,6 @@ ASTERISK_REGISTER_FILE()
#include "asterisk/time.h"
#define AST_API_MODULE /* ensure that inlinable API functions will be built in this module if required */
-#include "asterisk/stringfields.h"
-
-#define AST_API_MODULE /* ensure that inlinable API functions will be built in this module if required */
#include "asterisk/utils.h"
#define AST_API_MODULE
@@ -2023,374 +2020,6 @@ char *ast_to_camel_case_delim(const char *s, const char *delim)
return res;
}
-/*
- * stringfields support routines.
- */
-
-/* this is a little complex... string fields are stored with their
- allocated size in the bytes preceding the string; even the
- constant 'empty' string has to be this way, so the code that
- checks to see if there is enough room for a new string doesn't
- have to have any special case checks
-*/
-
-static const struct {
- ast_string_field_allocation allocation;
- char string[1];
-} __ast_string_field_empty_buffer;
-
-ast_string_field __ast_string_field_empty = __ast_string_field_empty_buffer.string;
-
-#define ALLOCATOR_OVERHEAD 48
-
-static size_t optimal_alloc_size(size_t size)
-{
- unsigned int count;
-
- size += ALLOCATOR_OVERHEAD;
-
- for (count = 1; size; size >>= 1, count++);
-
- return (1 << count) - ALLOCATOR_OVERHEAD;
-}
-
-/*! \brief add a new block to the pool.
- * We can only allocate from the topmost pool, so the
- * fields in *mgr reflect the size of that only.
- */
-static int add_string_pool(struct ast_string_field_mgr *mgr, struct ast_string_field_pool **pool_head,
- size_t size, const char *file, int lineno, const char *func)
-{
- struct ast_string_field_pool *pool;
- size_t alloc_size = optimal_alloc_size(sizeof(*pool) + size);
-
-#if defined(__AST_DEBUG_MALLOC)
- if (!(pool = __ast_calloc(1, alloc_size, file, lineno, func))) {
- return -1;
- }
-#else
- if (!(pool = ast_calloc(1, alloc_size))) {
- return -1;
- }
-#endif
-
- pool->prev = *pool_head;
- pool->size = alloc_size - sizeof(*pool);
- *pool_head = pool;
- mgr->last_alloc = NULL;
-
- return 0;
-}
-
-/*
- * This is an internal API, code should not use it directly.
- * It initializes all fields as empty, then uses 'size' for 3 functions:
- * size > 0 means initialize the pool list with a pool of given size.
- * This must be called right after allocating the object.
- * size = 0 means release all pools except the most recent one.
- * If the first pool was allocated via embedding in another
- * object, that pool will be preserved instead.
- * This is useful to e.g. reset an object to the initial value.
- * size < 0 means release all pools.
- * This must be done before destroying the object.
- */
-int __ast_string_field_init(struct ast_string_field_mgr *mgr, struct ast_string_field_pool **pool_head,
- int needed, const char *file, int lineno, const char *func)
-{
- const char **p = (const char **) pool_head + 1;
- struct ast_string_field_pool *cur = NULL;
- struct ast_string_field_pool *preserve = NULL;
-
- /* clear fields - this is always necessary */
- while ((struct ast_string_field_mgr *) p != mgr) {
- *p++ = __ast_string_field_empty;
- }
-
- mgr->last_alloc = NULL;
-#if defined(__AST_DEBUG_MALLOC)
- mgr->owner_file = file;
- mgr->owner_func = func;
- mgr->owner_line = lineno;
-#endif
- if (needed > 0) { /* allocate the initial pool */
- *pool_head = NULL;
- mgr->embedded_pool = NULL;
- return add_string_pool(mgr, pool_head, needed, file, lineno, func);
- }
-
- /* if there is an embedded pool, we can't actually release *all*
- * pools, we must keep the embedded one. if the caller is about
- * to free the structure that contains the stringfield manager
- * and embedded pool anyway, it will be freed as part of that
- * operation.
- */
- if ((needed < 0) && mgr->embedded_pool) {
- needed = 0;
- }
-
- if (needed < 0) { /* reset all pools */
- cur = *pool_head;
- } else if (mgr->embedded_pool) { /* preserve the embedded pool */
- preserve = mgr->embedded_pool;
- cur = *pool_head;
- } else { /* preserve the last pool */
- if (*pool_head == NULL) {
- ast_log(LOG_WARNING, "trying to reset empty pool\n");
- return -1;
- }
- preserve = *pool_head;
- cur = preserve->prev;
- }
-
- if (preserve) {
- preserve->prev = NULL;
- preserve->used = preserve->active = 0;
- }
-
- while (cur) {
- struct ast_string_field_pool *prev = cur->prev;
-
- if (cur != preserve) {
- ast_free(cur);
- }
- cur = prev;
- }
-
- *pool_head = preserve;
-
- return 0;
-}
-
-ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr,
- struct ast_string_field_pool **pool_head, size_t needed)
-{
- char *result = NULL;
- size_t space = (*pool_head)->size - (*pool_head)->used;
- size_t to_alloc;
-
- /* Make room for ast_string_field_allocation and make it a multiple of that. */
- to_alloc = ast_make_room_for(needed, ast_string_field_allocation);
- ast_assert(to_alloc % ast_alignof(ast_string_field_allocation) == 0);
-
- if (__builtin_expect(to_alloc > space, 0)) {
- size_t new_size = (*pool_head)->size;
-
- while (new_size < to_alloc) {
- new_size *= 2;
- }
-
-#if defined(__AST_DEBUG_MALLOC)
- if (add_string_pool(mgr, pool_head, new_size, mgr->owner_file, mgr->owner_line, mgr->owner_func))
- return NULL;
-#else
- if (add_string_pool(mgr, pool_head, new_size, __FILE__, __LINE__, __FUNCTION__))
- return NULL;
-#endif
- }
-
- /* pool->base is always aligned (gcc aligned attribute). We ensure that
- * to_alloc is also a multiple of ast_alignof(ast_string_field_allocation)
- * causing result to always be aligned as well; which in turn fixes that
- * AST_STRING_FIELD_ALLOCATION(result) is aligned. */
- result = (*pool_head)->base + (*pool_head)->used;
- (*pool_head)->used += to_alloc;
- (*pool_head)->active += needed;
- result += ast_alignof(ast_string_field_allocation);
- AST_STRING_FIELD_ALLOCATION(result) = needed;
- mgr->last_alloc = result;
-
- return result;
-}
-
-int __ast_string_field_ptr_grow(struct ast_string_field_mgr *mgr,
- struct ast_string_field_pool **pool_head, size_t needed,
- const ast_string_field *ptr)
-{
- ssize_t grow = needed - AST_STRING_FIELD_ALLOCATION(*ptr);
- size_t space = (*pool_head)->size - (*pool_head)->used;
-
- if (*ptr != mgr->last_alloc) {
- return 1;
- }
-
- if (space < grow) {
- return 1;
- }
-
- (*pool_head)->used += grow;
- (*pool_head)->active += grow;
- AST_STRING_FIELD_ALLOCATION(*ptr) += grow;
-
- return 0;
-}
-
-void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
- const ast_string_field ptr)
-{
- struct ast_string_field_pool *pool, *prev;
-
- if (ptr == __ast_string_field_empty) {
- return;
- }
-
- for (pool = pool_head, prev = NULL; pool; prev = pool, pool = pool->prev) {
- if ((ptr >= pool->base) && (ptr <= (pool->base + pool->size))) {
- pool->active -= AST_STRING_FIELD_ALLOCATION(ptr);
- if (pool->active == 0) {
- if (prev) {
- prev->prev = pool->prev;
- ast_free(pool);
- } else {
- pool->used = 0;
- }
- }
- break;
- }
- }
-}
-
-void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
- struct ast_string_field_pool **pool_head,
- ast_string_field *ptr, const char *format, va_list ap)
-{
- size_t needed;
- size_t available;
- size_t space = (*pool_head)->size - (*pool_head)->used;
- int res;
- ssize_t grow;
- char *target;
- va_list ap2;
-
- /* if the field already has space allocated, try to reuse it;
- otherwise, try to use the empty space at the end of the current
- pool
- */
- if (*ptr != __ast_string_field_empty) {
- target = (char *) *ptr;
- available = AST_STRING_FIELD_ALLOCATION(*ptr);
- if (*ptr == mgr->last_alloc) {
- available += space;
- }
- } else {
- /* pool->used is always a multiple of ast_alignof(ast_string_field_allocation)
- * so we don't need to re-align anything here.
- */
- target = (*pool_head)->base + (*pool_head)->used + ast_alignof(ast_string_field_allocation);
- if (space > ast_alignof(ast_string_field_allocation)) {
- available = space - ast_alignof(ast_string_field_allocation);
- } else {
- available = 0;
- }
- }
-
- va_copy(ap2, ap);
- res = vsnprintf(target, available, format, ap2);
- va_end(ap2);
-
- if (res < 0) {
- /* Are we out of memory? */
- return;
- }
- if (res == 0) {
- __ast_string_field_release_active(*pool_head, *ptr);
- *ptr = __ast_string_field_empty;
- return;
- }
- needed = (size_t)res + 1; /* NUL byte */
-
- if (needed > available) {
- /* the allocation could not be satisfied using the field's current allocation
- (if it has one), or the space available in the pool (if it does not). allocate
- space for it, adding a new string pool if necessary.
- */
- if (!(target = (char *) __ast_string_field_alloc_space(mgr, pool_head, needed))) {
- return;
- }
- vsprintf(target, format, ap);
- va_end(ap); /* XXX va_end without va_start? */
- __ast_string_field_release_active(*pool_head, *ptr);
- *ptr = target;
- } else if (*ptr != target) {
- /* the allocation was satisfied using available space in the pool, but not
- using the space already allocated to the field
- */
- __ast_string_field_release_active(*pool_head, *ptr);
- mgr->last_alloc = *ptr = target;
- ast_assert(needed < (ast_string_field_allocation)-1);
- AST_STRING_FIELD_ALLOCATION(target) = (ast_string_field_allocation)needed;
- (*pool_head)->used += ast_make_room_for(needed, ast_string_field_allocation);
- (*pool_head)->active += needed;
- } else if ((grow = (needed - AST_STRING_FIELD_ALLOCATION(*ptr))) > 0) {
- /* the allocation was satisfied by using available space in the pool *and*
- the field was the last allocated field from the pool, so it grew
- */
- AST_STRING_FIELD_ALLOCATION(*ptr) += grow;
- (*pool_head)->used += ast_align_for(grow, ast_string_field_allocation);
- (*pool_head)->active += grow;
- }
-}
-
-void __ast_string_field_ptr_build(struct ast_string_field_mgr *mgr,
- struct ast_string_field_pool **pool_head,
- ast_string_field *ptr, const char *format, ...)
-{
- va_list ap;
-
- va_start(ap, format);
- __ast_string_field_ptr_build_va(mgr, pool_head, ptr, format, ap);
- va_end(ap);
-}
-
-void *__ast_calloc_with_stringfields(unsigned int num_structs, size_t struct_size, size_t field_mgr_offset,
- size_t field_mgr_pool_offset, size_t pool_size, const char *file,
- int lineno, const char *func)
-{
- struct ast_string_field_mgr *mgr;
- struct ast_string_field_pool *pool;
- struct ast_string_field_pool **pool_head;
- size_t pool_size_needed = sizeof(*pool) + pool_size;
- size_t size_to_alloc = optimal_alloc_size(struct_size + pool_size_needed);
- void *allocation;
- unsigned int x;
-
-#if defined(__AST_DEBUG_MALLOC)
- if (!(allocation = __ast_calloc(num_structs, size_to_alloc, file, lineno, func))) {
- return NULL;
- }
-#else
- if (!(allocation = ast_calloc(num_structs, size_to_alloc))) {
- return NULL;
- }
-#endif
-
- for (x = 0; x < num_structs; x++) {
- void *base = allocation + (size_to_alloc * x);
- const char **p;
-
- mgr = base + field_mgr_offset;
- pool_head = base + field_mgr_pool_offset;
- pool = base + struct_size;
-
- p = (const char **) pool_head + 1;
- while ((struct ast_string_field_mgr *) p != mgr) {
- *p++ = __ast_string_field_empty;
- }
-
- mgr->embedded_pool = pool;
- *pool_head = pool;
- pool->size = size_to_alloc - struct_size - sizeof(*pool);
-#if defined(__AST_DEBUG_MALLOC)
- mgr->owner_file = file;
- mgr->owner_func = func;
- mgr->owner_line = lineno;
-#endif
- }
-
- return allocation;
-}
-
-/* end of stringfields support */
-
AST_MUTEX_DEFINE_STATIC(fetchadd_m); /* used for all fetc&add ops */
int ast_atomic_fetchadd_int_slow(volatile int *p, int v)
diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c
index 297560175..c838bc39c 100644
--- a/res/ari/resource_channels.c
+++ b/res/ari/resource_channels.c
@@ -1461,3 +1461,180 @@ void ast_ari_channels_snoop_channel_with_id(struct ast_variable *headers,
args->snoop_id,
response);
}
+
+struct ari_channel_thread_data {
+ struct ast_channel *chan;
+ struct ast_str *stasis_stuff;
+};
+
+static void chan_data_destroy(struct ari_channel_thread_data *chan_data)
+{
+ ast_free(chan_data->stasis_stuff);
+ ast_hangup(chan_data->chan);
+ ast_free(chan_data);
+}
+
+/*!
+ * \brief Thread that owns stasis-created channel.
+ *
+ * The channel enters into a Stasis application immediately upon creation. In this
+ * way, the channel can be manipulated by the Stasis application. Once the channel
+ * exits the Stasis application, it is hung up.
+ */
+static void *ari_channel_thread(void *data)
+{
+ struct ari_channel_thread_data *chan_data = data;
+ struct ast_app *stasis_app;
+
+ stasis_app = pbx_findapp("Stasis");
+ if (!stasis_app) {
+ ast_log(LOG_ERROR, "Stasis dialplan application is not registered");
+ chan_data_destroy(chan_data);
+ return NULL;
+ }
+
+ pbx_exec(chan_data->chan, stasis_app, ast_str_buffer(chan_data->stasis_stuff));
+
+ chan_data_destroy(chan_data);
+
+ return NULL;
+}
+
+void ast_ari_channels_create(struct ast_variable *headers,
+ struct ast_ari_channels_create_args *args,
+ struct ast_ari_response *response)
+{
+ struct ast_assigned_ids assignedids = {
+ .uniqueid = args->channel_id,
+ .uniqueid2 = args->other_channel_id,
+ };
+ struct ari_channel_thread_data *chan_data;
+ struct ast_channel_snapshot *snapshot;
+ pthread_t thread;
+ char *dialtech;
+ char dialdevice[AST_CHANNEL_NAME];
+ char *stuff;
+ int cause;
+ struct ast_format_cap *request_cap;
+ struct ast_channel *originator;
+
+ chan_data = ast_calloc(1, sizeof(*chan_data));
+ if (!chan_data) {
+ ast_ari_response_alloc_failed(response);
+ return;
+ }
+
+ chan_data->stasis_stuff = ast_str_create(32);
+ if (!chan_data->stasis_stuff) {
+ ast_ari_response_alloc_failed(response);
+ chan_data_destroy(chan_data);
+ return;
+ }
+
+ ast_str_append(&chan_data->stasis_stuff, 0, "%s", args->app);
+ if (!ast_strlen_zero(args->app_args)) {
+ ast_str_append(&chan_data->stasis_stuff, 0, ",%s", args->app_args);
+ }
+
+ dialtech = ast_strdupa(args->endpoint);
+ if ((stuff = strchr(dialtech, '/'))) {
+ *stuff++ = '\0';
+ ast_copy_string(dialdevice, stuff, sizeof(dialdevice));
+ }
+
+ originator = ast_channel_get_by_name(args->originator);
+ if (originator) {
+ request_cap = ao2_bump(ast_channel_nativeformats(originator));
+ } else {
+ request_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+ ast_format_cap_append_by_type(request_cap, AST_MEDIA_TYPE_AUDIO);
+ }
+
+ chan_data->chan = ast_request(dialtech, request_cap, &assignedids, originator, dialdevice, &cause);
+ ao2_cleanup(request_cap);
+ ast_channel_cleanup(originator);
+ if (!chan_data->chan) {
+ ast_ari_response_alloc_failed(response);
+ chan_data_destroy(chan_data);
+ return;
+ }
+
+ snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan_data->chan));
+
+ if (ast_pthread_create_detached(&thread, NULL, ari_channel_thread, chan_data)) {
+ ast_ari_response_alloc_failed(response);
+ chan_data_destroy(chan_data);
+ } else {
+ ast_ari_response_ok(response, ast_channel_snapshot_to_json(snapshot, NULL));
+ }
+
+ ao2_ref(snapshot, -1);
+}
+
+void ast_ari_channels_dial(struct ast_variable *headers,
+ struct ast_ari_channels_dial_args *args,
+ struct ast_ari_response *response)
+{
+ RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
+ RAII_VAR(struct ast_channel *, caller, NULL, ast_channel_cleanup);
+ struct ast_channel *callee;
+ struct ast_dial *dial;
+
+ control = find_control(response, args->channel_id);
+ if (control == NULL) {
+ /* Response filled in by find_control */
+ return;
+ }
+
+ caller = ast_channel_get_by_name(args->caller);
+
+ callee = ast_channel_get_by_name(args->channel_id);
+ if (!callee) {
+ ast_ari_response_error(response, 404, "Not Found",
+ "Callee not found");
+ return;
+ }
+
+ if (ast_channel_state(callee) != AST_STATE_DOWN) {
+ ast_channel_unref(callee);
+ ast_ari_response_error(response, 409, "Conflict",
+ "Channel is not in the 'Down' state");
+ return;
+ }
+
+ dial = ast_dial_create();
+ if (!dial) {
+ ast_channel_unref(callee);
+ ast_ari_response_alloc_failed(response);
+ return;
+ }
+
+ if (ast_dial_append_channel(dial, callee) < 0) {
+ ast_channel_unref(callee);
+ ast_dial_destroy(dial);
+ ast_ari_response_alloc_failed(response);
+ return;
+ }
+
+ /* From this point, we don't have to unref the callee channel on
+ * failure paths because the dial owns the reference to the called
+ * channel and will unref the channel for us
+ */
+
+ if (ast_dial_prerun(dial, caller, NULL)) {
+ ast_dial_destroy(dial);
+ ast_ari_response_alloc_failed(response);
+ return;
+ }
+
+ ast_dial_set_user_data(dial, control);
+ ast_dial_set_global_timeout(dial, args->timeout * 1000);
+
+ if (stasis_app_control_dial(control, dial)) {
+ ast_dial_destroy(dial);
+ ast_ari_response_alloc_failed(response);
+ return;
+ }
+
+ ast_ari_response_no_content(response);
+}
diff --git a/res/ari/resource_channels.h b/res/ari/resource_channels.h
index 4d3ad5f8b..89b466d00 100644
--- a/res/ari/resource_channels.h
+++ b/res/ari/resource_channels.h
@@ -100,6 +100,40 @@ int ast_ari_channels_originate_parse_body(
* \param[out] response HTTP response
*/
void ast_ari_channels_originate(struct ast_variable *headers, struct ast_ari_channels_originate_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_channels_create() */
+struct ast_ari_channels_create_args {
+ /*! Endpoint for channel communication */
+ const char *endpoint;
+ /*! Stasis Application to place channel into */
+ const char *app;
+ /*! The application arguments to pass to the Stasis application provided by 'app'. Mutually exclusive with 'context', 'extension', 'priority', and 'label'. */
+ const char *app_args;
+ /*! The unique id to assign the channel on creation. */
+ const char *channel_id;
+ /*! The unique id to assign the second channel when using local channels. */
+ const char *other_channel_id;
+ /*! Unique ID of the calling channel */
+ const char *originator;
+};
+/*!
+ * \brief Body parsing function for /channels/create.
+ * \param body The JSON body from which to parse parameters.
+ * \param[out] args The args structure to parse into.
+ * \retval zero on success
+ * \retval non-zero on failure
+ */
+int ast_ari_channels_create_parse_body(
+ struct ast_json *body,
+ struct ast_ari_channels_create_args *args);
+
+/*!
+ * \brief Create channel.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_channels_create(struct ast_variable *headers, struct ast_ari_channels_create_args *args, struct ast_ari_response *response);
/*! Argument struct for ast_ari_channels_get() */
struct ast_ari_channels_get_args {
/*! Channel's id */
@@ -705,5 +739,33 @@ int ast_ari_channels_snoop_channel_with_id_parse_body(
* \param[out] response HTTP response
*/
void ast_ari_channels_snoop_channel_with_id(struct ast_variable *headers, struct ast_ari_channels_snoop_channel_with_id_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_channels_dial() */
+struct ast_ari_channels_dial_args {
+ /*! Channel's id */
+ const char *channel_id;
+ /*! Channel ID of caller */
+ const char *caller;
+ /*! Dial timeout */
+ int timeout;
+};
+/*!
+ * \brief Body parsing function for /channels/{channelId}/dial.
+ * \param body The JSON body from which to parse parameters.
+ * \param[out] args The args structure to parse into.
+ * \retval zero on success
+ * \retval non-zero on failure
+ */
+int ast_ari_channels_dial_parse_body(
+ struct ast_json *body,
+ struct ast_ari_channels_dial_args *args);
+
+/*!
+ * \brief Dial a created channel.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_channels_dial(struct ast_variable *headers, struct ast_ari_channels_dial_args *args, struct ast_ari_response *response);
#endif /* _ASTERISK_RESOURCE_CHANNELS_H */
diff --git a/res/res_ari_channels.c b/res/res_ari_channels.c
index 05d8d0714..1f0818170 100644
--- a/res/res_ari_channels.c
+++ b/res/res_ari_channels.c
@@ -268,6 +268,131 @@ static void ast_ari_channels_originate_cb(
fin: __attribute__((unused))
return;
}
+int ast_ari_channels_create_parse_body(
+ struct ast_json *body,
+ struct ast_ari_channels_create_args *args)
+{
+ struct ast_json *field;
+ /* Parse query parameters out of it */
+ field = ast_json_object_get(body, "endpoint");
+ if (field) {
+ args->endpoint = ast_json_string_get(field);
+ }
+ field = ast_json_object_get(body, "app");
+ if (field) {
+ args->app = ast_json_string_get(field);
+ }
+ field = ast_json_object_get(body, "appArgs");
+ if (field) {
+ args->app_args = ast_json_string_get(field);
+ }
+ field = ast_json_object_get(body, "channelId");
+ if (field) {
+ args->channel_id = ast_json_string_get(field);
+ }
+ field = ast_json_object_get(body, "otherChannelId");
+ if (field) {
+ args->other_channel_id = ast_json_string_get(field);
+ }
+ field = ast_json_object_get(body, "originator");
+ if (field) {
+ args->originator = ast_json_string_get(field);
+ }
+ return 0;
+}
+
+/*!
+ * \brief Parameter parsing callback for /channels/create.
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_channels_create_cb(
+ struct ast_tcptls_session_instance *ser,
+ struct ast_variable *get_params, struct ast_variable *path_vars,
+ struct ast_variable *headers, struct ast_ari_response *response)
+{
+ struct ast_ari_channels_create_args args = {};
+ struct ast_variable *i;
+ RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+ int is_valid;
+ int code;
+#endif /* AST_DEVMODE */
+
+ for (i = get_params; i; i = i->next) {
+ if (strcmp(i->name, "endpoint") == 0) {
+ args.endpoint = (i->value);
+ } else
+ if (strcmp(i->name, "app") == 0) {
+ args.app = (i->value);
+ } else
+ if (strcmp(i->name, "appArgs") == 0) {
+ args.app_args = (i->value);
+ } else
+ if (strcmp(i->name, "channelId") == 0) {
+ args.channel_id = (i->value);
+ } else
+ if (strcmp(i->name, "otherChannelId") == 0) {
+ args.other_channel_id = (i->value);
+ } else
+ if (strcmp(i->name, "originator") == 0) {
+ args.originator = (i->value);
+ } else
+ {}
+ }
+ /* Look for a JSON request entity */
+ body = ast_http_get_json(ser, headers);
+ if (!body) {
+ switch (errno) {
+ case EFBIG:
+ ast_ari_response_error(response, 413, "Request Entity Too Large", "Request body too large");
+ goto fin;
+ case ENOMEM:
+ ast_ari_response_error(response, 500, "Internal Server Error", "Error processing request");
+ goto fin;
+ case EIO:
+ ast_ari_response_error(response, 400, "Bad Request", "Error parsing request body");
+ goto fin;
+ }
+ }
+ if (ast_ari_channels_create_parse_body(body, &args)) {
+ ast_ari_response_alloc_failed(response);
+ goto fin;
+ }
+ ast_ari_channels_create(headers, &args, response);
+#if defined(AST_DEVMODE)
+ code = response->response_code;
+
+ switch (code) {
+ case 0: /* Implementation is still a stub, or the code wasn't set */
+ is_valid = response->message == NULL;
+ break;
+ case 500: /* Internal Server Error */
+ case 501: /* Not Implemented */
+ is_valid = 1;
+ break;
+ default:
+ if (200 <= code && code <= 299) {
+ is_valid = ast_ari_validate_channel(
+ response->message);
+ } else {
+ ast_log(LOG_ERROR, "Invalid error response %d for /channels/create\n", code);
+ is_valid = 0;
+ }
+ }
+
+ if (!is_valid) {
+ ast_log(LOG_ERROR, "Response validation failed for /channels/create\n");
+ ast_ari_response_error(response, 500,
+ "Internal Server Error", "Response validation failed");
+ }
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+ return;
+}
/*!
* \brief Parameter parsing callback for /channels/{channelId}.
* \param get_params GET parameters in the HTTP request.
@@ -2552,8 +2677,122 @@ static void ast_ari_channels_snoop_channel_with_id_cb(
fin: __attribute__((unused))
return;
}
+int ast_ari_channels_dial_parse_body(
+ struct ast_json *body,
+ struct ast_ari_channels_dial_args *args)
+{
+ struct ast_json *field;
+ /* Parse query parameters out of it */
+ field = ast_json_object_get(body, "caller");
+ if (field) {
+ args->caller = ast_json_string_get(field);
+ }
+ field = ast_json_object_get(body, "timeout");
+ if (field) {
+ args->timeout = ast_json_integer_get(field);
+ }
+ return 0;
+}
+
+/*!
+ * \brief Parameter parsing callback for /channels/{channelId}/dial.
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_channels_dial_cb(
+ struct ast_tcptls_session_instance *ser,
+ struct ast_variable *get_params, struct ast_variable *path_vars,
+ struct ast_variable *headers, struct ast_ari_response *response)
+{
+ struct ast_ari_channels_dial_args args = {};
+ struct ast_variable *i;
+ RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+ int is_valid;
+ int code;
+#endif /* AST_DEVMODE */
+
+ for (i = get_params; i; i = i->next) {
+ if (strcmp(i->name, "caller") == 0) {
+ args.caller = (i->value);
+ } else
+ if (strcmp(i->name, "timeout") == 0) {
+ args.timeout = atoi(i->value);
+ } else
+ {}
+ }
+ for (i = path_vars; i; i = i->next) {
+ if (strcmp(i->name, "channelId") == 0) {
+ args.channel_id = (i->value);
+ } else
+ {}
+ }
+ /* Look for a JSON request entity */
+ body = ast_http_get_json(ser, headers);
+ if (!body) {
+ switch (errno) {
+ case EFBIG:
+ ast_ari_response_error(response, 413, "Request Entity Too Large", "Request body too large");
+ goto fin;
+ case ENOMEM:
+ ast_ari_response_error(response, 500, "Internal Server Error", "Error processing request");
+ goto fin;
+ case EIO:
+ ast_ari_response_error(response, 400, "Bad Request", "Error parsing request body");
+ goto fin;
+ }
+ }
+ if (ast_ari_channels_dial_parse_body(body, &args)) {
+ ast_ari_response_alloc_failed(response);
+ goto fin;
+ }
+ ast_ari_channels_dial(headers, &args, response);
+#if defined(AST_DEVMODE)
+ code = response->response_code;
+
+ switch (code) {
+ case 0: /* Implementation is still a stub, or the code wasn't set */
+ is_valid = response->message == NULL;
+ break;
+ case 500: /* Internal Server Error */
+ case 501: /* Not Implemented */
+ case 404: /* Channel cannot be found. */
+ case 409: /* Channel cannot be dialed. */
+ is_valid = 1;
+ break;
+ default:
+ if (200 <= code && code <= 299) {
+ is_valid = ast_ari_validate_void(
+ response->message);
+ } else {
+ ast_log(LOG_ERROR, "Invalid error response %d for /channels/{channelId}/dial\n", code);
+ is_valid = 0;
+ }
+ }
+
+ if (!is_valid) {
+ ast_log(LOG_ERROR, "Response validation failed for /channels/{channelId}/dial\n");
+ ast_ari_response_error(response, 500,
+ "Internal Server Error", "Response validation failed");
+ }
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+ return;
+}
/*! \brief REST handler for /api-docs/channels.{format} */
+static struct stasis_rest_handlers channels_create = {
+ .path_segment = "create",
+ .callbacks = {
+ [AST_HTTP_POST] = ast_ari_channels_create_cb,
+ },
+ .num_children = 0,
+ .children = { }
+};
+/*! \brief REST handler for /api-docs/channels.{format} */
static struct stasis_rest_handlers channels_channelId_continue = {
.path_segment = "continue",
.callbacks = {
@@ -2697,6 +2936,15 @@ static struct stasis_rest_handlers channels_channelId_snoop = {
.children = { &channels_channelId_snoop_snoopId, }
};
/*! \brief REST handler for /api-docs/channels.{format} */
+static struct stasis_rest_handlers channels_channelId_dial = {
+ .path_segment = "dial",
+ .callbacks = {
+ [AST_HTTP_POST] = ast_ari_channels_dial_cb,
+ },
+ .num_children = 0,
+ .children = { }
+};
+/*! \brief REST handler for /api-docs/channels.{format} */
static struct stasis_rest_handlers channels_channelId = {
.path_segment = "channelId",
.is_wildcard = 1,
@@ -2705,8 +2953,8 @@ static struct stasis_rest_handlers channels_channelId = {
[AST_HTTP_POST] = ast_ari_channels_originate_with_id_cb,
[AST_HTTP_DELETE] = ast_ari_channels_hangup_cb,
},
- .num_children = 13,
- .children = { &channels_channelId_continue,&channels_channelId_redirect,&channels_channelId_answer,&channels_channelId_ring,&channels_channelId_dtmf,&channels_channelId_mute,&channels_channelId_hold,&channels_channelId_moh,&channels_channelId_silence,&channels_channelId_play,&channels_channelId_record,&channels_channelId_variable,&channels_channelId_snoop, }
+ .num_children = 14,
+ .children = { &channels_channelId_continue,&channels_channelId_redirect,&channels_channelId_answer,&channels_channelId_ring,&channels_channelId_dtmf,&channels_channelId_mute,&channels_channelId_hold,&channels_channelId_moh,&channels_channelId_silence,&channels_channelId_play,&channels_channelId_record,&channels_channelId_variable,&channels_channelId_snoop,&channels_channelId_dial, }
};
/*! \brief REST handler for /api-docs/channels.{format} */
static struct stasis_rest_handlers channels = {
@@ -2715,8 +2963,8 @@ static struct stasis_rest_handlers channels = {
[AST_HTTP_GET] = ast_ari_channels_list_cb,
[AST_HTTP_POST] = ast_ari_channels_originate_cb,
},
- .num_children = 1,
- .children = { &channels_channelId, }
+ .num_children = 2,
+ .children = { &channels_create,&channels_channelId, }
};
static int load_module(void)
diff --git a/res/res_http_websocket.c b/res/res_http_websocket.c
index 265457854..d63caec3e 100644
--- a/res/res_http_websocket.c
+++ b/res/res_http_websocket.c
@@ -24,7 +24,7 @@
*/
/*** MODULEINFO
- <support_level>extended</support_level>
+ <support_level>core</support_level>
***/
#include "asterisk.h"
diff --git a/res/res_pjsip/config_global.c b/res/res_pjsip/config_global.c
index ad03379fd..8348a1eb5 100644
--- a/res/res_pjsip/config_global.c
+++ b/res/res_pjsip/config_global.c
@@ -369,10 +369,10 @@ int ast_sip_initialize_sorcery_global(void)
DEFAULT_VOICEMAIL_EXTENSION, OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config,
default_voicemail_extension));
ast_sorcery_object_field_register(sorcery, "global", "regcontext", DEFAULT_REGCONTEXT,
- OPT_UINT_T, 0, FLDSET(struct global_config, contact_expiration_check_interval));
+ OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, regcontext));
ast_sorcery_object_field_register(sorcery, "global", "contact_expiration_check_interval",
__stringify(DEFAULT_CONTACT_EXPIRATION_CHECK_INTERVAL),
- OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, regcontext));
+ OPT_UINT_T, 0, FLDSET(struct global_config, contact_expiration_check_interval));
if (ast_sorcery_instance_observer_add(sorcery, &observer_callbacks_global)) {
return -1;
diff --git a/res/res_pjsip_registrar_expire.c b/res/res_pjsip_registrar_expire.c
index 87edf5390..e52363e13 100644
--- a/res/res_pjsip_registrar_expire.c
+++ b/res/res_pjsip_registrar_expire.c
@@ -91,6 +91,7 @@ static void expiration_global_loaded(const char *object_type)
} else {
if (check_thread != AST_PTHREADT_NULL) {
pthread_kill(check_thread, SIGURG);
+ pthread_join(check_thread, NULL);
check_thread = AST_PTHREADT_NULL;
ast_debug(3, "Interval = 0, shutting thread down\n");
}
@@ -105,7 +106,10 @@ static struct ast_sorcery_observer expiration_global_observer = {
static int unload_module(void)
{
if (check_thread != AST_PTHREADT_NULL) {
+ check_interval = 0;
pthread_kill(check_thread, SIGURG);
+ pthread_join(check_thread, NULL);
+
check_thread = AST_PTHREADT_NULL;
}
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 80fe8f393..59d759fdc 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -1125,7 +1125,54 @@ static pj_bool_t session_reinvite_on_rx_request(pjsip_rx_data *rdata)
}
if (!sdp_info->sdp) {
+ const pjmedia_sdp_session *local;
+ int i;
+
ast_queue_unhold(session->channel);
+
+ pjmedia_sdp_neg_get_active_local(session->inv_session->neg, &local);
+ if (!local) {
+ return PJ_FALSE;
+ }
+
+ /*
+ * Some devices indicate hold with deferred SDP reinvites (i.e. no SDP in the reinvite).
+ * When hold is initially indicated, we
+ * - Receive an INVITE with no SDP
+ * - Send a 200 OK with SDP, indicating sendrecv in the media streams
+ * - Receive an ACK with SDP, indicating sendonly in the media streams
+ *
+ * At this point, the pjmedia negotiator saves the state of the media direction so that
+ * if we are to send any offers, we'll offer recvonly in the media streams. This is
+ * problematic if the device is attempting to unhold, though. If the device unholds
+ * by sending a reinvite with no SDP, then we will respond with a 200 OK with recvonly.
+ * According to RFC 3264, if an offerer offers recvonly, then the answerer MUST respond
+ * with sendonly or inactive. The result of this is that the stream is not off hold.
+ *
+ * Therefore, in this case, when we receive a reinvite while the stream is on hold, we
+ * need to be sure to offer sendrecv. This way, the answerer can respond with sendrecv
+ * in order to get the stream off hold. If this is actually a different purpose reinvite
+ * (like a session timer refresh), then the answerer can respond to our sendrecv with
+ * sendonly, keeping the stream on hold.
+ */
+ for (i = 0; i < local->media_count; ++i) {
+ pjmedia_sdp_media *m = local->media[i];
+ pjmedia_sdp_attr *recvonly;
+ pjmedia_sdp_attr *inactive;
+
+ recvonly = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "recvonly", NULL);
+ inactive = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "inactive", NULL);
+ if (recvonly || inactive) {
+ pjmedia_sdp_attr *to_remove = recvonly ?: inactive;
+ pjmedia_sdp_attr *sendrecv;
+
+ pjmedia_sdp_attr_remove(&m->attr_count, m->attr, to_remove);
+
+ sendrecv = pjmedia_sdp_attr_create(session->inv_session->pool, "sendrecv", NULL);
+ pjmedia_sdp_media_add_attr(m, sendrecv);
+ }
+ }
+
return PJ_FALSE;
}
diff --git a/res/res_stasis.c b/res/res_stasis.c
index 5aa0aa9ac..346be563c 100644
--- a/res/res_stasis.c
+++ b/res/res_stasis.c
@@ -1287,6 +1287,7 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
int r;
int command_count;
RAII_VAR(struct ast_bridge *, last_bridge, NULL, ao2_cleanup);
+ struct ast_dial *dial;
/* Check to see if a bridge absorbed our hangup frame */
if (ast_check_hangup_locked(chan)) {
@@ -1296,6 +1297,7 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
last_bridge = bridge;
bridge = ao2_bump(stasis_app_get_bridge(control));
+ dial = stasis_app_get_dial(control);
if (bridge != last_bridge) {
app_unsubscribe_bridge(app, last_bridge);
@@ -1304,8 +1306,8 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
}
}
- if (bridge) {
- /* Bridge is handling channel frames */
+ if (bridge || dial) {
+ /* Bridge/dial is handling channel frames */
control_wait(control);
control_dispatch_all(control, chan);
continue;
diff --git a/res/stasis/control.c b/res/stasis/control.c
index 86f94423d..3c5b75041 100644
--- a/res/stasis/control.c
+++ b/res/stasis/control.c
@@ -78,6 +78,10 @@ struct stasis_app_control {
*/
struct stasis_app *app;
/*!
+ * If channel is being dialed, the dial structure.
+ */
+ struct ast_dial *dial;
+ /*!
* When set, /c app_stasis should exit and continue in the dialplan.
*/
int is_done:1;
@@ -276,89 +280,6 @@ static struct stasis_app_command *exec_command(
return exec_command_on_condition(control, command_fn, data, data_destructor, NULL);
}
-struct stasis_app_control_dial_data {
- char endpoint[AST_CHANNEL_NAME];
- int timeout;
-};
-
-static int app_control_dial(struct stasis_app_control *control,
- struct ast_channel *chan, void *data)
-{
- RAII_VAR(struct ast_dial *, dial, ast_dial_create(), ast_dial_destroy);
- struct stasis_app_control_dial_data *dial_data = data;
- enum ast_dial_result res;
- char *tech, *resource;
- struct ast_channel *new_chan;
- RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
-
- tech = dial_data->endpoint;
- if (!(resource = strchr(tech, '/'))) {
- return -1;
- }
- *resource++ = '\0';
-
- if (!dial) {
- ast_log(LOG_ERROR, "Failed to create dialing structure.\n");
- return -1;
- }
-
- if (ast_dial_append(dial, tech, resource, NULL) < 0) {
- ast_log(LOG_ERROR, "Failed to add %s/%s to dialing structure.\n", tech, resource);
- return -1;
- }
-
- ast_dial_set_global_timeout(dial, dial_data->timeout);
-
- res = ast_dial_run(dial, NULL, 0);
- if (res != AST_DIAL_RESULT_ANSWERED || !(new_chan = ast_dial_answered_steal(dial))) {
- return -1;
- }
-
- if (!(bridge = ast_bridge_basic_new())) {
- ast_log(LOG_ERROR, "Failed to create basic bridge.\n");
- return -1;
- }
-
- if (ast_bridge_impart(bridge, new_chan, NULL, NULL,
- AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
- ast_hangup(new_chan);
- } else {
- control_add_channel_to_bridge(control, chan, bridge);
- }
-
- return 0;
-}
-
-int stasis_app_control_dial(struct stasis_app_control *control, const char *endpoint, const char *exten, const char *context,
- int timeout)
-{
- struct stasis_app_control_dial_data *dial_data;
-
- if (!(dial_data = ast_calloc(1, sizeof(*dial_data)))) {
- return -1;
- }
-
- if (!ast_strlen_zero(endpoint)) {
- ast_copy_string(dial_data->endpoint, endpoint, sizeof(dial_data->endpoint));
- } else if (!ast_strlen_zero(exten) && !ast_strlen_zero(context)) {
- snprintf(dial_data->endpoint, sizeof(dial_data->endpoint), "Local/%s@%s", exten, context);
- } else {
- return -1;
- }
-
- if (timeout > 0) {
- dial_data->timeout = timeout * 1000;
- } else if (timeout == -1) {
- dial_data->timeout = -1;
- } else {
- dial_data->timeout = 30000;
- }
-
- stasis_app_send_command_async(control, app_control_dial, dial_data, ast_free_ptr);
-
- return 0;
-}
-
static int app_control_add_role(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
@@ -1208,3 +1129,84 @@ struct stasis_app *control_app(struct stasis_app_control *control)
{
return control->app;
}
+
+static void app_control_dial_destroy(void *data)
+{
+ struct ast_dial *dial = data;
+
+ ast_dial_join(dial);
+ ast_dial_destroy(dial);
+}
+
+static int app_control_remove_dial(struct stasis_app_control *control,
+ struct ast_channel *chan, void *data)
+{
+ if (ast_dial_state(control->dial) != AST_DIAL_RESULT_ANSWERED) {
+ ast_softhangup(chan, AST_SOFTHANGUP_EXPLICIT);
+ }
+ control->dial = NULL;
+ return 0;
+}
+
+static void on_dial_state(struct ast_dial *dial)
+{
+ enum ast_dial_result state;
+ struct stasis_app_control *control;
+ struct ast_channel *chan;
+
+ state = ast_dial_state(dial);
+ control = ast_dial_get_user_data(dial);
+
+ switch (state) {
+ case AST_DIAL_RESULT_ANSWERED:
+ /* Need to steal the reference to the answered channel so that dial doesn't
+ * try to hang it up when we destroy the dial structure.
+ */
+ chan = ast_dial_answered_steal(dial);
+ ast_channel_unref(chan);
+ /* Fall through intentionally */
+ case AST_DIAL_RESULT_INVALID:
+ case AST_DIAL_RESULT_FAILED:
+ case AST_DIAL_RESULT_TIMEOUT:
+ case AST_DIAL_RESULT_HANGUP:
+ case AST_DIAL_RESULT_UNANSWERED:
+ /* The dial has completed, so we need to break the Stasis loop so
+ * that the channel's frames are handled in the proper place now.
+ */
+ stasis_app_send_command_async(control, app_control_remove_dial, dial, app_control_dial_destroy);
+ break;
+ case AST_DIAL_RESULT_TRYING:
+ case AST_DIAL_RESULT_RINGING:
+ case AST_DIAL_RESULT_PROGRESS:
+ case AST_DIAL_RESULT_PROCEEDING:
+ break;
+ }
+}
+
+static int app_control_dial(struct stasis_app_control *control,
+ struct ast_channel *chan, void *data)
+{
+ struct ast_dial *dial = data;
+
+ ast_dial_set_state_callback(dial, on_dial_state);
+ /* The dial API gives the option of providing a caller channel, but for
+ * Stasis, we really don't want to do that. The Dial API will take liberties such
+ * as passing frames along to the calling channel (think ringing, progress, etc.).
+ * This is not desirable in ARI applications since application writers should have
+ * control over what does/does not get indicated to the calling channel
+ */
+ ast_dial_run(dial, NULL, 1);
+ control->dial = dial;
+
+ return 0;
+}
+
+struct ast_dial *stasis_app_get_dial(struct stasis_app_control *control)
+{
+ return control->dial;
+}
+
+int stasis_app_control_dial(struct stasis_app_control *control, struct ast_dial *dial)
+{
+ return stasis_app_send_command_async(control, app_control_dial, dial, NULL);
+}
diff --git a/rest-api/api-docs/channels.json b/rest-api/api-docs/channels.json
index bc0879b57..2389f7cb9 100644
--- a/rest-api/api-docs/channels.json
+++ b/rest-api/api-docs/channels.json
@@ -140,6 +140,68 @@
]
},
{
+ "path": "/channels/create",
+ "description": "Create a channel and place it in a Stasis app, but do not dial the channel yet.",
+ "operations": [
+ {
+ "httpMethod": "POST",
+ "summary": "Create channel.",
+ "nickname": "create",
+ "responseClass": "Channel",
+ "parameters": [
+ {
+ "name": "endpoint",
+ "description": "Endpoint for channel communication",
+ "paramType": "query",
+ "required": true,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "app",
+ "description": "Stasis Application to place channel into",
+ "paramType": "query",
+ "required": true,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "appArgs",
+ "description": "The application arguments to pass to the Stasis application provided by 'app'. Mutually exclusive with 'context', 'extension', 'priority', and 'label'.",
+ "paramType": "query",
+ "required": false,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "channelId",
+ "description": "The unique id to assign the channel on creation.",
+ "paramType": "query",
+ "required": false,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "otherChannelId",
+ "description": "The unique id to assign the second channel when using local channels.",
+ "paramType": "query",
+ "required": false,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "originator",
+ "description": "Unique ID of the calling channel",
+ "paramType": "query",
+ "required": false,
+ "allowMultiple": false,
+ "dataType": "string"
+ }
+ ]
+ }
+ ]
+ },
+ {
"path": "/channels/{channelId}",
"description": "Active channel",
"operations": [
@@ -1440,6 +1502,59 @@
]
}
]
+ },
+ {
+ "path": "/channels/{channelId}/dial",
+ "description": "Dial a channel",
+ "operations": [
+ {
+ "httpMethod": "POST",
+ "summary": "Dial a created channel.",
+ "nickname": "dial",
+ "responseClass": "void",
+ "parameters": [
+ {
+ "name": "channelId",
+ "description": "Channel's id",
+ "paramType": "path",
+ "required": true,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "caller",
+ "description": "Channel ID of caller",
+ "paramType": "query",
+ "required": false,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "timeout",
+ "description": "Dial timeout",
+ "paramType": "query",
+ "required": false,
+ "allowMultiple": false,
+ "dataType": "int",
+ "defaultValue": 0,
+ "allowableValues": {
+ "valueType": "RANGE",
+ "min": 0
+ }
+ }
+ ],
+ "errorResponses": [
+ {
+ "code": 404,
+ "reason": "Channel cannot be found."
+ },
+ {
+ "code": 409,
+ "reason": "Channel cannot be dialed."
+ }
+ ]
+ }
+ ]
}
],
"models": {
diff --git a/tests/test_stringfields.c b/tests/test_stringfields.c
index 2eeb83d45..e57f8d7e8 100644
--- a/tests/test_stringfields.c
+++ b/tests/test_stringfields.c
@@ -24,7 +24,6 @@
*
* Test module for string fields API
* \ingroup tests
- * \todo need to test ast_calloc_with_stringfields
*/
/*** MODULEINFO
@@ -50,16 +49,16 @@ AST_TEST_DEFINE(string_field_test)
struct {
AST_DECLARE_STRING_FIELDS (
AST_STRING_FIELD(string1);
- AST_STRING_FIELD(string2);
);
+ AST_STRING_FIELD_EXTENDED(string2);
} test_struct;
struct {
AST_DECLARE_STRING_FIELDS (
AST_STRING_FIELD(string1);
AST_STRING_FIELD(string2);
- AST_STRING_FIELD(string3);
);
+ AST_STRING_FIELD_EXTENDED(string3);
} test_struct2;
switch (cmd) {
@@ -74,6 +73,9 @@ AST_TEST_DEFINE(string_field_test)
break;
}
+ memset(&test_struct, 0, sizeof(test_struct));
+ memset(&test_struct2, 0, sizeof(test_struct));
+
ast_test_status_update(test, "First things first. Let's see if we can actually allocate string fields\n");
if (ast_string_field_init(&test_struct, 32)) {
@@ -82,6 +84,7 @@ AST_TEST_DEFINE(string_field_test)
} else {
ast_test_status_update(test, "All right! Successfully allocated! Now let's get down to business\n");
}
+ ast_string_field_init_extended(&test_struct, string2);
ast_test_status_update(test,"We're going to set some string fields and perform some checks\n");
@@ -255,6 +258,8 @@ AST_TEST_DEFINE(string_field_test)
ast_string_field_init(&test_struct2, 32);
ast_test_status_update(test, "Now using a totally separate area of memory we're going to test a basic pool freeing scenario\n");
+ ast_string_field_init_extended(&test_struct2, string3);
+
ast_string_field_set(&test_struct2, string1, "first");
ast_string_field_set(&test_struct2, string2, "second");
@@ -294,15 +299,22 @@ error:
return AST_TEST_FAIL;
}
+struct test_struct {
+ int foo;
+ AST_DECLARE_STRING_FIELDS (
+ AST_STRING_FIELD(string1);
+ );
+ int foo2;
+ AST_STRING_FIELD_EXTENDED(string2);
+};
+
AST_TEST_DEFINE(string_field_aggregate_test)
{
- struct test_struct {
- AST_DECLARE_STRING_FIELDS (
- AST_STRING_FIELD(string1);
- AST_STRING_FIELD(string2);
- );
- int foo;
- } inst1, inst2, inst3, inst4;
+ enum ast_test_result_state res = AST_TEST_PASS;
+ struct test_struct *inst1 = NULL;
+ struct test_struct *inst2 = NULL;
+ struct test_struct *inst3 = NULL;
+ struct test_struct *inst4 = NULL;
switch (cmd) {
case TEST_INIT:
@@ -316,88 +328,189 @@ AST_TEST_DEFINE(string_field_aggregate_test)
break;
}
- ast_string_field_init(&inst1, 32);
- ast_string_field_init(&inst2, 32);
- ast_string_field_init(&inst3, 32);
- ast_string_field_init(&inst4, 32);
+ inst1 = ast_calloc_with_stringfields(1, struct test_struct, 32);
+ if (!inst1) {
+ ast_test_status_update(test, "Unable to allocate structure 1!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ }
+ ast_string_field_init_extended(inst1, string2);
+
+ inst2 = ast_calloc_with_stringfields(1, struct test_struct, 32);
+ if (!inst2) {
+ ast_test_status_update(test, "Unable to allocate structure 2!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ }
+ ast_string_field_init_extended(inst2, string2);
+
+ inst3 = ast_calloc_with_stringfields(1, struct test_struct, 32);
+ if (!inst3) {
+ ast_test_status_update(test, "Unable to allocate structure 3!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ }
+ ast_string_field_init_extended(inst3, string2);
+
+ inst4 = ast_calloc_with_stringfields(1, struct test_struct, 32);
+ if (!inst4) {
+ ast_test_status_update(test, "Unable to allocate structure 4!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ }
+ ast_string_field_init_extended(inst4, string2);
+
+
+ ast_string_field_set(inst1, string1, "foo");
+ ast_string_field_set(inst1, string2, "bar");
+ inst1->foo = 1;
+
+ ast_string_field_ptr_set_by_fields(inst2->__field_mgr_pool, inst2->__field_mgr, &inst2->string2, "bar");
+ ast_string_field_ptr_set_by_fields(inst2->__field_mgr_pool, inst2->__field_mgr, &inst2->string1, "foo");
+ inst2->foo = 2;
+
+ if (inst3->__field_mgr.header->embedded_pool->prev) {
+ ast_test_status_update(test, "Structure 3 embedded pool should not have a previous pool!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ }
+
+ ast_string_field_set(inst3, string1, "foo");
+
+ if (inst3->__field_mgr.header->embedded_pool != inst3->__field_mgr_pool) {
+ ast_test_status_update(test, "Structure 3 embedded pool should have been the current pool!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ }
+
+ if (inst3->__field_mgr.header->embedded_pool->prev) {
+ ast_test_status_update(test, "Structure 3 embedded pool should not have a previous pool!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ }
+
+ ast_test_status_update(test, "Structures 3 embedded pool initialized successfully.\n");
+
+ /* Exhaust the embedded pool */
+ ast_string_field_set(inst3, string2, "baz 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890");
+ inst3->foo = 3;
- ast_string_field_set(&inst1, string1, "foo");
- ast_string_field_set(&inst1, string2, "bar");
- inst1.foo = 1;
+ if (inst3->__field_mgr_pool == inst3->__field_mgr.header->embedded_pool) {
+ ast_test_status_update(test, "Structure 3 embedded pool should not have been the current pool!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ }
- ast_string_field_set(&inst2, string2, "bar");
- ast_string_field_set(&inst2, string1, "foo");
- inst2.foo = 2;
+ if (inst3->__field_mgr.header->embedded_pool != inst3->__field_mgr_pool->prev) {
+ ast_test_status_update(test, "Structure 3 embedded pool should be the current pool's previous!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ }
- ast_string_field_set(&inst3, string1, "foo");
- ast_string_field_set(&inst3, string2, "baz");
- inst3.foo = 3;
+ ast_test_status_update(test, "Structures 3 additional pool initialized successfully.\n");
- ast_string_field_set(&inst4, string1, "faz");
- ast_string_field_set(&inst4, string2, "baz");
- inst4.foo = 3;
+ ast_string_field_set(inst4, string1, "faz");
+ /* Exhaust the embedded pool */
+ ast_string_field_set(inst4, string2, "baz 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890");
+ inst4->foo = 4;
- if (ast_string_fields_cmp(&inst1, &inst2)) {
+ if (ast_string_fields_cmp(inst1, inst2)) {
ast_test_status_update(test, "Structures 1/2 should be equal!\n");
+ res = AST_TEST_FAIL;
goto error;
} else {
ast_test_status_update(test, "Structures 1/2 are equal as expected.\n");
}
- if (!ast_string_fields_cmp(&inst1, &inst3)) {
+ if (!ast_string_fields_cmp(inst1, inst3)) {
ast_test_status_update(test, "Structures 1/3 should be different!\n");
+ res = AST_TEST_FAIL;
goto error;
} else {
ast_test_status_update(test, "Structures 1/3 are different as expected.\n");
}
- if (!ast_string_fields_cmp(&inst2, &inst3)) {
+ if (!ast_string_fields_cmp(inst2, inst3)) {
ast_test_status_update(test, "Structures 2/3 should be different!\n");
+ res = AST_TEST_FAIL;
goto error;
} else {
ast_test_status_update(test, "Structures 2/3 are different as expected.\n");
}
- if (!ast_string_fields_cmp(&inst3, &inst4)) {
+ if (!ast_string_fields_cmp(inst3, inst4)) {
ast_test_status_update(test, "Structures 3/4 should be different!\n");
+ res = AST_TEST_FAIL;
goto error;
} else {
ast_test_status_update(test, "Structures 3/4 are different as expected.\n");
}
- if (ast_string_fields_copy(&inst1, &inst3)) {
- ast_test_status_update(test, "Copying from structure 3 to structure 4 failed!\n");
+ if (ast_string_fields_copy(inst1, inst3)) {
+ ast_test_status_update(test, "Copying from structure 3 to structure 1 failed!\n");
+ res = AST_TEST_FAIL;
goto error;
} else {
- ast_test_status_update(test, "Copying from structure 3 to structure 4 succeeded!\n");
+ ast_test_status_update(test, "Copying from structure 3 to structure 1 succeeded!\n");
}
/* inst1 and inst3 should now be equal and inst1 should no longer be equal to inst2 */
- if (ast_string_fields_cmp(&inst1, &inst3)) {
+ if (ast_string_fields_cmp(inst1, inst3)) {
ast_test_status_update(test, "Structures 1/3 should be equal!\n");
+ res = AST_TEST_FAIL;
goto error;
} else {
ast_test_status_update(test, "Structures 1/3 are equal as expected.\n");
}
- if (!ast_string_fields_cmp(&inst1, &inst2)) {
+ if (!ast_string_fields_cmp(inst1, inst2)) {
ast_test_status_update(test, "Structures 1/2 should be different!\n");
- goto error;
+ res = AST_TEST_FAIL;
} else {
ast_test_status_update(test, "Structures 1/2 are different as expected.\n");
}
- ast_string_field_free_memory(&inst1);
- ast_string_field_free_memory(&inst2);
- ast_string_field_free_memory(&inst3);
- ast_string_field_free_memory(&inst4);
- return AST_TEST_PASS;
+ ast_test_status_update(test, "Reset but don't free.\n");
+
+ ast_string_field_init(inst1, AST_STRINGFIELD_RESET);
+ ast_string_field_init(inst2, AST_STRINGFIELD_RESET);
+ ast_string_field_init(inst3, AST_STRINGFIELD_RESET);
+ ast_string_field_init(inst4, AST_STRINGFIELD_RESET);
+
+ if (ast_string_fields_cmp(inst1, inst2)) {
+ ast_test_status_update(test, "Structures 1/2 should be the same (empty)!\n");
+ res = AST_TEST_FAIL;
+ } else {
+ ast_test_status_update(test, "Structures 1/2 are the same (empty) as expected.\n");
+ }
+
+ if (inst4->__field_mgr.header->embedded_pool != inst4->__field_mgr_pool) {
+ ast_test_status_update(test, "Structure 4 embedded pool should have been the current pool!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ } else {
+ ast_test_status_update(test, "Structure 4 embedded pool is the current pool as expected.\n");
+ }
+
+ if (inst4->__field_mgr.header->embedded_pool->prev) {
+ ast_test_status_update(test, "Structure 4 embedded pool should not have a previous pool!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ } else {
+ ast_test_status_update(test, "Structure 4 embedded pool does not have a previous as expected.\n");
+ }
+
error:
- ast_string_field_free_memory(&inst1);
- ast_string_field_free_memory(&inst2);
- ast_string_field_free_memory(&inst3);
- ast_string_field_free_memory(&inst4);
- return AST_TEST_FAIL;
+ ast_string_field_free_memory(inst1);
+ ast_free(inst1);
+ ast_string_field_free_memory(inst2);
+ ast_free(inst2);
+ ast_string_field_free_memory(inst3);
+ ast_free(inst3);
+ ast_string_field_free_memory(inst4);
+ ast_free(inst4);
+
+ return res;
}
static int unload_module(void)