Merge "cel, cdr: Assigned separator for column name and values."
authorJoshua Colp <jcolp@digium.com>
Fri, 22 May 2015 10:16:30 +0000 (05:16 -0500)
committerGerrit Code Review <gerrit2@gerrit.digium.api>
Fri, 22 May 2015 10:16:30 +0000 (05:16 -0500)
68 files changed:
CHANGES
apps/app_playback.c
channels/chan_pjsip.c
codecs/lpc10/lpcini.c
configs/samples/pjsip.conf.sample
configs/samples/sip.conf.sample
contrib/scripts/get_mp3_source.sh
funcs/func_periodic_hook.c
include/asterisk/astmm.h
include/asterisk/audiohook.h
include/asterisk/config.h
include/asterisk/event.h
include/asterisk/http_websocket.h
include/asterisk/rtp_engine.h
include/asterisk/sem.h
include/asterisk/sorcery.h
include/asterisk/stasis.h
include/asterisk/stasis_test.h
include/asterisk/tcptls.h
main/ast_expr2.c
main/ast_expr2.y
main/ast_expr2f.c
main/astmm.c
main/audiohook.c
main/cdr.c
main/config.c
main/file.c
main/hashtab.c
main/logger.c
main/message.c
main/sdp_srtp.c
main/sorcery.c
main/tcptls.c
res/ael/ael.flex
res/ael/ael.tab.c
res/ael/ael.y
res/ael/ael_lex.c
res/ael/pval.c
res/res_fax_spandsp.c
res/res_http_websocket.c
res/res_mwi_external_ami.c
res/res_pjsip_config_wizard.c
res/res_resolver_unbound.c
res/res_stasis_test.c
res/snmp/agent.c
rest-api/api-docs/applications.json
rest-api/api-docs/asterisk.json
rest-api/api-docs/bridges.json
rest-api/api-docs/channels.json
rest-api/api-docs/deviceStates.json
rest-api/api-docs/endpoints.json
rest-api/api-docs/events.json
rest-api/api-docs/mailboxes.json
rest-api/api-docs/playbacks.json
rest-api/api-docs/recordings.json
rest-api/api-docs/sounds.json
tests/test_ari.c
tests/test_config.c
tests/test_endpoints.c
tests/test_json.c
tests/test_message.c
tests/test_optional_api.c
tests/test_res_stasis.c
tests/test_sorcery.c
tests/test_stasis.c
tests/test_stasis_channels.c
tests/test_stasis_endpoints.c
utils/extconf.c

diff --git a/CHANGES b/CHANGES
index 58291cf..12bbea4 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -111,6 +111,10 @@ Core
    set execincludes=yes in asterisk.conf.  Any other option set on the
    command-line will now override the equivalent setting from asterisk.conf.
 
+ * The TLS core in Asterisk now supports X.509 certificate subject alternative
+   names. This way one X.509 certificate can be used for hosts that can be
+   reached under multiple DNS names or for multiple hosts.
+
 Functions
 ------------------
 
index feb7b63..2875ec2 100644 (file)
@@ -490,7 +490,9 @@ static int playback_exec(struct ast_channel *chan, const char *data)
                                ast_stopstream(chan);
                        }
                        if (res) {
-                               ast_log(LOG_WARNING, "Playback failed on %s for %s\n", ast_channel_name(chan), (char *)data);
+                               if (!ast_check_hangup(chan)) {
+                                       ast_log(LOG_WARNING, "Playback failed on %s for %s\n", ast_channel_name(chan), (char *)data);
+                               }
                                res = 0;
                                mres = 1;
                        }
