summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--channels/chan_sip.c2
-rw-r--r--configs/samples/pjsip.conf.sample28
-rw-r--r--contrib/ast-db-manage/config/versions/1c688d9a003c_pjsip_voicemail_extension.py31
-rwxr-xr-xcontrib/scripts/install_prereq9
-rw-r--r--include/asterisk/res_pjsip.h17
-rw-r--r--include/asterisk/res_pjsip_body_generator_types.h2
-rw-r--r--include/asterisk/res_pjsip_pubsub.h20
-rw-r--r--include/asterisk/rtp_engine.h6
-rw-r--r--include/asterisk/stasis_app.h10
-rw-r--r--include/asterisk/stringfields.h295
-rw-r--r--main/Makefile20
-rw-r--r--main/config.c33
-rw-r--r--main/core_unreal.c2
-rw-r--r--main/rtp_engine.c29
-rw-r--r--main/sdp_srtp.c2
-rw-r--r--main/stringfields.c508
-rw-r--r--main/utils.c373
-rw-r--r--res/ari/resource_bridges.c9
-rw-r--r--res/res_http_websocket.c2
-rw-r--r--res/res_pjsip.c12
-rw-r--r--res/res_pjsip/config_global.c44
-rw-r--r--res/res_pjsip/location.c20
-rw-r--r--res/res_pjsip/pjsip_configuration.c20
-rw-r--r--res/res_pjsip_mwi.c189
-rw-r--r--res/res_pjsip_mwi_body_generator.c5
-rw-r--r--res/res_pjsip_pubsub.c11
-rw-r--r--res/res_pjsip_pubsub.exports.in40
-rw-r--r--res/res_rtp_asterisk.c42
-rw-r--r--res/res_stasis.c22
-rw-r--r--res/res_stasis_playback.c2
-rw-r--r--res/res_stasis_recording.c20
-rw-r--r--res/stasis/control.c75
-rw-r--r--res/stasis/control.h18
-rw-r--r--tests/test_stringfields.c205
34 files changed, 1448 insertions, 675 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 09ab1a196..ffc2084a1 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -13531,7 +13531,7 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
}
/* Finally our remaining audio/video codecs */
- for (x = 0; ast_test_flag(&p->flags[0], SIP_OUTGOING) && x < ast_format_cap_count(p->caps); x++) {
+ for (x = 0; p->outgoing_call && x < ast_format_cap_count(p->caps); x++) {
tmp_fmt = ast_format_cap_get_format(p->caps, x);
if (ast_format_cap_iscompatible_format(alreadysent, tmp_fmt) != AST_FORMAT_CMP_NOT_EQUAL) {
diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index faa928598..94d179208 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -624,7 +624,14 @@
; "username")
;redirect_method=user ; How redirects received from an endpoint are handled
; (default: "user")
-;mailboxes= ; Mailbox es to be associated with (default: "")
+;mailboxes= ; NOTIFY the endpoint when state changes for any of the specified mailboxes.
+ ; Asterisk will send unsolicited MWI NOTIFY messages to the endpoint when state
+ ; changes happen for any of the specified mailboxes. (default: "")
+;voicemail_extension= ; The voicemail extension to send in the NOTIFY Message-Account header
+ ; (default: global/default_voicemail_extension)
+;mwi_subscribe_replaces_unsolicited=no
+ ; An MWI subscribe will replace unsoliticed NOTIFYs
+ ; (default: "no")
;moh_suggest=default ; Default Music On Hold class (default: "default")
;moh_passthrough=yes ; Pass Music On Hold through using SIP re-invites with sendonly
; when placing on hold and sendrecv when taking off hold
@@ -832,7 +839,11 @@
;default_expiration=3600 ; Default expiration time in seconds for
; contacts that are dynamically bound to an AoR
; (default: "3600")
-;mailboxes= ; Mailbox es to be associated with (default: "")
+;mailboxes= ; Allow subscriptions for the specified mailbox(es)
+ ; This option applies when an external entity subscribes to an AoR
+ ; for Message Waiting Indications. (default: "")
+;voicemail_extension= ; The voicemail extension to send in the NOTIFY Message-Account header
+ ; (default: global/default_voicemail_extension)
;maximum_expiration=7200 ; Maximum time to keep an AoR (default: "7200")
;max_contacts=0 ; Maximum number of contacts that can bind to an AoR (default:
; "0")
@@ -904,10 +915,15 @@
; startup that qualifies should be attempted on all
; contacts. If greater than the qualify_frequency
; for an aor, qualify_frequency will be used instead.
-; If regcontext is specified, Asterisk will dynamically create and destroy a
-; NoOp priority 1 extension for a given endpoint who registers or unregisters
-; with us. The extension added is the name of the endpoint.
-;regcontext=sipregistrations
+;regcontext=sipregistrations ; If regcontext is specified, Asterisk will dynamically
+ ; create and destroy a NoOp priority 1 extension for a
+ ; given endpoint who registers or unregisters with us.
+ ; The extension added is the name of the endpoint.
+;default_voicemail_extension=asterisk
+ ; The voicemail extension to send in the NOTIFY Message-Account header
+ ; if not set on endpoint or aor.
+ ; (default: "")
+
; MODULE PROVIDING BELOW SECTION(S): res_pjsip_acl
;==========================ACL SECTION OPTIONS=========================
diff --git a/contrib/ast-db-manage/config/versions/1c688d9a003c_pjsip_voicemail_extension.py b/contrib/ast-db-manage/config/versions/1c688d9a003c_pjsip_voicemail_extension.py
new file mode 100644
index 000000000..781dca703
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/1c688d9a003c_pjsip_voicemail_extension.py
@@ -0,0 +1,31 @@
+"""pjsip voicemail extension
+
+Revision ID: 1c688d9a003c
+Revises: 5813202e92be
+Create Date: 2016-03-24 22:31:45.537895
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '1c688d9a003c'
+down_revision = '5813202e92be'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ op.add_column('ps_globals', sa.Column('default_voicemail_extension', sa.String(40)))
+ op.add_column('ps_aors', sa.Column('voicemail_extension', sa.String(40)))
+ op.add_column('ps_endpoints', sa.Column('voicemail_extension', sa.String(40)))
+ op.add_column('ps_endpoints', sa.Column('mwi_subscribe_replaces_unsolicited', sa.Integer))
+
+
+def downgrade():
+ with op.batch_alter_table('ps_globals') as batch_op:
+ batch_op.drop_column('default_voicemail_extension')
+ with op.batch_alter_table('ps_aors') as batch_op:
+ batch_op.drop_column('voicemail_extension')
+ with op.batch_alter_table('ps_endpoints') as batch_op:
+ batch_op.drop_column('voicemail_extension')
+ batch_op.drop_column('mwi_subscribe_replaces_unsolicited')
diff --git a/contrib/scripts/install_prereq b/contrib/scripts/install_prereq
index afad1f719..1682558ac 100755
--- a/contrib/scripts/install_prereq
+++ b/contrib/scripts/install_prereq
@@ -66,13 +66,12 @@ in_test_mode() {
}
check_installed_debs() {
- for pack in "$@"
- do
- tocheck="${tocheck} ^${pack}$"
+ for pack in "$@" ; do
+ tocheck="${tocheck} ^${pack}$ ~P^${pack}$"
done
pkgs=$(aptitude -F '%c %p' search ${tocheck} 2>/dev/null | awk '/^p/{print $2}')
- if ! [ ${#pkgs} -eq 0 ]; then
- echo $pkgs | grep -v ':i386$'
+ if [ ${#pkgs} -ne 0 ]; then
+ echo $pkgs | sed -r -e "s/ ?[^ :]+:i386//g"
fi
}
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index b0ae2cefa..2c26c25da 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -312,6 +312,8 @@ struct ast_sip_aor {
unsigned int support_path;
/*! Qualify timeout. 0 is diabled. */
double qualify_timeout;
+ /* Voicemail extension to set in Message-Account */
+ char *voicemail_extension;
};
/*!
@@ -466,6 +468,10 @@ struct ast_sip_mwi_configuration {
);
/* Should mailbox states be combined into a single notification? */
unsigned int aggregate;
+ /* Should a subscribe replace unsolicited notifies? */
+ unsigned int subscribe_replaces_unsolicited;
+ /* Voicemail extension to set in Message-Account */
+ char *voicemail_extension;
};
/*!
@@ -2118,6 +2124,16 @@ char *ast_sip_get_regcontext(void);
char *ast_sip_get_endpoint_identifier_order(void);
/*!
+ * \brief Retrieve the default voicemail extension.
+ * \since 13.9.0
+ *
+ * \note returned string needs to be de-allocated by caller.
+ *
+ * \retval the default voicemail extension
+ */
+char *ast_sip_get_default_voicemail_extension(void);
+
+/*!
* \brief Retrieve the global default from user.
*
* This is the value placed in outbound requests' From header if there
@@ -2265,4 +2281,5 @@ int ast_sip_set_tpselector_from_transport_name(const char *transport_name, pjsip
void ast_sip_modify_id_header(pj_pool_t *pool, pjsip_fromto_hdr *id_hdr,
const struct ast_party_id *id);
+
#endif /* _RES_PJSIP_H */
diff --git a/include/asterisk/res_pjsip_body_generator_types.h b/include/asterisk/res_pjsip_body_generator_types.h
index a2cc04313..aab147238 100644
--- a/include/asterisk/res_pjsip_body_generator_types.h
+++ b/include/asterisk/res_pjsip_body_generator_types.h
@@ -65,6 +65,8 @@ struct ast_sip_message_accumulator {
int old_msgs;
/*! Number of new messages */
int new_msgs;
+ /*! Message-Account */
+ char message_account[PJSIP_MAX_URL_SIZE];
};
#endif /* _RES_PJSIP_BODY_GENERATOR_TYPES_H */
diff --git a/include/asterisk/res_pjsip_pubsub.h b/include/asterisk/res_pjsip_pubsub.h
index c9b66dce3..84d86fb9e 100644
--- a/include/asterisk/res_pjsip_pubsub.h
+++ b/include/asterisk/res_pjsip_pubsub.h
@@ -339,6 +339,14 @@ struct ast_sip_subscription_handler {
struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_subscription_handler *handler,
struct ast_sip_endpoint *endpoint, const char *resource);
+/*!
+ * \brief Get the pjsip dialog that is associated with this subscription
+ * \since 13.9.0
+ *
+ * \retval NULL Could not get dialog
+ * \retval non-NULL The dialog
+ */
+pjsip_dialog *ast_sip_subscription_get_dialog(struct ast_sip_subscription *sub);
/*!
* \brief Get the endpoint that is associated with this subscription
@@ -379,6 +387,18 @@ struct ast_taskprocessor *ast_sip_subscription_get_serializer(struct ast_sip_sub
int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip_body_data *notify_data, int terminate);
/*!
+ * \brief Retrieve the local sip uri for this subscription
+ * \since 13.9.0
+ *
+ * This is the local sip URI of the subscribed resource.
+ *
+ * \param sub The subscription
+ * \retval NULL Could not get uri
+ * \retval non-NULL The local pjsip_sip_uri
+ */
+pjsip_sip_uri *ast_sip_subscription_get_sip_uri(struct ast_sip_subscription *sub);
+
+/*!
* \brief Retrieve the local URI for this subscription
*
* This is the local URI of the subscribed resource.
diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h
index 411ae8abf..a40472e9d 100644
--- a/include/asterisk/rtp_engine.h
+++ b/include/asterisk/rtp_engine.h
@@ -2236,20 +2236,22 @@ int ast_rtp_instance_sendcng(struct ast_rtp_instance *instance, int level);
* \param instance the RTP instance
* \param remote_policy the remote endpoint's policy
* \param local_policy our policy for this RTP instance's remote endpoint
+ * \param rtcp 1 for dedicated RTCP policies
*
* \retval 0 Success
* \retval non-zero Failure
*/
-int ast_rtp_instance_add_srtp_policy(struct ast_rtp_instance *instance, struct ast_srtp_policy* remote_policy, struct ast_srtp_policy *local_policy);
+int ast_rtp_instance_add_srtp_policy(struct ast_rtp_instance *instance, struct ast_srtp_policy* remote_policy, struct ast_srtp_policy *local_policy, int rtcp);
/*!
* \brief Obtain the SRTP instance associated with an RTP instance
*
* \param instance the RTP instance
+ * \param rtcp 1 to request instance for RTCP
* \retval the SRTP instance on success
* \retval NULL if no SRTP instance exists
*/
-struct ast_srtp *ast_rtp_instance_get_srtp(struct ast_rtp_instance *instance);
+struct ast_srtp *ast_rtp_instance_get_srtp(struct ast_rtp_instance *instance, int rtcp);
/*! \brief Custom formats declared in codecs.conf at startup must be communicated to the rtp_engine
* so their mime type can payload number can be initialized. */
diff --git a/include/asterisk/stasis_app.h b/include/asterisk/stasis_app.h
index f2b07e0bf..90ef82ebf 100644
--- a/include/asterisk/stasis_app.h
+++ b/include/asterisk/stasis_app.h
@@ -440,6 +440,16 @@ int stasis_app_control_is_done(
struct stasis_app_control *control);
/*!
+ * \brief Flush the control command queue.
+ * \since 13.9.0
+ *
+ * \param control Control object to flush command queue.
+ *
+ * \return Nothing
+ */
+void stasis_app_control_flush_queue(struct stasis_app_control *control);
+
+/*!
* \brief Returns the uniqueid of the channel associated with this control
*
* \param control Control object.
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/Makefile b/main/Makefile
index 50fdc5739..d52c3f0a7 100644
--- a/main/Makefile
+++ b/main/Makefile
@@ -225,11 +225,11 @@ endif
$(ASTSSL_LIB): $(ASTSSL_LIB).$(ASTSSL_SO_VERSION)
$(ECHO_PREFIX) echo " [LN] $< -> $@"
-ifneq ($(LDCONFIG),)
- $(CMD_PREFIX) $(LDCONFIG) $(LDCONFIG_FLAGS) . 2>/dev/null
-else
- $(CMD_PREFIX) $(LN) -sf $< $@
-endif
+ $(CMD_PREFIX) if [ -x "$(LDCONFIG)" ] ; then \
+ $(LDCONFIG) $(LDCONFIG_FLAGS) . 2>/dev/null ;\
+ else \
+ $(LN) -sf $< $@ ;\
+ fi
else # Darwin
ASTSSL_LIB:=libasteriskssl.dylib
@@ -305,11 +305,11 @@ $(ASTPJ_LIB).$(ASTPJ_SO_VERSION): libasteriskpj.o libasteriskpj.exports
$(ASTPJ_LIB): $(ASTPJ_LIB).$(ASTPJ_SO_VERSION)
$(ECHO_PREFIX) echo " [LN] $< -> $@"
-ifneq ($(LDCONFIG),)
- $(CMD_PREFIX) $(LDCONFIG) $(LDCONFIG_FLAGS) . 2>/dev/null
-else
- $(CMD_PREFIX) $(LN) -sf $< $@
-endif
+ $(CMD_PREFIX) if [ -x "$(LDCONFIG)" ] ; then \
+ $(LDCONFIG) $(LDCONFIG_FLAGS) . 2>/dev/null ;\
+ else \
+ $(LN) -sf $< $@ ;\
+ fi
else # Darwin
ASTPJ_LIB:=libasteriskpj.dylib
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/core_unreal.c b/main/core_unreal.c
index da0bb43bb..1f5c202ba 100644
--- a/main/core_unreal.c
+++ b/main/core_unreal.c
@@ -805,9 +805,11 @@ int ast_unreal_channel_push_to_bridge(struct ast_channel *ast, struct ast_bridge
return -1;
}
+ /* The bridge thread now controls the chan ref from the ast_unreal_pvt */
ao2_lock(p);
ast_set_flag(p, AST_UNREAL_CARETAKER_THREAD);
ao2_unlock(p);
+
ast_channel_unref(chan);
return 0;
diff --git a/main/rtp_engine.c b/main/rtp_engine.c
index fd472ba72..11e94c699 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -188,6 +188,8 @@ struct ast_rtp_instance {
struct ast_rtp_glue *glue;
/*! SRTP info associated with the instance */
struct ast_srtp *srtp;
+ /*! SRTP info dedicated for RTCP associated with the instance */
+ struct ast_srtp *rtcp_srtp;
/*! Channel unique ID */
char channel_uniqueid[AST_MAX_UNIQUEID];
/*! Time of last packet sent */
@@ -364,6 +366,10 @@ static void instance_destructor(void *obj)
res_srtp->destroy(instance->srtp);
}
+ if (instance->rtcp_srtp) {
+ res_srtp->destroy(instance->rtcp_srtp);
+ }
+
ast_rtp_codecs_payloads_destroy(&instance->codecs);
/* Drop our engine reference */
@@ -2052,29 +2058,38 @@ int ast_rtp_engine_srtp_is_registered(void)
return res_srtp && res_srtp_policy;
}
-int ast_rtp_instance_add_srtp_policy(struct ast_rtp_instance *instance, struct ast_srtp_policy *remote_policy, struct ast_srtp_policy *local_policy)
+int ast_rtp_instance_add_srtp_policy(struct ast_rtp_instance *instance, struct ast_srtp_policy *remote_policy, struct ast_srtp_policy *local_policy, int rtcp)
{
int res = 0;
+ struct ast_srtp **srtp;
if (!res_srtp) {
return -1;
}
- if (!instance->srtp) {
- res = res_srtp->create(&instance->srtp, instance, remote_policy);
+
+ srtp = rtcp ? &instance->rtcp_srtp : &instance->srtp;
+
+ if (!*srtp) {
+ res = res_srtp->create(srtp, instance, remote_policy);
} else {
- res = res_srtp->replace(&instance->srtp, instance, remote_policy);
+ res = res_srtp->replace(srtp, instance, remote_policy);
}
if (!res) {
- res = res_srtp->add_stream(instance->srtp, local_policy);
+ res = res_srtp->add_stream(*srtp, local_policy);
}
return res;
}
-struct ast_srtp *ast_rtp_instance_get_srtp(struct ast_rtp_instance *instance)
+struct ast_srtp *ast_rtp_instance_get_srtp(struct ast_rtp_instance *instance, int rtcp)
{
- return instance->srtp;
+ if (rtcp && instance->rtcp_srtp) {
+ return instance->rtcp_srtp;
+ }
+ else {
+ return instance->srtp;
+ }
}
int ast_rtp_instance_sendcng(struct ast_rtp_instance *instance, int level)
diff --git a/main/sdp_srtp.c b/main/sdp_srtp.c
index e576258c3..c92387bc2 100644
--- a/main/sdp_srtp.c
+++ b/main/sdp_srtp.c
@@ -183,7 +183,7 @@ static int crypto_activate(struct ast_sdp_crypto *p, int suite_val, unsigned cha
}
/* Add the SRTP policies */
- if (ast_rtp_instance_add_srtp_policy(rtp, remote_policy, local_policy)) {
+ if (ast_rtp_instance_add_srtp_policy(rtp, remote_policy, local_policy, 0)) {
ast_log(LOG_WARNING, "Could not set SRTP policies\n");
goto err;
}
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 6a778b90c..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
@@ -1153,7 +1150,7 @@ static char *handle_show_locks(struct ast_cli_entry *e, int cmd, struct ast_cli_
"Usage: core show locks\n"
" This command is for lock debugging. It prints out which locks\n"
"are owned by each active thread.\n";
- ast_cli_allow_on_shutdown(e);
+ ast_cli_allow_at_shutdown(e);
return NULL;
case CLI_GENERATE:
@@ -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_bridges.c b/res/ari/resource_bridges.c
index 7b9b94665..6018c43be 100644
--- a/res/ari/resource_bridges.c
+++ b/res/ari/resource_bridges.c
@@ -296,10 +296,11 @@ static void *bridge_channel_control_thread(void *data)
thread_data = NULL;
stasis_app_control_execute_until_exhausted(bridge_channel, control);
+ stasis_app_control_flush_queue(control);
- ast_hangup(bridge_channel);
- ao2_cleanup(control);
stasis_forward_cancel(forward);
+ ao2_cleanup(control);
+ ast_hangup(bridge_channel);
return NULL;
}
@@ -526,9 +527,7 @@ static enum play_found_result ari_bridges_play_found(const char *args_media,
control = stasis_app_control_find_by_channel(play_channel);
if (!control) {
- ast_ari_response_error(
- response, 500, "Internal Error", "Failed to get control snapshot");
- return PLAY_FOUND_FAILURE;
+ return PLAY_FOUND_CHANNEL_UNAVAILABLE;
}
ao2_lock(control);
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.c b/res/res_pjsip.c
index b9d6cb6de..f4dc72549 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -302,6 +302,12 @@
configuration.
</para></description>
</configOption>
+ <configOption name="mwi_subscribe_replaces_unsolicited">
+ <synopsis>An MWI subscribe will replace sending unsolicited NOTIFYs</synopsis>
+ </configOption>
+ <configOption name="voicemail_extension">
+ <synopsis>The voicemail extension to send in the NOTIFY Message-Account header</synopsis>
+ </configOption>
<configOption name="moh_suggest" default="default">
<synopsis>Default Music On Hold class</synopsis>
</configOption>
@@ -1138,6 +1144,9 @@
endpoint configuration section to enable unsolicited MWI NOTIFYs to the endpoint.
</para></description>
</configOption>
+ <configOption name="voicemail_extension">
+ <synopsis>The voicemail extension to send in the NOTIFY Message-Account header</synopsis>
+ </configOption>
<configOption name="maximum_expiration" default="7200">
<synopsis>Maximum time to keep an AoR</synopsis>
<description><para>
@@ -1302,6 +1311,9 @@
<configOption name="default_outbound_endpoint" default="default_outbound_endpoint">
<synopsis>Endpoint to use when sending an outbound request to a URI without a specified endpoint.</synopsis>
</configOption>
+ <configOption name="default_voicemail_extension">
+ <synopsis>The voicemail extension to send in the NOTIFY Message-Account header if not specified on endpoint or aor</synopsis>
+ </configOption>
<configOption name="debug" default="no">
<synopsis>Enable/Disable SIP debug logging. Valid options include yes|no or
a host address</synopsis>
diff --git a/res/res_pjsip/config_global.c b/res/res_pjsip/config_global.c
index c0fede64d..ad03379fd 100644
--- a/res/res_pjsip/config_global.c
+++ b/res/res_pjsip/config_global.c
@@ -37,6 +37,7 @@
#define DEFAULT_FROM_USER "asterisk"
#define DEFAULT_REGCONTEXT ""
#define DEFAULT_CONTACT_EXPIRATION_CHECK_INTERVAL 30
+#define DEFAULT_VOICEMAIL_EXTENSION ""
static char default_useragent[256];
@@ -52,6 +53,8 @@ struct global_config {
AST_STRING_FIELD(endpoint_identifier_order);
/*! User name to place in From header if there is no better option */
AST_STRING_FIELD(default_from_user);
+ /*! Default voicemail extension */
+ AST_STRING_FIELD(default_voicemail_extension);
);
/* Value to put in Max-Forwards header */
unsigned int max_forwards;
@@ -144,20 +147,35 @@ char *ast_sip_get_debug(void)
char *ast_sip_get_regcontext(void)
{
- char *res;
- struct global_config *cfg;
+ char *res;
+ struct global_config *cfg;
- cfg = get_global_cfg();
- if (!cfg) {
- return ast_strdup(DEFAULT_REGCONTEXT);
- }
+ cfg = get_global_cfg();
+ if (!cfg) {
+ return ast_strdup(DEFAULT_REGCONTEXT);
+ }
- res = ast_strdup(cfg->regcontext);
- ao2_ref(cfg, -1);
+ res = ast_strdup(cfg->regcontext);
+ ao2_ref(cfg, -1);
- return res;
+ return res;
}
+char *ast_sip_get_default_voicemail_extension(void)
+{
+ char *res;
+ struct global_config *cfg;
+
+ cfg = get_global_cfg();
+ if (!cfg) {
+ return ast_strdup(DEFAULT_VOICEMAIL_EXTENSION);
+ }
+
+ res = ast_strdup(cfg->default_voicemail_extension);
+ ao2_ref(cfg, -1);
+
+ return res;
+}
char *ast_sip_get_endpoint_identifier_order(void)
{
@@ -347,12 +365,14 @@ int ast_sip_initialize_sorcery_global(void)
OPT_UINT_T, 0, FLDSET(struct global_config, max_initial_qualify_time));
ast_sorcery_object_field_register(sorcery, "global", "default_from_user", DEFAULT_FROM_USER,
OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, default_from_user));
+ ast_sorcery_object_field_register(sorcery, "global", "default_voicemail_extension",
+ 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_STRINGFIELD_T, 0, STRFLDSET(struct global_config, regcontext));
+ OPT_UINT_T, 0, FLDSET(struct global_config, contact_expiration_check_interval));
ast_sorcery_object_field_register(sorcery, "global", "contact_expiration_check_interval",
__stringify(DEFAULT_CONTACT_EXPIRATION_CHECK_INTERVAL),
- OPT_UINT_T, 0, FLDSET(struct global_config, contact_expiration_check_interval));
-
+ OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, regcontext));
if (ast_sorcery_instance_observer_add(sorcery, &observer_callbacks_global)) {
return -1;
diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c
index 4008abad1..3145daca0 100644
--- a/res/res_pjsip/location.c
+++ b/res/res_pjsip/location.c
@@ -35,6 +35,7 @@ static void aor_destroy(void *obj)
ao2_cleanup(aor->permanent_contacts);
ast_string_field_free_memory(aor);
+ ast_free(aor->voicemail_extension);
}
/*! \brief Allocator for AOR */
@@ -437,6 +438,24 @@ static int contacts_to_var_list(const void *obj, struct ast_variable **fields)
return 0;
}
+static int voicemail_extension_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+ struct ast_sip_aor *aor = obj;
+
+ aor->voicemail_extension = ast_strdup(var->value);
+
+ return aor->voicemail_extension ? 0 : -1;
+}
+
+static int voicemail_extension_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+ const struct ast_sip_aor *aor = obj;
+
+ *buf = ast_strdup(aor->voicemail_extension);
+
+ return 0;
+}
+
int ast_sip_for_each_aor(const char *aors, ao2_callback_fn on_aor, void *arg)
{
char *copy, *name;
@@ -987,6 +1006,7 @@ int ast_sip_initialize_sorcery_location(void)
ast_sorcery_object_field_register(sorcery, "aor", "remove_existing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, remove_existing));
ast_sorcery_object_field_register_custom(sorcery, "aor", "contact", "", permanent_uri_handler, contacts_to_str, contacts_to_var_list, 0, 0);
ast_sorcery_object_field_register(sorcery, "aor", "mailboxes", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, mailboxes));
+ ast_sorcery_object_field_register_custom(sorcery, "aor", "voicemail_extension", "", voicemail_extension_handler, voicemail_extension_to_str, NULL, 0, 0);
ast_sorcery_object_field_register(sorcery, "aor", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, outbound_proxy));
ast_sorcery_object_field_register(sorcery, "aor", "support_path", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, support_path));
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index b497f02a2..baa5063e4 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -1052,6 +1052,23 @@ static int set_var_to_vl(const void *obj, struct ast_variable **fields)
return 0;
}
+static int voicemail_extension_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+ struct ast_sip_endpoint *endpoint = obj;
+
+ endpoint->subscription.mwi.voicemail_extension = ast_strdup(var->value);
+
+ return endpoint->subscription.mwi.voicemail_extension ? 0 : -1;
+}
+
+static int voicemail_extension_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+ const struct ast_sip_endpoint *endpoint = obj;
+
+ *buf = ast_strdup(endpoint->subscription.mwi.voicemail_extension);
+
+ return 0;
+}
static void *sip_nat_hook_alloc(const char *name)
{
@@ -1647,7 +1664,9 @@ int ast_res_pjsip_initialize_configuration(void)
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rpid_immediate", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.rpid_immediate));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_diversion", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_diversion));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mailboxes", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, subscription.mwi.mailboxes));
+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "voicemail_extension", "", voicemail_extension_handler, voicemail_extension_to_str, NULL, 0, 0);
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "aggregate_mwi", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, subscription.mwi.aggregate));
+ ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mwi_subscribe_replaces_unsolicited", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, subscription.mwi.subscribe_replaces_unsolicited));
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "media_encryption", "no", media_encryption_handler, media_encryption_to_str, NULL, 0, 0);
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "use_avpf", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.use_avpf));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "force_avp", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.force_avp));
@@ -1799,6 +1818,7 @@ int ast_res_pjsip_reload_configuration(void)
static void subscription_configuration_destroy(struct ast_sip_endpoint_subscription_configuration *subscription)
{
ast_string_field_free_memory(&subscription->mwi);
+ ast_free(subscription->mwi.voicemail_extension);
}
static void info_configuration_destroy(struct ast_sip_endpoint_info_configuration *info)
diff --git a/res/res_pjsip_mwi.c b/res/res_pjsip_mwi.c
index be38b44c1..f9bfc1904 100644
--- a/res/res_pjsip_mwi.c
+++ b/res/res_pjsip_mwi.c
@@ -42,6 +42,8 @@
struct mwi_subscription;
static struct ao2_container *unsolicited_mwi;
+static char *default_voicemail_extension;
+
#define STASIS_BUCKETS 13
#define MWI_BUCKETS 53
@@ -326,11 +328,30 @@ static int get_message_count(void *obj, void *arg, int flags)
return 0;
}
+static void set_voicemail_extension(pj_pool_t *pool, pjsip_sip_uri *local_uri,
+ struct ast_sip_message_accumulator *counter, const char *voicemail_extension)
+{
+ pjsip_sip_uri *account_uri;
+ const char *vm_exten;
+
+ if (ast_strlen_zero(voicemail_extension)) {
+ vm_exten = default_voicemail_extension;
+ } else {
+ vm_exten = voicemail_extension;
+ }
+
+ if (!ast_strlen_zero(vm_exten)) {
+ account_uri = pjsip_uri_clone(pool, local_uri);
+ pj_strdup2(pool, &account_uri->user, vm_exten);
+ pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, account_uri, counter->message_account, sizeof(counter->message_account));
+ }
+}
+
struct unsolicited_mwi_data {
struct mwi_subscription *sub;
struct ast_sip_endpoint *endpoint;
pjsip_evsub_state state;
- const struct ast_sip_body *body;
+ struct ast_sip_message_accumulator *counter;
};
static int send_unsolicited_mwi_notify_to_contact(void *obj, void *arg, int flags)
@@ -339,27 +360,50 @@ static int send_unsolicited_mwi_notify_to_contact(void *obj, void *arg, int flag
struct mwi_subscription *sub = mwi_data->sub;
struct ast_sip_endpoint *endpoint = mwi_data->endpoint;
pjsip_evsub_state state = mwi_data->state;
- const struct ast_sip_body *body = mwi_data->body;
struct ast_sip_contact *contact = obj;
const char *state_name;
pjsip_tx_data *tdata;
pjsip_sub_state_hdr *sub_state;
pjsip_event_hdr *event;
+ pjsip_from_hdr *from;
+ pjsip_sip_uri *from_uri;
const pjsip_hdr *allow_events = pjsip_evsub_get_allow_events_hdr(NULL);
+ struct ast_sip_body body;
+ struct ast_str *body_text;
+ struct ast_sip_body_data body_data = {
+ .body_type = AST_SIP_MESSAGE_ACCUMULATOR,
+ .body_data = mwi_data->counter,
+ };
if (ast_sip_create_request("NOTIFY", NULL, endpoint, NULL, contact, &tdata)) {
ast_log(LOG_WARNING, "Unable to create unsolicited NOTIFY request to endpoint %s URI %s\n", sub->id, contact->uri);
return 0;
}
- if (!ast_strlen_zero(endpoint->subscription.mwi.fromuser)) {
- pjsip_fromto_hdr *from = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL);
- pjsip_name_addr *from_name_addr = (pjsip_name_addr *) from->uri;
- pjsip_sip_uri *from_uri = pjsip_uri_get_uri(from_name_addr->uri);
+ body.type = MWI_TYPE;
+ body.subtype = MWI_SUBTYPE;
+ body_text = ast_str_create(64);
+ if (!body_text) {
+ return 0;
+ }
+ from = PJSIP_MSG_FROM_HDR(tdata->msg);
+ from_uri = pjsip_uri_get_uri(from->uri);
+
+ if (!ast_strlen_zero(endpoint->subscription.mwi.fromuser)) {
pj_strdup2(tdata->pool, &from_uri->user, endpoint->subscription.mwi.fromuser);
}
+ set_voicemail_extension(tdata->pool, from_uri, mwi_data->counter, endpoint->subscription.mwi.voicemail_extension);
+
+ if (ast_sip_pubsub_generate_body_content(body.type, body.subtype, &body_data, &body_text)) {
+ ast_log(LOG_WARNING, "Unable to generate SIP MWI NOTIFY body.\n");
+ ast_free(body_text);
+ return 0;
+ }
+
+ body.body_text = ast_str_buffer(body_text);
+
switch (state) {
case PJSIP_EVSUB_STATE_ACTIVE:
state_name = "active";
@@ -379,12 +423,54 @@ static int send_unsolicited_mwi_notify_to_contact(void *obj, void *arg, int flag
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) event);
pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, allow_events));
- ast_sip_add_body(tdata, body);
+ ast_sip_add_body(tdata, &body);
ast_sip_send_request(tdata, NULL, endpoint, NULL, NULL);
+ ast_free(body_text);
+
return 0;
}
+static struct ast_sip_aor *find_aor_for_resource(struct ast_sip_endpoint *endpoint, const char *resource)
+{
+ struct ast_sip_aor *aor;
+ char *aor_name;
+ char *aors_copy;
+
+ /* Direct match */
+ if ((aor = ast_sip_location_retrieve_aor(resource))) {
+ return aor;
+ }
+
+ if (!endpoint) {
+ return NULL;
+ }
+
+ /*
+ * This may be a subscribe to the voicemail_extension. If so,
+ * look for an aor belonging to this endpoint that has a matching
+ * voicemail_extension.
+ */
+ aors_copy = ast_strdupa(endpoint->aors);
+ while ((aor_name = ast_strip(strsep(&aors_copy, ",")))) {
+ struct ast_sip_aor *check_aor = ast_sip_location_retrieve_aor(aor_name);
+
+ if (!check_aor) {
+ continue;
+ }
+
+ if (!ast_strlen_zero(check_aor->voicemail_extension)
+ && !strcasecmp(check_aor->voicemail_extension, resource)) {
+ ast_debug(1, "Found an aor (%s) that matches voicemail_extension %s\n", aor_name, resource);
+ return check_aor;
+ }
+
+ ao2_ref(check_aor, -1);
+ }
+
+ return NULL;
+}
+
static void send_unsolicited_mwi_notify(struct mwi_subscription *sub,
struct ast_sip_message_accumulator *counter)
{
@@ -392,12 +478,6 @@ static void send_unsolicited_mwi_notify(struct mwi_subscription *sub,
"endpoint", sub->id), ao2_cleanup);
char *endpoint_aors;
char *aor_name;
- struct ast_sip_body body;
- struct ast_str *body_text;
- struct ast_sip_body_data body_data = {
- .body_type = AST_SIP_MESSAGE_ACCUMULATOR,
- .body_data = counter,
- };
if (!endpoint) {
ast_log(LOG_WARNING, "Unable to send unsolicited MWI to %s because endpoint does not exist\n",
@@ -410,23 +490,6 @@ static void send_unsolicited_mwi_notify(struct mwi_subscription *sub,
return;
}
- body.type = MWI_TYPE;
- body.subtype = MWI_SUBTYPE;
-
- body_text = ast_str_create(64);
-
- if (!body_text) {
- return;
- }
-
- if (ast_sip_pubsub_generate_body_content(body.type, body.subtype, &body_data, &body_text)) {
- ast_log(LOG_WARNING, "Unable to generate SIP MWI NOTIFY body.\n");
- ast_free(body_text);
- return;
- }
-
- body.body_text = ast_str_buffer(body_text);
-
endpoint_aors = ast_strdupa(endpoint->aors);
ast_debug(5, "Sending unsolicited MWI NOTIFY to endpoint %s, new messages: %d, old messages: %d\n",
@@ -438,7 +501,7 @@ static void send_unsolicited_mwi_notify(struct mwi_subscription *sub,
struct unsolicited_mwi_data mwi_data = {
.sub = sub,
.endpoint = endpoint,
- .body = &body,
+ .counter = counter,
};
if (!aor) {
@@ -454,8 +517,6 @@ static void send_unsolicited_mwi_notify(struct mwi_subscription *sub,
ao2_callback(contacts, OBJ_NODATA, send_unsolicited_mwi_notify_to_contact, &mwi_data);
}
-
- ast_free(body_text);
}
static void send_mwi_notify(struct mwi_subscription *sub)
@@ -463,16 +524,30 @@ static void send_mwi_notify(struct mwi_subscription *sub)
struct ast_sip_message_accumulator counter = {
.old_msgs = 0,
.new_msgs = 0,
+ .message_account[0] = '\0',
};
struct ast_sip_body_data data = {
.body_type = AST_SIP_MESSAGE_ACCUMULATOR,
.body_data = &counter,
};
+ const char *resource = ast_sip_subscription_get_resource_name(sub->sip_sub);
ao2_callback(sub->stasis_subs, OBJ_NODATA, get_message_count, &counter);
if (sub->is_solicited) {
+ struct ast_sip_endpoint *endpoint = ast_sip_subscription_get_endpoint(sub->sip_sub);
+ struct ast_sip_aor *aor = find_aor_for_resource(endpoint, resource);
+ pjsip_dialog *dlg = ast_sip_subscription_get_dialog(sub->sip_sub);
+ pjsip_sip_uri *sip_uri = ast_sip_subscription_get_sip_uri(sub->sip_sub);
+
+ if (aor && dlg && sip_uri) {
+ set_voicemail_extension(dlg->pool, sip_uri, &counter, aor->voicemail_extension);
+ }
+
+ ao2_cleanup(aor);
+ ao2_cleanup(endpoint);
ast_sip_subscription_notify(sub->sip_sub, &data, 0);
+
return;
}
@@ -565,7 +640,12 @@ static int endpoint_receives_unsolicited_mwi_for_mailbox(struct ast_sip_endpoint
mwi_stasis = ao2_find(mwi_sub->stasis_subs, mailbox, OBJ_SEARCH_KEY);
if (mwi_stasis) {
- ret = 1;
+ if (endpoint->subscription.mwi.subscribe_replaces_unsolicited) {
+ unsubscribe_stasis(mwi_stasis, NULL, 0);
+ ao2_unlink(mwi_sub->stasis_subs, mwi_stasis);
+ } else {
+ ret = 1;
+ }
ao2_cleanup(mwi_stasis);
}
}
@@ -670,14 +750,9 @@ static struct mwi_subscription *mwi_subscribe_single(
struct ast_sip_aor *aor;
struct mwi_subscription *sub;
- aor = ast_sip_location_retrieve_aor(name);
+ aor = find_aor_for_resource(endpoint, name);
if (!aor) {
- /*! I suppose it's possible for the AOR to disappear on us
- * between accepting the subscription and sending the first
- * NOTIFY...
- */
- ast_log(LOG_WARNING, "Unable to locate aor %s. MWI subscription failed.\n",
- name);
+ ast_log(LOG_WARNING, "Unable to locate aor %s. MWI subscription failed.\n", name);
return NULL;
}
@@ -716,10 +791,9 @@ static int mwi_new_subscribe(struct ast_sip_endpoint *endpoint,
return 200;
}
- aor = ast_sip_location_retrieve_aor(resource);
+ aor = find_aor_for_resource(endpoint, resource);
if (!aor) {
- ast_debug(1, "Unable to locate aor %s. MWI subscription failed.\n",
- resource);
+ ast_debug(1, "Unable to locate aor %s. MWI subscription failed.\n", resource);
return 404;
}
@@ -771,6 +845,8 @@ static void *mwi_get_notify_data(struct ast_sip_subscription *sub)
struct ast_sip_message_accumulator *counter;
struct mwi_subscription *mwi_sub;
struct ast_datastore *mwi_datastore;
+ struct ast_sip_aor *aor;
+ struct ast_sip_endpoint *endpoint = ast_sip_subscription_get_endpoint(sub);
mwi_datastore = ast_sip_subscription_get_datastore(sub, MWI_DATASTORE);
if (!mwi_datastore) {
@@ -784,6 +860,17 @@ static void *mwi_get_notify_data(struct ast_sip_subscription *sub)
return NULL;
}
+ if ((aor = find_aor_for_resource(endpoint, ast_sip_subscription_get_resource_name(sub)))) {
+ pjsip_dialog *dlg = ast_sip_subscription_get_dialog(sub);
+ pjsip_sip_uri *sip_uri = ast_sip_subscription_get_sip_uri(sub);
+
+ if (dlg && sip_uri) {
+ set_voicemail_extension(dlg->pool, sip_uri, counter, aor->voicemail_extension);
+ }
+ ao2_ref(aor, -1);
+ }
+ ao2_cleanup(endpoint);
+
ao2_callback(mwi_sub->stasis_subs, OBJ_NODATA, get_message_count, counter);
ao2_cleanup(mwi_datastore);
return counter;
@@ -1084,6 +1171,16 @@ static void mwi_startup_event_cb(void *data, struct stasis_subscription *sub, st
stasis_unsubscribe(sub);
}
+static void global_loaded(const char *object_type)
+{
+ ast_free(default_voicemail_extension);
+ default_voicemail_extension = ast_sip_get_default_voicemail_extension();
+}
+
+static struct ast_sorcery_observer global_observer = {
+ .loaded = global_loaded,
+};
+
static int reload(void)
{
create_mwi_subscriptions();
@@ -1106,6 +1203,8 @@ static int load_module(void)
create_mwi_subscriptions();
ast_sorcery_observer_add(ast_sip_get_sorcery(), "contact", &mwi_contact_observer);
+ ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &global_observer);
+ ast_sorcery_reload_object(ast_sip_get_sorcery(), "global");
if (ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) {
ast_sip_push_task(NULL, send_initial_notify_all, NULL);
@@ -1120,8 +1219,10 @@ static int unload_module(void)
{
ao2_callback(unsolicited_mwi, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe, NULL);
ao2_ref(unsolicited_mwi, -1);
+ ast_sorcery_observer_remove(ast_sip_get_sorcery(), "global", &global_observer);
ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &mwi_contact_observer);
ast_sip_unregister_subscription_handler(&mwi_handler);
+ ast_free(default_voicemail_extension);
return 0;
}
diff --git a/res/res_pjsip_mwi_body_generator.c b/res/res_pjsip_mwi_body_generator.c
index e4b39d534..f46ce04e3 100644
--- a/res/res_pjsip_mwi_body_generator.c
+++ b/res/res_pjsip_mwi_body_generator.c
@@ -46,7 +46,7 @@ static void *mwi_allocate_body(void *data)
if (!mwi_str) {
return NULL;
}
- *mwi_str = ast_str_create(64);
+ *mwi_str = ast_str_create(128);
if (!*mwi_str) {
ast_free(mwi_str);
return NULL;
@@ -63,6 +63,9 @@ static int mwi_generate_body_content(void *body, void *data)
counter->new_msgs ? "yes" : "no");
ast_str_append(mwi, 0, "Voice-Message: %d/%d (0/0)\r\n",
counter->new_msgs, counter->old_msgs);
+ if (!ast_strlen_zero(counter->message_account)) {
+ ast_str_append(mwi, 0, "Message-Account: %s\r\n", counter->message_account);
+ }
return 0;
}
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index 57ca95d8c..141c2fcf4 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -1644,6 +1644,12 @@ struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_su
return sub;
}
+pjsip_dialog *ast_sip_subscription_get_dialog(struct ast_sip_subscription *sub)
+{
+ ast_assert(sub->tree->dlg != NULL);
+ return sub->tree->dlg;
+}
+
struct ast_sip_endpoint *ast_sip_subscription_get_endpoint(struct ast_sip_subscription *sub)
{
ast_assert(sub->tree->endpoint != NULL);
@@ -2271,6 +2277,11 @@ int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip
return res;
}
+pjsip_sip_uri *ast_sip_subscription_get_sip_uri(struct ast_sip_subscription *sub)
+{
+ return sub->uri;
+}
+
void ast_sip_subscription_get_local_uri(struct ast_sip_subscription *sub, char *buf, size_t size)
{
pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, sub->uri, buf, size);
diff --git a/res/res_pjsip_pubsub.exports.in b/res/res_pjsip_pubsub.exports.in
index 661652489..a75103bb6 100644
--- a/res/res_pjsip_pubsub.exports.in
+++ b/res/res_pjsip_pubsub.exports.in
@@ -1,44 +1,6 @@
{
global:
- LINKER_SYMBOL_PREFIXast_sip_create_subscription;
- LINKER_SYMBOL_PREFIXast_sip_subscription_get_endpoint;
- LINKER_SYMBOL_PREFIXast_sip_subscription_get_serializer;
- LINKER_SYMBOL_PREFIXast_sip_subscription_get_evsub;
- LINKER_SYMBOL_PREFIXast_sip_subscription_get_dlg;
- LINKER_SYMBOL_PREFIXast_sip_subscription_accept;
- LINKER_SYMBOL_PREFIXast_sip_subscription_send_request;
- LINKER_SYMBOL_PREFIXast_sip_subscription_alloc_datastore;
- LINKER_SYMBOL_PREFIXast_sip_subscription_add_datastore;
- LINKER_SYMBOL_PREFIXast_sip_subscription_get_datastore;
- LINKER_SYMBOL_PREFIXast_sip_subscription_remove_datastore;
- LINKER_SYMBOL_PREFIXast_sip_register_subscription_handler;
- LINKER_SYMBOL_PREFIXast_sip_unregister_subscription_handler;
- LINKER_SYMBOL_PREFIXast_sip_create_publication;
- LINKER_SYMBOL_PREFIXast_sip_publication_get_endpoint;
- LINKER_SYMBOL_PREFIXast_sip_publication_get_resource;
- LINKER_SYMBOL_PREFIXast_sip_publication_get_event_configuration;
- LINKER_SYMBOL_PREFIXast_sip_publication_create_response;
- LINKER_SYMBOL_PREFIXast_sip_publication_send_response;
- LINKER_SYMBOL_PREFIXast_sip_register_publish_handler;
- LINKER_SYMBOL_PREFIXast_sip_unregister_publish_handler;
- LINKER_SYMBOL_PREFIXast_sip_publication_add_datastore;
- LINKER_SYMBOL_PREFIXast_sip_publication_get_datastore;
- LINKER_SYMBOL_PREFIXast_sip_publication_remove_datastore;
- LINKER_SYMBOL_PREFIXast_sip_publication_remove_datastore;
- LINKER_SYMBOL_PREFIXast_sip_pubsub_register_body_generator;
- LINKER_SYMBOL_PREFIXast_sip_pubsub_unregister_body_generator;
- LINKER_SYMBOL_PREFIXast_sip_pubsub_register_body_supplement;
- LINKER_SYMBOL_PREFIXast_sip_pubsub_unregister_body_supplement;
- LINKER_SYMBOL_PREFIXast_sip_pubsub_generate_body_content;
- LINKER_SYMBOL_PREFIXast_sip_subscription_get_body_type;
- LINKER_SYMBOL_PREFIXast_sip_subscription_get_body_subtype;
- LINKER_SYMBOL_PREFIXast_sip_subscription_get_resource_name;
- LINKER_SYMBOL_PREFIXast_sip_subscription_notify;
- LINKER_SYMBOL_PREFIXast_sip_subscription_get_local_uri;
- LINKER_SYMBOL_PREFIXast_sip_subscription_get_remote_uri;
- LINKER_SYMBOL_PREFIXast_sip_subscription_get_header;
- LINKER_SYMBOL_PREFIXast_sip_subscription_is_terminated;
- LINKER_SYMBOL_PREFIXast_sip_subscription_destroy;
+ LINKER_SYMBOL_PREFIXast_sip_*;
local:
*;
};
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index 45bc3103a..9e3ff757c 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -1982,19 +1982,20 @@ static int dtls_srtp_renegotiate(const void *data)
return 0;
}
-static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct ast_rtp_instance *instance)
+static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct ast_rtp_instance *instance, int rtcp)
{
unsigned char material[SRTP_MASTER_LEN * 2];
unsigned char *local_key, *local_salt, *remote_key, *remote_salt;
struct ast_srtp_policy *local_policy, *remote_policy = NULL;
struct ast_rtp_instance_stats stats = { 0, };
int res = -1;
+ struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls;
/* If a fingerprint is present in the SDP make sure that the peer certificate matches it */
if (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_FINGERPRINT) {
X509 *certificate;
- if (!(certificate = SSL_get_peer_certificate(rtp->dtls.ssl))) {
+ if (!(certificate = SSL_get_peer_certificate(dtls->ssl))) {
ast_log(LOG_WARNING, "No certificate was provided by the peer on RTP instance '%p'\n", instance);
return -1;
}
@@ -2028,14 +2029,14 @@ static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct as
}
/* Ensure that certificate verification was successful */
- if ((rtp->dtls_verify & AST_RTP_DTLS_VERIFY_CERTIFICATE) && SSL_get_verify_result(rtp->dtls.ssl) != X509_V_OK) {
+ if ((rtp->dtls_verify & AST_RTP_DTLS_VERIFY_CERTIFICATE) && SSL_get_verify_result(dtls->ssl) != X509_V_OK) {
ast_log(LOG_WARNING, "Peer certificate on RTP instance '%p' failed verification test\n",
instance);
return -1;
}
/* Produce key information and set up SRTP */
- if (!SSL_export_keying_material(rtp->dtls.ssl, material, SRTP_MASTER_LEN * 2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) {
+ if (!SSL_export_keying_material(dtls->ssl, material, SRTP_MASTER_LEN * 2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) {
ast_log(LOG_WARNING, "Unable to extract SRTP keying material from DTLS-SRTP negotiation on RTP instance '%p'\n",
instance);
return -1;
@@ -2090,7 +2091,7 @@ static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct as
res_srtp_policy->set_ssrc(remote_policy, 0, 1);
- if (ast_rtp_instance_add_srtp_policy(instance, remote_policy, local_policy)) {
+ if (ast_rtp_instance_add_srtp_policy(instance, remote_policy, local_policy, rtcp)) {
ast_log(LOG_WARNING, "Could not set policies when setting up DTLS-SRTP on '%p'\n", rtp);
goto error;
}
@@ -2121,7 +2122,7 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
{
int len;
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
- struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance);
+ struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance, rtcp);
char *in = buf;
#ifdef HAVE_PJPROJECT
struct ast_sockaddr *loop = rtcp ? &rtp->rtcp_loop : &rtp->rtp_loop;
@@ -2178,10 +2179,8 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
if (SSL_is_init_finished(dtls->ssl)) {
/* Any further connections will be existing since this is now established */
dtls->connection = AST_RTP_DTLS_CONNECTION_EXISTING;
- if (!rtcp) {
- /* Use the keying material to set up key/salt information */
- res = dtls_srtp_setup(rtp, srtp, instance);
- }
+ /* Use the keying material to set up key/salt information */
+ res = dtls_srtp_setup(rtp, srtp, instance, rtcp);
} else {
/* Since we've sent additional traffic start the timeout timer for retransmission */
dtls_srtp_start_timeout_timer(instance, rtp, rtcp);
@@ -2250,9 +2249,8 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz
int len = size;
void *temp = buf;
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
- struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance);
+ struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance, rtcp);
int res;
- int hdrlen = 12;
*ice = 0;
@@ -2260,9 +2258,6 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz
return -1;
}
- rtp->txcount++;
- rtp->txoctetcount += (len - hdrlen);
-
#ifdef HAVE_PJPROJECT
if (rtp->ice) {
pj_thread_register_check();
@@ -2289,7 +2284,16 @@ static int rtcp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size
static int rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int *ice)
{
- return __rtp_sendto(instance, buf, size, flags, sa, 0, ice, 1);
+ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+ int hdrlen = 12;
+ int res;
+
+ if ((res = __rtp_sendto(instance, buf, size, flags, sa, 0, ice, 1)) > 0) {
+ rtp->txcount++;
+ rtp->txoctetcount += (res - hdrlen);
+ }
+
+ return res;
}
static int rtp_get_rate(struct ast_format *format)
@@ -2955,7 +2959,8 @@ static void ast_rtp_update_source(struct ast_rtp_instance *instance)
static void ast_rtp_change_source(struct ast_rtp_instance *instance)
{
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
- struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance);
+ struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance, 0);
+ struct ast_srtp *rtcp_srtp = ast_rtp_instance_get_srtp(instance, 1);
unsigned int ssrc = ast_random();
if (!rtp->lastts) {
@@ -2971,6 +2976,9 @@ static void ast_rtp_change_source(struct ast_rtp_instance *instance)
if (srtp) {
ast_debug(3, "Changing ssrc for SRTP from %u to %u\n", rtp->ssrc, ssrc);
res_srtp->change_source(srtp, rtp->ssrc, ssrc);
+ if (rtcp_srtp != srtp) {
+ res_srtp->change_source(srtp, rtp->ssrc, ssrc);
+ }
}
rtp->ssrc = ssrc;
diff --git a/res/res_stasis.c b/res/res_stasis.c
index 63c565d44..5aa0aa9ac 100644
--- a/res/res_stasis.c
+++ b/res/res_stasis.c
@@ -1179,6 +1179,11 @@ int stasis_app_control_is_done(struct stasis_app_control *control)
return control_is_done(control);
}
+void stasis_app_control_flush_queue(struct stasis_app_control *control)
+{
+ control_flush_queue(control);
+}
+
struct ast_datastore_info set_end_published_info = {
.type = "stasis_end_published",
};
@@ -1188,10 +1193,11 @@ void stasis_app_channel_set_stasis_end_published(struct ast_channel *chan)
struct ast_datastore *datastore;
datastore = ast_datastore_alloc(&set_end_published_info, NULL);
-
- ast_channel_lock(chan);
- ast_channel_datastore_add(chan, datastore);
- ast_channel_unlock(chan);
+ if (datastore) {
+ ast_channel_lock(chan);
+ ast_channel_datastore_add(chan, datastore);
+ ast_channel_unlock(chan);
+ }
}
int stasis_app_channel_is_stasis_end_published(struct ast_channel *chan)
@@ -1211,12 +1217,11 @@ static void remove_stasis_end_published(struct ast_channel *chan)
ast_channel_lock(chan);
datastore = ast_channel_datastore_find(chan, &set_end_published_info, NULL);
- ast_channel_unlock(chan);
-
if (datastore) {
ast_channel_datastore_remove(chan, datastore);
ast_datastore_free(datastore);
}
+ ast_channel_unlock(chan);
}
/*! /brief Stasis dialplan application callback */
@@ -1371,6 +1376,11 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
remove_stasis_end_published(chan);
}
+ control_flush_queue(control);
+
+ /* Stop any lingering silence generator */
+ control_silence_stop_now(control);
+
/* There's an off chance that app is ready for cleanup. Go ahead
* and clean up, just in case
*/
diff --git a/res/res_stasis_playback.c b/res/res_stasis_playback.c
index 74336abdc..97191c26d 100644
--- a/res/res_stasis_playback.c
+++ b/res/res_stasis_playback.c
@@ -118,6 +118,7 @@ static void playback_dtor(void *obj)
{
struct stasis_app_playback *playback = obj;
+ ao2_cleanup(playback->control);
ast_string_field_free_memory(playback);
}
@@ -143,6 +144,7 @@ static struct stasis_app_playback *playback_create(
ast_string_field_set(playback, id, uuid);
}
+ ao2_ref(control, +1);
playback->control = control;
ao2_ref(playback, +1);
diff --git a/res/res_stasis_recording.c b/res/res_stasis_recording.c
index dcabfa699..af5c41e87 100644
--- a/res/res_stasis_recording.c
+++ b/res/res_stasis_recording.c
@@ -265,7 +265,13 @@ static enum stasis_app_control_channel_result check_rule_recording(
return STASIS_APP_CHANNEL_RECORDING;
}
-struct stasis_app_control_rule rule_recording = {
+/*
+ * XXX This only works because there is one and only one rule in
+ * the system so it can be added to any number of channels
+ * without issue. However, as soon as there is another rule then
+ * watch out for weirdness because of cross linked lists.
+ */
+static struct stasis_app_control_rule rule_recording = {
.check_rule = check_rule_recording
};
@@ -358,6 +364,7 @@ static void recording_dtor(void *obj)
struct stasis_app_recording *recording = obj;
ast_free(recording->absolute_name);
+ ao2_cleanup(recording->control);
ao2_cleanup(recording->options);
}
@@ -413,6 +420,7 @@ struct stasis_app_recording *stasis_app_control_record(
ao2_ref(options, +1);
recording->options = options;
+ ao2_ref(control, +1);
recording->control = control;
recording->state = STASIS_APP_RECORDING_STATE_QUEUED;
@@ -465,15 +473,7 @@ const char *stasis_app_recording_get_name(
struct stasis_app_recording *stasis_app_recording_find_by_name(const char *name)
{
- RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup);
-
- recording = ao2_find(recordings, name, OBJ_KEY);
- if (recording == NULL) {
- return NULL;
- }
-
- ao2_ref(recording, +1);
- return recording;
+ return ao2_find(recordings, name, OBJ_KEY);
}
struct ast_json *stasis_app_recording_to_json(
diff --git a/res/stasis/control.c b/res/stasis/control.c
index 41d538cbe..86f94423d 100644
--- a/res/stasis/control.c
+++ b/res/stasis/control.c
@@ -87,21 +87,19 @@ static void control_dtor(void *obj)
{
struct stasis_app_control *control = obj;
- AST_LIST_HEAD_DESTROY(&control->add_rules);
- AST_LIST_HEAD_DESTROY(&control->remove_rules);
+ ao2_cleanup(control->command_queue);
- /* We may have a lingering silence generator; free it */
- ast_channel_stop_silence_generator(control->channel, control->silgen);
- control->silgen = NULL;
+ ast_channel_cleanup(control->channel);
+ ao2_cleanup(control->app);
- ao2_cleanup(control->command_queue);
ast_cond_destroy(&control->wait_cond);
- ao2_cleanup(control->app);
+ AST_LIST_HEAD_DESTROY(&control->add_rules);
+ AST_LIST_HEAD_DESTROY(&control->remove_rules);
}
struct stasis_app_control *control_create(struct ast_channel *channel, struct stasis_app *app)
{
- RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
+ struct stasis_app_control *control;
int res;
control = ao2_alloc(sizeof(*control), control_dtor);
@@ -109,28 +107,29 @@ struct stasis_app_control *control_create(struct ast_channel *channel, struct st
return NULL;
}
- control->app = ao2_bump(app);
+ AST_LIST_HEAD_INIT(&control->add_rules);
+ AST_LIST_HEAD_INIT(&control->remove_rules);
res = ast_cond_init(&control->wait_cond, NULL);
if (res != 0) {
ast_log(LOG_ERROR, "Error initializing ast_cond_t: %s\n",
strerror(errno));
+ ao2_ref(control, -1);
return NULL;
}
+ control->app = ao2_bump(app);
+
+ ast_channel_ref(channel);
+ control->channel = channel;
+
control->command_queue = ao2_container_alloc_list(
AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, NULL);
-
if (!control->command_queue) {
+ ao2_ref(control, -1);
return NULL;
}
- control->channel = channel;
-
- AST_LIST_HEAD_INIT(&control->add_rules);
- AST_LIST_HEAD_INIT(&control->remove_rules);
-
- ao2_ref(control, +1);
return control;
}
@@ -252,6 +251,11 @@ static struct stasis_app_command *exec_command_on_condition(
}
ao2_lock(control->command_queue);
+ if (control->is_done) {
+ ao2_unlock(control->command_queue);
+ ao2_ref(command, -1);
+ return NULL;
+ }
if (can_exec_fn && (retval = can_exec_fn(control))) {
ao2_unlock(control->command_queue);
command_complete(command, retval);
@@ -403,7 +407,10 @@ int control_is_done(struct stasis_app_control *control)
void control_mark_done(struct stasis_app_control *control)
{
+ /* Locking necessary to sync with other threads adding commands to the queue. */
+ ao2_lock(control->command_queue);
control->is_done = 1;
+ ao2_unlock(control->command_queue);
}
struct stasis_app_control_continue_data {
@@ -428,7 +435,7 @@ static int app_control_continue(struct stasis_app_control *control,
/* Called from stasis_app_exec thread; no lock needed */
ast_explicit_goto(control->channel, continue_data->context, continue_data->extension, continue_data->priority);
- control->is_done = 1;
+ control_mark_done(control);
return 0;
}
@@ -785,8 +792,7 @@ void stasis_app_control_silence_start(struct stasis_app_control *control)
stasis_app_send_command_async(control, app_control_silence_start, NULL, NULL);
}
-static int app_control_silence_stop(struct stasis_app_control *control,
- struct ast_channel *chan, void *data)
+void control_silence_stop_now(struct stasis_app_control *control)
{
if (control->silgen) {
ast_debug(3, "%s: Stopping silence generator\n",
@@ -795,7 +801,12 @@ static int app_control_silence_stop(struct stasis_app_control *control,
control->channel, control->silgen);
control->silgen = NULL;
}
+}
+static int app_control_silence_stop(struct stasis_app_control *control,
+ struct ast_channel *chan, void *data)
+{
+ control_silence_stop_now(control);
return 0;
}
@@ -1112,24 +1123,36 @@ int stasis_app_control_queue_control(struct stasis_app_control *control,
return ast_queue_control(control->channel, frame_type);
}
+void control_flush_queue(struct stasis_app_control *control)
+{
+ struct ao2_iterator iter;
+ struct stasis_app_command *command;
+
+ iter = ao2_iterator_init(control->command_queue, AO2_ITERATOR_UNLINK);
+ while ((command = ao2_iterator_next(&iter))) {
+ command_complete(command, -1);
+ ao2_ref(command, -1);
+ }
+ ao2_iterator_destroy(&iter);
+}
+
int control_dispatch_all(struct stasis_app_control *control,
struct ast_channel *chan)
{
int count = 0;
- struct ao2_iterator i;
- void *obj;
+ struct ao2_iterator iter;
+ struct stasis_app_command *command;
ast_assert(control->channel == chan);
- i = ao2_iterator_init(control->command_queue, AO2_ITERATOR_UNLINK);
-
- while ((obj = ao2_iterator_next(&i))) {
- RAII_VAR(struct stasis_app_command *, command, obj, ao2_cleanup);
+ iter = ao2_iterator_init(control->command_queue, AO2_ITERATOR_UNLINK);
+ while ((command = ao2_iterator_next(&iter))) {
command_invoke(command, control, chan);
+ ao2_ref(command, -1);
++count;
}
+ ao2_iterator_destroy(&iter);
- ao2_iterator_destroy(&i);
return count;
}
diff --git a/res/stasis/control.h b/res/stasis/control.h
index a139f82e4..1d37a494a 100644
--- a/res/stasis/control.h
+++ b/res/stasis/control.h
@@ -41,6 +41,16 @@
struct stasis_app_control *control_create(struct ast_channel *channel, struct stasis_app *app);
/*!
+ * \brief Flush the control command queue.
+ * \since 13.9.0
+ *
+ * \param control Control object to flush command queue.
+ *
+ * \return Nothing
+ */
+void control_flush_queue(struct stasis_app_control *control);
+
+/*!
* \brief Dispatch all commands enqueued to this control.
*
* \param control Control object to dispatch.
@@ -108,5 +118,13 @@ int control_add_channel_to_bridge(
struct stasis_app_control *control,
struct ast_channel *chan, void *obj);
+/*!
+ * \brief Stop playing silence to a channel right now.
+ * \since 13.9.0
+ *
+ * \param control The control for chan
+ */
+void control_silence_stop_now(struct stasis_app_control *control);
+
#endif /* _ASTERISK_RES_STASIS_CONTROL_H */
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)