index 1195f16..14ba4a2 100644 (file)
@@ -1864,6 +1864,7 @@ static int request(void *obj)
        if (ast_strlen_zero(endpoint_name)) {
                ast_log(LOG_ERROR, "Unable to create PJSIP channel with empty endpoint name\n");
                req_data->cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;
+               return -1;
        } else if (!(endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
                ast_log(LOG_ERROR, "Unable to create PJSIP channel - endpoint '%s' was not found\n", endpoint_name);
                req_data->cause = AST_CAUSE_NO_ROUTE_DESTINATION;
index 8efb640..ea68176 100644 (file)
@@ -34,7 +34,7 @@ Some OSS fixes and a few lpc changes to make it actually work
        -lf2c -lm   (in that order)
 */
 
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
 #include "asterisk.h"
 #include "f2c.h"
 
index 5e37571..276e214 100644 (file)
                 ; (default: "")
 ;cert_file=     ; Certificate file for endpoint TLS ONLY
                 ; Will read .crt or .pem file but only uses cert,
-                ; a .key file must be specified via priv_key_file
+                ; a .key file must be specified via priv_key_file.
+                ; Since PJProject version 2.5: If the file name ends in _rsa,
+                ; for example "asterisk_rsa.pem", the files "asterisk_dsa.pem"
+                ; and/or "asterisk_ecc.pem" are loaded (certificate, inter-
+                ; mediates, private key), to support multiple algorithms for
+                ; server authentication (RSA, DSA, ECDSA). If the chains are
+                ; different, at least OpenSSL 1.0.2 is required.
                 ; (default: "")
 ;cipher=        ; Preferred cryptography cipher names TLS ONLY (default: "")
 ;domain=        ; Domain the transport comes from (default: "")
index e52fa6d..71e3fb7 100644 (file)
@@ -561,7 +561,12 @@ srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
 ;------------------------ TLS settings ------------------------------------------------------------
 ;tlscertfile=</path/to/certificate.pem> ; Certificate chain (*.pem format only) to use for TLS connections
                                         ; The certificates must be sorted starting with the subject's certificate
-                                        ; and followed by intermediate CA certificates if applicable.
+                                        ; and followed by intermediate CA certificates if applicable. If the
+                                        ; file name ends in _rsa, for example "asterisk_rsa.pem", the files
+                                        ; "asterisk_dsa.pem" and/or "asterisk_ecc.pem" are loaded
+                                        ; (certificate, intermediates, private key), to support multiple
+                                        ; algorithms for server authentication (RSA, DSA, ECDSA). If the chains
+                                        ; are different, at least OpenSSL 1.0.2 is required.
                                         ; Default is to look for "asterisk.pem" in current directory
 
 ;tlsprivatekey=</path/to/private.pem> ; Private key file (*.pem format only) for TLS connections.
index 860e2bc..6d98465 100755 (executable)
@@ -7,8 +7,8 @@ if [ -f addons/mp3/mpg123.h ]; then
     echo "***"
 
     # Manually patch interface.c if not done yet.
-    if ! grep -q WRAP_LIBC_MALLOC addons/mp3/interface.c; then
-        sed -i -e '/#include "asterisk.h"/i#define WRAP_LIBC_MALLOC' \
+    if ! grep -q ASTMM_LIBC addons/mp3/interface.c; then
+        sed -i -e '/#include "asterisk.h"/i#define ASTMM_LIBC ASTMM_REDIRECT' \
             addons/mp3/interface.c
     fi
 
@@ -18,8 +18,8 @@ fi
 svn export http://svn.digium.com/svn/thirdparty/mp3/trunk addons/mp3 $@
 
 # Manually patch interface.c if not done yet.
-if ! grep -q WRAP_LIBC_MALLOC addons/mp3/interface.c; then
-    sed -i -e '/#include "asterisk.h"/i#define WRAP_LIBC_MALLOC' \
+if ! grep -q ASTMM_LIBC addons/mp3/interface.c; then
+    sed -i -e '/#include "asterisk.h"/i#define ASTMM_LIBC ASTMM_REDIRECT' \
         addons/mp3/interface.c
 fi
 
index 6ddab56..bb0ee0d 100644 (file)
@@ -446,13 +446,9 @@ static struct ast_custom_function hook_function = {
        .write = hook_write,
 };
 
-static struct ast_context *func_periodic_hook_context;
-
 static int unload_module(void)
 {
-       if (func_periodic_hook_context) {
-               ast_context_destroy(func_periodic_hook_context, AST_MODULE);
-       }
+       ast_context_destroy(NULL, AST_MODULE);
 
        return ast_custom_function_unregister(&hook_function);
 }
@@ -461,9 +457,7 @@ static int load_module(void)
 {
        int res;
 
-       func_periodic_hook_context = ast_context_find_or_create(NULL, NULL,
-                       context_name, AST_MODULE);
-       if (!func_periodic_hook_context) {
+       if (!ast_context_find_or_create(NULL, NULL, context_name, AST_MODULE)) {
                ast_log(LOG_ERROR, "Failed to create %s dialplan context.\n", context_name);
                return AST_MODULE_LOAD_DECLINE;
        }
index 1d778d4..6c9a8ae 100644 (file)
@@ -44,16 +44,6 @@ extern "C" {
 #include <stdio.h>
 #include <stdarg.h>
 
-/* Undefine any macros */
-#undef malloc
-#undef calloc
-#undef realloc
-#undef strdup
-#undef strndup
-#undef asprintf
-#undef vasprintf
-#undef free
-
 void *ast_std_malloc(size_t size);
 void *ast_std_calloc(size_t nmemb, size_t size);
 void *ast_std_realloc(void *ptr, size_t size);
@@ -74,9 +64,72 @@ int __ast_vasprintf(char **strp, const char *format, va_list ap, const char *fil
 void __ast_mm_init_phase_1(void);
 void __ast_mm_init_phase_2(void);
 
-/* Redefine libc malloc to our own versions */
+/*!
+ * \brief ASTMM_LIBC can be defined to control the meaning of standard allocators.
+ *
+ * \note The standard allocators effected by this compiler define are:
+ *    malloc, calloc, realloc, strdup, strndup, asprintf, vasprintf and free.
+ *
+ * @{
+ */
+
+/*!
+ * \brief Produce compiler errors if standard allocators are used.
+ *
+ * \note This is the default option, and in most cases the correct option.
+ * Any use of standard allocators will cause an error, even if those uses
+ * are in unused static inline header functions.
+ */
+#define ASTMM_BLOCK    0
+
+/*!
+ * \brief Redirect standard allocators to use Asterisk functions.
+ *
+ * \note This option is used in some cases instead of changing the
+ * existing source to use Asterisk functions.  New code should
+ * generally avoid this option, except where it's needed to work
+ * with situations where switching the code is unreasonable, such
+ * as output from code generators that are hard coded to use
+ * standard functions.
+ */
+#define ASTMM_REDIRECT 1
+
+/*!
+ * \brief Standard allocators are used directly.
+ *
+ * \note This option is needed when including 3rd party headers with calls
+ * to standard allocators from inline functions.  Using ASTMM_REDIRECT in
+ * this situation could result in an object being allocated by malloc and
+ * freed by ast_free, or the reverse.
+ */
+#define ASTMM_IGNORE   2
+
+/*!
+ * }@
+ */
+
+#if !defined(ASTMM_LIBC)
+/* BLOCK libc allocators by default. */
+#define ASTMM_LIBC ASTMM_BLOCK
+#endif
 
-#ifdef WRAP_LIBC_MALLOC
+#if ASTMM_LIBC == ASTMM_IGNORE
+/* Don't touch the libc functions. */
+#else
+
+/* Undefine any macros */
+#undef malloc
+#undef calloc
+#undef realloc
+#undef strdup
+#undef strndup
+#undef asprintf
+#undef vasprintf
+#undef free
+
+#if ASTMM_LIBC == ASTMM_REDIRECT
+
+/* Redefine libc functions to our own versions */
 #define calloc(a,b) \
        __ast_calloc(a,b,__FILE__, __LINE__, __PRETTY_FUNCTION__)
 #define malloc(a) \
@@ -93,7 +146,10 @@ void __ast_mm_init_phase_2(void);
        __ast_asprintf(__FILE__, __LINE__, __PRETTY_FUNCTION__, a, b, c)
 #define vasprintf(a,b,c) \
        __ast_vasprintf(a,b,c,__FILE__, __LINE__, __PRETTY_FUNCTION__)
-#else
+
+#elif ASTMM_LIBC == ASTMM_BLOCK
+
+/* Redefine libc functions to cause compile errors */
 #define calloc(a,b) \
        Do_not_use_calloc__use_ast_calloc->fail(a,b)
 #define malloc(a) \
@@ -110,6 +166,11 @@ void __ast_mm_init_phase_2(void);
        Do_not_use_asprintf__use_ast_asprintf->fail(a,b,c)
 #define vasprintf(a,b,c) \
        Do_not_use_vasprintf__use_ast_vasprintf->fail(a,b,c)
+
+#else
+#error "Unacceptable value for the macro ASTMM_LIBC"
+#endif
+
 #endif
 
 /* Provide our own definitions */
index 375b2dd..cae8cc0 100644 (file)
@@ -63,6 +63,7 @@ enum ast_audiohook_flags {
        AST_AUDIOHOOK_SMALL_QUEUE   = (1 << 4),
        AST_AUDIOHOOK_MUTE_READ     = (1 << 5), /*!< audiohook should be mute frames read */
        AST_AUDIOHOOK_MUTE_WRITE    = (1 << 6), /*!< audiohook should be mute frames written */
+       AST_AUDIOHOOK_COMPATIBLE    = (1 << 7), /*!< is the audiohook native slin compatible */
 };
 
 enum ast_audiohook_init_flags {
index 6489344..bd268a3 100644 (file)
@@ -335,6 +335,23 @@ const char *ast_variable_find(const struct ast_category *category, const char *v
 const char *ast_variable_find_in_list(const struct ast_variable *list, const char *variable);
 
 /*!
+ * \brief Gets the LAST occurrence of a variable from a variable list
+ *
+ * \param list The ast_variable list to search
+ * \param variable The name of the ast_variable you wish to fetch data for
+ *
+ * \details
+ * Iterates over a given ast_variable list to search for the last occurrence of an
+ * ast_variable entry with a name attribute matching the given name (variable).
+ * This is useful if the list has duplicate entries (such as in cases where entries
+ * are created by a template)
+ *
+ * \retval The variable value on success
+ * \retval NULL if unable to find it.
+ */
+const char *ast_variable_find_last_in_list(const struct ast_variable *list, const char *variable);
+
+/*!
  * \brief Retrieve a category if it exists
  *
  * \param config which config to use
index 7eea058..dbc080d 100644 (file)
@@ -35,7 +35,7 @@
  * modules in Asterisk.
  *  - CEL uses the \ref ast_event representation to pass information to registered
  *    backends.
- *  - The \file res_corosync module publishes \ref ast_event representations of
+ *  - The \file res_corosync.c module publishes \ref ast_event representations of
  *    information to other Asterisk instances in a cluster.
  *  - Security event represent their event types and data using this system.
  *  - Theoretically, any \ref stasis message can use this system to pass
index 3e07e60..5adc089 100644 (file)
@@ -68,6 +68,24 @@ struct ast_websocket_server;
 struct ast_websocket;
 
 /*!
+ * \brief Callback from the HTTP request attempting to establish a websocket connection
+ *
+ * This callback occurs when an HTTP request is made to establish a websocket connection.
+ * Implementers of \ref ast_websocket_protocol can use this to deny a request, or to
+ * set up application specific data before invocation of \ref ast_websocket_callback.
+ *
+ * \param ser The TCP/TLS session
+ * \param parameters Parameters extracted from the request URI
+ * \param headers Headers included in the request
+ *
+ * \retval 0 The session should be accepted
+ * \retval -1 The session should be rejected. Note that the caller must send an error
+ * response using \ref ast_http_error.
+ * \since 13.5.0
+ */
+typedef int (*ast_websocket_pre_callback)(struct ast_tcptls_session_instance *ser, struct ast_variable *parameters, struct ast_variable *headers);
+
+/*!
  * \brief Callback for when a new connection for a sub-protocol is established
  *
  * \param session A WebSocket session structure
@@ -81,6 +99,32 @@ struct ast_websocket;
 typedef void (*ast_websocket_callback)(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers);
 
 /*!
+ * \brief A websocket protocol implementation
+ *
+ * Users of the Websocket API can register themselves as a websocket
+ * protocol. See \ref ast_websocket_add_protocol2 and \ref ast_websocket_server_add_protocol2.
+ * Simpler implementations may use only \ref ast_websocket_add_protocol and
+ * \ref ast_websocket_server_add_protocol.
+ *
+ * \since 13.5.0
+ */
+struct ast_websocket_protocol {
+       /*! \brief Name of the protocol */
+       char *name;
+/*!
+ * \brief Protocol version. This prevents dynamically loadable modules from registering
+ * if this struct is changed.
+ */
+#define AST_WEBSOCKET_PROTOCOL_VERSION 1
+       /*! \brief Protocol version. Should be set to /ref AST_WEBSOCKET_PROTOCOL_VERSION */
+       unsigned int version;
+       /*! \brief Callback called when a new session is attempted. Optional. */
+       ast_websocket_pre_callback session_attempted;
+       /* \brief Callback called when a new session is established. Mandatory. */
+       ast_websocket_callback session_established;
+};
+
+/*!
  * \brief Creates a \ref websocket_server
  *
  * \retval New \ref websocket_server instance
@@ -98,6 +142,15 @@ AST_OPTIONAL_API(struct ast_websocket_server *, ast_websocket_server_create, (vo
 AST_OPTIONAL_API(int, ast_websocket_uri_cb, (struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers), { return -1; });
 
 /*!
+ * \brief Allocate a websocket sub-protocol instance
+ *
+ * \retval An instance of \ref ast_websocket_protocol on success
+ * \retval NULL on error
+ * \since 13.5.0
+ */
+AST_OPTIONAL_API(struct ast_websocket_protocol *, ast_websocket_sub_protocol_alloc, (const char *name), {return NULL;});
+
+/*!
  * \brief Add a sub-protocol handler to the default /ws server
  *
  * \param name Name of the sub-protocol to register
@@ -109,10 +162,25 @@ AST_OPTIONAL_API(int, ast_websocket_uri_cb, (struct ast_tcptls_session_instance
 AST_OPTIONAL_API(int, ast_websocket_add_protocol, (const char *name, ast_websocket_callback callback), {return -1;});
 
 /*!
+ * \brief Add a sub-protocol handler to the default /ws server
+ *
+ * \param protocol The sub-protocol to register. Note that this must
+ * be allocated using /ref ast_websocket_sub_protocol_alloc.
+ *
+ * \note This method is reference stealing. It will steal the reference to \ref protocol
+ * on success.
+ *
+ * \retval 0 success
+ * \retval -1 if sub-protocol handler could not be registered
+ * \since 13.5.0
+ */
+AST_OPTIONAL_API(int, ast_websocket_add_protocol2, (struct ast_websocket_protocol *protocol), {return -1;});
+
+/*!
  * \brief Remove a sub-protocol handler from the default /ws server.
  *
  * \param name Name of the sub-protocol to unregister
- * \param callback Callback that was previously registered with the sub-protocol
+ * \param callback Session Established callback that was previously registered with the sub-protocol
  *
  * \retval 0 success
  * \retval -1 if sub-protocol was not found or if callback did not match
@@ -132,6 +200,22 @@ AST_OPTIONAL_API(int, ast_websocket_remove_protocol, (const char *name, ast_webs
 AST_OPTIONAL_API(int, ast_websocket_server_add_protocol, (struct ast_websocket_server *server, const char *name, ast_websocket_callback callback), {return -1;});
 
 /*!
+ * \brief Add a sub-protocol handler to the given server.
+ *
+ * \param server The server to add the sub-protocol to.
+ * \param protocol The sub-protocol to register. Note that this must
+ * be allocated using /ref ast_websocket_sub_protocol_alloc.
+ *
+ * \note This method is reference stealing. It will steal the reference to \ref protocol
+ * on success.
+ *
+ * \retval 0 success
+ * \retval -1 if sub-protocol handler could not be registered
+ * \since 13.5.0
+ */
+AST_OPTIONAL_API(int, ast_websocket_server_add_protocol2, (struct ast_websocket_server *server, struct ast_websocket_protocol *protocol), {return -1;});
+
+/*!
  * \brief Remove a sub-protocol handler from the given server.
  *
  * \param name Name of the sub-protocol to unregister
index a94cb42..c7f6511 100644 (file)
@@ -2316,7 +2316,7 @@ struct stasis_message_type *ast_rtp_rtcp_received_type(void);
  */
 struct stasis_topic *ast_rtp_topic(void);
 
-/* }@ */
+/* @} */
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }
index 6d655d6..fcab82a 100644 (file)
@@ -20,7 +20,9 @@
 #define ASTERISK_SEMAPHORE_H
 
 /*!
- * \file Asterisk semaphore API
+ * \file
+ *
+ * \brief Asterisk semaphore API
  *
  * This API is a thin wrapper around the POSIX semaphore API (when available),
  * so see the POSIX documentation for further details.
index a5061c6..9d39ce4 100644 (file)
@@ -497,6 +497,125 @@ enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorc
 #define ast_sorcery_apply_wizard_mapping(sorcery, type, name, data, caching) \
        __ast_sorcery_apply_wizard_mapping((sorcery), (type), AST_MODULE, (name), (data), (caching));
 
+
+/*!
+ * \brief Pre-defined locations to insert at
+ */
+enum ast_sorcery_wizard_position {
+       AST_SORCERY_WIZARD_POSITION_LAST = -1,
+       AST_SORCERY_WIZARD_POSITION_FIRST = 0,
+};
+
+/*!
+ * \brief Insert an additional object wizard mapping at a specific position
+ * in the wizard list
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object to apply to
+ * \param module The name of the module, typically AST_MODULE
+ * \param name Name of the wizard to use
+ * \param data Data to be passed to wizard
+ * \param caching Wizard should cache
+ * \param position An index to insert to or one of ast_sorcery_wizard_position
+ *
+ * \return What occurred when applying the mapping
+ *
+ * \note This should be called *after* applying default mappings
+ * \note Wizards can be retrieved by using ast_sorcery_get_wizard_mapping_count
+ * and iterating over them using ast_sorcery_get_wizard_mapping.
+ *
+ * \since 13.4.0
+ */
+enum ast_sorcery_apply_result __ast_sorcery_insert_wizard_mapping(struct ast_sorcery *sorcery,
+       const char *type, const char *module, const char *name, const char *data,
+       unsigned int caching, int position);
+
+/*!
+ * \brief Insert an additional object wizard mapping at a specific position
+ * in the wizard list
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object to apply to
+ * \param module The name of the module, typically AST_MODULE
+ * \param name Name of the wizard to use
+ * \param data Data to be passed to wizard
+ * \param position One of ast_sorcery_wizard_position
+ *
+ * \return What occurred when applying the mapping
+ *
+ * \note This should be called *after* applying default mappings
+ * \since 13.4.0
+ */
+#define ast_sorcery_insert_wizard_mapping(sorcery, type, name, data, caching, position) \
+       __ast_sorcery_insert_wizard_mapping((sorcery), (type), AST_MODULE, (name), (data), \
+               (caching), (position))
+
+/*!
+ * \brief Remove an object wizard mapping
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object to remove from
+ * \param module The name of the module, typically AST_MODULE
+ * \param name The name of the wizard to remove
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \since 13.4.0
+ */
+int __ast_sorcery_remove_wizard_mapping(struct ast_sorcery *sorcery,
+       const char *type, const char *module, const char *name);
+
+/*!
+ * \brief Remove an object wizard mapping
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object to remove from
+ * \param name The name of the wizard to remove
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \since 13.4.0
+ */
+#define ast_sorcery_remove_wizard_mapping(sorcery, type, name) \
+       __ast_sorcery_remove_wizard_mapping((sorcery), (type), AST_MODULE, (name))
+
+/*!
+ * \brief Return the number of wizards mapped to an object type
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object
+ *
+ * \return Number of wizards or -1 for error
+ * \since 13.4.0
+ */
+int ast_sorcery_get_wizard_mapping_count(struct ast_sorcery *sorcery,
+       const char *type);
+
+/*!
+ * \brief By index, return a wizard mapped to an object type
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object
+ * \param index Index of the wizard
+ * \param wizard A pointer to receive the wizard pointer
+ * \param data A pointer to receive the data pointer
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \warning The wizard will have its reference count bumped so you must
+ * call ao2_cleanup when you're done with it.
+ *
+ * \note The wizard and data returned are valid only for this object type
+ * and only while the wizard is applied to the object type.
+ *
+ * \since 13.4.0
+ */
+int ast_sorcery_get_wizard_mapping(struct ast_sorcery *sorcery,
+       const char *type, int index, struct ast_sorcery_wizard **wizard, void **data);
+
 /*!
  * \brief Register an object type
  *
index 0b1b1e8..aa681e1 100644 (file)
 #include "asterisk/utils.h"
 #include "asterisk/event.h"
 
-/*! @{ */
-
 /*!
  * \brief Metadata about a \ref stasis_message.
  * \since 12
@@ -451,10 +449,6 @@ struct ast_manager_event_blob *stasis_message_to_ami(
 struct ast_event *stasis_message_to_event(
        struct stasis_message *message);
 
-/*! @} */
-
-/*! @{ */
-
 /*!
  * \brief A topic to which messages may be posted, and subscribers, well, subscribe
  * \since 12
@@ -508,10 +502,6 @@ void stasis_publish(struct stasis_topic *topic, struct stasis_message *message);
  */
 void stasis_publish_sync(struct stasis_subscription *sub, struct stasis_message *message);
 
-/*! @} */
-
-/*! @{ */
-
 /*!
  * \brief Callback function type for Stasis subscriptions.
  * \param data Data field provided with subscription.
@@ -699,8 +689,6 @@ struct stasis_message_type *stasis_subscription_change_type(void);
 
 /*! @} */
 
-/*! @{ */
-
 /*!
  * \brief Pool for topic aggregation
  */
@@ -723,8 +711,6 @@ struct stasis_topic_pool *stasis_topic_pool_create(struct stasis_topic *pooled_t
  */
 struct stasis_topic *stasis_topic_pool_get_topic(struct stasis_topic_pool *pool, const char *topic_name);
 
-/*! @} */
-
 /*! \addtogroup StasisTopicsAndMessages
  * @{
  */
@@ -757,8 +743,6 @@ struct stasis_message_type *stasis_cache_clear_type(void);
 
 /*! @} */
 
-/*! @{ */
-
 /*!
  * \brief A message cache, for use with \ref stasis_caching_topic.
  * \since 12
@@ -1090,6 +1074,10 @@ struct ao2_container *stasis_cache_dump_by_eid(struct stasis_cache *cache, struc
  */
 struct ao2_container *stasis_cache_dump_all(struct stasis_cache *cache, struct stasis_message_type *type);
 
+/*! \addtogroup StasisTopicsAndMessages
+ * @{
+ */
+
 /*!
  * \brief Object type code for multi user object snapshots
  */
@@ -1163,8 +1151,6 @@ void ast_multi_object_blob_single_channel_publish(struct ast_channel *chan, stru
 
 /*! @} */
 
-/*! @{ */
-
 /*!
  * \internal
  * \brief Log a message about invalid attempt to access a type.
@@ -1267,10 +1253,6 @@ void stasis_log_bad_type_access(const char *name);
                _priv_ ## name = NULL;          \
        })
 
-/*! @} */
-
-/*! @{ */
-
 /*!
  * \brief Initialize the Stasis subsystem.
  * \return 0 on success.
@@ -1279,10 +1261,6 @@ void stasis_log_bad_type_access(const char *name);
  */
 int stasis_init(void);
 
-/*! @} */
-
-/*! @{ */
-
 /*!
  * \internal
  * \brief called by stasis_init() for cache initialization.
@@ -1301,12 +1279,10 @@ int stasis_cache_init(void);
  */
 int stasis_config_init(void);
 
-/*! @} */
-
 /*!
  * \defgroup StasisTopicsAndMessages Stasis topics, and their messages.
  *
- * This group contains the topics, messages and corresponding message types
+ * \brief This group contains the topics, messages and corresponding message types
  * found within Asterisk.
  */
 
index ad4020a..d9df1c9 100644 (file)
@@ -20,7 +20,8 @@
 #define _ASTERISK_STASIS_TEST_H
 
 /*!
- * \file \brief Test infrastructure for dealing with Stasis.
+ * \file
+ * \brief Test infrastructure for dealing with Stasis.
  *
  * \author David M. Lee, II <dlee@digium.com>
  *
index 0e8d9d0..a3f3f28 100644 (file)
@@ -65,6 +65,7 @@
 #ifdef DO_SSL
 #include <openssl/ssl.h>
 #include <openssl/err.h>
+#include <openssl/x509v3.h>
 #else
 /* declare dummy types so we can define a pointer to them */
 typedef struct {} SSL;
index 84a3d7b..d41072d 100644 (file)
@@ -91,7 +91,7 @@
  * $FreeBSD: src/bin/expr/expr.y,v 1.16 2000/07/22 10:59:36 se Exp $
  */
 
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
 #include "asterisk.h"
 
 #include <sys/types.h>
index 8a15167..762e83d 100644 (file)
@@ -12,7 +12,7 @@
  * $FreeBSD: src/bin/expr/expr.y,v 1.16 2000/07/22 10:59:36 se Exp $
  */
 
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
 #include "asterisk.h"
 
 #include <sys/types.h>
index c2d4e00..c6a1b97 100644 (file)
@@ -1,4 +1,4 @@
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
 #include "asterisk.h"
 
 #line 2 "ast_expr2f.c"
index a4d5602..8260460 100644 (file)
@@ -28,6 +28,7 @@
        <support_level>core</support_level>
  ***/
 
+#define ASTMM_LIBC ASTMM_IGNORE
 #include "asterisk.h"
 
 #if defined(__AST_DEBUG_MALLOC)
@@ -61,16 +62,6 @@ enum func_type {
        FUNC_ASPRINTF
 };
 
-/* Undefine all our macros */
-#undef malloc
-#undef calloc
-#undef realloc
-#undef strdup
-#undef strndup
-#undef free
-#undef vasprintf
-#undef asprintf
-
 #define FENCE_MAGIC            0xfeedbabe      /*!< Allocated memory high/low fence overwrite check. */
 #define FREED_MAGIC            0xdeaddead      /*!< Freed memory wipe filler. */
 #define MALLOC_FILLER  0x55            /*!< Malloced memory filler.  Must not be zero. */
index b754f23..3e233fa 100644 (file)
@@ -46,6 +46,8 @@ ASTERISK_REGISTER_FILE()
 #define AST_AUDIOHOOK_SYNC_TOLERANCE 100 /*!< Tolerance in milliseconds for audiohooks synchronization */
 #define AST_AUDIOHOOK_SMALL_QUEUE_TOLERANCE 100 /*!< When small queue is enabled, this is the maximum amount of audio that can remain queued at a time. */
 
+#define DEFAULT_INTERNAL_SAMPLE_RATE 8000
+
 struct ast_audiohook_translate {
        struct ast_trans_pvt *trans_pvt;
        struct ast_format *format;
@@ -117,7 +119,7 @@ int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type
        audiohook->init_flags = init_flags;
 
        /* initialize internal rate at 8khz, this will adjust if necessary */
-       audiohook_set_internal_rate(audiohook, 8000, 0);
+       audiohook_set_internal_rate(audiohook, DEFAULT_INTERNAL_SAMPLE_RATE, 0);
 
        /* Since we are just starting out... this audiohook is new */
        ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_NEW);
@@ -361,7 +363,19 @@ static struct ast_frame *audiohook_read_frame_helper(struct ast_audiohook *audio
        struct ast_frame *read_frame = NULL, *final_frame = NULL;
        struct ast_format *slin;
 
-       audiohook_set_internal_rate(audiohook, ast_format_get_sample_rate(format), 1);
+       /*
+        * Update the rate if compatibility mode is turned off or if it is
+        * turned on and the format rate is higher than the current rate.
+        *
+        * This makes it so any unnecessary rate switching/resetting does
+        * not take place and also any associated audiohook_list's internal
+        * sample rate maintains the highest sample rate between hooks.
+        */
+       if (!ast_test_flag(audiohook, AST_AUDIOHOOK_COMPATIBLE) ||
+           (ast_test_flag(audiohook, AST_AUDIOHOOK_COMPATIBLE) &&
+             ast_format_get_sample_rate(format) > audiohook->hook_internal_samp_rate)) {
+               audiohook_set_internal_rate(audiohook, ast_format_get_sample_rate(format), 1);
+       }
 
        if (!(read_frame = (direction == AST_AUDIOHOOK_DIRECTION_BOTH ?
                audiohook_read_frame_both(audiohook, samples, read_reference, write_reference) :
@@ -425,6 +439,22 @@ struct ast_frame *ast_audiohook_read_frame_all(struct ast_audiohook *audiohook,
 static void audiohook_list_set_samplerate_compatibility(struct ast_audiohook_list *audiohook_list)
 {
        struct ast_audiohook *ah = NULL;
+
+       /*
+        * Anytime the samplerate compatibility is set (attach/remove an audiohook) the
+        * list's internal sample rate needs to be reset so that the next time processing
+        * through write_list, if needed, it will get updated to the correct rate.
+        *
+        * A list's internal rate always chooses the higher between its own rate and a
+        * given rate. If the current rate is being driven by an audiohook that wanted a
+        * higher rate then when this audiohook is removed the list's rate would remain
+        * at that level when it should be lower, and with no way to lower it since any
+        * rate compared against it would be lower.
+        *
+        * By setting it back to the lowest rate it can recalulate the new highest rate.
+        */
+       audiohook_list->list_internal_samp_rate = DEFAULT_INTERNAL_SAMPLE_RATE;
+
        audiohook_list->native_slin_compatible = 1;
        AST_LIST_TRAVERSE(&audiohook_list->manipulate_list, ah, list) {
                if (!(ah->init_flags & AST_AUDIOHOOK_MANIPULATE_ALL_RATES)) {
@@ -455,7 +485,7 @@ int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audioho
                AST_LIST_HEAD_INIT_NOLOCK(&ast_channel_audiohooks(chan)->whisper_list);
                AST_LIST_HEAD_INIT_NOLOCK(&ast_channel_audiohooks(chan)->manipulate_list);
                /* This sample rate will adjust as necessary when writing to the list. */
-               ast_channel_audiohooks(chan)->list_internal_samp_rate = 8000;
+               ast_channel_audiohooks(chan)->list_internal_samp_rate = DEFAULT_INTERNAL_SAMPLE_RATE;
        }
 
        /* Drop into respective list */
@@ -467,8 +497,11 @@ int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audioho
                AST_LIST_INSERT_TAIL(&ast_channel_audiohooks(chan)->manipulate_list, audiohook, list);
        }
 
-
-       audiohook_set_internal_rate(audiohook, ast_channel_audiohooks(chan)->list_internal_samp_rate, 1);
+       /*
+        * Initialize the audiohook's rate to the default. If it needs to be,
+        * it will get updated later.
+        */
+       audiohook_set_internal_rate(audiohook, DEFAULT_INTERNAL_SAMPLE_RATE, 1);
        audiohook_list_set_samplerate_compatibility(ast_channel_audiohooks(chan));
 
        /* Change status over to running since it is now attached */
@@ -766,14 +799,14 @@ static struct ast_frame *audiohook_list_translate_to_slin(struct ast_audiohook_l
        struct ast_frame *new_frame = frame;
        struct ast_format *slin;
 
-       /* If we are capable of maintaining doing samplerates other that 8khz, update
-        * the internal audiohook_list's rate and higher samplerate audio arrives. By
-        * updating the list's rate, all the audiohooks in the list will be updated as well
-        * as the are written and read from. */
-       if (audiohook_list->native_slin_compatible) {
-               audiohook_list->list_internal_samp_rate =
-                       MAX(ast_format_get_sample_rate(frame->subclass.format), audiohook_list->list_internal_samp_rate);
-       }
+       /*
+        * If we are capable of sample rates other that 8khz, update the internal
+        * audiohook_list's rate and higher sample rate audio arrives. If native
+        * slin compatibility is turned on all audiohooks in the list will be
+        * updated as well during read/write processing.
+        */
+       audiohook_list->list_internal_samp_rate =
+               MAX(ast_format_get_sample_rate(frame->subclass.format), audiohook_list->list_internal_samp_rate);
 
        slin = ast_format_cache_get_slin_by_rate(audiohook_list->list_internal_samp_rate);
        if (ast_format_cmp(frame->subclass.format, slin) == AST_FORMAT_CMP_EQUAL) {
@@ -822,6 +855,36 @@ static struct ast_frame *audiohook_list_translate_to_native(struct ast_audiohook
 }
 
 /*!
+ *\brief Set the audiohook's internal sample rate to the audiohook_list's rate,
+ *       but only when native slin compatibility is turned on.
+ *
+ * \param audiohook_list audiohook_list data object
+ * \param audiohook the audiohook to update
+ * \param rate the current max internal sample rate
+ */
+static void audiohook_list_set_hook_rate(struct ast_audiohook_list *audiohook_list,
+                                        struct ast_audiohook *audiohook, int *rate)
+{
+       /* The rate should always be the max between itself and the hook */
+       if (audiohook->hook_internal_samp_rate > *rate) {
+               *rate = audiohook->hook_internal_samp_rate;
+       }
+
+       /*
+        * If native slin compatibility is turned on then update the audiohook
+        * with the audiohook_list's current rate. Note, the audiohook's rate is
+        * set to the audiohook_list's rate and not the given rate. If there is
+        * a change in rate the hook's rate is changed on its next check.
+        */
+       if (audiohook_list->native_slin_compatible) {
+               ast_set_flag(audiohook, AST_AUDIOHOOK_COMPATIBLE);
+               audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1);
+       } else {
+               ast_clear_flag(audiohook, AST_AUDIOHOOK_COMPATIBLE);
+       }
+}
+
+/*!
  * \brief Pass an AUDIO frame off to be handled by the audiohook core
  *
  * \details
@@ -851,6 +914,7 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
        int samples;
        int middle_frame_manipulated = 0;
        int removed = 0;
+       int internal_sample_rate;
 
        /* ---Part_1. translate start_frame to SLINEAR if necessary. */
        if (!(middle_frame = audiohook_list_translate_to_slin(audiohook_list, direction, start_frame))) {
@@ -858,6 +922,19 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
        }
        samples = middle_frame->samples;
 
+       /*
+        * While processing each audiohook check to see if the internal sample rate needs
+        * to be adjusted (it should be the highest rate specified between formats and
+        * hooks). The given audiohook_list's internal sample rate is then set to the
+        * updated value before returning.
+        *
+        * If slin compatibility mode is turned on then an audiohook's internal sample
+        * rate is set to its audiohook_list's rate. If an audiohook_list's rate is
+        * adjusted during this pass then the change is picked up by the audiohooks
+        * on the next pass.
+        */
+       internal_sample_rate = audiohook_list->list_internal_samp_rate;
+
        /* ---Part_2: Send middle_frame to spy and manipulator lists.  middle_frame is guaranteed to be SLINEAR here.*/
        /* Queue up signed linear frame to each spy */
        AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->spy_list, audiohook, list) {
@@ -872,7 +949,7 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
                        }
                        continue;
                }
-               audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1);
+               audiohook_list_set_hook_rate(audiohook_list, audiohook, &internal_sample_rate);
                ast_audiohook_write_frame(audiohook, direction, middle_frame);
                ast_audiohook_unlock(audiohook);
        }
@@ -896,7 +973,7 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
                                }
                                continue;
                        }
-                       audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1);
+                       audiohook_list_set_hook_rate(audiohook_list, audiohook, &internal_sample_rate);
                        if (ast_slinfactory_available(factory) >= samples && ast_slinfactory_read(factory, read_buf, samples)) {
                                /* Take audio from this whisper source and combine it into our main buffer */
                                for (i = 0, data1 = combine_buf, data2 = read_buf; i < samples; i++, data1++, data2++) {
@@ -929,14 +1006,16 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
                                }
                                continue;
                        }
-                       audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1);
-                       /* Feed in frame to manipulation. */
-                       if (!audiohook->manipulate_callback(audiohook, chan, middle_frame, direction)) {
-                               /* If the manipulation fails then the frame will be returned in its original state.
-                                * Since there are potentially more manipulator callbacks in the list, no action should
-                                * be taken here to exit early. */
-                                middle_frame_manipulated = 1;
-                       }
+                       audiohook_list_set_hook_rate(audiohook_list, audiohook, &internal_sample_rate);
+                       /*
+                        * Feed in frame to manipulation.
+                        *
+                        * XXX FAILURES ARE IGNORED XXX
+                        * If the manipulation fails then the frame will be returned in its original state.
+                        * Since there are potentially more manipulator callbacks in the list, no action should
+                        * be taken here to exit early.
+                        */
+                       audiohook->manipulate_callback(audiohook, chan, middle_frame, direction);
                        ast_audiohook_unlock(audiohook);
                }
                AST_LIST_TRAVERSE_SAFE_END;
@@ -960,6 +1039,12 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
        /* Before returning, if an audiohook got removed, reset samplerate compatibility */
        if (removed) {
                audiohook_list_set_samplerate_compatibility(audiohook_list);
+       } else {
+               /*
+                * Set the audiohook_list's rate to the updated rate. Note that if a hook
+                * was removed then the list's internal rate is reset to the default.
+                */
+               audiohook_list->list_internal_samp_rate = internal_sample_rate;
        }
 
        return end_frame;
index 9b32f9d..c6f49f1 100644 (file)
@@ -3096,13 +3096,9 @@ int ast_cdr_serialize_variables(const char *channel_name, struct ast_str **buf,
        struct cdr_object *it_cdr;
        struct ast_var_t *variable;
        const char *var;
-       RAII_VAR(char *, workspace, ast_malloc(256), ast_free);
+       char workspace[256];
        int total = 0, x = 0, i;
 
-       if (!workspace) {
-               return 0;
-       }
-
        if (!cdr) {
                RAII_VAR(struct module_config *, mod_cfg,
                         ao2_global_obj_ref(module_configs), ao2_cleanup);
index bc622cc..d6a077b 100644 (file)
@@ -735,6 +735,19 @@ const char *ast_variable_find_in_list(const struct ast_variable *list, const cha
        return NULL;
 }
 
+const char *ast_variable_find_last_in_list(const struct ast_variable *list, const char *variable)
+{
+       const struct ast_variable *v;
+       const char *found = NULL;
+
+       for (v = list; v; v = v->next) {
+               if (!strcasecmp(variable, v->name)) {
+                       found = v->value;
+               }
+       }
+       return found;
+}
+
 static struct ast_variable *variable_clone(const struct ast_variable *old)
 {
        struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
index acd2cc6..bfad6e0 100644 (file)
@@ -897,7 +897,7 @@ static enum fsread_res ast_readaudio_callback(struct ast_filestream *s)
 
                if (!fr /* stream complete */ || ast_write(s->owner, fr) /* error writing */) {
                        if (fr) {
-                               ast_log(LOG_WARNING, "Failed to write frame\n");
+                               ast_debug(2, "Failed to write frame\n");
                                ast_frfree(fr);
                        }
                        goto return_failure;
@@ -954,7 +954,7 @@ static enum fsread_res ast_readvideo_callback(struct ast_filestream *s)
 
                if (!fr /* stream complete */ || ast_write(s->owner, fr) /* error writing */) {
                        if (fr) {
-                               ast_log(LOG_WARNING, "Failed to write frame\n");
+                               ast_debug(2, "Failed to write frame\n");
                                ast_frfree(fr);
                        }
                        ast_channel_vstreamid_set(s->owner, -1);
index 27a700a..c08880c 100644 (file)
@@ -26,7 +26,7 @@
        <support_level>core</support_level>
  ***/
 
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
 #include "asterisk.h"
 
 ASTERISK_REGISTER_FILE()
index 41f26e8..bdcd6c4 100644 (file)
@@ -375,16 +375,25 @@ static int init_logger_chain(int locked, const char *altconf)
        const char *s;
        struct ast_flags config_flags = { 0 };
 
-       display_callids = 1;
-
        if (!(cfg = ast_config_load2(S_OR(altconf, "logger.conf"), "logger", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
                cfg = NULL;
        }
 
-       /* delete our list of log channels */
        if (!locked) {
                AST_RWLIST_WRLOCK(&logchannels);
        }
+
+       /* Set defaults */
+       hostname[0] = '\0';
+       display_callids = 1;
+       memset(&logfiles, 0, sizeof(logfiles));
+       logfiles.queue_log = 1;
+       ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat));
+       ast_copy_string(queue_log_name, QUEUELOG, sizeof(queue_log_name));
+       exec_after_rotate[0] = '\0';
+       rotatestrategy = SEQUENTIAL;
+
+       /* delete our list of log channels */
        while ((chan = AST_RWLIST_REMOVE_HEAD(&logchannels, list))) {
                ast_free(chan);
        }
@@ -424,17 +433,14 @@ static int init_logger_chain(int locked, const char *altconf)
                                ast_copy_string(hostname, "unknown", sizeof(hostname));
                                fprintf(stderr, "What box has no hostname???\n");
                        }
-               } else
-                       hostname[0] = '\0';
-       } else
-               hostname[0] = '\0';
+               }
+       }
        if ((s = ast_variable_retrieve(cfg, "general", "display_callids"))) {
                display_callids = ast_true(s);
        }
-       if ((s = ast_variable_retrieve(cfg, "general", "dateformat")))
+       if ((s = ast_variable_retrieve(cfg, "general", "dateformat"))) {
                ast_copy_string(dateformat, s, sizeof(dateformat));
-       else
-               ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat));
+       }
        if ((s = ast_variable_retrieve(cfg, "general", "queue_log"))) {
                logfiles.queue_log = ast_true(s);
        }
index 64cefcb..7098f69 100644 (file)
@@ -743,6 +743,7 @@ static void chan_cleanup(struct ast_channel *chan)
        struct ast_datastore *msg_ds, *ds;
        struct varshead *headp;
        struct ast_var_t *vardata;
+       struct ast_frame *cur;
 
        ast_channel_lock(chan);
 
@@ -772,6 +773,13 @@ static void chan_cleanup(struct ast_channel *chan)
        }
 
        /*
+        * Remove frames from read queue
+        */
+       while ((cur = AST_LIST_REMOVE_HEAD(ast_channel_readq(chan), frame_list))) {
+               ast_frfree(cur);
+       }
+
+       /*
         * Restore msg datastore.
         */
        if (msg_ds) {
index 8c86f14..e576258 100644 (file)
@@ -16,7 +16,7 @@
  * at the top of the source tree.
  */
 
-/*! \file ast_sdp_crypto.c
+/*! \file
  *
  * \brief SRTP and SDP Security descriptions
  *
@@ -238,7 +238,8 @@ int ast_sdp_crypto_process(struct ast_rtp_instance *rtp, struct ast_sdp_srtp *sr
                return -1;
        }
 
-       if (sscanf(tag, "%30d", &crypto->tag) != 1 || crypto->tag <= 0 || crypto->tag > 9) {
+       /* RFC4568 9.1 - tag is 1-9 digits, greater than zero */
+       if (sscanf(tag, "%30d", &crypto->tag) != 1 || crypto->tag <= 0 || crypto->tag > 999999999) {
                ast_log(LOG_WARNING, "Unacceptable a=crypto tag: %s\n", tag);
                return -1;
        }
index 1a4b3a0..c779548 100644 (file)
@@ -43,6 +43,7 @@ ASTERISK_REGISTER_FILE()
 #include "asterisk/taskprocessor.h"
 #include "asterisk/threadpool.h"
 #include "asterisk/json.h"
+#include "asterisk/vector.h"
 
 /* To prevent DEBUG_FD_LEAKS from interfering with things we undef open and close */
 #undef open
@@ -86,6 +87,35 @@ ASTERISK_REGISTER_FILE()
 /*! \brief Thread pool for observers */
 static struct ast_threadpool *threadpool;
 
+/*! \brief Structure for an internal wizard instance */
+struct ast_sorcery_internal_wizard {
+       /*!
+        * \brief Wizard interface itself
+        * \warning Callbacks must always be declared first in this structure
+        * so an ao2_ref on &callbacks will adjust the ref count on
+        * internal_wizard.
+        */
+       struct ast_sorcery_wizard callbacks;
+
+       /*! \brief Observers */
+       struct ao2_container *observers;
+};
+
+/*! \brief Structure for a wizard instance which operates on objects */
+struct ast_sorcery_object_wizard {
+       /*! \brief Wizard interface itself */
+       struct ast_sorcery_internal_wizard *wizard;
+
+       /*! \brief Unique data for the wizard */
+       void *data;
+
+       /*! \brief Wizard is acting as an object cache */
+       unsigned int caching:1;
+};
+
+/*! \brief Interface for a sorcery object type wizards */
+AST_VECTOR_RW(ast_sorcery_object_wizards, struct ast_sorcery_object_wizard *);
+
 /*! \brief Structure for internal sorcery object information */
 struct ast_sorcery_object {
        /*! \brief Unique identifier of this object */
@@ -119,7 +149,7 @@ struct ast_sorcery_object_type {
        sorcery_diff_handler diff;
 
        /*! \brief Wizard instances */
-       struct ao2_container *wizards;
+       struct ast_sorcery_object_wizards wizards;
 
        /*! \brief Object fields */
        struct ao2_container *fields;
@@ -176,27 +206,6 @@ struct ast_sorcery_object_field {
        intptr_t args[];
 };
 
-/*! \brief Structure for an internal wizard instance */
-struct ast_sorcery_internal_wizard {
-       /*! \brief Wizard interface itself */
-       struct ast_sorcery_wizard callbacks;
-
-       /*! \brief Observers */
-       struct ao2_container *observers;
-};
-
-/*! \brief Structure for a wizard instance which operates on objects */
-struct ast_sorcery_object_wizard {
-       /*! \brief Wizard interface itself */
-       struct ast_sorcery_internal_wizard *wizard;
-
-       /*! \brief Unique data for the wizard */
-       void *data;
-
-       /*! \brief Wizard is acting as an object cache */
-       unsigned int caching:1;
-};
-
 /*! \brief Full structure for sorcery */
 struct ast_sorcery {
        /*! \brief Container for known object types */
@@ -789,7 +798,10 @@ static void sorcery_object_type_destructor(void *obj)
 {
        struct ast_sorcery_object_type *object_type = obj;
 
-       ao2_cleanup(object_type->wizards);
+       AST_VECTOR_RW_WRLOCK(&object_type->wizards);
+       AST_VECTOR_CALLBACK_VOID(&object_type->wizards, ao2_cleanup);
+       AST_VECTOR_RW_UNLOCK(&object_type->wizards);
+       AST_VECTOR_RW_FREE(&object_type->wizards);
        ao2_cleanup(object_type->fields);
        ao2_cleanup(object_type->observers);
 
@@ -806,6 +818,7 @@ static void sorcery_object_type_destructor(void *obj)
 /*! \brief Internal function which allocates an object type structure */
 static struct ast_sorcery_object_type *sorcery_object_type_alloc(const char *type, const char *module)
 {
+#define INITIAL_WIZARD_VECTOR_SIZE 5
        struct ast_sorcery_object_type *object_type;
        char uuid[AST_UUID_STR_LEN];
 
@@ -814,7 +827,7 @@ static struct ast_sorcery_object_type *sorcery_object_type_alloc(const char *typ
        }
 
        /* Order matters for object wizards */
-       if (!(object_type->wizards = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, sorcery_wizard_cmp))) {
+       if (AST_VECTOR_RW_INIT(&object_type->wizards, INITIAL_WIZARD_VECTOR_SIZE) != 0) {
                ao2_ref(object_type, -1);
                return NULL;
        }
@@ -864,7 +877,7 @@ static void sorcery_object_wizard_destructor(void *obj)
 {
        struct ast_sorcery_object_wizard *object_wizard = obj;
 
-       if (object_wizard->data) {
+       if (object_wizard->data && object_wizard->wizard->callbacks.close) {
                object_wizard->wizard->callbacks.close(object_wizard->data);
        }
 
@@ -875,9 +888,73 @@ static void sorcery_object_wizard_destructor(void *obj)
        ao2_cleanup(object_wizard->wizard);
 }
 
-/*! \brief Internal function which creates an object type and adds a wizard mapping */
-enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery,
-               const char *type, const char *module, const char *name, const char *data, unsigned int caching)
+/*! \brief Return the number of wizards mapped to an object type */
+int ast_sorcery_get_wizard_mapping_count(struct ast_sorcery *sorcery,
+       const char *type)
+{
+       RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+
+       if (!object_type) {
+               return -1;
+       }
+
+       return AST_VECTOR_SIZE(&object_type->wizards);
+}
+
+int ast_sorcery_get_wizard_mapping(struct ast_sorcery *sorcery,
+       const char *type, int index, struct ast_sorcery_wizard **wizard, void **data)
+{
+       RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+       struct ast_sorcery_object_wizard *owizard;
+
+       if (!object_type) {
+               return -1;
+       }
+
+       if (index < 0 || index >= AST_VECTOR_SIZE(&object_type->wizards)) {
+               return -1;
+       }
+
+       owizard = AST_VECTOR_GET(&object_type->wizards, index);
+
+       if (wizard != NULL) {
+               *wizard = &(owizard->wizard->callbacks);
+               ao2_bump(owizard->wizard);
+       } else {
+               return -1;
+       }
+
+       if (data != NULL) {
+               *data = owizard->data;
+       }
+
+       return 0;
+}
+
+/*! \brief Internal function removes a wizard mapping */
+int __ast_sorcery_remove_wizard_mapping(struct ast_sorcery *sorcery,
+               const char *type, const char *module, const char *name)
+{
+       RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+       int res;
+
+       if (!object_type) {
+               return -1;
+       }
+
+       AST_VECTOR_RW_WRLOCK(&object_type->wizards);
+#define WIZARD_NAME_COMPARE(a, b) (strcmp((a)->wizard->callbacks.name, (b)) == 0)
+       res = AST_VECTOR_REMOVE_CMP_ORDERED(&object_type->wizards, name, WIZARD_NAME_COMPARE, ao2_cleanup);
+#undef WIZARD_NAME_COMPARE
+       AST_VECTOR_RW_UNLOCK(&object_type->wizards);
+
+       return res;
+}
+
+/*! \brief Internal function which creates an object type and inserts a wizard mapping */
+enum ast_sorcery_apply_result __ast_sorcery_insert_wizard_mapping(struct ast_sorcery *sorcery,
+               const char *type, const char *module, const char *name, const char *data,
+               unsigned int caching, int position)
 {
        RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
        RAII_VAR(struct ast_sorcery_internal_wizard *, wizard, ao2_find(wizards, name, OBJ_KEY), ao2_cleanup);
@@ -899,19 +976,23 @@ enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorc
                created = 1;
        }
 
+       AST_VECTOR_RW_WRLOCK(&object_type->wizards);
        if (!created) {
-               struct ast_sorcery_wizard *found;
+               struct ast_sorcery_object_wizard *found;
 
-               found = ao2_find(object_type->wizards, wizard, OBJ_SEARCH_OBJECT);
+#define WIZARD_COMPARE(a, b) ((a)->wizard == (b))
+               found = AST_VECTOR_GET_CMP(&object_type->wizards, wizard, WIZARD_COMPARE);
+#undef WIZARD_COMPARE
                if (found) {
                        ast_debug(1, "Wizard %s already applied to object type %s\n",
                                        wizard->callbacks.name, object_type->name);
-                       ao2_cleanup(found);
+                       AST_VECTOR_RW_UNLOCK(&object_type->wizards);
                        return AST_SORCERY_APPLY_DUPLICATE;
                }
        }
 
        if (wizard->callbacks.open && !(object_wizard->data = wizard->callbacks.open(data))) {
+               AST_VECTOR_RW_UNLOCK(&object_type->wizards);
                return AST_SORCERY_APPLY_FAIL;
        }
 
@@ -920,7 +1001,16 @@ enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorc
        object_wizard->wizard = ao2_bump(wizard);
        object_wizard->caching = caching;
 
-       ao2_link(object_type->wizards, object_wizard);
+       if (position == AST_SORCERY_WIZARD_POSITION_LAST) {
+               position = AST_VECTOR_SIZE(&object_type->wizards);
+       }
+
+       if (AST_VECTOR_INSERT_AT(&object_type->wizards, position, object_wizard) != 0) {
+               AST_VECTOR_RW_UNLOCK(&object_type->wizards);
+               return AST_SORCERY_APPLY_FAIL;
+       }
+       AST_VECTOR_RW_UNLOCK(&object_type->wizards);
+       ao2_bump(object_wizard);
 
        if (created) {
                ao2_link(sorcery->types, object_type);
@@ -932,6 +1022,14 @@ enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorc
        return AST_SORCERY_APPLY_SUCCESS;
 }
 
+/*! \brief Internal function which creates an object type and adds a wizard mapping */
+enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery,
+               const char *type, const char *module, const char *name, const char *data, unsigned int caching)
+{
+       return __ast_sorcery_insert_wizard_mapping(sorcery, type, module, name, data,
+               caching, AST_SORCERY_WIZARD_POSITION_LAST);
+}
+
 enum ast_sorcery_apply_result  __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, const char *module)
 {
        struct ast_flags flags = { 0 };
@@ -1260,7 +1358,9 @@ static int sorcery_object_load(void *obj, void *arg, int flags)
        NOTIFY_INSTANCE_OBSERVERS(details->sorcery->observers, object_type_loading,
                details->sorcery->module_name, details->sorcery, type->name, details->reload);
 
-       ao2_callback(type->wizards, OBJ_NODATA, sorcery_wizard_load, details);
+       AST_VECTOR_RW_RDLOCK(&type->wizards);
+       AST_VECTOR_CALLBACK(&type->wizards, sorcery_wizard_load, NULL, details, 0);
+       AST_VECTOR_RW_UNLOCK(&type->wizards);
 
        NOTIFY_INSTANCE_OBSERVERS(details->sorcery->observers, object_type_loaded,
                details->sorcery->module_name, details->sorcery, type->name, details->reload);
@@ -1700,31 +1800,31 @@ void *ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *
 {
        RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
        void *object = NULL;
-       struct ao2_iterator i;
-       struct ast_sorcery_object_wizard *wizard;
+       int i;
        unsigned int cached = 0;
 
        if (!object_type || ast_strlen_zero(id)) {
                return NULL;
        }
 
-       i = ao2_iterator_init(object_type->wizards, 0);
-       for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) {
+       AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+       for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+               struct ast_sorcery_object_wizard *wizard =
+                       AST_VECTOR_GET(&object_type->wizards, i);
+
                if (wizard->wizard->callbacks.retrieve_id &&
                        !(object = wizard->wizard->callbacks.retrieve_id(sorcery, wizard->data, object_type->name, id))) {
                        continue;
                }
 
                cached = wizard->caching;
-
-               ao2_ref(wizard, -1);
                break;
        }
-       ao2_iterator_destroy(&i);
 
        if (!cached && object) {
-               ao2_callback(object_type->wizards, 0, sorcery_cache_create, object);
+               AST_VECTOR_CALLBACK(&object_type->wizards, sorcery_cache_create, NULL, object, 0);
        }
+       AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
        return object;
 }
@@ -1733,8 +1833,7 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch
 {
        RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
        void *object = NULL;
-       struct ao2_iterator i;
-       struct ast_sorcery_object_wizard *wizard;
+       int i;
        unsigned int cached = 0;
 
        if (!object_type) {
@@ -1748,9 +1847,11 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch
                }
        }
 
-       /* Inquire with the available wizards for retrieval */
-       i = ao2_iterator_init(object_type->wizards, 0);
-       for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) {
+       AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+       for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+               struct ast_sorcery_object_wizard *wizard =
+                       AST_VECTOR_GET(&object_type->wizards, i);
+
                if ((flags & AST_RETRIEVE_FLAG_MULTIPLE)) {
                        if (wizard->wizard->callbacks.retrieve_multiple) {
                                wizard->wizard->callbacks.retrieve_multiple(sorcery, wizard->data, object_type->name, object, fields);
@@ -1767,15 +1868,14 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch
 
                cached = wizard->caching;
 
-               ao2_ref(wizard, -1);
                break;
        }
-       ao2_iterator_destroy(&i);
 
        /* If we are returning a single object and it came from a non-cache source create it in any caches */
        if (!(flags & AST_RETRIEVE_FLAG_MULTIPLE) && !cached && object) {
-               ao2_callback(object_type->wizards, 0, sorcery_cache_create, object);
+               AST_VECTOR_CALLBACK(&object_type->wizards, sorcery_cache_create, NULL, object, 0);
        }
+       AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
        return object;
 }
@@ -1784,22 +1884,24 @@ struct ao2_container *ast_sorcery_retrieve_by_regex(const struct ast_sorcery *so
 {
        RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
        struct ao2_container *objects;
-       struct ao2_iterator i;
-       struct ast_sorcery_object_wizard *wizard;
+       int i;
 
        if (!object_type || !(objects = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) {
                return NULL;
        }
 
-       i = ao2_iterator_init(object_type->wizards, 0);
-       for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) {
+       AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+       for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+               struct ast_sorcery_object_wizard *wizard =
+                       AST_VECTOR_GET(&object_type->wizards, i);
+
                if (!wizard->wizard->callbacks.retrieve_regex) {
                        continue;
                }
 
                wizard->wizard->callbacks.retrieve_regex(sorcery, wizard->data, object_type->name, objects, regex);
        }
-       ao2_iterator_destroy(&i);
+       AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
        return objects;
 }
@@ -1846,7 +1948,9 @@ int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object)
 {
        const struct ast_sorcery_object_details *details = object;
        RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->object->type, OBJ_KEY), ao2_cleanup);
-       RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
+       struct ast_sorcery_object_wizard *object_wizard = NULL;
+       struct ast_sorcery_object_wizard *found_wizard;
+       int i;
        struct sorcery_details sdetails = {
                .sorcery = sorcery,
                .obj = object,
@@ -1856,14 +1960,21 @@ int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object)
                return -1;
        }
 
-       if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_create, &sdetails)) &&
-               ao2_container_count(object_type->observers)) {
-               struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
+       AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+       for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+               found_wizard = AST_VECTOR_GET(&object_type->wizards, i);
+               if (sorcery_wizard_create(found_wizard, &sdetails, 0) == (CMP_MATCH | CMP_STOP)) {
+                       object_wizard = found_wizard;
+                       if(ao2_container_count(object_type->observers)) {
+                               struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
 
-               if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_create, invocation)) {
-                       ao2_cleanup(invocation);
+                               if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_create, invocation)) {
+                                       ao2_cleanup(invocation);
+                               }
+                       }
                }
        }
+       AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
        return object_wizard ? 0 : -1;
 }
@@ -1905,7 +2016,9 @@ int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object)
 {
        const struct ast_sorcery_object_details *details = object;
        RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->object->type, OBJ_KEY), ao2_cleanup);
-       RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
+       struct ast_sorcery_object_wizard *object_wizard = NULL;
+       struct ast_sorcery_object_wizard *found_wizard;
+       int i;
        struct sorcery_details sdetails = {
                .sorcery = sorcery,
                .obj = object,
@@ -1915,14 +2028,21 @@ int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object)
                return -1;
        }
 
-       if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_update, &sdetails)) &&
-               ao2_container_count(object_type->observers)) {
-               struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
+       AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+       for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+               found_wizard = AST_VECTOR_GET(&object_type->wizards, i);
+               if (sorcery_wizard_update(found_wizard, &sdetails, 0) == (CMP_MATCH | CMP_STOP)) {
+                       object_wizard = found_wizard;
+                       if (ao2_container_count(object_type->observers)) {
+                               struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
 
-               if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_update, invocation)) {
-                       ao2_cleanup(invocation);
+                               if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_update, invocation)) {
+                                       ao2_cleanup(invocation);
+                               }
+                       }
                }
        }
+       AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
        return object_wizard ? 0 : -1;
 }
@@ -1964,7 +2084,9 @@ int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
 {
        const struct ast_sorcery_object_details *details = object;
        RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->object->type, OBJ_KEY), ao2_cleanup);
-       RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
+       struct ast_sorcery_object_wizard *object_wizard = NULL;
+       struct ast_sorcery_object_wizard *found_wizard;
+       int i;
        struct sorcery_details sdetails = {
                .sorcery = sorcery,
                .obj = object,
@@ -1974,14 +2096,21 @@ int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
                return -1;
        }
 
-       if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_delete, &sdetails)) &&
-               ao2_container_count(object_type->observers)) {
-               struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
+       AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+       for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+               found_wizard = AST_VECTOR_GET(&object_type->wizards, i);
+               if (sorcery_wizard_delete(found_wizard, &sdetails, 0) == (CMP_MATCH | CMP_STOP)) {
+                       object_wizard = found_wizard;
+                       if (ao2_container_count(object_type->observers)) {
+                               struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
 
-               if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_delete, invocation)) {
-                       ao2_cleanup(invocation);
+                               if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_delete, invocation)) {
+                                       ao2_cleanup(invocation);
+                               }
+                       }
                }
        }
+       AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
        return object_wizard ? 0 : -1;
 }
index 0b06d22..338b8bb 100644 (file)
@@ -555,6 +555,34 @@ static void session_instance_destructor(void *obj)
        ao2_cleanup(i->private_data);
 }
 
+#ifdef DO_SSL
+static int check_tcptls_cert_name(ASN1_STRING *cert_str, const char *hostname, const char *desc)
+{
+       unsigned char *str;
+       int ret;
+
+       ret = ASN1_STRING_to_UTF8(&str, cert_str);
+       if (ret < 0 || !str) {
+               return -1;
+       }
+
+       if (strlen((char *) str) != ret) {
+               ast_log(LOG_WARNING, "Invalid certificate %s length (contains NULL bytes?)\n", desc);
+
+               ret = -1;
+       } else if (!strcasecmp(hostname, (char *) str)) {
+               ret = 0;
+       } else {
+               ret = -1;
+       }
+
+       ast_debug(3, "SSL %s compare s1='%s' s2='%s'\n", desc, hostname, str);
+       OPENSSL_free(str);
+
+       return ret;
+}
+#endif
+
 /*! \brief
 * creates a FILE * from the fd passed by the accept thread.
 * This operation is potentially expensive (certificate verification),
@@ -631,8 +659,8 @@ static void *handle_tcptls_connection(void *data)
                                }
                                if (!ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_IGNORE_COMMON_NAME)) {
                                        ASN1_STRING *str;
-                                       unsigned char *str2;
                                        X509_NAME *name = X509_get_subject_name(peer);
+                                       STACK_OF(GENERAL_NAME) *alt_names;
                                        int pos = -1;
                                        int found = 0;
 
@@ -643,25 +671,36 @@ static void *handle_tcptls_connection(void *data)
                                                if (pos < 0) {
                                                        break;
                                                }
+
                                                str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, pos));
-                                               ret = ASN1_STRING_to_UTF8(&str2, str);
-                                               if (ret < 0) {
-                                                       continue;
+                                               if (!check_tcptls_cert_name(str, tcptls_session->parent->hostname, "common name")) {
+                                                       found = 1;
+                                                       break;
                                                }
+                                       }
+
+                                       if (!found) {
+                                               alt_names = X509_get_ext_d2i(peer, NID_subject_alt_name, NULL, NULL);
+                                               if (alt_names != NULL) {
+                                                       int alt_names_count = sk_GENERAL_NAME_num(alt_names);
+
+                                                       for (pos = 0; pos < alt_names_count; pos++) {
+                                                               const GENERAL_NAME *alt_name = sk_GENERAL_NAME_value(alt_names, pos);
+
+                                                               if (alt_name->type != GEN_DNS) {
+                                                                       continue;
+                                                               }
 
-                                               if (str2) {
-                                                       if (strlen((char *) str2) != ret) {
-                                                               ast_log(LOG_WARNING, "Invalid certificate common name length (contains NULL bytes?)\n");
-                                                       } else if (!strcasecmp(tcptls_session->parent->hostname, (char *) str2)) {
-                                                               found = 1;
+                                                               if (!check_tcptls_cert_name(alt_name->d.dNSName, tcptls_session->parent->hostname, "alt name")) {
+                                                                       found = 1;
+                                                                       break;
+                                                               }
                                                        }
-                                                       ast_debug(3, "SSL Common Name compare s1='%s' s2='%s'\n", tcptls_session->parent->hostname, str2);
-                                                       OPENSSL_free(str2);
-                                               }
-                                               if (found) {
-                                                       break;
+
+                                                       sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
                                                }
                                        }
+
                                        if (!found) {
                                                ast_log(LOG_ERROR, "Certificate common name did not match (%s)\n", tcptls_session->parent->hostname);
                                                X509_free(peer);
@@ -752,6 +791,22 @@ void *ast_tcptls_server_root(void *data)
        return NULL;
 }
 
+static void __ssl_setup_certs(struct ast_tls_config *cfg, const size_t cert_file_len, const char *key_type_extension, const char *key_type)
+{
+       char *cert_file = ast_strdupa(cfg->certfile);
+
+       memcpy(cert_file + cert_file_len - 8, key_type_extension, 5);
+       if (access(cert_file, F_OK) == 0) {
+               if (SSL_CTX_use_certificate_chain_file(cfg->ssl_ctx, cert_file) == 0) {
+                       ast_log(LOG_WARNING, "TLS/SSL error loading public %s key (certificate) from <%s>.\n", key_type, cert_file);
+               } else if (SSL_CTX_use_PrivateKey_file(cfg->ssl_ctx, cert_file, SSL_FILETYPE_PEM) == 0) {
+                       ast_log(LOG_WARNING, "TLS/SSL error loading private %s key from <%s>.\n", key_type, cert_file);
+               } else if (SSL_CTX_check_private_key(cfg->ssl_ctx) == 0) {
+                       ast_log(LOG_WARNING, "TLS/SSL error matching private %s key and certificate in <%s>.\n", key_type, cert_file);
+               }
+       }
+}
+
 static int __ssl_setup(struct ast_tls_config *cfg, int client)
 {
 #ifndef DO_SSL
@@ -839,6 +894,17 @@ static int __ssl_setup(struct ast_tls_config *cfg, int client)
                                return 0;
                        }
                }
+               if (!client) {
+                       size_t certfile_len = strlen(cfg->certfile);
+
+                       /* expects a file name which contains _rsa. like asterisk_rsa.pem
+                        * ignores any 3-character file-extension like .pem, .cer, .crt
+                        */
+                       if (certfile_len >= 8 && !strncmp(cfg->certfile + certfile_len - 8, "_rsa.", 5)) {
+                               __ssl_setup_certs(cfg, certfile_len, "_ecc.", "ECC");
+                               __ssl_setup_certs(cfg, certfile_len, "_dsa.", "DSA");
+                       }
+               }
        }
        if (!ast_strlen_zero(cfg->cipher)) {
                if (SSL_CTX_set_cipher_list(cfg->ssl_ctx, cfg->cipher) == 0 ) {
index a412eaf..b1b2bd7 100644 (file)
@@ -68,7 +68,7 @@
 %option bison-locations
 
 %{
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
 #include "asterisk.h"
 ASTERISK_REGISTER_FILE()
 
index f146ffb..9f1f19b 100644 (file)
@@ -99,7 +99,7 @@
  *
  */
 
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
 #include "asterisk.h"
 
 ASTERISK_REGISTER_FILE()
index 5ab9c96..e5c1655 100644 (file)
@@ -22,7 +22,7 @@
  *
  */
 
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
 #include "asterisk.h"
 
 ASTERISK_REGISTER_FILE()
index c5eba92..a7a20aa 100644 (file)
@@ -1,4 +1,4 @@
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
 #include "asterisk.h"
 
 #line 2 "ael_lex.c"
index d29bbcf..d5ea5ac 100644 (file)
@@ -27,7 +27,7 @@
        <support_level>extended</support_level>
  ***/
 
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
 #include "asterisk.h"
 
 ASTERISK_REGISTER_FILE()
index 86ba7e4..46db864 100644 (file)
        <support_level>extended</support_level>
 ***/
 
-/* Include spandsp headers before asterisk.h so the inline functions can continue using
- * malloc and free, even with MALLOC_DEBUG enabled. */
-#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
-#include <spandsp.h>
-#include <spandsp/version.h>
-
+/* Needed for spandsp headers */
+#define ASTMM_LIBC ASTMM_IGNORE
 #include "asterisk.h"
 
 ASTERISK_REGISTER_FILE()
@@ -69,6 +65,10 @@ ASTERISK_REGISTER_FILE()
 #include "asterisk/channel.h"
 #include "asterisk/format_cache.h"
 
+#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
+#include <spandsp.h>
+#include <spandsp/version.h>
+
 #define SPANDSP_FAX_SAMPLES 160
 #define SPANDSP_FAX_TIMER_RATE 8000 / SPANDSP_FAX_SAMPLES      /* 50 ticks per second, 20ms, 160 samples per second */
 #define SPANDSP_ENGAGE_UDPTL_NAT_RETRY 3
index 046c76e..40aedff 100644 (file)
@@ -88,16 +88,10 @@ struct ast_websocket {
        struct websocket_client *client;  /*!< Client object when connected as a client websocket */
 };
 
-/*! \brief Structure definition for protocols */
-struct websocket_protocol {
-       char *name;                      /*!< Name of the protocol */
-       ast_websocket_callback callback; /*!< Callback called when a new session is established */
-};
-
 /*! \brief Hashing function for protocols */
 static int protocol_hash_fn(const void *obj, const int flags)
 {
-       const struct websocket_protocol *protocol = obj;
+       const struct ast_websocket_protocol *protocol = obj;
        const char *name = obj;
 
        return ast_str_case_hash(flags & OBJ_KEY ? name : protocol->name);
@@ -106,7 +100,7 @@ static int protocol_hash_fn(const void *obj, const int flags)
 /*! \brief Comparison function for protocols */
 static int protocol_cmp_fn(void *obj, void *arg, int flags)
 {
-       const struct websocket_protocol *protocol1 = obj, *protocol2 = arg;
+       const struct ast_websocket_protocol *protocol1 = obj, *protocol2 = arg;
        const char *protocol = arg;
 
        return !strcasecmp(protocol1->name, flags & OBJ_KEY ? protocol : protocol2->name) ? CMP_MATCH | CMP_STOP : 0;
@@ -115,7 +109,7 @@ static int protocol_cmp_fn(void *obj, void *arg, int flags)
 /*! \brief Destructor function for protocols */
 static void protocol_destroy_fn(void *obj)
 {
-       struct websocket_protocol *protocol = obj;
+       struct ast_websocket_protocol *protocol = obj;
        ast_free(protocol->name);
 }
 
@@ -182,54 +176,91 @@ static void session_destroy_fn(void *obj)
        ast_free(session->payload);
 }
 
+struct ast_websocket_protocol *AST_OPTIONAL_API_NAME(ast_websocket_sub_protocol_alloc)(const char *name)
+{
+       struct ast_websocket_protocol *protocol;
+
+       protocol = ao2_alloc(sizeof(*protocol), protocol_destroy_fn);
+       if (!protocol) {
+               return NULL;
+       }
+
+       protocol->name = ast_strdup(name);
+       if (!protocol->name) {
+               ao2_ref(protocol, -1);
+               return NULL;
+       }
+       protocol->version = AST_WEBSOCKET_PROTOCOL_VERSION;
+
+       return protocol;
+}
+
 int AST_OPTIONAL_API_NAME(ast_websocket_server_add_protocol)(struct ast_websocket_server *server, const char *name, ast_websocket_callback callback)
 {
-       struct websocket_protocol *protocol;
+       struct ast_websocket_protocol *protocol;
 
        if (!server->protocols) {
                return -1;
        }
 
-       ao2_lock(server->protocols);
+       protocol = ast_websocket_sub_protocol_alloc(name);
+       if (!protocol) {
+               ao2_unlock(server->protocols);
+               return -1;
+       }
+       protocol->session_established = callback;
 
-       /* Ensure a second protocol handler is not registered for the same protocol */
-       if ((protocol = ao2_find(server->protocols, name, OBJ_KEY | OBJ_NOLOCK))) {
+       if (ast_websocket_server_add_protocol2(server, protocol)) {
                ao2_ref(protocol, -1);
-               ao2_unlock(server->protocols);
                return -1;
        }
 
-       if (!(protocol = ao2_alloc(sizeof(*protocol), protocol_destroy_fn))) {
-               ao2_unlock(server->protocols);
+       return 0;
+}
+
+int AST_OPTIONAL_API_NAME(ast_websocket_server_add_protocol2)(struct ast_websocket_server *server, struct ast_websocket_protocol *protocol)
+{
+       struct ast_websocket_protocol *existing;
+
+       if (!server->protocols) {
                return -1;
        }
 
-       if (!(protocol->name = ast_strdup(name))) {
-               ao2_ref(protocol, -1);
-               ao2_unlock(server->protocols);
+       if (protocol->version != AST_WEBSOCKET_PROTOCOL_VERSION) {
+               ast_log(LOG_WARNING, "WebSocket could not register sub-protocol '%s': "
+                       "expected version '%u', got version '%u'\n",
+                       protocol->name, AST_WEBSOCKET_PROTOCOL_VERSION, protocol->version);
                return -1;
        }
 
-       protocol->callback = callback;
+       ao2_lock(server->protocols);
+
+       /* Ensure a second protocol handler is not registered for the same protocol */
+       existing = ao2_find(server->protocols, protocol->name, OBJ_KEY | OBJ_NOLOCK);
+       if (existing) {
+               ao2_ref(existing, -1);
+               ao2_unlock(server->protocols);
+               return -1;
+       }
 
        ao2_link_flags(server->protocols, protocol, OBJ_NOLOCK);
        ao2_unlock(server->protocols);
-       ao2_ref(protocol, -1);
 
-       ast_verb(2, "WebSocket registered sub-protocol '%s'\n", name);
+       ast_verb(2, "WebSocket registered sub-protocol '%s'\n", protocol->name);
+       ao2_ref(protocol, -1);
 
        return 0;
 }
 
 int AST_OPTIONAL_API_NAME(ast_websocket_server_remove_protocol)(struct ast_websocket_server *server, const char *name, ast_websocket_callback callback)
 {
-       struct websocket_protocol *protocol;
+       struct ast_websocket_protocol *protocol;
 
        if (!(protocol = ao2_find(server->protocols, name, OBJ_KEY))) {
                return -1;
        }
 
-       if (protocol->callback != callback) {
+       if (protocol->session_established != callback) {
                ao2_ref(protocol, -1);
                return -1;
        }
@@ -600,7 +631,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_read)(struct ast_websocket *session, cha
 /*!
  * \brief If the server has exactly one configured protocol, return it.
  */
-static struct websocket_protocol *one_protocol(
+static struct ast_websocket_protocol *one_protocol(
        struct ast_websocket_server *server)
 {
        SCOPED_AO2LOCK(lock, server->protocols);
@@ -643,7 +674,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan
        struct ast_variable *v;
        char *upgrade = NULL, *key = NULL, *key1 = NULL, *key2 = NULL, *protos = NULL, *requested_protocols = NULL, *protocol = NULL;
        int version = 0, flags = 1;
-       struct websocket_protocol *protocol_handler = NULL;
+       struct ast_websocket_protocol *protocol_handler = NULL;
        struct ast_websocket *session;
        struct ast_websocket_server *server;
 
@@ -742,6 +773,14 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan
                }
                session->timeout =  AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT;
 
+               if (protocol_handler->session_attempted
+                   && protocol_handler->session_attempted(ser, get_vars, headers)) {
+                       ast_debug(3, "WebSocket connection from '%s' rejected by protocol handler '%s'\n",
+                               ast_sockaddr_stringify(&ser->remote_address), protocol_handler->name);
+                       ao2_ref(protocol_handler, -1);
+                       return 0;
+               }
+
                fprintf(ser->f, "HTTP/1.1 101 Switching Protocols\r\n"
                        "Upgrade: %s\r\n"
                        "Connection: Upgrade\r\n"
@@ -797,7 +836,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan
 
        /* Give up ownership of the socket and pass it to the protocol handler */
        ast_tcptls_stream_set_exclusive_input(ser->stream_cookie, 0);
-       protocol_handler->callback(session, get_vars, headers);
+       protocol_handler->session_established(session, get_vars, headers);
        ao2_ref(protocol_handler, -1);
 
        /*
@@ -881,6 +920,22 @@ int AST_OPTIONAL_API_NAME(ast_websocket_add_protocol)(const char *name, ast_webs
        return res;
 }
 
+int AST_OPTIONAL_API_NAME(ast_websocket_add_protocol2)(struct ast_websocket_protocol *protocol)
+{
+       struct ast_websocket_server *ws_server = websocketuri.data;
+
+       if (!ws_server) {
+               return -1;
+       }
+
+       if (ast_websocket_server_add_protocol2(ws_server, protocol)) {
+               return -1;
+       }
+
+       ast_module_ref(ast_module_info->self);
+       return 0;
+}
+
 static int websocket_remove_protocol_internal(const char *name, ast_websocket_callback callback)
 {
        struct ast_websocket_server *ws_server = websocketuri.data;
index 24562e1..7777214 100644 (file)
@@ -357,9 +357,9 @@ static int load_module(void)
        ast_mwi_external_ref();
 
        res = 0;
-       res |= ast_manager_register_xml_core("MWIGet", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, mwi_mailbox_get);
-       res |= ast_manager_register_xml_core("MWIDelete", EVENT_FLAG_CALL, mwi_mailbox_delete);
-       res |= ast_manager_register_xml_core("MWIUpdate", EVENT_FLAG_CALL, mwi_mailbox_update);
+       res |= ast_manager_register_xml("MWIGet", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, mwi_mailbox_get);
+       res |= ast_manager_register_xml("MWIDelete", EVENT_FLAG_CALL, mwi_mailbox_delete);
+       res |= ast_manager_register_xml("MWIUpdate", EVENT_FLAG_CALL, mwi_mailbox_update);
        if (res) {
                unload_module();
                return AST_MODULE_LOAD_DECLINE;
index 19e7657..9d85a46 100644 (file)
@@ -334,10 +334,10 @@ static void *create_object(const struct ast_sorcery *sorcery,
        return obj;
 }
 
-/*! \brief Finds a variable in a list and tests it */
+/*! \brief Finds the last variable in a list and tests it */
 static int is_variable_true(struct ast_variable *vars, const char *name)
 {
-       return ast_true(ast_variable_find_in_list(vars, name));
+       return ast_true(ast_variable_find_last_in_list(vars, name));
 }
 
 /*! \brief Appends a variable to the end of an existing list */
@@ -539,7 +539,7 @@ static int handle_auth(const struct ast_sorcery *sorcery, struct object_type_wiz
        }
 
        if (is_variable_true(wizvars, test_variable)) {
-               if (!ast_variable_find_in_list(vars, "username")) {
+               if (!ast_variable_find_last_in_list(vars, "username")) {
                        ast_log(LOG_ERROR,
                                "Wizard '%s' must have '%s_auth/username' if it %s.\n", id, direction, test_variable);
                        return -1;
@@ -557,7 +557,7 @@ static int handle_auth(const struct ast_sorcery *sorcery, struct object_type_wiz
        variable_list_append_return(&vars, "@pjsip_wizard", id);
 
        /* If the user set auth_type, don't override it. */
-       if (!ast_variable_find_in_list(vars, "auth_type")) {
+       if (!ast_variable_find_last_in_list(vars, "auth_type")) {
                variable_list_append_return(&vars, "auth_type", "userpass");
        }
 
@@ -599,8 +599,8 @@ static int handle_aor(const struct ast_sorcery *sorcery, struct object_type_wiza
        variable_list_append(&vars, "@pjsip_wizard", id);
 
        /* If the user explicitly specified an aor/contact, don't use remote hosts. */
-       if (!ast_variable_find_in_list(vars, "contact")) {
-               if (!(contact_pattern = ast_variable_find_in_list(wizvars, "contact_pattern"))) {
+       if (!ast_variable_find_last_in_list(vars, "contact")) {
+               if (!(contact_pattern = ast_variable_find_last_in_list(wizvars, "contact_pattern"))) {
                        contact_pattern = "sip:${REMOTE_HOST}";
                }
 
@@ -645,10 +645,10 @@ static int handle_endpoint(const struct ast_sorcery *sorcery, struct object_type
        struct ast_variable *wizvars = ast_category_first(wiz);
        struct ast_sorcery_object *obj = NULL;
        const char *id = ast_category_get_name(wiz);
-       const char *transport = ast_variable_find_in_list(wizvars, "transport");
-       const char *hint_context = hint_context = ast_variable_find_in_list(wizvars, "hint_context");
-       const char *hint_exten = ast_variable_find_in_list(wizvars, "hint_exten");
-       const char *hint_application= ast_variable_find_in_list(wizvars, "hint_application");
+       const char *transport = ast_variable_find_last_in_list(wizvars, "transport");
+       const char *hint_context = hint_context = ast_variable_find_last_in_list(wizvars, "hint_context");
+       const char *hint_exten = ast_variable_find_last_in_list(wizvars, "hint_exten");
+       const char *hint_application= ast_variable_find_last_in_list(wizvars, "hint_application");
        char new_id[strlen(id) + MAX_ID_SUFFIX];
        RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "endpoint/"), ast_variables_destroy);
 
@@ -656,7 +656,7 @@ static int handle_endpoint(const struct ast_sorcery *sorcery, struct object_type
        variable_list_append_return(&vars, "aors", id);
 
        if (ast_strlen_zero(hint_context)) {
-               hint_context = ast_variable_find_in_list(vars, "context");
+               hint_context = ast_variable_find_last_in_list(vars, "context");
        }
 
        if (ast_strlen_zero(hint_context)) {
@@ -737,7 +737,7 @@ static int handle_identify(const struct ast_sorcery *sorcery, struct object_type
        variable_list_append_return(&vars, "endpoint", id);
        variable_list_append_return(&vars, "@pjsip_wizard", id);
 
-       if (!ast_variable_find_in_list(vars, "match")) {
+       if (!ast_variable_find_last_in_list(vars, "match")) {
                for (host_counter = 0; host_counter < host_count; host_counter++) {
                        char *rhost = AST_VECTOR_GET(remote_hosts_vector, host_counter);
                        char host[strlen(rhost) + 1];
@@ -787,7 +787,7 @@ static int handle_phoneprov(const struct ast_sorcery *sorcery, struct object_typ
                return 0;
        }
 
-       if (!ast_variable_find_in_list(wizvars, "phoneprov/MAC")) {
+       if (!ast_variable_find_last_in_list(wizvars, "phoneprov/MAC")) {
                ast_log(LOG_ERROR,
                        "Wizard '%s' must have 'phoneprov/MAC' if it has_phoneprov.\n", id);
                return -1;
@@ -834,7 +834,7 @@ static int handle_registrations(const struct ast_sorcery *sorcery, struct object
        const char *id = ast_category_get_name(wiz);
        const char *server_uri_pattern;
        const char *client_uri_pattern;
-       const char *transport = ast_variable_find_in_list(wizvars, "transport");
+       const char *transport = ast_variable_find_last_in_list(wizvars, "transport");
        const char *username;
        char new_id[strlen(id) + MAX_ID_SUFFIX];
        int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
@@ -871,16 +871,16 @@ static int handle_registrations(const struct ast_sorcery *sorcery, struct object
 
        variable_list_append_return(&vars, "@pjsip_wizard", id);
 
-       if (!(server_uri_pattern = ast_variable_find_in_list(wizvars, "server_uri_pattern"))) {
+       if (!(server_uri_pattern = ast_variable_find_last_in_list(wizvars, "server_uri_pattern"))) {
                server_uri_pattern = "sip:${REMOTE_HOST}";
        }
 
-       if (!(client_uri_pattern = ast_variable_find_in_list(wizvars, "client_uri_pattern"))) {
+       if (!(client_uri_pattern = ast_variable_find_last_in_list(wizvars, "client_uri_pattern"))) {
                client_uri_pattern = "sip:${USERNAME}@${REMOTE_HOST}";
        }
 
        if(is_variable_true(wizvars, "sends_auth")) {
-               username = ast_variable_find_in_list(wizvars, "outbound_auth/username");
+               username = ast_variable_find_last_in_list(wizvars, "outbound_auth/username");
        } else {
                username = id;
        }
@@ -958,7 +958,7 @@ static int wizard_apply_handler(const struct ast_sorcery *sorcery, struct object
        int rc = -1;
 
        AST_VECTOR_INIT(&remote_hosts_vector, 16);
-       remote_hosts = ast_variable_find_in_list(wizvars, "remote_hosts");
+       remote_hosts = ast_variable_find_last_in_list(wizvars, "remote_hosts");
 
        if (!ast_strlen_zero(remote_hosts)) {
                char *host;
index 436a8ee..a4e86a6 100644 (file)
@@ -25,6 +25,7 @@
 
 ASTERISK_REGISTER_FILE()
 
+#include <signal.h>
 #include <unbound.h>
 #include <arpa/nameser.h>
 
index 9164293..efdbc4b 100644 (file)
@@ -17,7 +17,8 @@
  */
 
 /*!
- * \file \brief Test infrastructure for dealing with Stasis.
+ * \file
+ * \brief Test infrastructure for dealing with Stasis.
  *
  * \author David M. Lee, II <dlee@digium.com>
  */
index bebcee1..f0e089f 100644 (file)
@@ -18,6 +18,8 @@
        <support_level>extended</support_level>
  ***/
 
+/* Needed for net-snmp headers */
+#define ASTMM_LIBC ASTMM_IGNORE
 #include "asterisk.h"
 
 ASTERISK_REGISTER_FILE()
index f6fe72a..6dcdb78 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2013, Digium, Inc.",
        "_author": "David M. Lee, II <dlee@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/applications.{format}",
index f13b049..263bfd6 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
        "_author": "David M. Lee, II <dlee@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/asterisk.{format}",
index 4f7f724..b608be6 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
        "_author": "David M. Lee, II <dlee@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/bridges.{format}",
index 9bcfda9..bc0879b 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
        "_author": "David M. Lee, II <dlee@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/channels.{format}",
index 02d5d20..e232273 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
        "_author": "Kevin Harwell <kharwell@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/deviceStates.{format}",
index 17b8847..4fd077c 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
        "_author": "David M. Lee, II <dlee@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/endpoints.{format}",
index 35c9377..392b0ac 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
        "_author": "David M. Lee, II <dlee@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.2",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/events.{format}",
index 87c6f03..324e378 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2013, Digium, Inc.",
        "_author": "Jonathan Rose <jrose@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/mailboxes.{format}",
index 65ef524..63df3f2 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
        "_author": "David M. Lee, II <dlee@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/playbacks.{format}",
index d96184b..51f0a21 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
        "_author": "David M. Lee, II <dlee@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/recordings.{format}",
index 906eb6b..628aa19 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
        "_author": "David M. Lee, II <dlee@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/sounds.{format}",
index 55f4432..efec810 100644 (file)
@@ -17,7 +17,8 @@
  */
 
 /*!
- * \file \brief Test ARI API.
+ * \file
+ * \brief Test ARI API.
  * \author\verbatim David M. Lee, II <dlee@digium.com> \endverbatim
  *
  * \ingroup tests
index ad7ddca..12c75bf 100644 (file)
@@ -234,6 +234,7 @@ AST_TEST_DEFINE(config_basic_ops)
        struct ast_config *cfg = NULL;
        struct ast_category *cat = NULL;
        struct ast_variable *var;
+       struct ast_variable *varlist;
        char temp[32];
        const char *cat_name;
        const char *var_value;
@@ -537,6 +538,22 @@ AST_TEST_DEFINE(config_basic_ops)
                goto out;
        }
 
+       varlist = ast_variable_new("name1", "value1", "");
+       ast_variable_list_append_hint(&varlist, NULL, ast_variable_new("name1", "value2", ""));
+       ast_variable_list_append_hint(&varlist, NULL, ast_variable_new("name1", "value3", ""));
+
+       var_value = ast_variable_find_in_list(varlist, "name1");
+       if (strcmp(var_value, "value1") != 0) {
+               ast_test_status_update(test, "Wrong variable retrieved %s.\n", var_value);
+               goto out;
+       }
+
+       var_value = ast_variable_find_last_in_list(varlist, "name1");
+       if (strcmp(var_value, "value3") != 0) {
+               ast_test_status_update(test, "Wrong variable retrieved %s.\n", var_value);
+               goto out;
+       }
+
        res = AST_TEST_PASS;
 
 out:
index d90cd3a..d123903 100644 (file)
@@ -17,7 +17,8 @@
  */
 
 /*!
- * \file \brief Test endpoints.
+ * \file
+ * \brief Test endpoints.
  *
  * \author\verbatim David M. Lee, II <dlee@digium.com> \endverbatim
  *
index 7080fac..9155781 100644 (file)
@@ -17,7 +17,8 @@
  */
 
 /*!
- * \file \brief Test JSON API.
+ * \file
+ * \brief Test JSON API.
  *
  * While some of these tests are actually testing our JSON library wrapper, the bulk of
  * them are exploratory tests to determine what the behavior of the underlying JSON
index 26cd90a..2c9334a 100644 (file)
@@ -51,8 +51,6 @@ ASTERISK_REGISTER_FILE()
 /*! \brief The number of user events we should get in a dialplan test */
 #define DEFAULT_EXPECTED_EVENTS 4
 
-static struct ast_context *test_message_context;
-
 /*! \brief The current number of received user events */
 static int received_user_events;
 
@@ -822,9 +820,7 @@ static int unload_module(void)
        AST_TEST_UNREGISTER(test_message_has_destination_handler);
        AST_TEST_UNREGISTER(test_message_msg_send);
 
-       if (test_message_context) {
-               ast_context_destroy(test_message_context, AST_MODULE);
-       }
+       ast_context_destroy(NULL, AST_MODULE);
 
        ast_manager_unregister_hook(&user_event_hook);
 
@@ -835,8 +831,7 @@ static int create_test_dialplan(void)
 {
        int res = 0;
 
-       test_message_context = ast_context_find_or_create(NULL, NULL, TEST_CONTEXT, AST_MODULE);
-       if (!test_message_context) {
+       if (!ast_context_find_or_create(NULL, NULL, TEST_CONTEXT, AST_MODULE)) {
                return -1;
        }
 
index a89b461..f7809d3 100644 (file)
@@ -17,7 +17,8 @@
  */
 
 /*!
- * \file \brief Test optional API.
+ * \file
+ * \brief Test optional API.
  *
  * This tests exercise the underlying implementation functions. Acutal usage
  * won't look anything like this; it would use the wrapper macros.
index 7edc985..4e28d44 100644 (file)
@@ -17,7 +17,8 @@
  */
 
 /*!
- * \file \brief Test Stasis Application API.
+ * \file
+ * \brief Test Stasis Application API.
  * \author\verbatim David M. Lee, II <dlee@digium.com> \endverbatim
  *
  * \ingroup tests
index ce04c62..b6b3d09 100644 (file)
@@ -179,6 +179,19 @@ static struct sorcery_test_caching cache = { 0, };
 /*! \brief Global scope observer structure for testing */
 static struct sorcery_test_observer observer;
 
+static void *wizard2_data;
+
+static void *sorcery_test_open(const char *data)
+{
+       wizard2_data = (void *)data;
+       return wizard2_data;
+}
+
+static void sorcery_test_close(void *data)
+{
+
+}
+
 static int sorcery_test_create(const struct ast_sorcery *sorcery, void *data, void *object)
 {
        cache.created = 1;
@@ -204,7 +217,7 @@ static int sorcery_test_delete(const struct ast_sorcery *sorcery, void *data, vo
        return 0;
 }
 
-/*! \brief Dummy sorcery wizard, not actually used so we only populate the name and nothing else */
+/*! \brief Dummy sorcery wizards, not actually used so we only populate the name and nothing else */
 static struct ast_sorcery_wizard test_wizard = {
        .name = "test",
        .create = sorcery_test_create,
@@ -213,6 +226,16 @@ static struct ast_sorcery_wizard test_wizard = {
        .delete = sorcery_test_delete,
 };
 
+static struct ast_sorcery_wizard test_wizard2 = {
+       .name = "test2",
+       .open = sorcery_test_open,
+       .close = sorcery_test_close,
+       .create = sorcery_test_create,
+       .retrieve_id = sorcery_test_retrieve_id,
+       .update = sorcery_test_update,
+       .delete = sorcery_test_delete,
+};
+
 static void sorcery_observer_created(const void *object)
 {
        SCOPED_MUTEX(lock, &observer.lock);
@@ -3340,6 +3363,111 @@ AST_TEST_DEFINE(wizard_observation)
        return AST_TEST_PASS;
 }
 
+AST_TEST_DEFINE(wizard_apply_and_insert)
+{
+       RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+       RAII_VAR(struct ast_sorcery_wizard *, wizard1, &test_wizard, ast_sorcery_wizard_unregister);
+       RAII_VAR(struct ast_sorcery_wizard *, wizard2, &test_wizard2, ast_sorcery_wizard_unregister);
+       RAII_VAR(struct ast_sorcery_wizard *, wizard, NULL, ao2_cleanup);
+       void *data;
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "wizard_apply_and_insert";
+               info->category = "/main/sorcery/";
+               info->summary = "sorcery wizard apply and insert test";
+               info->description =
+                       "sorcery wizard apply and insert test";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       wizard1->load = sorcery_test_load;
+       wizard1->reload = sorcery_test_load;
+
+       wizard2->load = sorcery_test_load;
+       wizard2->reload = sorcery_test_load;
+
+       if (!(sorcery = ast_sorcery_open())) {
+               ast_test_status_update(test, "Failed to open a sorcery instance\n");
+               return AST_TEST_FAIL;
+       }
+
+       ast_sorcery_wizard_register(wizard1);
+       ast_sorcery_wizard_register(wizard2);
+
+       /* test_object_type isn't registered yet so count should return error */
+       ast_test_validate(test,
+               ast_sorcery_get_wizard_mapping_count(sorcery, "test_object_type") == -1);
+
+       ast_sorcery_apply_default(sorcery, "test_object_type", "test", NULL);
+
+       ast_test_validate(test,
+               ast_sorcery_get_wizard_mapping_count(sorcery, "test_object_type") == 1);
+
+       ast_test_validate(test,
+               ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", 0, &wizard, NULL) == 0);
+       ast_test_validate(test, strcmp("test", wizard->name) == 0);
+       ao2_ref(wizard, -1);
+       wizard = NULL;
+
+       ast_test_validate(test,
+               ast_sorcery_insert_wizard_mapping(sorcery, "test_object_type", "test2", "test2data", 0, 0) == 0);
+
+       ast_test_validate(test,
+               ast_sorcery_insert_wizard_mapping(sorcery, "test_object_type", "test2", "test2data", 0, 0) != 0);
+
+       ast_test_validate(test,
+               ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", 0, &wizard, &data) == 0);
+       ast_test_validate(test, strcmp("test2", wizard->name) == 0);
+       ast_test_validate(test, strcmp("test2data", data) == 0);
+       ao2_ref(wizard, -1);
+       wizard = NULL;
+
+       ast_test_validate(test,
+               ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", 1, &wizard, NULL) == 0);
+       ast_test_validate(test, strcmp("test", wizard->name) == 0);
+       ao2_ref(wizard, -1);
+       wizard = NULL;
+
+       /* Test failures */
+       ast_test_validate(test,
+               ast_sorcery_get_wizard_mapping(sorcery, "non-existent-type", 0, &wizard, NULL) != 0);
+
+       ast_test_validate(test,
+               ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", -1, &wizard, &data) != 0);
+
+       ast_test_validate(test,
+               ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", 2, &wizard, NULL) != 0);
+
+       ast_test_validate(test,
+               ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", 2, NULL, NULL) != 0);
+
+       /* Test remove */
+       /* Should fail */
+       ast_test_validate(test,
+               ast_sorcery_remove_wizard_mapping(sorcery, "non-existent-type", "somewizard") != 0);
+       ast_test_validate(test,
+               ast_sorcery_remove_wizard_mapping(sorcery, "test_object_type", "somewizard") != 0);
+
+       /* should work */
+       ast_test_validate(test,
+               ast_sorcery_remove_wizard_mapping(sorcery, "test_object_type", "test") == 0);
+
+       ast_test_validate(test,
+               ast_sorcery_get_wizard_mapping_count(sorcery, "test_object_type") == 1);
+
+       ast_test_validate(test,
+               ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", 0, &wizard, &data) == 0);
+       ast_test_validate(test, strcmp("test2", wizard->name) == 0);
+       ast_test_validate(test, strcmp("test2data", data) == 0);
+       ao2_ref(wizard, -1);
+       wizard = NULL;
+
+       return AST_TEST_PASS;
+}
+
 static int unload_module(void)
 {
        AST_TEST_UNREGISTER(wizard_registration);
@@ -3390,12 +3518,14 @@ static int unload_module(void)
        AST_TEST_UNREGISTER(global_observation);
        AST_TEST_UNREGISTER(instance_observation);
        AST_TEST_UNREGISTER(wizard_observation);
+       AST_TEST_UNREGISTER(wizard_apply_and_insert);
 
        return 0;
 }
 
 static int load_module(void)
 {
+       AST_TEST_REGISTER(wizard_apply_and_insert);
        AST_TEST_REGISTER(wizard_registration);
        AST_TEST_REGISTER(sorcery_open);
        AST_TEST_REGISTER(apply_default);
index 7061b97..b5755c8 100644 (file)
@@ -17,7 +17,8 @@
  */
 
 /*!
- * \file \brief Test Stasis message bus.
+ * \file
+ * \brief Test Stasis message bus.
  *
  * \author\verbatim David M. Lee, II <dlee@digium.com> \endverbatim
  *
index f9bbb96..fe320f2 100644 (file)
@@ -17,7 +17,8 @@
  */
 
 /*!
- * \file \brief Test Stasis Channel messages and objects
+ * \file
+ * \brief Test Stasis Channel messages and objects
  *
  * \author\verbatim Matt Jordan <mjordan@digium.com> \endverbatim
  *
index 8e3907f..848d86b 100644 (file)
@@ -17,7 +17,8 @@
  */
 
 /*!
- * \file \brief Test endpoints.
+ * \file
+ * \brief Test endpoints.
  *
  * \author\verbatim David M. Lee, II <dlee@digium.com> \endverbatim
  *
index 8729801..4eaea3c 100644 (file)
@@ -18,7 +18,7 @@
 
 
 /*!
- * \file extconf
+ * \file
  * A condensation of the pbx_config stuff, to read into exensions.conf, and provide an interface to the data there,
  * for operations outside of asterisk. A huge, awful hack.
  *
@@ -43,7 +43,7 @@
        <support_level>extended</support_level>
  ***/
 
-#define WRAP_LIBC_MALLOC
+#define ASTMM_LIBC ASTMM_REDIRECT
 #include "asterisk.h"
 
 #undef DEBUG_THREADS