<p>Friendly Automation <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/18429">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  George Joseph: Looks good to me, approved
  Friendly Automation: Approved for Submit

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_aeap & res_speech_aeap: Add Asterisk External Application Protocol<br><br>Add framework to connect to, and read and write protocol based<br>messages from and to an external application using an Asterisk<br>External Application Protocol (AEAP). This has been divided into<br>several abstractions:<br><br> 1. transport - base communication layer (currently websocket only)<br> 2. message - AEAP description and data (currently JSON only)<br> 3. transaction - links/binds requests and responses<br> 4. aeap - transport, message, and transaction handler/manager<br><br>This patch also adds an AEAP implementation for speech to text.<br>Existing speech API callbacks for speech to text have been completed<br>making it possible for Asterisk to connect to a configured external<br>translator service and provide audio for STT. Results can also be<br>received from the external translator, and made available as speech<br>results in Asterisk.<br><br>Unit tests have also been created that test the AEAP framework, and<br>also the speech to text implementation.<br><br>ASTERISK-29726 #close<br><br>Change-Id: Iaa4b259f84aa63501e5fd2a6fb107f900b4d4ed2<br>---<br>M configs/samples/aeap.conf.sample<br>A include/asterisk/res_aeap.h<br>A include/asterisk/res_aeap_message.h<br>M include/asterisk/speech.h<br>M res/Makefile<br>M res/res_aeap.c<br>A res/res_aeap.exports.in<br>A res/res_aeap/aeap.c<br>A res/res_aeap/general.c<br>A res/res_aeap/general.h<br>A res/res_aeap/logger.h<br>A res/res_aeap/message.c<br>A res/res_aeap/message_json.c<br>A res/res_aeap/transaction.c<br>A res/res_aeap/transaction.h<br>A res/res_aeap/transport.c<br>A res/res_aeap/transport.h<br>A res/res_aeap/transport_websocket.c<br>A res/res_aeap/transport_websocket.h<br>M res/res_speech.c<br>A res/res_speech_aeap.c<br>A tests/test_aeap.c<br>A tests/test_aeap_speech.c<br>A tests/test_aeap_transaction.c<br>A tests/test_aeap_transport.c<br>25 files changed, 4,831 insertions(+), 57 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/configs/samples/aeap.conf.sample b/configs/samples/aeap.conf.sample</span><br><span>index 8941dd1..2431ea3 100644</span><br><span>--- a/configs/samples/aeap.conf.sample</span><br><span>+++ b/configs/samples/aeap.conf.sample</span><br><span>@@ -2,14 +2,20 @@</span><br><span> ; This file is used by the res_aeap module to configure parameters</span><br><span> ; used for AEAP applications.</span><br><span> ;</span><br><span style="color: hsl(0, 100%, 40%);">-;[myserver]</span><br><span style="color: hsl(120, 100%, 40%);">+;[myclient]</span><br><span> ;</span><br><span style="color: hsl(0, 100%, 40%);">-; type must be "server".</span><br><span style="color: hsl(0, 100%, 40%);">-;type=server</span><br><span style="color: hsl(120, 100%, 40%);">+; type must be "client".</span><br><span style="color: hsl(120, 100%, 40%);">+;type=client</span><br><span> ;</span><br><span style="color: hsl(0, 100%, 40%);">-; server_url must be a websocket URL (ws or wss).</span><br><span style="color: hsl(0, 100%, 40%);">-;server_url</span><br><span style="color: hsl(120, 100%, 40%);">+; URL used to connect to a server. It must be a websocket URL (ws or wss).</span><br><span style="color: hsl(120, 100%, 40%);">+;url=ws://127.0.0.1:9099</span><br><span> ;</span><br><span style="color: hsl(0, 100%, 40%);">-; codecs is an optional list of codecs that will be used over the codecs</span><br><span style="color: hsl(0, 100%, 40%);">-; specified on an endpoint if this option is present.</span><br><span style="color: hsl(0, 100%, 40%);">-;codecs=ulaw,alaw,g722,opus</span><br><span style="color: hsl(120, 100%, 40%);">+; codecs is comma separated string of allowed/disallowed codec names.</span><br><span style="color: hsl(120, 100%, 40%);">+;codecs=!all,ulaw,alaw,opus</span><br><span style="color: hsl(120, 100%, 40%);">+;</span><br><span style="color: hsl(120, 100%, 40%);">+; protocol is the implementation specific sub-protocol</span><br><span style="color: hsl(120, 100%, 40%);">+;protocol=speech_to_text</span><br><span style="color: hsl(120, 100%, 40%);">+;</span><br><span style="color: hsl(120, 100%, 40%);">+; "@" parameters can be specified and are used to to set custom values to</span><br><span style="color: hsl(120, 100%, 40%);">+; be passed as "params" in the initial "setup" request.</span><br><span style="color: hsl(120, 100%, 40%);">+;@language=en-US</span><br><span>diff --git a/include/asterisk/res_aeap.h b/include/asterisk/res_aeap.h</span><br><span>new file mode 100644</span><br><span>index 0000000..0a1747e</span><br><span>--- /dev/null</span><br><span>+++ b/include/asterisk/res_aeap.h</span><br><span>@@ -0,0 +1,370 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Asterisk External Application Protocol API</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifndef AST_RES_AEAP_H</span><br><span style="color: hsl(120, 100%, 40%);">+#define AST_RES_AEAP_H</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdint.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ao2_container;</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_sorcery;</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_variable;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_client_config;</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_message;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define AEAP_CONFIG_CLIENT "client"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve the AEAP sorcery object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns the AEAP sorcery object</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_sorcery *ast_aeap_sorcery(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve a listing of all client configuration objects by protocol.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note Caller is responsible for the returned container's reference.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param protocol An optional protocol to filter on (if NULL returns all client configs)</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns A container of client configuration objects</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ao2_container *ast_aeap_client_configs_get(const char *protocol);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve codec capabilities from the configuration</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param config A configuration object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns The configuration's codec capabilities</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const struct ast_format_cap *ast_aeap_client_config_codecs(const struct ast_aeap_client_config *cfg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Check a given protocol against that in an Asterisk external application configuration</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param config A configuration object</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param protocol The protocol to check</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns True if the configuration's protocol matches, false otherwise</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_client_config_has_protocol(const struct ast_aeap_client_config *cfg,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *protocol);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve a list of custom configuration fields</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param id configuration id/sorcery lookup key</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns variables, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_variable *ast_aeap_custom_fields_get(const char *id);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief An Asterisk external application object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Connects to an external application, sending and receiving data, and</span><br><span style="color: hsl(120, 100%, 40%);">+ * dispatches received data to registered handlers.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Event raised when a message is received</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param aeap An Asterisk external application object</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param message The received message</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param obj Associated user object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on if message handled, otherwise non-zero</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+typedef int (*ast_aeap_on_message)(struct ast_aeap *aeap, struct ast_aeap_message *message, void *obj);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief An Asterisk external application message handler</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Used to register message handlers with an AEAP object.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_message_handler {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! The handler name */</span><br><span style="color: hsl(120, 100%, 40%);">+       const char *name;</span><br><span style="color: hsl(120, 100%, 40%);">+     /*! Callback triggered when on a name match */</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_aeap_on_message on_message;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Event raised when a sent message does not receive a reply within</span><br><span style="color: hsl(120, 100%, 40%);">+ *        a specified time interval</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param aeap An Asterisk external application object</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param message The message sent that received no response</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param obj Associated user object</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+typedef void (*ast_aeap_on_timeout)(struct ast_aeap *aeap, struct ast_aeap_message *message, void *obj);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Callback to cleanup a user object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param obj The user object</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+typedef void (*ast_aeap_user_obj_cleanup)(void *obj);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Supported Asterisk external application data types</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+enum AST_AEAP_DATA_TYPE {</span><br><span style="color: hsl(120, 100%, 40%);">+    AST_AEAP_DATA_TYPE_NONE,</span><br><span style="color: hsl(120, 100%, 40%);">+      AST_AEAP_DATA_TYPE_BINARY,</span><br><span style="color: hsl(120, 100%, 40%);">+    AST_AEAP_DATA_TYPE_STRING,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Callbacks and other parameters used by an Asterisk external application object</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_params {</span><br><span style="color: hsl(120, 100%, 40%);">+        /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * If true pass along error messages to the implementation.</span><br><span style="color: hsl(120, 100%, 40%);">+    * Otherwise log it only, and consider it handled.</span><br><span style="color: hsl(120, 100%, 40%);">+     */</span><br><span style="color: hsl(120, 100%, 40%);">+   unsigned int emit_error;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /*! The message type used for communication */</span><br><span style="color: hsl(120, 100%, 40%);">+        const struct ast_aeap_message_type *msg_type;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /*! Response handlers array */</span><br><span style="color: hsl(120, 100%, 40%);">+        const struct ast_aeap_message_handler *response_handlers;</span><br><span style="color: hsl(120, 100%, 40%);">+     /*! The number of response handlers */</span><br><span style="color: hsl(120, 100%, 40%);">+        uintmax_t response_handlers_size;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*! Request handlers array */</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_aeap_message_handler *request_handlers;</span><br><span style="color: hsl(120, 100%, 40%);">+      /*! The number of request handlers */</span><br><span style="color: hsl(120, 100%, 40%);">+ uintmax_t request_handlers_size;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Raised when binary data is received</span><br><span style="color: hsl(120, 100%, 40%);">+  *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param aeap An Asterisk external application object</span><br><span style="color: hsl(120, 100%, 40%);">+         * \param buf The buffer containing binary data</span><br><span style="color: hsl(120, 100%, 40%);">+        * \param size The size of the buffer</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+   void (*on_binary)(struct ast_aeap *aeap, const void *buf, intmax_t size);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Raised when string data is received</span><br><span style="color: hsl(120, 100%, 40%);">+  *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param aeap An Asterisk external application object</span><br><span style="color: hsl(120, 100%, 40%);">+         * \param buf The buffer containing string data</span><br><span style="color: hsl(120, 100%, 40%);">+        * \param size The size/length of the string</span><br><span style="color: hsl(120, 100%, 40%);">+   */</span><br><span style="color: hsl(120, 100%, 40%);">+   void (*on_string)(struct ast_aeap *aeap, const char *buf, intmax_t size);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Raised when an error occurs during reading</span><br><span style="color: hsl(120, 100%, 40%);">+   *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \note This is an AEAP transport level read error event</span><br><span style="color: hsl(120, 100%, 40%);">+      *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \note When this event is triggered the client has also</span><br><span style="color: hsl(120, 100%, 40%);">+      *       been disconnected.</span><br><span style="color: hsl(120, 100%, 40%);">+    *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param aeap An Asterisk external application object</span><br><span style="color: hsl(120, 100%, 40%);">+         */</span><br><span style="color: hsl(120, 100%, 40%);">+   void (*on_error)(struct ast_aeap *aeap);</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create an Asterisk external application object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param type The type of underlying transport</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param params Callbacks and other parameters to use</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns A new ao2 reference counted aeap object, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap *ast_aeap_create(const char *type, const struct ast_aeap_params *params);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create an Asterisk external application object by sorcery id</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param id The sorcery id to lookup</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param params Callbacks and other parameters to use</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns A new ao2 reference counted aeap object, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap *ast_aeap_create_by_id(const char *id, const struct ast_aeap_params *params);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Connect to an external application</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param aeap An Asterisk external application object</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param url The url to connect to</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param protocol A protocol to use</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param timeout How long (in milliseconds) to attempt to connect (-1 equals infinite)</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 if able to connect, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_connect(struct ast_aeap *aeap, const char *url, const char *protocol, int timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create and connect to an Asterisk external application by sorcery id</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param id The sorcery id to lookup</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param params Callbacks and other parameters to use</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param timeout How long (in milliseconds) to attempt to connect (-1 equals infinite)</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns A new ao2 reference counted aeap object, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap *ast_aeap_create_and_connect_by_id(const char *id,</span><br><span style="color: hsl(120, 100%, 40%);">+  const struct ast_aeap_params *params, int timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create and connect to an Asterisk external application</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param type The type of client connection to make</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param params Callbacks and other parameters to use</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param url The url to connect to</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param protocol A protocol to use</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param timeout How long (in milliseconds) to attempt to connect (-1 equals infinite)</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns A new ao2 reference counted aeap object, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap *ast_aeap_create_and_connect(const char *type,</span><br><span style="color: hsl(120, 100%, 40%);">+    const struct ast_aeap_params *params, const char *url, const char *protocol, int timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Disconnect an Asterisk external application object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note Depending on the underlying transport this call may block</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param aeap An Asterisk external application object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_disconnect(struct ast_aeap *aeap);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Register a user data object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note The "cleanup" is called on un-register, if one is specified</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param aeap An Asterisk external application object</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param id The look up id for the object</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param obj The user object to register</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param cleanup Optional user object clean up callback</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_user_data_register(struct ast_aeap *aeap, const char *id, void *obj,</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_aeap_user_obj_cleanup cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Un-register a user data object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note If specified on register, the "cleanup" callback is called during unregister.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param aeap An Asterisk external application object</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param id The look up id for the object</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_aeap_user_data_unregister(struct ast_aeap *aeap, const char *id);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve a registered user data object by its id</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note Depending on how it was registered the returned user data object's lifetime</span><br><span style="color: hsl(120, 100%, 40%);">+ *       may be managed by the given "aeap" object. If it was registered with a cleanup</span><br><span style="color: hsl(120, 100%, 40%);">+ *       handler that [potentially] frees it the caller of this function must ensure</span><br><span style="color: hsl(120, 100%, 40%);">+ *       it's done using the returned object before it's unregistered.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param data A user data object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns A user data object</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void *ast_aeap_user_data_object_by_id(struct ast_aeap *aeap, const char *id);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Send a binary data to an external application</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param aeap An Asterisk external application object</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param buf Binary data to send</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param size The size of the binary data</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_send_binary(struct ast_aeap *aeap, const void *buf, uintmax_t size);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Send a message to an external application</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note "Steals" the given message reference, thus callers are not required to un-ref</span><br><span style="color: hsl(120, 100%, 40%);">+ *       the message object after calling this function.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param aeap An Asterisk external application object</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param msg The message to send</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_send_msg(struct ast_aeap *aeap, struct ast_aeap_message *msg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Parameters to be used when sending a transaction based message</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_tsx_params {</span><br><span style="color: hsl(120, 100%, 40%);">+        /*! The message to send */</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_aeap_message *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! The amount of time (in milliseconds) to wait for a received message */</span><br><span style="color: hsl(120, 100%, 40%);">+    int timeout;</span><br><span style="color: hsl(120, 100%, 40%);">+  /*! Optional callback raised when no message is received in an allotted time */</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_aeap_on_timeout on_timeout;</span><br><span style="color: hsl(120, 100%, 40%);">+       /*! Whether or not to block the current thread, and wait for a received message */</span><br><span style="color: hsl(120, 100%, 40%);">+    int wait;</span><br><span style="color: hsl(120, 100%, 40%);">+     /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * Optional user object to pass to handlers. User is responsible for object's lifetime</span><br><span style="color: hsl(120, 100%, 40%);">+     * unless an obj_cleanup callback is specified that handles its cleanup (e.g. freeing</span><br><span style="color: hsl(120, 100%, 40%);">+  * of memory).</span><br><span style="color: hsl(120, 100%, 40%);">+         */</span><br><span style="color: hsl(120, 100%, 40%);">+   void *obj;</span><br><span style="color: hsl(120, 100%, 40%);">+    /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * Optional user object cleanup callback. If specified, called upon "this" param's</span><br><span style="color: hsl(120, 100%, 40%);">+       * destruction (including on error).</span><br><span style="color: hsl(120, 100%, 40%);">+   */</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_aeap_user_obj_cleanup obj_cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Send a transaction based message to an external application using the given parameters</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note "Steals" the given message reference, thus callers are not required to un-ref</span><br><span style="color: hsl(120, 100%, 40%);">+ *       the message object after calling this function.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note Also handles cleaning up the user object if the obj_cleanup callback</span><br><span style="color: hsl(120, 100%, 40%);">+ *       is specified in "params".</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param aeap An Asterisk external application object</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param msg The message to send</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param params (optional) Additional parameters to consider when sending. Heap allocation</span><br><span style="color: hsl(120, 100%, 40%);">+ *     not required.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_send_msg_tsx(struct ast_aeap *aeap, struct ast_aeap_tsx_params *params);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#endif /* AST_RES_AEAP_H */</span><br><span>diff --git a/include/asterisk/res_aeap_message.h b/include/asterisk/res_aeap_message.h</span><br><span>new file mode 100644</span><br><span>index 0000000..294039e</span><br><span>--- /dev/null</span><br><span>+++ b/include/asterisk/res_aeap_message.h</span><br><span>@@ -0,0 +1,374 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Asterisk External Application Protocol Message API</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifndef AST_AEAP_MESSAGE_H</span><br><span style="color: hsl(120, 100%, 40%);">+#define AST_AEAP_MESSAGE_H</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdint.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_aeap.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_message;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Message type virtual method table</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_message_type {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! The size of the message implementation type. Used for allocations. */</span><br><span style="color: hsl(120, 100%, 40%);">+     size_t type_size;</span><br><span style="color: hsl(120, 100%, 40%);">+     /*! The name of this type */</span><br><span style="color: hsl(120, 100%, 40%);">+  const char *type_name;</span><br><span style="color: hsl(120, 100%, 40%);">+        /*! The type to serialize to, and de-serialize from */</span><br><span style="color: hsl(120, 100%, 40%);">+        enum AST_AEAP_DATA_TYPE serial_type;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Construct/Initialize a message object</span><br><span style="color: hsl(120, 100%, 40%);">+        *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param self The message object to initialize</span><br><span style="color: hsl(120, 100%, 40%);">+        * \param params Other optional parameter(s) to possibly use</span><br><span style="color: hsl(120, 100%, 40%);">+   *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+   int (*construct1)(struct ast_aeap_message *self, const void *params);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Construct/Initialize a message object</span><br><span style="color: hsl(120, 100%, 40%);">+        *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param self The message object to initialize</span><br><span style="color: hsl(120, 100%, 40%);">+        * \param msg_type The type of message (e.g. request or response)</span><br><span style="color: hsl(120, 100%, 40%);">+      * \param name The name of the message</span><br><span style="color: hsl(120, 100%, 40%);">+         * \param id The message id</span><br><span style="color: hsl(120, 100%, 40%);">+    * \param params Other optional parameter(s) to possibly use</span><br><span style="color: hsl(120, 100%, 40%);">+   *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+   int (*construct2)(struct ast_aeap_message *self, const char *msg_type, const char *name,</span><br><span style="color: hsl(120, 100%, 40%);">+              const char *id, const void *params);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Destruct/Cleanup object resources</span><br><span style="color: hsl(120, 100%, 40%);">+    *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param self The message object being destructed</span><br><span style="color: hsl(120, 100%, 40%);">+     */</span><br><span style="color: hsl(120, 100%, 40%);">+   void (*destruct)(struct ast_aeap_message *self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Deserialize the given buffer into a message object</span><br><span style="color: hsl(120, 100%, 40%);">+   *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param self The message object to deserialize into</span><br><span style="color: hsl(120, 100%, 40%);">+  * \param buf The buffer to deserialize</span><br><span style="color: hsl(120, 100%, 40%);">+        * \param size The size/length of the buffer</span><br><span style="color: hsl(120, 100%, 40%);">+   *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+   int (*deserialize)(struct ast_aeap_message *self, const void *buf, intmax_t size);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Serialize the message object into byte/char buffer</span><br><span style="color: hsl(120, 100%, 40%);">+   *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param self The message object to serialize</span><br><span style="color: hsl(120, 100%, 40%);">+         * \param buf [out] The buffer to hold the "packed" data</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param size [out] The size of the data written to the buffer</span><br><span style="color: hsl(120, 100%, 40%);">+        *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+   int (*serialize)(const struct ast_aeap_message *self, void **buf, intmax_t *size);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Retrieve a message id</span><br><span style="color: hsl(120, 100%, 40%);">+        *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param self The message object</span><br><span style="color: hsl(120, 100%, 40%);">+      *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \returns The message id</span><br><span style="color: hsl(120, 100%, 40%);">+     */</span><br><span style="color: hsl(120, 100%, 40%);">+   const char *(*id)(const struct ast_aeap_message *self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Set a message id.</span><br><span style="color: hsl(120, 100%, 40%);">+    *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param self The message object</span><br><span style="color: hsl(120, 100%, 40%);">+      * \param id The id to set</span><br><span style="color: hsl(120, 100%, 40%);">+     *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+   int (*id_set)(struct ast_aeap_message *self, const char *id);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Retrieve a message name</span><br><span style="color: hsl(120, 100%, 40%);">+      *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param self The message object</span><br><span style="color: hsl(120, 100%, 40%);">+      *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \returns The message name</span><br><span style="color: hsl(120, 100%, 40%);">+   */</span><br><span style="color: hsl(120, 100%, 40%);">+   const char *(*name)(const struct ast_aeap_message *self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Retrieve the core message data/body</span><br><span style="color: hsl(120, 100%, 40%);">+  *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param self This message object</span><br><span style="color: hsl(120, 100%, 40%);">+     */</span><br><span style="color: hsl(120, 100%, 40%);">+   void *(*data)(struct ast_aeap_message *self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Retrieve whether or not this is a request message</span><br><span style="color: hsl(120, 100%, 40%);">+    *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param self The message object</span><br><span style="color: hsl(120, 100%, 40%);">+      *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \returns True if message is a request, false otherwise</span><br><span style="color: hsl(120, 100%, 40%);">+      */</span><br><span style="color: hsl(120, 100%, 40%);">+   int (*is_request)(const struct ast_aeap_message *self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Retrieve whether or not this is a response message</span><br><span style="color: hsl(120, 100%, 40%);">+   *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param self The message object</span><br><span style="color: hsl(120, 100%, 40%);">+      *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \returns True if message is a response, false otherwise</span><br><span style="color: hsl(120, 100%, 40%);">+     */</span><br><span style="color: hsl(120, 100%, 40%);">+   int (*is_response)(const struct ast_aeap_message *self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Retrieve the error message if it has one</span><br><span style="color: hsl(120, 100%, 40%);">+     *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param self The message object</span><br><span style="color: hsl(120, 100%, 40%);">+      *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \returns The error message if available, or NULL</span><br><span style="color: hsl(120, 100%, 40%);">+    */</span><br><span style="color: hsl(120, 100%, 40%);">+   const char *(*error_msg)(const struct ast_aeap_message *self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Set an error message</span><br><span style="color: hsl(120, 100%, 40%);">+         *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param self The message object</span><br><span style="color: hsl(120, 100%, 40%);">+      * \param error_msg The error message string to set</span><br><span style="color: hsl(120, 100%, 40%);">+    *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+   int (*error_msg_set)(struct ast_aeap_message *self, const char *error_msg);</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Asterisk external application base message</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_message {</span><br><span style="color: hsl(120, 100%, 40%);">+  /*! The type virtual table */</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_aeap_message_type *type;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve the serial type a message type</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param type A message type</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns The type's serial type</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+enum AST_AEAP_DATA_TYPE ast_aeap_message_serial_type(const struct ast_aeap_message_type *type);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create an Asterisk external application message object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param type The type of message object to create</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param params Any parameter(s) to pass to the type's constructor</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns An ao2 reference counted AEAP message object, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_message *ast_aeap_message_create1(const struct ast_aeap_message_type *type,</span><br><span style="color: hsl(120, 100%, 40%);">+     const void *params);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create an Asterisk external application message object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param type The type of message object to create</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param msg_type The type of message (e.g. request or response)</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name The name of the message</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param id The message id</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param params Other optional parameter(s) to possibly use</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns An ao2 reference counted AEAP message object, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_message *ast_aeap_message_create2(const struct ast_aeap_message_type *type,</span><br><span style="color: hsl(120, 100%, 40%);">+        const char *msg_type, const char *name, const char *id, const void *params);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create an Asterisk external application request object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param type The type of message object to create</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name The name of the message</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param id Optional id (if NULL an id is generated)</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param params Other optional parameter(s) to possibly use</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns An ao2 reference counted AEAP request object, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_message *ast_aeap_message_create_request(const struct ast_aeap_message_type *type,</span><br><span style="color: hsl(120, 100%, 40%);">+  const char *name, const char *id, const void *params);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create an Asterisk external application response object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param type The type of message object to create</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name The name of the message</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param id Optional id</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param params Other optional parameter(s) to possibly use</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns An ao2 reference counted AEAP response object, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_message *ast_aeap_message_create_response(const struct ast_aeap_message_type *type,</span><br><span style="color: hsl(120, 100%, 40%);">+  const char *name, const char *id, const void *params);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create an Asterisk external application error response object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param type The type of message object to create</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name The name of the message</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param id Optional id</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param error_msg Error message to set</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param params Other optional parameter(s) to possibly use</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns An ao2 reference counted AEAP response object, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_message *ast_aeap_message_create_error(const struct ast_aeap_message_type *type,</span><br><span style="color: hsl(120, 100%, 40%);">+     const char *name, const char *id, const char *error_msg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Deserialize the given buffer into an Asterisk external application message object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param type The message type to create, and deserialize to</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param buf The buffer to deserialize</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param size The size/length of the buffer</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns An ao2 reference counted AEAP message object, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_message *ast_aeap_message_deserialize(const struct ast_aeap_message_type *type,</span><br><span style="color: hsl(120, 100%, 40%);">+ const void *buf, intmax_t size);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Serialize the given message object into a byte/char buffer</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param message The message object to serialize</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param buf [out] The buffer to hold the "packed" data</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param size [out] The size of the data written to the buffer</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_message_serialize(const struct ast_aeap_message *message,</span><br><span style="color: hsl(120, 100%, 40%);">+     void **buf, intmax_t *size);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve a message id</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param message A message object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns The message id, or an empty string</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_aeap_message_id(const struct ast_aeap_message *message);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Set a message id.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param message A message object</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param id The id to set</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_message_id_set(struct ast_aeap_message *message, const char *id);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Generate an id, and set it for the message</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param message A message object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns the generated id on success, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_aeap_message_id_generate(struct ast_aeap_message *message);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve a message name</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param message A message object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns The message name, or an empty string</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_aeap_message_name(const struct ast_aeap_message *message);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Check whether or not a message's name matches the given one</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note Case insensitive</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param message A message object</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param message name The name to check against</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns True if matched, false otherwise</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_message_is_named(const struct ast_aeap_message *message, const char *name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve the core message data/body</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param message A message object</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void *ast_aeap_message_data(struct ast_aeap_message *message);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve whether or not this is a request message</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param message A message object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns True if the message is a request, false otherwise</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_message_is_request(const struct ast_aeap_message *message);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve whether or not this is a response message</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param message A message object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns True if the message is a response, false otherwise</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_message_is_response(const struct ast_aeap_message *message);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve the error message if it has one</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param message A message object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns The error message if available, or NULL</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_aeap_message_error_msg(const struct ast_aeap_message *message);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Set an error message.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param message A message object</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param error_msg The error string to set</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_message_error_msg_set(struct ast_aeap_message *message,</span><br><span style="color: hsl(120, 100%, 40%);">+     const char *error_msg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Asterisk external application JSON message type</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+extern const struct ast_aeap_message_type *ast_aeap_message_type_json;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#endif /* AST_AEAP_MESSAGE_H */</span><br><span>diff --git a/include/asterisk/speech.h b/include/asterisk/speech.h</span><br><span>index 681c536..ec5abaf 100644</span><br><span>--- a/include/asterisk/speech.h</span><br><span>+++ b/include/asterisk/speech.h</span><br><span>@@ -158,6 +158,12 @@</span><br><span> /*! \brief Unregister a speech recognition engine */</span><br><span> struct ast_speech_engine *ast_speech_unregister2(const char *engine_name);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Retrieve a speech recognition engine */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_speech_engine *ast_speech_find_engine(const char *engine_name);</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Unregister all speech recognition engines told to by callback */</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_speech_unregister_engines(</span><br><span style="color: hsl(120, 100%, 40%);">+ int (*should_unregister)(const struct ast_speech_engine *engine, void *data), void *data,</span><br><span style="color: hsl(120, 100%, 40%);">+     void (*on_unregistered)(void *obj));</span><br><span> </span><br><span> #if defined(__cplusplus) || defined(c_plusplus)</span><br><span> }</span><br><span>diff --git a/res/Makefile b/res/Makefile</span><br><span>index 45ef749..d541300 100644</span><br><span>--- a/res/Makefile</span><br><span>+++ b/res/Makefile</span><br><span>@@ -69,6 +69,7 @@</span><br><span> $(call MOD_ADD_C,res_ari_model,ari/ari_model_validators.c)</span><br><span> $(call MOD_ADD_C,res_stasis_recording,stasis_recording/stored.c)</span><br><span> $(call MOD_ADD_C,res_stir_shaken,$(wildcard res_stir_shaken/*.c))</span><br><span style="color: hsl(120, 100%, 40%);">+$(call MOD_ADD_C,res_aeap,$(wildcard res_aeap/*.c))</span><br><span> </span><br><span> res_parking.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION)</span><br><span> snmp/agent.o: _ASTCFLAGS+=-fPIC</span><br><span>diff --git a/res/res_aeap.c b/res/res_aeap.c</span><br><span>index b6c5841..e78956e 100644</span><br><span>--- a/res/res_aeap.c</span><br><span>+++ b/res/res_aeap.c</span><br><span>@@ -17,29 +17,37 @@</span><br><span>  */</span><br><span> </span><br><span> /*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+       <depend>res_http_websocket</depend></span><br><span>      <support_level>core</support_level></span><br><span>  ***/</span><br><span> </span><br><span> #include "asterisk.h"</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/astobj2.h"</span><br><span> #include "asterisk/module.h"</span><br><span> #include "asterisk/sorcery.h"</span><br><span> #include "asterisk/cli.h"</span><br><span> #include "asterisk/format.h"</span><br><span> #include "asterisk/format_cap.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_aeap.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "res_aeap/general.h"</span><br><span> </span><br><span> /*** DOCUMENTATION</span><br><span>      <configInfo name="res_aeap" language="en_US"></span><br><span>              <synopsis>Asterisk External Application Protocol (AEAP) module for Asterisk</synopsis></span><br><span>           <configFile name="aeap.conf"></span><br><span style="color: hsl(0, 100%, 40%);">-                   <configObject name="server"></span><br><span style="color: hsl(0, 100%, 40%);">-                            <synopsis>AEAP server options</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                  <configObject name="client"></span><br><span style="color: hsl(120, 100%, 40%);">+                          <synopsis>AEAP client options</synopsis></span><br><span>                                 <configOption name="type"></span><br><span style="color: hsl(0, 100%, 40%);">-                                      <synopsis>Must be of type 'server'.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                                    <synopsis>Must be of type 'client'.</synopsis></span><br><span>                           </configOption></span><br><span style="color: hsl(0, 100%, 40%);">-                           <configOption name="server_url"></span><br><span style="color: hsl(120, 100%, 40%);">+                              <configOption name="url"></span><br><span>                                    <synopsis>The URL of the server to connect to.</synopsis></span><br><span>                                </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="protocol"></span><br><span style="color: hsl(120, 100%, 40%);">+                                        <synopsis>The application protocol.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                            </configOption></span><br><span>                                <configOption name="codecs"></span><br><span>                                         <synopsis>Optional media codec(s)</synopsis></span><br><span>                                     <description><para></span><br><span>@@ -56,30 +64,36 @@</span><br><span> /* Asterisk External Application Protocol sorcery object */</span><br><span> static struct ast_sorcery *aeap_sorcery;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-struct aeap_server</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_sorcery *ast_aeap_sorcery(void) {</span><br><span style="color: hsl(120, 100%, 40%);">+  return aeap_sorcery;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_client_config</span><br><span> {</span><br><span>        SORCERY_OBJECT(details);</span><br><span>     AST_DECLARE_STRING_FIELDS(</span><br><span>           /*! The URL of the server to connect to */</span><br><span style="color: hsl(0, 100%, 40%);">-              AST_STRING_FIELD(server_url);</span><br><span style="color: hsl(120, 100%, 40%);">+         AST_STRING_FIELD(url);</span><br><span style="color: hsl(120, 100%, 40%);">+                /*! The application protocol */</span><br><span style="color: hsl(120, 100%, 40%);">+               AST_STRING_FIELD(protocol);</span><br><span>  );</span><br><span>   /*! An optional list of codecs that will be used if provided */</span><br><span>      struct ast_format_cap *codecs;</span><br><span> };</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static void aeap_server_destructor(void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+static void client_config_destructor(void *obj)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-     struct aeap_server *cfg = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_aeap_client_config *cfg = obj;</span><br><span> </span><br><span>        ast_string_field_free_memory(cfg);</span><br><span>   ao2_cleanup(cfg->codecs);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static void *aeap_server_alloc(const char *name)</span><br><span style="color: hsl(120, 100%, 40%);">+static void *client_config_alloc(const char *name)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-  struct aeap_server *cfg;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_aeap_client_config *cfg;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- cfg = ast_sorcery_generic_alloc(sizeof(*cfg), aeap_server_destructor);</span><br><span style="color: hsl(120, 100%, 40%);">+        cfg = ast_sorcery_generic_alloc(sizeof(*cfg), client_config_destructor);</span><br><span>     if (!cfg) {</span><br><span>          return NULL;</span><br><span>         }</span><br><span>@@ -97,32 +111,52 @@</span><br><span>     return cfg;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static int aeap_server_apply(const struct ast_sorcery *sorcery, void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+static int client_config_apply(const struct ast_sorcery *sorcery, void *obj)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-       struct aeap_server *cfg = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_aeap_client_config *cfg = obj;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (ast_strlen_zero(cfg->server_url)) {</span><br><span style="color: hsl(0, 100%, 40%);">-              ast_log(LOG_ERROR, "AEAP - Server URL must be present for server '%s'\n", ast_sorcery_object_get_id(cfg));</span><br><span style="color: hsl(120, 100%, 40%);">+  if (ast_strlen_zero(cfg->url)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_ERROR, "AEAP - URL must be present for '%s'\n", ast_sorcery_object_get_id(cfg));</span><br><span>               return -1;</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (!ast_begins_with(cfg->server_url, "ws")) {</span><br><span style="color: hsl(0, 100%, 40%);">-             ast_log(LOG_ERROR, "AEAP - Server URL must be ws or wss for server '%s'\n", ast_sorcery_object_get_id(cfg));</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!ast_begins_with(cfg->url, "ws")) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_ERROR, "AEAP - URL must be ws or wss for '%s'\n", ast_sorcery_object_get_id(cfg));</span><br><span>             return -1;</span><br><span>   }</span><br><span> </span><br><span>        return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static struct aeap_server *aeap_server_get(const char *id)</span><br><span style="color: hsl(120, 100%, 40%);">+const struct ast_format_cap *ast_aeap_client_config_codecs(const struct ast_aeap_client_config *cfg)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- return ast_sorcery_retrieve_by_id(aeap_sorcery, "server", id);</span><br><span style="color: hsl(120, 100%, 40%);">+      return cfg->codecs;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static struct ao2_container *aeap_server_get_all(void)</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_client_config_has_protocol(const struct ast_aeap_client_config *cfg,</span><br><span style="color: hsl(120, 100%, 40%);">+      const char *protocol)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-      return ast_sorcery_retrieve_by_fields(aeap_sorcery, "server",</span><br><span style="color: hsl(0, 100%, 40%);">-         AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+    return !strcmp(protocol, cfg->protocol);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ao2_container *ast_aeap_client_configs_get(const char *protocol)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ao2_container *container;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_variable *var;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   var = protocol ? ast_variable_new("protocol ==", protocol, "") : NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  container = ast_sorcery_retrieve_by_fields(aeap_sorcery,</span><br><span style="color: hsl(120, 100%, 40%);">+              AEAP_CONFIG_CLIENT, AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, var);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_variables_destroy(var);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return container;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_aeap_client_config *client_config_get(const char *id)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    return ast_sorcery_retrieve_by_id(aeap_sorcery, AEAP_CONFIG_CLIENT, id);</span><br><span> }</span><br><span> </span><br><span> static char *aeap_tab_complete_name(const char *word, struct ao2_container *container)</span><br><span>@@ -145,6 +179,8 @@</span><br><span>    }</span><br><span>    ao2_iterator_destroy(&it);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+    ao2_ref(container, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>    return NULL;</span><br><span> }</span><br><span> </span><br><span>@@ -159,8 +195,7 @@</span><br><span>          return 0;</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   options = ast_variable_list_sort(ast_sorcery_objectset_create2(</span><br><span style="color: hsl(0, 100%, 40%);">-         aeap_sorcery, obj, AST_HANDLER_ONLY_STRING));</span><br><span style="color: hsl(120, 100%, 40%);">+ options = ast_variable_list_sort(ast_sorcery_objectset_create(aeap_sorcery, obj));</span><br><span>   if (!options) {</span><br><span>              return 0;</span><br><span>    }</span><br><span>@@ -179,20 +214,20 @@</span><br><span>    return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static char *aeap_server_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)</span><br><span style="color: hsl(120, 100%, 40%);">+static char *client_config_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-       struct aeap_server *cfg;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_aeap_client_config *cfg;</span><br><span> </span><br><span>      switch(cmd) {</span><br><span>        case CLI_INIT:</span><br><span style="color: hsl(0, 100%, 40%);">-          e->command = "aeap show server";</span><br><span style="color: hsl(120, 100%, 40%);">+         e->command = "aeap show client";</span><br><span>                e->usage =</span><br><span style="color: hsl(0, 100%, 40%);">-                   "Usage: aeap show server <id>\n"</span><br><span style="color: hsl(0, 100%, 40%);">-                        "       Show the AEAP settings for a given server\n";</span><br><span style="color: hsl(120, 100%, 40%);">+                       "Usage: aeap show client <id>\n"</span><br><span style="color: hsl(120, 100%, 40%);">+                      "       Show the AEAP settings for a given client\n";</span><br><span>              return NULL;</span><br><span>         case CLI_GENERATE:</span><br><span>           if (a->pos == 3) {</span><br><span style="color: hsl(0, 100%, 40%);">-                   return aeap_tab_complete_name(a->word, aeap_server_get_all());</span><br><span style="color: hsl(120, 100%, 40%);">+                     return aeap_tab_complete_name(a->word, ast_aeap_client_configs_get(NULL));</span><br><span>                } else {</span><br><span>                     return NULL;</span><br><span>                 }</span><br><span>@@ -202,23 +237,23 @@</span><br><span>            return CLI_SHOWUSAGE;</span><br><span>        }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   cfg = aeap_server_get(a->argv[3]);</span><br><span style="color: hsl(120, 100%, 40%);">+ cfg = client_config_get(a->argv[3]);</span><br><span>      aeap_cli_show(cfg, a, 0);</span><br><span>    ao2_cleanup(cfg);</span><br><span> </span><br><span>        return CLI_SUCCESS;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static char *aeap_server_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)</span><br><span style="color: hsl(120, 100%, 40%);">+static char *client_config_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)</span><br><span> {</span><br><span>  struct ao2_container *container;</span><br><span> </span><br><span>         switch(cmd) {</span><br><span>        case CLI_INIT:</span><br><span style="color: hsl(0, 100%, 40%);">-          e->command = "aeap show servers";</span><br><span style="color: hsl(120, 100%, 40%);">+                e->command = "aeap show clients";</span><br><span>               e->usage =</span><br><span style="color: hsl(0, 100%, 40%);">-                   "Usage: aeap show servers\n"</span><br><span style="color: hsl(0, 100%, 40%);">-                  "       Show all configured AEAP servers\n";</span><br><span style="color: hsl(120, 100%, 40%);">+                        "Usage: aeap show clients\n"</span><br><span style="color: hsl(120, 100%, 40%);">+                        "       Show all configured AEAP clients\n";</span><br><span>               return NULL;</span><br><span>         case CLI_GENERATE:</span><br><span>           return NULL;</span><br><span>@@ -228,9 +263,9 @@</span><br><span>           return CLI_SHOWUSAGE;</span><br><span>        }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   container = aeap_server_get_all();</span><br><span style="color: hsl(120, 100%, 40%);">+    container = ast_aeap_client_configs_get(NULL);</span><br><span>       if (!container || ao2_container_count(container) == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                ast_cli(a->fd, "No AEAP servers found\n");</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_cli(a->fd, "No AEAP clients found\n");</span><br><span>              ao2_cleanup(container);</span><br><span>              return CLI_SUCCESS;</span><br><span>  }</span><br><span>@@ -242,12 +277,75 @@</span><br><span> }</span><br><span> </span><br><span> static struct ast_cli_entry aeap_cli[] = {</span><br><span style="color: hsl(0, 100%, 40%);">-        AST_CLI_DEFINE(aeap_server_show, "Show AEAP server configuration by id"),</span><br><span style="color: hsl(0, 100%, 40%);">-     AST_CLI_DEFINE(aeap_server_show_all, "Show all AEAP server configurations"),</span><br><span style="color: hsl(120, 100%, 40%);">+        AST_CLI_DEFINE(client_config_show, "Show AEAP client configuration by id"),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_CLI_DEFINE(client_config_show_all, "Show all AEAP client configurations"),</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_aeap *aeap_create(const char *id, const struct ast_aeap_params *params,</span><br><span style="color: hsl(120, 100%, 40%);">+ int connect, int timeout)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_aeap_client_config *cfg;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_aeap *aeap;</span><br><span style="color: hsl(120, 100%, 40%);">+        const char *url = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+       const char *protocol = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        cfg = client_config_get(id);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (cfg) {</span><br><span style="color: hsl(120, 100%, 40%);">+            url = cfg->url;</span><br><span style="color: hsl(120, 100%, 40%);">+            protocol = cfg->protocol;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef TEST_FRAMEWORK</span><br><span style="color: hsl(120, 100%, 40%);">+    else if (ast_begins_with(id, "_aeap_test_")) {</span><br><span style="color: hsl(120, 100%, 40%);">+              url = "ws://127.0.0.1:8088/ws";</span><br><span style="color: hsl(120, 100%, 40%);">+             protocol = id;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!url && !protocol) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_ERROR, "AEAP: unable to get configuration for '%s'\n", id);</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   aeap = connect ? ast_aeap_create_and_connect(url, params, url, protocol, timeout) :</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_aeap_create(url, params);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       ao2_cleanup(cfg);</span><br><span style="color: hsl(120, 100%, 40%);">+     return aeap;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap *ast_aeap_create_by_id(const char *id, const struct ast_aeap_params *params)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    return aeap_create(id, params, 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap *ast_aeap_create_and_connect_by_id(const char *id,</span><br><span style="color: hsl(120, 100%, 40%);">+        const struct ast_aeap_params *params, int timeout)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return aeap_create(id, params, 1, timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_variable *ast_aeap_custom_fields_get(const char *id)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_aeap_client_config *cfg;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_variable *vars;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  cfg = client_config_get(id);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!cfg) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_WARNING, "AEAP: no client configuration '%s' to get fields\n", id);</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   vars = ast_sorcery_objectset_create(aeap_sorcery, cfg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     ao2_ref(cfg, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+     return vars;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int reload_module(void)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_sorcery_reload(aeap_sorcery);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  return 0;</span><br><span> }</span><br><span> </span><br><span>@@ -258,28 +356,35 @@</span><br><span> </span><br><span>       ast_cli_unregister_multiple(aeap_cli, ARRAY_LEN(aeap_cli));</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+       aeap_general_finalize();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>   return 0;</span><br><span> }</span><br><span> </span><br><span> static int load_module(void)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+       if (aeap_general_initialize()) {</span><br><span style="color: hsl(120, 100%, 40%);">+              return AST_MODULE_LOAD_DECLINE;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  if (!(aeap_sorcery = ast_sorcery_open()))</span><br><span>    {</span><br><span>            ast_log(LOG_ERROR, "AEAP - failed to open sorcery\n");</span><br><span>             return AST_MODULE_LOAD_DECLINE;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   ast_sorcery_apply_default(aeap_sorcery, "server", "config", "aeap.conf,criteria=type=server");</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_sorcery_apply_default(aeap_sorcery, AEAP_CONFIG_CLIENT, "config", "aeap.conf,criteria=type=client");</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        if (ast_sorcery_object_register(aeap_sorcery, "server", aeap_server_alloc,</span><br><span style="color: hsl(0, 100%, 40%);">-            NULL, aeap_server_apply)) {</span><br><span style="color: hsl(0, 100%, 40%);">-             ast_log(LOG_ERROR, "AEAP - failed to register server sorcery object\n");</span><br><span style="color: hsl(120, 100%, 40%);">+    if (ast_sorcery_object_register(aeap_sorcery, "client", client_config_alloc,</span><br><span style="color: hsl(120, 100%, 40%);">+                NULL, client_config_apply)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_ERROR, "AEAP - failed to register client sorcery object\n");</span><br><span>           return AST_MODULE_LOAD_DECLINE;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   ast_sorcery_object_field_register(aeap_sorcery, "server", "type", "", OPT_NOOP_T, 0, 0);</span><br><span style="color: hsl(0, 100%, 40%);">-  ast_sorcery_object_field_register(aeap_sorcery, "server", "server_url", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct aeap_server, server_url));</span><br><span style="color: hsl(0, 100%, 40%);">-     ast_sorcery_object_field_register(aeap_sorcery, "server", "codecs", "", OPT_CODEC_T, 1, FLDSET(struct aeap_server, codecs));</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_sorcery_object_field_register(aeap_sorcery, AEAP_CONFIG_CLIENT, "type", "", OPT_NOOP_T, 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_sorcery_object_field_register(aeap_sorcery, AEAP_CONFIG_CLIENT, "url", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_aeap_client_config, url));</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_sorcery_object_field_register(aeap_sorcery, AEAP_CONFIG_CLIENT, "protocol", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_aeap_client_config, protocol));</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_sorcery_object_field_register(aeap_sorcery, AEAP_CONFIG_CLIENT, "codecs", "", OPT_CODEC_T, 1, FLDSET(struct ast_aeap_client_config, codecs));</span><br><span> </span><br><span>    ast_sorcery_load(aeap_sorcery);</span><br><span> </span><br><span>@@ -295,4 +400,5 @@</span><br><span>    .unload = unload_module,</span><br><span>     .reload = reload_module,</span><br><span>     .load_pri = AST_MODPRI_CHANNEL_DEPEND,</span><br><span style="color: hsl(120, 100%, 40%);">+        .requires = "res_http_websocket",</span><br><span> );</span><br><span>diff --git a/res/res_aeap.exports.in b/res/res_aeap.exports.in</span><br><span>new file mode 100644</span><br><span>index 0000000..c1fc5c4</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_aeap.exports.in</span><br><span>@@ -0,0 +1,7 @@</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       global:</span><br><span style="color: hsl(120, 100%, 40%);">+               LINKER_SYMBOL_PREFIXaeap_*;</span><br><span style="color: hsl(120, 100%, 40%);">+           LINKER_SYMBOL_PREFIXast_aeap_*;</span><br><span style="color: hsl(120, 100%, 40%);">+       local:</span><br><span style="color: hsl(120, 100%, 40%);">+                *;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span>diff --git a/res/res_aeap/aeap.c b/res/res_aeap/aeap.c</span><br><span>new file mode 100644</span><br><span>index 0000000..9094bbb</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_aeap/aeap.c</span><br><span>@@ -0,0 +1,501 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <pthread.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/astobj2.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/strings.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_aeap.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_aeap_message.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "logger.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "transaction.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "transport.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define AEAP_RECV_SIZE 32768</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct aeap_user_data {</span><br><span style="color: hsl(120, 100%, 40%);">+  /*! The user data object */</span><br><span style="color: hsl(120, 100%, 40%);">+   void *obj;</span><br><span style="color: hsl(120, 100%, 40%);">+    /*! A user data identifier */</span><br><span style="color: hsl(120, 100%, 40%);">+ char id[0];</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AO2_STRING_FIELD_HASH_FN(aeap_user_data, id);</span><br><span style="color: hsl(120, 100%, 40%);">+AO2_STRING_FIELD_CMP_FN(aeap_user_data, id);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define USER_DATA_BUCKETS 11</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap {</span><br><span style="color: hsl(120, 100%, 40%);">+   /*! This object's configuration parameters */</span><br><span style="color: hsl(120, 100%, 40%);">+     const struct ast_aeap_params *params;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Container for registered user data objects */</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ao2_container *user_data;</span><br><span style="color: hsl(120, 100%, 40%);">+      /*! Transactions container */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ao2_container *transactions;</span><br><span style="color: hsl(120, 100%, 40%);">+   /*! Transport layer communicator */</span><br><span style="color: hsl(120, 100%, 40%);">+   struct aeap_transport *transport;</span><br><span style="color: hsl(120, 100%, 40%);">+     /*! Id of thread that reads data from the transport */</span><br><span style="color: hsl(120, 100%, 40%);">+        pthread_t read_thread_id;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int tsx_end(void *obj, void *arg, int flags)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       aeap_transaction_end(obj, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void aeap_destructor(void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ast_aeap *aeap = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Disconnect things first, which keeps transactions from further executing */</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_aeap_disconnect(aeap);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  aeap_transport_destroy(aeap->transport);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * Each contained transaction holds a pointer back to this transactions container,</span><br><span style="color: hsl(120, 100%, 40%);">+     * which is removed upon transaction end. Thus by explicitly ending each transaction</span><br><span style="color: hsl(120, 100%, 40%);">+   * here we can ensure all references to the transactions container are removed.</span><br><span style="color: hsl(120, 100%, 40%);">+        */</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_callback(aeap->transactions, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,</span><br><span style="color: hsl(120, 100%, 40%);">+                   tsx_end, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+       ao2_cleanup(aeap->transactions);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(aeap->user_data);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap *ast_aeap_create(const char *transport_type,</span><br><span style="color: hsl(120, 100%, 40%);">+   const struct ast_aeap_params *params)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_aeap *aeap;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      aeap = ao2_alloc(sizeof(*aeap), aeap_destructor);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!aeap) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_ERROR, "AEAP: unable to create");</span><br><span style="color: hsl(120, 100%, 40%);">+               return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   aeap->params = params;</span><br><span style="color: hsl(120, 100%, 40%);">+     aeap->read_thread_id = AST_PTHREADT_NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        aeap->user_data = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, USER_DATA_BUCKETS,</span><br><span style="color: hsl(120, 100%, 40%);">+         aeap_user_data_hash_fn, NULL, aeap_user_data_cmp_fn);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!aeap->user_data) {</span><br><span style="color: hsl(120, 100%, 40%);">+            aeap_error(aeap, NULL, "unable to create user data container");</span><br><span style="color: hsl(120, 100%, 40%);">+             ao2_ref(aeap, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+            return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   aeap->transactions = aeap_transactions_create();</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!aeap->transactions) {</span><br><span style="color: hsl(120, 100%, 40%);">+         aeap_error(aeap, NULL, "unable to create transactions container");</span><br><span style="color: hsl(120, 100%, 40%);">+          ao2_ref(aeap, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+            return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   aeap->transport = aeap_transport_create(transport_type);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!aeap->transport) {</span><br><span style="color: hsl(120, 100%, 40%);">+            aeap_error(aeap, NULL, "unable to create transport");</span><br><span style="color: hsl(120, 100%, 40%);">+               ao2_ref(aeap, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+            return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return aeap;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct aeap_user_data *aeap_user_data_create(const char *id, void *obj,</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_aeap_user_obj_cleanup cleanup)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct aeap_user_data *data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_assert(id != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     data = ao2_t_alloc_options(sizeof(*data) + strlen(id) + 1, cleanup,</span><br><span style="color: hsl(120, 100%, 40%);">+           AO2_ALLOC_OPT_LOCK_NOLOCK, "");</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!data) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (cleanup) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        cleanup(obj);</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   strcpy(data->id, id); /* safe */</span><br><span style="color: hsl(120, 100%, 40%);">+   data->obj = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return data;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_user_data_register(struct ast_aeap *aeap, const char *id, void *obj,</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_aeap_user_obj_cleanup cleanup)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct aeap_user_data *data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        data = aeap_user_data_create(id, obj, cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!data) {</span><br><span style="color: hsl(120, 100%, 40%);">+          return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!ao2_link(aeap->user_data, data)) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ao2_ref(data, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+            return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_ref(data, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+    return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_aeap_user_data_unregister(struct ast_aeap *aeap, const char *id)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  ao2_find(aeap->user_data, id, OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void *ast_aeap_user_data_object_by_id(struct ast_aeap *aeap, const char *id)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct aeap_user_data *data;</span><br><span style="color: hsl(120, 100%, 40%);">+  void *obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  data = ao2_find(aeap->user_data, id, OBJ_SEARCH_KEY);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!data) {</span><br><span style="color: hsl(120, 100%, 40%);">+          return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   obj = data->obj;</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_ref(data, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * Returned object's lifetime is based on how it was registered.</span><br><span style="color: hsl(120, 100%, 40%);">+   * See public function docs for more info</span><br><span style="color: hsl(120, 100%, 40%);">+      */</span><br><span style="color: hsl(120, 100%, 40%);">+   return obj;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int raise_msg_handler(struct ast_aeap *aeap,   const struct ast_aeap_message_handler *handlers,</span><br><span style="color: hsl(120, 100%, 40%);">+      size_t size, struct ast_aeap_message *msg, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_aeap_on_message on_message = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+        size_t i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!aeap->params->emit_error) {</span><br><span style="color: hsl(120, 100%, 40%);">+                const char *error_msg = ast_aeap_message_error_msg(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            if (error_msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      aeap_error(aeap, NULL, "%s", error_msg);</span><br><span style="color: hsl(120, 100%, 40%);">+                    return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           /* If no error_msg then it's assumed this is not an error message */</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   for (i = 0; i < size; ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+               if (ast_strlen_zero(handlers[i].name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      /* A default handler is specified. Use it if no other match is found */</span><br><span style="color: hsl(120, 100%, 40%);">+                       on_message = handlers[i].on_message;</span><br><span style="color: hsl(120, 100%, 40%);">+                  continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           if (ast_aeap_message_is_named(msg, handlers[i].name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       on_message = handlers[i].on_message;</span><br><span style="color: hsl(120, 100%, 40%);">+                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (on_message) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return on_message(aeap, msg, data);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Respond with un-handled error */</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_aeap_send_msg(aeap, ast_aeap_message_create_error(aeap->params->msg_type,</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_aeap_message_name(msg), ast_aeap_message_id(msg),</span><br><span style="color: hsl(120, 100%, 40%);">+         "Unsupported and/or un-handled message"));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void raise_msg(struct ast_aeap *aeap, const void *buf, intmax_t size,</span><br><span style="color: hsl(120, 100%, 40%);">+  enum AST_AEAP_DATA_TYPE serial_type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ast_aeap_message *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct aeap_transaction *tsx;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!aeap->params || !aeap->params->msg_type ||</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_aeap_message_serial_type(aeap->params->msg_type) != serial_type ||</span><br><span style="color: hsl(120, 100%, 40%);">+          !(msg = ast_aeap_message_deserialize(aeap->params->msg_type, buf, size))) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* See if this msg is involved in a transaction */</span><br><span style="color: hsl(120, 100%, 40%);">+    tsx = aeap_transaction_get(aeap->transactions, ast_aeap_message_id(msg));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* If so go ahead and cancel the timeout timer */</span><br><span style="color: hsl(120, 100%, 40%);">+     aeap_transaction_cancel_timer(tsx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (aeap->params->request_handlers && ast_aeap_message_is_request(msg)) {</span><br><span style="color: hsl(120, 100%, 40%);">+               res = raise_msg_handler(aeap, aeap->params->request_handlers, aeap->params->request_handlers_size,</span><br><span style="color: hsl(120, 100%, 40%);">+                        msg, tsx ? aeap_transaction_user_obj(tsx) : NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+    } else if (aeap->params->response_handlers && ast_aeap_message_is_response(msg)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              res = raise_msg_handler(aeap, aeap->params->response_handlers, aeap->params->response_handlers_size,</span><br><span style="color: hsl(120, 100%, 40%);">+                      msg, tsx ? aeap_transaction_user_obj(tsx) : NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Complete transaction (Note, removes tsx ref) */</span><br><span style="color: hsl(120, 100%, 40%);">+    aeap_transaction_end(tsx, res);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     ao2_ref(msg, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void *aeap_receive(void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_aeap *aeap = data;</span><br><span style="color: hsl(120, 100%, 40%);">+ void *buf;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  buf = ast_calloc(1, AEAP_RECV_SIZE);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!buf) {</span><br><span style="color: hsl(120, 100%, 40%);">+           aeap_error(aeap, NULL, "unable to create read buffer");</span><br><span style="color: hsl(120, 100%, 40%);">+             goto aeap_receive_error;</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   while (aeap_transport_is_connected(aeap->transport)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             enum AST_AEAP_DATA_TYPE rtype;</span><br><span style="color: hsl(120, 100%, 40%);">+                intmax_t size;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              size = aeap_transport_read(aeap->transport, buf, AEAP_RECV_SIZE, &rtype);</span><br><span style="color: hsl(120, 100%, 40%);">+              if (size < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    goto aeap_receive_error;</span><br><span style="color: hsl(120, 100%, 40%);">+              }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!size) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           switch (rtype) {</span><br><span style="color: hsl(120, 100%, 40%);">+              case AST_AEAP_DATA_TYPE_BINARY:</span><br><span style="color: hsl(120, 100%, 40%);">+                       if (aeap->params && aeap->params->on_binary) {</span><br><span style="color: hsl(120, 100%, 40%);">+                               aeap->params->on_binary(aeap, buf, size);</span><br><span style="color: hsl(120, 100%, 40%);">+                       }</span><br><span style="color: hsl(120, 100%, 40%);">+                     break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case AST_AEAP_DATA_TYPE_STRING:</span><br><span style="color: hsl(120, 100%, 40%);">+                       ast_debug(3, "AEAP: received message: %s\n", (char *)buf);</span><br><span style="color: hsl(120, 100%, 40%);">+                  if (aeap->params && aeap->params->on_string) {</span><br><span style="color: hsl(120, 100%, 40%);">+                               aeap->params->on_string(aeap, (const char *)buf, size - 1);</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+                     break;</span><br><span style="color: hsl(120, 100%, 40%);">+                default:</span><br><span style="color: hsl(120, 100%, 40%);">+                      break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           raise_msg(aeap, buf, size, rtype);</span><br><span style="color: hsl(120, 100%, 40%);">+    };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_free(buf);</span><br><span style="color: hsl(120, 100%, 40%);">+        return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+aeap_receive_error:</span><br><span style="color: hsl(120, 100%, 40%);">+   /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * An unrecoverable error occurred so ensure the aeap and transport reset</span><br><span style="color: hsl(120, 100%, 40%);">+      * to a disconnected state. We don't want this thread to "join" itself so set</span><br><span style="color: hsl(120, 100%, 40%);">+    * its id to NULL prior to disconnecting.</span><br><span style="color: hsl(120, 100%, 40%);">+      */</span><br><span style="color: hsl(120, 100%, 40%);">+   aeap_error(aeap, NULL, "unrecoverable read error, disconnecting");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        ao2_lock(aeap);</span><br><span style="color: hsl(120, 100%, 40%);">+       aeap->read_thread_id = AST_PTHREADT_NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  ao2_unlock(aeap);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_aeap_disconnect(aeap);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_free(buf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (aeap->params && aeap->params->on_error) {</span><br><span style="color: hsl(120, 100%, 40%);">+                aeap->params->on_error(aeap);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_connect(struct ast_aeap *aeap, const char *url, const char *protocol, int timeout)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPED_AO2LOCK(lock, aeap);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (aeap_transport_is_connected(aeap->transport)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                /* Should already be connected, so nothing to do */</span><br><span style="color: hsl(120, 100%, 40%);">+           return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (aeap_transport_connect(aeap->transport, url, protocol, timeout)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             aeap_error(aeap, NULL, "unable to connect transport");</span><br><span style="color: hsl(120, 100%, 40%);">+              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_pthread_create_background(&aeap->read_thread_id, NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+                 aeap_receive, aeap)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                aeap_error(aeap, NULL, "unable to start read thread: %s",</span><br><span style="color: hsl(120, 100%, 40%);">+                   strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_aeap_disconnect(aeap);</span><br><span style="color: hsl(120, 100%, 40%);">+            return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap *ast_aeap_create_and_connect(const char *type,</span><br><span style="color: hsl(120, 100%, 40%);">+        const struct ast_aeap_params *params, const char *url, const char *protocol, int timeout)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_aeap *aeap;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      aeap = ast_aeap_create(type, params);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!aeap) {</span><br><span style="color: hsl(120, 100%, 40%);">+          return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_aeap_connect(aeap, url, protocol, timeout)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ao2_ref(aeap, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+            return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return aeap;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_disconnect(struct ast_aeap *aeap)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  ao2_lock(aeap);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     aeap_transport_disconnect(aeap->transport);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (aeap->read_thread_id != AST_PTHREADT_NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+           /*</span><br><span style="color: hsl(120, 100%, 40%);">+             * The read thread calls disconnect if an error occurs, so</span><br><span style="color: hsl(120, 100%, 40%);">+             * unlock the aeap before "joining" to avoid a deadlock.</span><br><span style="color: hsl(120, 100%, 40%);">+             */</span><br><span style="color: hsl(120, 100%, 40%);">+           ao2_unlock(aeap);</span><br><span style="color: hsl(120, 100%, 40%);">+             pthread_join(aeap->read_thread_id, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+          ao2_lock(aeap);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             aeap->read_thread_id = AST_PTHREADT_NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_unlock(aeap);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int aeap_send(struct ast_aeap *aeap, const void *buf, uintmax_t size,</span><br><span style="color: hsl(120, 100%, 40%);">+  enum AST_AEAP_DATA_TYPE type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      intmax_t num;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       num = aeap_transport_write(aeap->transport, buf, size, type);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (num == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+               /* Nothing written, could be disconnected */</span><br><span style="color: hsl(120, 100%, 40%);">+          return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (num < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             aeap_error(aeap, NULL, "error sending data");</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (num < size) {</span><br><span style="color: hsl(120, 100%, 40%);">+          aeap_error(aeap, NULL, "not all data sent");</span><br><span style="color: hsl(120, 100%, 40%);">+                return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (num > size) {</span><br><span style="color: hsl(120, 100%, 40%);">+          aeap_error(aeap, NULL, "sent data truncated");</span><br><span style="color: hsl(120, 100%, 40%);">+              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_send_binary(struct ast_aeap *aeap, const void *buf, uintmax_t size)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   return aeap_send(aeap, buf, size, AST_AEAP_DATA_TYPE_BINARY);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_send_msg(struct ast_aeap *aeap, struct ast_aeap_message *msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     void *buf;</span><br><span style="color: hsl(120, 100%, 40%);">+    intmax_t size;</span><br><span style="color: hsl(120, 100%, 40%);">+        int res;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+           aeap_error(aeap, NULL, "no message to send");</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_aeap_message_serialize(msg, &buf, &size)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           aeap_error(aeap, NULL, "unable to serialize outgoing message");</span><br><span style="color: hsl(120, 100%, 40%);">+             ao2_ref(msg, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+             return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   res = aeap_send(aeap, buf, size, msg->type->serial_type);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_free(buf);</span><br><span style="color: hsl(120, 100%, 40%);">+        ao2_ref(msg, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_send_msg_tsx(struct ast_aeap *aeap, struct ast_aeap_tsx_params *params)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct aeap_transaction *tsx = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!params) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!params->msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+                aeap_transaction_params_cleanup(params);</span><br><span style="color: hsl(120, 100%, 40%);">+              aeap_error(aeap, NULL, "no message to send");</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* The transaction will take over params cleanup, which includes the msg reference */</span><br><span style="color: hsl(120, 100%, 40%);">+ tsx = aeap_transaction_create_and_add(aeap->transactions,</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_aeap_message_id(params->msg), params, aeap);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!tsx) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_aeap_send_msg(aeap, ao2_bump(params->msg))) {</span><br><span style="color: hsl(120, 100%, 40%);">+              aeap_transaction_end(tsx, -1); /* Removes container, and tsx ref */</span><br><span style="color: hsl(120, 100%, 40%);">+           return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (aeap_transaction_start(tsx)) {</span><br><span style="color: hsl(120, 100%, 40%);">+            aeap_transaction_end(tsx, -1); /* Removes container, and tsx ref */</span><br><span style="color: hsl(120, 100%, 40%);">+           return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   res = aeap_transaction_result(tsx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(tsx, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/res/res_aeap/general.c b/res/res_aeap/general.c</span><br><span>new file mode 100644</span><br><span>index 0000000..7bd1807</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_aeap/general.c</span><br><span>@@ -0,0 +1,58 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/astobj2.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/sched.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "general.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Scheduler for transaction timeouts */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_sched_context *sched = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_sched_context *aeap_sched_context(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   return sched;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void aeap_general_finalize(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       if (sched) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_sched_context_destroy(sched);</span><br><span style="color: hsl(120, 100%, 40%);">+             sched = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int aeap_general_initialize(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  sched = ast_sched_context_create();</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!sched) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_ERROR, "AEAP scheduler: unable to create context");</span><br><span style="color: hsl(120, 100%, 40%);">+             return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_sched_start_thread(sched)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_ERROR, "AEAP scheduler: unable to start thread");</span><br><span style="color: hsl(120, 100%, 40%);">+               aeap_general_finalize();</span><br><span style="color: hsl(120, 100%, 40%);">+              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>diff --git a/res/res_aeap/general.h b/res/res_aeap/general.h</span><br><span>new file mode 100644</span><br><span>index 0000000..52a092b</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_aeap/general.h</span><br><span>@@ -0,0 +1,41 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifndef RES_AEAP_GENERAL_H</span><br><span style="color: hsl(120, 100%, 40%);">+#define RES_AEAP_GENERAL_H</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve the scheduling context</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns The scheduling context</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_sched_context *aeap_sched_context(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Initialize general/common AEAP facilities</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int aeap_general_initialize(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Finalize/cleanup general AEAP facilities</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void aeap_general_finalize(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#endif /* RES_AEAP_GENERAL_H */</span><br><span>diff --git a/res/res_aeap/logger.h b/res/res_aeap/logger.h</span><br><span>new file mode 100644</span><br><span>index 0000000..db264c3</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_aeap/logger.h</span><br><span>@@ -0,0 +1,60 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifndef RES_AEAP_LOGGER_H</span><br><span style="color: hsl(120, 100%, 40%);">+#define RES_AEAP_LOGGER_H</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/logger.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/strings.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Log an Asterisk external application message</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param level The logging level</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param obj The object being logged</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name Optional subsystem name</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param fmt Format string</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param ... Parameters for the format string</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define aeap_log(level, obj, name, fmt, ...) \</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_log(level, "AEAP%s%s (%p): " fmt "\n", ast_strlen_zero(name) ? "" : " ", \</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_strlen_zero(name) ? "" : name, obj, ##__VA_ARGS__)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Log an Asterisk external application error</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param obj The object being logged</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name Optional subsystem name</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param fmt Format string</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param ... Parameters for the format string</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define aeap_error(obj, name, fmt, ...) aeap_log(LOG_ERROR, obj, name, fmt, ##__VA_ARGS__)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Log an Asterisk external application warning</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param obj The object being logged</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name Optional subsystem name</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param fmt Format string</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param ... Parameters for the format string</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define aeap_warn(obj, name, fmt, ...) aeap_log(LOG_WARNING, obj, name, fmt, ##__VA_ARGS__)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#endif /* RES_AEAP_LOGGER_H */</span><br><span>diff --git a/res/res_aeap/message.c b/res/res_aeap/message.c</span><br><span>new file mode 100644</span><br><span>index 0000000..826e93b</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_aeap/message.c</span><br><span>@@ -0,0 +1,270 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/astobj2.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/utils.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/uuid.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_aeap.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_aeap_message.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum AST_AEAP_DATA_TYPE ast_aeap_message_serial_type(const struct ast_aeap_message_type *type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_assert(type != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return type->serial_type;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void message_destructor(void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ast_aeap_message *msg = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (msg->type->destruct) {</span><br><span style="color: hsl(120, 100%, 40%);">+              msg->type->destruct(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_aeap_message *message_create(const struct ast_aeap_message_type *type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_aeap_message *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       msg = ao2_t_alloc_options(type->type_size, message_destructor,</span><br><span style="color: hsl(120, 100%, 40%);">+             AO2_ALLOC_OPT_LOCK_NOLOCK, type->type_name);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_ERROR, "AEAP message %s: unable to create\n", type->type_name);</span><br><span style="color: hsl(120, 100%, 40%);">+              return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   msg->type = type;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return msg;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_message *ast_aeap_message_create1(const struct ast_aeap_message_type *type,</span><br><span style="color: hsl(120, 100%, 40%);">+ const void *params)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_aeap_message *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_assert(type != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_assert(type->construct1 != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    msg = message_create(type);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (type->construct1(msg, params)) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "AEAP message %s: unable to construct1\n", type->type_name);</span><br><span style="color: hsl(120, 100%, 40%);">+          ao2_ref(msg, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return msg;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_message *ast_aeap_message_create2(const struct ast_aeap_message_type *type,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *msg_type, const char *name, const char *id, const void *params)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_aeap_message *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_assert(type != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_assert(type->construct2 != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_assert(msg_type != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_assert(name != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   msg = message_create(type);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (type->construct2(msg, msg_type, name, id, params)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_ERROR, "AEAP message %s: unable to construct2\n", type->type_name);</span><br><span style="color: hsl(120, 100%, 40%);">+          ao2_ref(msg, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return msg;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_message *ast_aeap_message_create_request(const struct ast_aeap_message_type *type,</span><br><span style="color: hsl(120, 100%, 40%);">+  const char *name, const char *id, const void *params)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_aeap_message *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       msg = ast_aeap_message_create2(type, "request", name, id, params);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!id && !ast_aeap_message_id_generate(msg)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ao2_ref(msg, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return msg;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_message *ast_aeap_message_create_response(const struct ast_aeap_message_type *type,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *name, const char *id, const void *params)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      return ast_aeap_message_create2(type, "response", name, id, params);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_message *ast_aeap_message_create_error(const struct ast_aeap_message_type *type,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *name, const char *id, const char *error_msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_aeap_message *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       msg = ast_aeap_message_create_response(type, name, id, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_aeap_message_error_msg_set(msg, error_msg)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ao2_ref(msg, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return msg;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_message *ast_aeap_message_deserialize(const struct ast_aeap_message_type *type,</span><br><span style="color: hsl(120, 100%, 40%);">+     const void *buf, intmax_t size)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_aeap_message *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_assert(type != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_assert(type->deserialize != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   msg = ast_aeap_message_create1(type, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (type->deserialize(msg, buf, size)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ao2_ref(msg, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return msg;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_message_serialize(const struct ast_aeap_message *message,</span><br><span style="color: hsl(120, 100%, 40%);">+      void **buf, intmax_t *size)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_assert(message != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_assert(message->type != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return message->type->serialize ? message->type->serialize(message, buf, size) : 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_aeap_message_id(const struct ast_aeap_message *message)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      const char *id = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_assert(message != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_assert(message->type != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (message->type->id) {</span><br><span style="color: hsl(120, 100%, 40%);">+                id = message->type->id(message);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return id ? id : "";</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_message_id_set(struct ast_aeap_message *message, const char *id)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_assert(message != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_assert(message->type != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return message->type->id_set ? message->type->id_set(message, id) : 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_aeap_message_id_generate(struct ast_aeap_message *message)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        char uuid_str[AST_UUID_STR_LEN];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_uuid_generate_str(uuid_str, sizeof(uuid_str));</span><br><span style="color: hsl(120, 100%, 40%);">+    if (strlen(uuid_str) != (AST_UUID_STR_LEN - 1)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_log(LOG_ERROR, "AEAP message %s failed to generate UUID for message '%s'",</span><br><span style="color: hsl(120, 100%, 40%);">+                      message->type->type_name, ast_aeap_message_name(message));</span><br><span style="color: hsl(120, 100%, 40%);">+              return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return ast_aeap_message_id_set(message, uuid_str) ? NULL : ast_aeap_message_id(message);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_aeap_message_name(const struct ast_aeap_message *message)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   const char *name = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_assert(message != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_assert(message->type != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (message->type->name) {</span><br><span style="color: hsl(120, 100%, 40%);">+              name = message->type->name(message);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return name ? name : "";</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_message_is_named(const struct ast_aeap_message *message, const char *name)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   return name ? !strcasecmp(ast_aeap_message_name(message), name) : 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void *ast_aeap_message_data(struct ast_aeap_message *message)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_assert(message != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_assert(message->type != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return message->type->data ? message->type->data(message) : NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_message_is_request(const struct ast_aeap_message *message)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_assert(message != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_assert(message->type != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return message->type->is_request ? message->type->is_request(message) : 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_message_is_response(const struct ast_aeap_message *message)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_assert(message != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_assert(message->type != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return message->type->is_response ? message->type->is_response(message) : 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_aeap_message_error_msg(const struct ast_aeap_message *message)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_assert(message != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_assert(message->type != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return message->type->error_msg ? message->type->error_msg(message) : NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_aeap_message_error_msg_set(struct ast_aeap_message *message, const char *error_msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_assert(message != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_assert(message->type != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return message->type->error_msg_set ? message->type->error_msg_set(message, error_msg) : 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/res/res_aeap/message_json.c b/res/res_aeap/message_json.c</span><br><span>new file mode 100644</span><br><span>index 0000000..f5cfe9d</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_aeap/message_json.c</span><br><span>@@ -0,0 +1,191 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/json.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_aeap_message.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define JSON_MSG(lvalue, rvalue) struct message_json *lvalue = \</span><br><span style="color: hsl(120, 100%, 40%);">+                ((struct message_json *)rvalue)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Asterisk external application JSON message type</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct message_json {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! The base message type (must be first) */</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_aeap_message base;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Underlying JSON data structure */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_json *json;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int message_json_construct1(struct ast_aeap_message *self, const void *params)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        JSON_MSG(msg, self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        msg->json = ast_json_ref((struct ast_json *)params) ?: ast_json_object_create();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return msg->json ? 0 : -1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int message_json_construct2(struct ast_aeap_message *self, const char *msg_type,</span><br><span style="color: hsl(120, 100%, 40%);">+   const char *name, const char *id, const void *params)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_json *msg_data;</span><br><span style="color: hsl(120, 100%, 40%);">+    int res;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    msg_data = ast_json_pack("{s:s,s:s*}", msg_type, name, "id", id);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!msg_data) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_ERROR, "AEAP message json: failed to create data for '%s: %s'", msg_type, name);</span><br><span style="color: hsl(120, 100%, 40%);">+                return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (params && ast_json_object_update(msg_data, (struct ast_json *)params)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_ERROR, "AEAP message json: failed to update data for '%s: %s'", msg_type, name);</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_json_unref(msg_data);</span><br><span style="color: hsl(120, 100%, 40%);">+             return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   res = message_json_construct1(self, msg_data);</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_json_unref(msg_data);</span><br><span style="color: hsl(120, 100%, 40%);">+     return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void message_json_destruct(struct ast_aeap_message *self)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ JSON_MSG(msg, self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_json_unref(msg->json);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int message_json_deserialize(struct ast_aeap_message *self, const void *buf, intmax_t size)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     JSON_MSG(msg, self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        msg->json = ast_json_load_buf(buf, size, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return msg->json ? 0 : -1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int message_json_serialize(const struct ast_aeap_message *self, void **buf, intmax_t *size)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     const JSON_MSG(msg, self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  *buf = ast_json_dump_string(msg->json);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!*buf) {</span><br><span style="color: hsl(120, 100%, 40%);">+          *size = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+            return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   *size = strlen(*buf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const char *message_json_id(const struct ast_aeap_message *self)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    const JSON_MSG(msg, self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return ast_json_object_string_get(msg->json, "id");</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int message_json_id_set(struct ast_aeap_message *self, const char *id)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       JSON_MSG(msg, self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (ast_json_object_set(msg->json, "id", ast_json_string_create(id))) {</span><br><span style="color: hsl(120, 100%, 40%);">+          return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const char *message_json_name(const struct ast_aeap_message *self)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  const JSON_MSG(msg, self);</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_json_iter *iter;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ iter = ast_json_object_iter_at(msg->json, "response");</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!iter) {</span><br><span style="color: hsl(120, 100%, 40%);">+          iter = ast_json_object_iter_at(msg->json, "request");</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return iter ? ast_json_string_get(ast_json_object_iter_value(iter)) : "";</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void *message_json_data(struct ast_aeap_message *self)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    JSON_MSG(msg, self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return msg->json;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int message_json_is_request(const struct ast_aeap_message *self)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const JSON_MSG(msg, self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return ast_json_object_iter_at(msg->json, "request") != NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int message_json_is_response(const struct ast_aeap_message *self)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  const JSON_MSG(msg, self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return ast_json_object_iter_at(msg->json, "response") != NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const char *message_json_error_msg(const struct ast_aeap_message *self)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   const JSON_MSG(msg, self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return ast_json_object_string_get(msg->json, "error_msg");</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int message_json_error_msg_set(struct ast_aeap_message *self, const char *error_msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  JSON_MSG(msg, self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (ast_json_object_set(msg->json, "error_msg", ast_json_string_create(error_msg))) {</span><br><span style="color: hsl(120, 100%, 40%);">+            return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct ast_aeap_message_type message_type_json = {</span><br><span style="color: hsl(120, 100%, 40%);">+       .type_size = sizeof(struct message_json),</span><br><span style="color: hsl(120, 100%, 40%);">+     .type_name = "json",</span><br><span style="color: hsl(120, 100%, 40%);">+        .serial_type = AST_AEAP_DATA_TYPE_STRING,</span><br><span style="color: hsl(120, 100%, 40%);">+     .construct1 = message_json_construct1,</span><br><span style="color: hsl(120, 100%, 40%);">+        .construct2 = message_json_construct2,</span><br><span style="color: hsl(120, 100%, 40%);">+        .destruct = message_json_destruct,</span><br><span style="color: hsl(120, 100%, 40%);">+    .deserialize = message_json_deserialize,</span><br><span style="color: hsl(120, 100%, 40%);">+      .serialize = message_json_serialize,</span><br><span style="color: hsl(120, 100%, 40%);">+  .id = message_json_id,</span><br><span style="color: hsl(120, 100%, 40%);">+        .id_set = message_json_id_set,</span><br><span style="color: hsl(120, 100%, 40%);">+        .name = message_json_name,</span><br><span style="color: hsl(120, 100%, 40%);">+    .data = message_json_data,</span><br><span style="color: hsl(120, 100%, 40%);">+    .is_request = message_json_is_request,</span><br><span style="color: hsl(120, 100%, 40%);">+        .is_response = message_json_is_response,</span><br><span style="color: hsl(120, 100%, 40%);">+      .error_msg = message_json_error_msg,</span><br><span style="color: hsl(120, 100%, 40%);">+  .error_msg_set = message_json_error_msg_set,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const struct ast_aeap_message_type *ast_aeap_message_type_json = &message_type_json;</span><br><span>diff --git a/res/res_aeap/transaction.c b/res/res_aeap/transaction.c</span><br><span>new file mode 100644</span><br><span>index 0000000..3f42cb2</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_aeap/transaction.c</span><br><span>@@ -0,0 +1,284 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/astobj2.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/sched.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/utils.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_aeap.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_aeap_message.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "general.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "logger.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "transaction.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct aeap_transaction {</span><br><span style="color: hsl(120, 100%, 40%);">+        /*! Pointer back to owner object */</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_aeap *aeap;</span><br><span style="color: hsl(120, 100%, 40%);">+        /*! The container this transaction is in */</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ao2_container *container;</span><br><span style="color: hsl(120, 100%, 40%);">+      /*! Scheduler ID message timeout */</span><br><span style="color: hsl(120, 100%, 40%);">+   int sched_id;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Whether or not the handler has been executed */</span><br><span style="color: hsl(120, 100%, 40%);">+   int handled;</span><br><span style="color: hsl(120, 100%, 40%);">+  /*! Used to sync matching received messages */</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_cond_t handled_cond;</span><br><span style="color: hsl(120, 100%, 40%);">+      /*! The result of this transaction */</span><br><span style="color: hsl(120, 100%, 40%);">+ int result;</span><br><span style="color: hsl(120, 100%, 40%);">+   /*! The timeout data */</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ast_aeap_tsx_params params;</span><br><span style="color: hsl(120, 100%, 40%);">+    /*! The transaction identifier */</span><br><span style="color: hsl(120, 100%, 40%);">+     char id[0];</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Number of transaction buckets */</span><br><span style="color: hsl(120, 100%, 40%);">+#define AEAP_TRANSACTION_BUCKETS 11</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AO2_STRING_FIELD_HASH_FN(aeap_transaction, id);</span><br><span style="color: hsl(120, 100%, 40%);">+AO2_STRING_FIELD_CMP_FN(aeap_transaction, id);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int aeap_transaction_cancel_timer(struct aeap_transaction *tsx)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  if (tsx && tsx->sched_id != -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+          AST_SCHED_DEL_UNREF(aeap_sched_context(), tsx->sched_id, ao2_ref(tsx, -1));</span><br><span style="color: hsl(120, 100%, 40%);">+                return tsx->sched_id != -1;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void aeap_transaction_params_cleanup(struct ast_aeap_tsx_params *params)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_cleanup(params->msg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (params->obj_cleanup) {</span><br><span style="color: hsl(120, 100%, 40%);">+         params->obj_cleanup(params->obj);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void transaction_destructor(void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct aeap_transaction *tsx = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Ensure timer is canceled */</span><br><span style="color: hsl(120, 100%, 40%);">+        aeap_transaction_cancel_timer(tsx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ aeap_transaction_params_cleanup(&tsx->params);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_cond_destroy(&tsx->handled_cond);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct aeap_transaction *transaction_create(const char *id,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_aeap_tsx_params *params, struct ast_aeap *aeap)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct aeap_transaction *tsx;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!id) {</span><br><span style="color: hsl(120, 100%, 40%);">+            aeap_error(aeap, "transaction", "missing transaction id");</span><br><span style="color: hsl(120, 100%, 40%);">+                aeap_transaction_params_cleanup(params);</span><br><span style="color: hsl(120, 100%, 40%);">+              return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   tsx = ao2_alloc(sizeof(*tsx) + strlen(id) + 1, transaction_destructor);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!tsx) {</span><br><span style="color: hsl(120, 100%, 40%);">+           aeap_error(aeap, "transaction", "unable to create for '%s'", id);</span><br><span style="color: hsl(120, 100%, 40%);">+         aeap_transaction_params_cleanup(params);</span><br><span style="color: hsl(120, 100%, 40%);">+              return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   strcpy(tsx->id, id); /* safe */</span><br><span style="color: hsl(120, 100%, 40%);">+    tsx->sched_id = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_cond_init(&tsx->handled_cond, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * Currently, transactions, and their lifetimes are fully managed by the given 'aeap'</span><br><span style="color: hsl(120, 100%, 40%);">+  * object, so do not bump its reference here as we want the 'aeap' object to stop</span><br><span style="color: hsl(120, 100%, 40%);">+      * transactions and not transactions potentially stopping the 'aeap' object.</span><br><span style="color: hsl(120, 100%, 40%);">+   */</span><br><span style="color: hsl(120, 100%, 40%);">+   tsx->aeap = aeap;</span><br><span style="color: hsl(120, 100%, 40%);">+  tsx->params = *params;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return tsx;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void transaction_end(struct aeap_transaction *tsx, int timed_out, int result)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!tsx) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_lock(tsx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      tsx->result = result;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (tsx->container) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ao2_unlink(tsx->container, tsx);</span><br><span style="color: hsl(120, 100%, 40%);">+           tsx->container = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!timed_out) {</span><br><span style="color: hsl(120, 100%, 40%);">+             aeap_transaction_cancel_timer(tsx);</span><br><span style="color: hsl(120, 100%, 40%);">+   } else if (tsx->sched_id != -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+          tsx->sched_id = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!tsx->handled) {</span><br><span style="color: hsl(120, 100%, 40%);">+               if (timed_out) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      if (tsx->params.on_timeout) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              tsx->params.on_timeout(tsx->aeap, tsx->params.msg, tsx->params.obj);</span><br><span style="color: hsl(120, 100%, 40%);">+                      } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                              aeap_error(tsx->aeap, "transaction", "message '%s' timed out",</span><br><span style="color: hsl(120, 100%, 40%);">+                                 ast_aeap_message_name(tsx->params.msg));</span><br><span style="color: hsl(120, 100%, 40%);">+                   }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           tsx->handled = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_cond_signal(&tsx->handled_cond);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_unlock(tsx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    ao2_ref(tsx, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int transaction_raise_timeout(const void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Ref added added at timer creation removed in end call */</span><br><span style="color: hsl(120, 100%, 40%);">+   transaction_end((struct aeap_transaction *)data, 1, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int transaction_sched_timer(struct aeap_transaction *tsx)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   if (tsx->params.timeout <= 0 || tsx->sched_id != -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+               return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   tsx->sched_id = ast_sched_add(aeap_sched_context(), tsx->params.timeout,</span><br><span style="color: hsl(120, 100%, 40%);">+                        transaction_raise_timeout, ao2_bump(tsx));</span><br><span style="color: hsl(120, 100%, 40%);">+    if (tsx->sched_id == -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+         aeap_error(tsx->aeap, "transaction", "unable to schedule timeout for '%s'", tsx->id);</span><br><span style="color: hsl(120, 100%, 40%);">+               ao2_ref(tsx, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+             return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void transaction_wait(struct aeap_transaction *tsx)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_lock(tsx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      while (!tsx->handled) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_cond_wait(&tsx->handled_cond, ao2_object_get_lockaddr(tsx));</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_unlock(tsx);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int aeap_transaction_start(struct aeap_transaction *tsx)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    if (transaction_sched_timer(tsx)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (tsx->params.wait) {</span><br><span style="color: hsl(120, 100%, 40%);">+            /* Wait until transaction completes, or times out */</span><br><span style="color: hsl(120, 100%, 40%);">+          transaction_wait(tsx);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct aeap_transaction *aeap_transaction_get(struct ao2_container *transactions, const char *id)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  return ao2_find(transactions, id, OBJ_SEARCH_KEY);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void aeap_transaction_end(struct aeap_transaction *tsx, int result)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       transaction_end(tsx, 0, result);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int aeap_transaction_result(struct aeap_transaction *tsx)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   return tsx->result;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void *aeap_transaction_user_obj(struct aeap_transaction *tsx)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return tsx->params.obj;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct aeap_transaction *aeap_transaction_create_and_add(struct ao2_container *transactions,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *id, struct ast_aeap_tsx_params *params, struct ast_aeap *aeap)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct aeap_transaction *tsx;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       tsx = transaction_create(id, params, aeap);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!tsx) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!ao2_link(transactions, tsx)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           aeap_error(tsx->aeap, "transaction", "unable to add '%s' to container", id);</span><br><span style="color: hsl(120, 100%, 40%);">+           ao2_ref(tsx, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * Yes, this creates a circular reference. This reference is removed though</span><br><span style="color: hsl(120, 100%, 40%);">+    * upon transaction end. It's assumed here that the given transactions container</span><br><span style="color: hsl(120, 100%, 40%);">+   * takes "ownership", and ultimate responsibility of its contained transactions.</span><br><span style="color: hsl(120, 100%, 40%);">+     * Thus when the given container needs to be unref'ed/freed it must call</span><br><span style="color: hsl(120, 100%, 40%);">+   * aeap_transaction_end for each transaction prior to doing so.</span><br><span style="color: hsl(120, 100%, 40%);">+        */</span><br><span style="color: hsl(120, 100%, 40%);">+   /* tsx->container = ao2_bump(transactions); */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * The transaction needs to know what container manages it, so it can remove</span><br><span style="color: hsl(120, 100%, 40%);">+   * itself from the given container under certain conditions (e.g. transaction</span><br><span style="color: hsl(120, 100%, 40%);">+  * timeout).</span><br><span style="color: hsl(120, 100%, 40%);">+   *</span><br><span style="color: hsl(120, 100%, 40%);">+     * It's expected that the given container will out live any contained transaction</span><br><span style="color: hsl(120, 100%, 40%);">+  * (i.e. the container will not itself be destroyed before ensuring all contained</span><br><span style="color: hsl(120, 100%, 40%);">+      * transactions are ended, and removed). Thus there is no reason to bump the given</span><br><span style="color: hsl(120, 100%, 40%);">+     * container's reference here.</span><br><span style="color: hsl(120, 100%, 40%);">+     */</span><br><span style="color: hsl(120, 100%, 40%);">+   tsx->container = transactions;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return tsx;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ao2_container *aeap_transactions_create(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ao2_container *transactions;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ transactions = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, AEAP_TRANSACTION_BUCKETS,</span><br><span style="color: hsl(120, 100%, 40%);">+                aeap_transaction_hash_fn, NULL, aeap_transaction_cmp_fn);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!transactions) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_ERROR, "AEAP transaction: unable to create container\n");</span><br><span style="color: hsl(120, 100%, 40%);">+               return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return transactions;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/res/res_aeap/transaction.h b/res/res_aeap/transaction.h</span><br><span>new file mode 100644</span><br><span>index 0000000..973ba1a</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_aeap/transaction.h</span><br><span>@@ -0,0 +1,123 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifndef RES_AEAP_TRANSACTION_H</span><br><span style="color: hsl(120, 100%, 40%);">+#define RES_AEAP_TRANSACTION_H</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_aeap.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ao2_container;</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_aeap_tsx_params;</span><br><span style="color: hsl(120, 100%, 40%);">+struct aeap_transaction;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create an Asterisk external application transactions container</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns A transaction object, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ao2_container *aeap_transactions_create(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create a transaction object, and add it to the given container</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param transactions A transactions container</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param id An id to use for the transaction</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param params Transaction parameters</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param aeap The aeap object that "owns" this transaction</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 if successfully create and added, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct aeap_transaction *aeap_transaction_create_and_add(struct ao2_container *transactions,</span><br><span style="color: hsl(120, 100%, 40%);">+     const char *id, struct ast_aeap_tsx_params *params, struct ast_aeap *aeap);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Clean up parameter references, and possibly call optional user object cleanup</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param params Transaction parameters</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void aeap_transaction_params_cleanup(struct ast_aeap_tsx_params *params);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve a transaction for the id from the container</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param transactions A transactions container</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param id A transaction id</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns an AEAP transaction object, NULL if no transaction is found</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct aeap_transaction *aeap_transaction_get(struct ao2_container *transactions,</span><br><span style="color: hsl(120, 100%, 40%);">+      const char *id);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Start the transaction</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param tsx The transaction to initiate</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 if successfully raised, and handled. Otherwise non zero.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int aeap_transaction_start(struct aeap_transaction *tsx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief End a transaction, and remove it from the given container</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * The "result" parameter is a value representing the state (success/failure,</span><br><span style="color: hsl(120, 100%, 40%);">+ * perhaps even something else) of transactional processing upon ending.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param tsx A transaction to end</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param result A result to give to the transaction</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void aeap_transaction_end(struct aeap_transaction *tsx, int result);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Get a transaction's result</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * A transaction's result is a value that represents the relative success (0), or</span><br><span style="color: hsl(120, 100%, 40%);">+ * failure (-1) of a transaction. For example, a timeout is considered a failure</span><br><span style="color: hsl(120, 100%, 40%);">+ * and will elicit a -1.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This value though is also dependent upon the result of the message handler</span><br><span style="color: hsl(120, 100%, 40%);">+ * associated with the transaction. Meaning if an associated message is handled,</span><br><span style="color: hsl(120, 100%, 40%);">+ * then its result is stored as the transaction result and returned here.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param tsx A transaction object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns The transaction result</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int aeap_transaction_result(struct aeap_transaction *tsx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Cancel the transaction timer</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Stops the transaction timer, but does not end/stop the transaction itself</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param transaction A transaction to cancel the timer on</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 if canceled, non zero otherwise</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int aeap_transaction_cancel_timer(struct aeap_transaction *tsx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve the user object associated with the transaction</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param transaction A transaction object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns A user object, or NULL if non associated</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void *aeap_transaction_user_obj(struct aeap_transaction *tsx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#endif /* RES_AEAP_TRANSACTION_H */</span><br><span>diff --git a/res/res_aeap/transport.c b/res/res_aeap/transport.c</span><br><span>new file mode 100644</span><br><span>index 0000000..c032e10</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_aeap/transport.c</span><br><span>@@ -0,0 +1,156 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/utils.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "logger.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "transport.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "transport_websocket.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct aeap_transport *aeap_transport_create(const char *type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct aeap_transport *transport = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!strncasecmp(type, "ws", 2)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          transport = (struct aeap_transport *)aeap_transport_websocket_create();</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!transport) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_log(LOG_ERROR, "AEAP transport: failed to create for type '%s'\n", type);</span><br><span style="color: hsl(120, 100%, 40%);">+               return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_mutex_init(&transport->read_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_init(&transport->write_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      transport->connected = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return transport;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int aeap_transport_connect(struct aeap_transport *transport, const char *url,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *protocol, int timeout)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    SCOPED_MUTEX(rlock, &transport->read_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+    SCOPED_MUTEX(wlock, &transport->write_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (aeap_transport_is_connected(transport)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   res = transport->vtable->connect(transport, url, protocol, timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!res) {</span><br><span style="color: hsl(120, 100%, 40%);">+           transport->connected = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct aeap_transport *aeap_transport_create_and_connect(const char *type,</span><br><span style="color: hsl(120, 100%, 40%);">+  const char *url, const char *protocol, int timeout)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct aeap_transport *transport = aeap_transport_create(type);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!transport) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (aeap_transport_connect(transport, url, protocol, timeout)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              aeap_transport_destroy(transport);</span><br><span style="color: hsl(120, 100%, 40%);">+            return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return transport;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int aeap_transport_is_connected(struct aeap_transport *transport)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * Avoid using a lock to 'read' the 'connected' variable in order to</span><br><span style="color: hsl(120, 100%, 40%);">+   * keep things slightly more efficient.</span><br><span style="color: hsl(120, 100%, 40%);">+        */</span><br><span style="color: hsl(120, 100%, 40%);">+   return ast_atomic_fetch_add(&transport->connected, 0, __ATOMIC_RELAXED);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int aeap_transport_disconnect(struct aeap_transport *transport)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      int res;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    SCOPED_MUTEX(rlock, &transport->read_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+    SCOPED_MUTEX(wlock, &transport->write_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!aeap_transport_is_connected(transport)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   res = transport->vtable->disconnect(transport);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * Even though the transport is locked here use atomics to set the value of</span><br><span style="color: hsl(120, 100%, 40%);">+    * 'connected' since it's possible the variable is being 'read' by another</span><br><span style="color: hsl(120, 100%, 40%);">+         * thread via the 'is_connected' call.</span><br><span style="color: hsl(120, 100%, 40%);">+         */</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_atomic_fetch_sub(&transport->connected, 1, __ATOMIC_RELAXED);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void aeap_transport_destroy(struct aeap_transport *transport)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!transport) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Ensure an orderly disconnect occurs before final destruction */</span><br><span style="color: hsl(120, 100%, 40%);">+    aeap_transport_disconnect(transport);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       transport->vtable->destroy(transport);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_mutex_destroy(&transport->read_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_mutex_destroy(&transport->write_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_free(transport);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+intmax_t aeap_transport_read(struct aeap_transport *transport, void *buf, intmax_t size,</span><br><span style="color: hsl(120, 100%, 40%);">+   enum AST_AEAP_DATA_TYPE *rtype)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    SCOPED_MUTEX(lock, &transport->read_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!aeap_transport_is_connected(transport)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return transport->vtable->read(transport, buf, size, rtype);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+intmax_t aeap_transport_write(struct aeap_transport *transport, const void *buf, intmax_t size,</span><br><span style="color: hsl(120, 100%, 40%);">+      enum AST_AEAP_DATA_TYPE wtype)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     SCOPED_MUTEX(lock, &transport->write_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!aeap_transport_is_connected(transport)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return transport->vtable->write(transport, buf, size, wtype);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/res/res_aeap/transport.h b/res/res_aeap/transport.h</span><br><span>new file mode 100644</span><br><span>index 0000000..622247e</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_aeap/transport.h</span><br><span>@@ -0,0 +1,209 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifndef RES_AEAP_TRANSPORT_H</span><br><span style="color: hsl(120, 100%, 40%);">+#define RES_AEAP_TRANSPORT_H</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdint.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_aeap.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct aeap_transport;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Asterisk external application transport virtual table</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Callbacks to be implemented by "derived" transports</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct aeap_transport_vtable {</span><br><span style="color: hsl(120, 100%, 40%);">+    /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Connect a transport</span><br><span style="color: hsl(120, 100%, 40%);">+  *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param self The transport object</span><br><span style="color: hsl(120, 100%, 40%);">+    * \param url The URL to connect to</span><br><span style="color: hsl(120, 100%, 40%);">+    * \param protocol The connection protocol to use if applicable</span><br><span style="color: hsl(120, 100%, 40%);">+        * \param timeout How long (in milliseconds) to attempt to connect (-1 equals infinite)</span><br><span style="color: hsl(120, 100%, 40%);">+        *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \returns 0 on success, or -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+       */</span><br><span style="color: hsl(120, 100%, 40%);">+   int (*connect)(struct aeap_transport *self, const char *url, const char *protocol, int timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Disconnect a transport</span><br><span style="color: hsl(120, 100%, 40%);">+       *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param self The transport object</span><br><span style="color: hsl(120, 100%, 40%);">+    *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \returns 0 on success, or -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+       */</span><br><span style="color: hsl(120, 100%, 40%);">+   int (*disconnect)(struct aeap_transport *self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Destroy a transport</span><br><span style="color: hsl(120, 100%, 40%);">+  *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param self The transport object</span><br><span style="color: hsl(120, 100%, 40%);">+    */</span><br><span style="color: hsl(120, 100%, 40%);">+   void (*destroy)(struct aeap_transport *self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Read data from a transport</span><br><span style="color: hsl(120, 100%, 40%);">+   *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param self The transport object</span><br><span style="color: hsl(120, 100%, 40%);">+    * \param buf The buffer data is read read into</span><br><span style="color: hsl(120, 100%, 40%);">+        * \param size The size of the given data buffer</span><br><span style="color: hsl(120, 100%, 40%);">+       * \param rtype [out] The type of data read</span><br><span style="color: hsl(120, 100%, 40%);">+    *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \returns Total number of bytes read, or less than zero on error</span><br><span style="color: hsl(120, 100%, 40%);">+     */</span><br><span style="color: hsl(120, 100%, 40%);">+   intmax_t (*read)(struct aeap_transport *self, void *buf, intmax_t size,</span><br><span style="color: hsl(120, 100%, 40%);">+               enum AST_AEAP_DATA_TYPE *rtype);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Write data to a transport</span><br><span style="color: hsl(120, 100%, 40%);">+    *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param self The transport object</span><br><span style="color: hsl(120, 100%, 40%);">+    * \param buf The data to write</span><br><span style="color: hsl(120, 100%, 40%);">+        * \param size The size of data to write</span><br><span style="color: hsl(120, 100%, 40%);">+       * \param wtype The type of data to write</span><br><span style="color: hsl(120, 100%, 40%);">+      *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \returns Total number of bytes written, or less than zero on error</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+   intmax_t (*write)(struct aeap_transport *self, const void *buf, intmax_t size,</span><br><span style="color: hsl(120, 100%, 40%);">+                enum AST_AEAP_DATA_TYPE wtype);</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Asterisk external application transport structure to be</span><br><span style="color: hsl(120, 100%, 40%);">+ *        "derived" by specific transport implementation types</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Transports are assumed to support simultaneous reading and writing,</span><br><span style="color: hsl(120, 100%, 40%);">+ * thus separate read and write locks. A transport type not supporting</span><br><span style="color: hsl(120, 100%, 40%);">+ * such can simply apply the opposing lock during a read or write, i.e.</span><br><span style="color: hsl(120, 100%, 40%);">+ * lock the write lock during a read and vice versa.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct aeap_transport {</span><br><span style="color: hsl(120, 100%, 40%);">+      /*! Transport virtual table */</span><br><span style="color: hsl(120, 100%, 40%);">+        struct aeap_transport_vtable *vtable;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Whether or not the transport is connected */</span><br><span style="color: hsl(120, 100%, 40%);">+      unsigned int connected;</span><br><span style="color: hsl(120, 100%, 40%);">+       /*! Lock used when reading */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_t read_lock;</span><br><span style="color: hsl(120, 100%, 40%);">+        /*! Lock used when writing */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_t write_lock;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create an Asterisk external application transport</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param type The type of transport to create</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns An Asterisk external application transport, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct aeap_transport *aeap_transport_create(const char *type);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Connect a transport</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param transport The transport to connect</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param url The URL to connect to</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param protocol The connection protocol to use if applicable</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param timeout How long (in milliseconds) to attempt to connect (-1 equals infinite)</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, or -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int aeap_transport_connect(struct aeap_transport *transport, const char *url,</span><br><span style="color: hsl(120, 100%, 40%);">+      const char *protocol, int timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create an Asterisk external application transport, and connect it</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param type The type of transport to create</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param url The URL to connect to</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param protocol The connection protocol to use if applicable</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param timeout How long (in milliseconds) to attempt to connect (-1 equals infinite)</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns An Asterisk external application transport, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct aeap_transport *aeap_transport_create_and_connect(const char* type,</span><br><span style="color: hsl(120, 100%, 40%);">+     const char *url, const char *protocol, int timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Disconnect a transport</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note Locks both the transport's read and write locks before calling transport</span><br><span style="color: hsl(120, 100%, 40%);">+ *       instance's disconnect, and unlocks both before returning.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param transport The transport to disconnect</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, or -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int aeap_transport_disconnect(struct aeap_transport *transport);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Whether or not the transport is in a connected state</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param transport The transport object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns True if connected, false otherwise</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int aeap_transport_is_connected(struct aeap_transport *transport);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Destroy a transport</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param transport The transport to destroy</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, or -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void aeap_transport_destroy(struct aeap_transport *transport);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Read data from the transport</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This is a blocking read, and will not return until the transport</span><br><span style="color: hsl(120, 100%, 40%);">+ * implementation returns.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note Locks transport's read lock before calling transport instance's</span><br><span style="color: hsl(120, 100%, 40%);">+ *       read, and unlocks it before returning.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param transport The transport to read from</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param buf The buffer data is read into</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param size The size of data given data buffer</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param rtype [out] The type of data read</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns Total number of bytes read, or less than zero on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+intmax_t aeap_transport_read(struct aeap_transport *transport, void *buf, intmax_t size,</span><br><span style="color: hsl(120, 100%, 40%);">+     enum AST_AEAP_DATA_TYPE *rtype);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Write data to the transport</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note Locks transport's write lock before calling transport instance's</span><br><span style="color: hsl(120, 100%, 40%);">+ *       write, and unlocks it before returning.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param transport The transport to write to</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param buf The data to write</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param size The size of data to write</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param wtype The type of data to write</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns Total number of bytes written, or less than zero on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+intmax_t aeap_transport_write(struct aeap_transport *transport, const void *buf, intmax_t size,</span><br><span style="color: hsl(120, 100%, 40%);">+     enum AST_AEAP_DATA_TYPE wtype);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#endif /* RES_AEAP_TRANSPORT_H */</span><br><span>diff --git a/res/res_aeap/transport_websocket.c b/res/res_aeap/transport_websocket.c</span><br><span>new file mode 100644</span><br><span>index 0000000..5f1a406</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_aeap/transport_websocket.c</span><br><span>@@ -0,0 +1,249 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/http_websocket.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/utils.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "logger.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "transport.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "transport_websocket.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define log_error(obj, fmt, ...) aeap_error(obj, "websocket", fmt, ##__VA_ARGS__)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct aeap_transport_websocket {</span><br><span style="color: hsl(120, 100%, 40%);">+     /*! Derive from base transport (must be first attribute) */</span><br><span style="color: hsl(120, 100%, 40%);">+   struct aeap_transport base;</span><br><span style="color: hsl(120, 100%, 40%);">+   /*! The underlying websocket */</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ast_websocket *ws;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int websocket_connect(struct aeap_transport *self, const char *url,</span><br><span style="color: hsl(120, 100%, 40%);">+   const char *protocol, int timeout)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct aeap_transport_websocket *transport = (struct aeap_transport_websocket *)self;</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_websocket_result ws_result;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_websocket_client_options ws_options = {</span><br><span style="color: hsl(120, 100%, 40%);">+            .uri = url,</span><br><span style="color: hsl(120, 100%, 40%);">+           .protocols = protocol,</span><br><span style="color: hsl(120, 100%, 40%);">+                .timeout = timeout,</span><br><span style="color: hsl(120, 100%, 40%);">+           .tls_cfg = NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+      };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  transport->ws = ast_websocket_client_create_with_options(&ws_options, &ws_result);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ws_result != WS_OK) {</span><br><span style="color: hsl(120, 100%, 40%);">+             log_error(self, "connect failure (%d)", (int)ws_result);</span><br><span style="color: hsl(120, 100%, 40%);">+            return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int websocket_disconnect(struct aeap_transport *self)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct aeap_transport_websocket *transport = (struct aeap_transport_websocket *)self;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (transport->ws) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_websocket_unref(transport->ws);</span><br><span style="color: hsl(120, 100%, 40%);">+                transport->ws = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void websocket_destroy(struct aeap_transport *self)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * Disconnect takes care of cleaning up the websocket. Note, disconnect</span><br><span style="color: hsl(120, 100%, 40%);">+        * was called by the base/dispatch interface prior to calling this</span><br><span style="color: hsl(120, 100%, 40%);">+     * function so nothing to do here.</span><br><span style="color: hsl(120, 100%, 40%);">+     */</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static intmax_t websocket_read(struct aeap_transport *self, void *buf, intmax_t size,</span><br><span style="color: hsl(120, 100%, 40%);">+       enum AST_AEAP_DATA_TYPE *rtype)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct aeap_transport_websocket *transport = (struct aeap_transport_websocket *)self;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       char *payload;</span><br><span style="color: hsl(120, 100%, 40%);">+        uint64_t bytes_read = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+      uint64_t total_bytes_read = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+        enum ast_websocket_opcode opcode;</span><br><span style="color: hsl(120, 100%, 40%);">+     int fragmented = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ *rtype = AST_AEAP_DATA_TYPE_NONE;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_websocket_fd(transport->ws) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              log_error(self, "unavailable for reading");</span><br><span style="color: hsl(120, 100%, 40%);">+         /* Ensure this transport is in a disconnected state */</span><br><span style="color: hsl(120, 100%, 40%);">+                aeap_transport_disconnect(self);</span><br><span style="color: hsl(120, 100%, 40%);">+              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * This function is called with the read_lock locked. However, the lock needs to be</span><br><span style="color: hsl(120, 100%, 40%);">+    * unlocked while waiting for input otherwise a deadlock can occur during disconnect</span><br><span style="color: hsl(120, 100%, 40%);">+   * (disconnect attempts to grab the lock but can't because read holds it here). So</span><br><span style="color: hsl(120, 100%, 40%);">+         * unlock it prior to waiting.</span><br><span style="color: hsl(120, 100%, 40%);">+         */</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_mutex_unlock(&transport->base.read_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (ast_websocket_wait_for_input(transport->ws, -1) <= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_mutex_lock(&transport->base.read_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+            log_error(self, "poll failure: %s", strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+               /* Ensure this transport is in a disconnected state */</span><br><span style="color: hsl(120, 100%, 40%);">+                aeap_transport_disconnect(self);</span><br><span style="color: hsl(120, 100%, 40%);">+              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_mutex_lock(&transport->base.read_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!transport->ws) {</span><br><span style="color: hsl(120, 100%, 40%);">+              /*</span><br><span style="color: hsl(120, 100%, 40%);">+             * It's possible the transport was told to disconnect while waiting for input.</span><br><span style="color: hsl(120, 100%, 40%);">+             * If so then the websocket will be NULL, so we don't want to continue.</span><br><span style="color: hsl(120, 100%, 40%);">+            */</span><br><span style="color: hsl(120, 100%, 40%);">+           return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   do {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (ast_websocket_read(transport->ws, &payload, &bytes_read, &opcode,</span><br><span style="color: hsl(120, 100%, 40%);">+                          &fragmented) != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      log_error(self, "read failure (%d): %s", opcode, strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+                  return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!bytes_read) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           if (total_bytes_read + bytes_read > size) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        log_error(self, "attempted to read too many bytes into (%jd) sized buffer", size);</span><br><span style="color: hsl(120, 100%, 40%);">+                  return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           memcpy(buf + total_bytes_read, payload, bytes_read);</span><br><span style="color: hsl(120, 100%, 40%);">+          total_bytes_read += bytes_read;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     } while (opcode == AST_WEBSOCKET_OPCODE_CONTINUATION);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      switch (opcode) {</span><br><span style="color: hsl(120, 100%, 40%);">+     case AST_WEBSOCKET_OPCODE_CLOSE:</span><br><span style="color: hsl(120, 100%, 40%);">+              log_error(self, "closed");</span><br><span style="color: hsl(120, 100%, 40%);">+          return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    case AST_WEBSOCKET_OPCODE_BINARY:</span><br><span style="color: hsl(120, 100%, 40%);">+             *rtype = AST_AEAP_DATA_TYPE_BINARY;</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case AST_WEBSOCKET_OPCODE_TEXT:</span><br><span style="color: hsl(120, 100%, 40%);">+               *rtype = AST_AEAP_DATA_TYPE_STRING;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         /* Append terminator, but check for overflow first */</span><br><span style="color: hsl(120, 100%, 40%);">+         if (total_bytes_read == size) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       log_error(self, "unable to write string terminator");</span><br><span style="color: hsl(120, 100%, 40%);">+                       return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           *((char *)(buf + total_bytes_read)) = '\0';</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              /* Ignore all other message types */</span><br><span style="color: hsl(120, 100%, 40%);">+          return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return total_bytes_read;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static intmax_t websocket_write(struct aeap_transport *self, const void *buf, intmax_t size,</span><br><span style="color: hsl(120, 100%, 40%);">+   enum AST_AEAP_DATA_TYPE wtype)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct aeap_transport_websocket *transport = (struct aeap_transport_websocket *)self;</span><br><span style="color: hsl(120, 100%, 40%);">+ intmax_t res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   switch (wtype) {</span><br><span style="color: hsl(120, 100%, 40%);">+      case AST_AEAP_DATA_TYPE_BINARY:</span><br><span style="color: hsl(120, 100%, 40%);">+               res = ast_websocket_write(transport->ws, AST_WEBSOCKET_OPCODE_BINARY,</span><br><span style="color: hsl(120, 100%, 40%);">+                      (char *)buf, size);</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case AST_AEAP_DATA_TYPE_STRING:</span><br><span style="color: hsl(120, 100%, 40%);">+               res = ast_websocket_write(transport->ws, AST_WEBSOCKET_OPCODE_TEXT,</span><br><span style="color: hsl(120, 100%, 40%);">+                        (char *)buf, size);</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (res < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             log_error(self, "problem writing to websocket (closed)");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         /*</span><br><span style="color: hsl(120, 100%, 40%);">+             * If the underlying socket is closed then ensure the</span><br><span style="color: hsl(120, 100%, 40%);">+          * transport is in a disconnected state as well.</span><br><span style="color: hsl(120, 100%, 40%);">+               */</span><br><span style="color: hsl(120, 100%, 40%);">+           aeap_transport_disconnect(self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            return res;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return size;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct aeap_transport_vtable *transport_websocket_vtable(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   static struct aeap_transport_vtable websocket_vtable = {</span><br><span style="color: hsl(120, 100%, 40%);">+              .connect = websocket_connect,</span><br><span style="color: hsl(120, 100%, 40%);">+         .disconnect = websocket_disconnect,</span><br><span style="color: hsl(120, 100%, 40%);">+           .destroy = websocket_destroy,</span><br><span style="color: hsl(120, 100%, 40%);">+         .read = websocket_read,</span><br><span style="color: hsl(120, 100%, 40%);">+               .write = websocket_write,</span><br><span style="color: hsl(120, 100%, 40%);">+     };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return &websocket_vtable;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Initialize a transport websocket object, and set its virtual table</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param transport The transport to initialize</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int transport_websocket_init(struct aeap_transport_websocket *transport)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        transport->ws = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    ((struct aeap_transport *)transport)->vtable = transport_websocket_vtable();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct aeap_transport_websocket *aeap_transport_websocket_create(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct aeap_transport_websocket *transport;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ transport = ast_calloc(1, sizeof(*transport));</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!transport) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_log(LOG_ERROR, "AEAP websocket: unable to create transport websocket");</span><br><span style="color: hsl(120, 100%, 40%);">+         return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (transport_websocket_init(transport)) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_free(transport);</span><br><span style="color: hsl(120, 100%, 40%);">+          return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return transport;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/res/res_aeap/transport_websocket.h b/res/res_aeap/transport_websocket.h</span><br><span>new file mode 100644</span><br><span>index 0000000..d72657e</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_aeap/transport_websocket.h</span><br><span>@@ -0,0 +1,34 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifndef RES_AEAP_TRANSPORT_WEBSOCKET_H</span><br><span style="color: hsl(120, 100%, 40%);">+#define RES_AEAP_TRANSPORT_WEBSOCKET_H</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Asterisk external application protocol websocket transport</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct aeap_transport_websocket;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Creates (heap allocated), and initializes a transport websocket</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns A transport websocket object, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct aeap_transport_websocket *aeap_transport_websocket_create(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#endif /* RES_AEAP_TRANSPORT_WEBSOCKET_H */</span><br><span>diff --git a/res/res_speech.c b/res/res_speech.c</span><br><span>index d425fde..2438e79 100644</span><br><span>--- a/res/res_speech.c</span><br><span>+++ b/res/res_speech.c</span><br><span>@@ -42,7 +42,7 @@</span><br><span> static struct ast_speech_engine *default_engine = NULL;</span><br><span> </span><br><span> /*! \brief Find a speech recognition engine of specified name, if NULL then use the default one */</span><br><span style="color: hsl(0, 100%, 40%);">-static struct ast_speech_engine *find_engine(const char *engine_name)</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_speech_engine *ast_speech_find_engine(const char *engine_name)</span><br><span> {</span><br><span>    struct ast_speech_engine *engine = NULL;</span><br><span> </span><br><span>@@ -185,7 +185,7 @@</span><br><span>   RAII_VAR(struct ast_format *, best, NULL, ao2_cleanup);</span><br><span> </span><br><span>  /* Try to find the speech recognition engine that was requested */</span><br><span style="color: hsl(0, 100%, 40%);">-      if (!(engine = find_engine(engine_name)))</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!(engine = ast_speech_find_engine(engine_name)))</span><br><span>                 return NULL;</span><br><span> </span><br><span>     joint = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);</span><br><span>@@ -313,7 +313,7 @@</span><br><span>     }</span><br><span> </span><br><span>        /* If an engine is already loaded with this name, error out */</span><br><span style="color: hsl(0, 100%, 40%);">-  if (find_engine(engine->name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_speech_find_engine(engine->name)) {</span><br><span>               ast_log(LOG_WARNING, "Speech recognition engine '%s' already exists.\n", engine->name);</span><br><span>                 return -1;</span><br><span>   }</span><br><span>@@ -366,6 +366,36 @@</span><br><span>     return engine;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+void ast_speech_unregister_engines(</span><br><span style="color: hsl(120, 100%, 40%);">+  int (*should_unregister)(const struct ast_speech_engine *engine, void *data), void *data,</span><br><span style="color: hsl(120, 100%, 40%);">+     void (*on_unregistered)(void *obj))</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_speech_engine *engine = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!should_unregister) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   AST_RWLIST_WRLOCK(&engines);</span><br><span style="color: hsl(120, 100%, 40%);">+      AST_RWLIST_TRAVERSE_SAFE_BEGIN(&engines, engine, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (should_unregister(engine, data)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        /* We have our engine... removed it */</span><br><span style="color: hsl(120, 100%, 40%);">+                        AST_RWLIST_REMOVE_CURRENT(list);</span><br><span style="color: hsl(120, 100%, 40%);">+                      /* If this was the default engine, we need to pick a new one */</span><br><span style="color: hsl(120, 100%, 40%);">+                       if (engine == default_engine) {</span><br><span style="color: hsl(120, 100%, 40%);">+                               default_engine = AST_RWLIST_FIRST(&engines);</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_verb(2, "Unregistered speech recognition engine '%s'\n", engine->name);</span><br><span style="color: hsl(120, 100%, 40%);">+                      /* All went well */</span><br><span style="color: hsl(120, 100%, 40%);">+                   if (on_unregistered) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                on_unregistered(engine);</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     AST_RWLIST_TRAVERSE_SAFE_END;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_UNLOCK(&engines);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int unload_module(void)</span><br><span> {</span><br><span>         /* We can not be unloaded */</span><br><span>diff --git a/res/res_speech_aeap.c b/res/res_speech_aeap.c</span><br><span>new file mode 100644</span><br><span>index 0000000..d81d6e5</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_speech_aeap.c</span><br><span>@@ -0,0 +1,731 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Asterisk External Application Speech Engine</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+       <support_level>core</support_level></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/astobj2.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/config.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/format.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/format_cap.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/json.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/speech.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/sorcery.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_aeap.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_aeap_message.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define SPEECH_AEAP_VERSION "0.1.0"</span><br><span style="color: hsl(120, 100%, 40%);">+#define SPEECH_PROTOCOL "speech_to_text"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define CONNECTION_TIMEOUT 2000</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define log_error(obj, fmt, ...) \</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_log(LOG_ERROR, "AEAP speech (%p): " fmt "\n", obj, ##__VA_ARGS__)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_json *custom_fields_to_params(const struct ast_variable *variables)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_variable *i;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_json *obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!variables) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   obj = ast_json_object_create();</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!obj) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   for (i = variables; i; i = i->next) {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (i->name[0] == '@' && i->name[1]) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  ast_json_object_set(obj, i->name + 1, ast_json_string_create(i->value));</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return obj;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create, and send a request to the external application</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Create, then sends a request to an Asterisk external application, and then blocks</span><br><span style="color: hsl(120, 100%, 40%);">+ * until a response is received or a time out occurs. Since this method waits until</span><br><span style="color: hsl(120, 100%, 40%);">+ * receiving a response the returned result is guaranteed to be pass/fail based upon</span><br><span style="color: hsl(120, 100%, 40%);">+ * a response handler's result.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name The name of the request to send</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param json The core json request data</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param data Optional user data to associate with request/response</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int speech_aeap_send_request(struct ast_aeap *aeap, const char *name,</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_json *json, void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * Wait for a response. Also since we're blocking,</span><br><span style="color: hsl(120, 100%, 40%);">+         * data is expected to be on the stack so no cleanup required.</span><br><span style="color: hsl(120, 100%, 40%);">+         */</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_aeap_tsx_params tsx_params = {</span><br><span style="color: hsl(120, 100%, 40%);">+             .timeout = 1000,</span><br><span style="color: hsl(120, 100%, 40%);">+              .wait = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+            .obj = obj,</span><br><span style="color: hsl(120, 100%, 40%);">+   };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* "steals" the json ref */</span><br><span style="color: hsl(120, 100%, 40%);">+ tsx_params.msg = ast_aeap_message_create_request(</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_aeap_message_type_json, name, NULL, json);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!tsx_params.msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Send "steals" the json msg ref */</span><br><span style="color: hsl(120, 100%, 40%);">+        return ast_aeap_send_msg_tsx(aeap, &tsx_params);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create, and send a "get" request to an external application</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Basic structure of the JSON message to send:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *   { param: [<param>, ...] }</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param speech The speech engine</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param param The name of the parameter to retrieve</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param data User data passed to the response handler</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int speech_aeap_get(struct ast_speech *speech, const char *param, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!param) {</span><br><span style="color: hsl(120, 100%, 40%);">+         return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* send_request handles json ref */</span><br><span style="color: hsl(120, 100%, 40%);">+   return speech_aeap_send_request(speech->data,</span><br><span style="color: hsl(120, 100%, 40%);">+              "get", ast_json_pack("{s:[s]}", "params", param), data);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct speech_param {</span><br><span style="color: hsl(120, 100%, 40%);">+    const char *name;</span><br><span style="color: hsl(120, 100%, 40%);">+     const char *value;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create, and send a "set" request to an external application</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Basic structure of the JSON message to send:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *   { params: { <name> : <value> }  }</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param speech The speech engine</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name The name of the parameter to set</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param value The value of the parameter to set</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int speech_aeap_set(struct ast_speech *speech, const char *name, const char *value)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!name) {</span><br><span style="color: hsl(120, 100%, 40%);">+          return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* send_request handles json ref */</span><br><span style="color: hsl(120, 100%, 40%);">+   return speech_aeap_send_request(speech->data,</span><br><span style="color: hsl(120, 100%, 40%);">+              "set", ast_json_pack("{s:{s:s}}", "params", name, value), NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int handle_response_set(struct ast_aeap *aeap, struct ast_aeap_message *message, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct speech_setting {</span><br><span style="color: hsl(120, 100%, 40%);">+       const char *param;</span><br><span style="color: hsl(120, 100%, 40%);">+    size_t len;</span><br><span style="color: hsl(120, 100%, 40%);">+   char *buf;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int handle_setting(struct ast_aeap *aeap, struct ast_json_iter *iter,</span><br><span style="color: hsl(120, 100%, 40%);">+        struct speech_setting *setting)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    const char *value;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (strcmp(ast_json_object_iter_key(iter), setting->param)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              log_error(aeap, "Unable to 'get' speech setting for '%s'", setting->param);</span><br><span style="color: hsl(120, 100%, 40%);">+              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   value = ast_json_string_get(ast_json_object_iter_value(iter));</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!value) {</span><br><span style="color: hsl(120, 100%, 40%);">+         log_error(aeap, "No value for speech setting '%s'", setting->param);</span><br><span style="color: hsl(120, 100%, 40%);">+             return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_copy_string(setting->buf, value, setting->len);</span><br><span style="color: hsl(120, 100%, 40%);">+     return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int handle_results(struct ast_aeap *aeap, struct ast_json_iter *iter,</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_speech_result **speech_results)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_speech_result *result = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_json *json_results;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_json *json_result;</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   json_results = ast_json_object_iter_value(iter);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!json_results || !speech_results) {</span><br><span style="color: hsl(120, 100%, 40%);">+               log_error(aeap, "Unable to 'get' speech results");</span><br><span style="color: hsl(120, 100%, 40%);">+          return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   for (i = 0; i < ast_json_array_size(json_results); ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (!(result = ast_calloc(1, sizeof(*result)))) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           json_result = ast_json_array_get(json_results, i);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          result->text = ast_strdup(ast_json_object_string_get(json_result, "text"));</span><br><span style="color: hsl(120, 100%, 40%);">+              result->score = ast_json_object_integer_get(json_result, "score");</span><br><span style="color: hsl(120, 100%, 40%);">+               result->grammar = ast_strdup(ast_json_object_string_get(json_result, "grammar"));</span><br><span style="color: hsl(120, 100%, 40%);">+                result->nbest_num = ast_json_object_integer_get(json_result, "best");</span><br><span style="color: hsl(120, 100%, 40%);">+            if (*speech_results) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        AST_LIST_NEXT(result, list) = *speech_results;</span><br><span style="color: hsl(120, 100%, 40%);">+                        *speech_results = result;</span><br><span style="color: hsl(120, 100%, 40%);">+             } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      *speech_results = result;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Handle a "get" response from an external application</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Basic structure of the expected JSON message to received:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *   {</span><br><span style="color: hsl(120, 100%, 40%);">+ *     response: "get"</span><br><span style="color: hsl(120, 100%, 40%);">+ *     "params" : { <name>: <value> | [ <results> ] }</span><br><span style="color: hsl(120, 100%, 40%);">+ *   }</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param speech The speech engine</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param param The name of the parameter to retrieve</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param data User data passed to the response handler</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int handle_response_get(struct ast_aeap *aeap, struct ast_aeap_message *message, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_json_iter *iter;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ iter = ast_json_object_iter(ast_json_object_get(ast_aeap_message_data(message), "params"));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!iter) {</span><br><span style="color: hsl(120, 100%, 40%);">+          log_error(aeap, "no 'get' parameters returned");</span><br><span style="color: hsl(120, 100%, 40%);">+            return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!strcmp(ast_json_object_iter_key(iter), "results")) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return handle_results(aeap, iter, data);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return handle_setting(aeap, iter, data);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int handle_response_setup(struct ast_aeap *aeap, struct ast_aeap_message *message, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ast_format *format = data;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ast_json *json = ast_aeap_message_data(message);</span><br><span style="color: hsl(120, 100%, 40%);">+       const char *codec_name;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!json) {</span><br><span style="color: hsl(120, 100%, 40%);">+          log_error(aeap, "no 'setup' object returned");</span><br><span style="color: hsl(120, 100%, 40%);">+              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   json = ast_json_object_get(json, "codecs");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!json || ast_json_array_size(json) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                log_error(aeap, "no 'setup' codecs available");</span><br><span style="color: hsl(120, 100%, 40%);">+             return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   codec_name = ast_json_object_string_get(ast_json_array_get(json, 0), "name");</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!codec_name || strcmp(codec_name, ast_format_get_codec_name(format))) {</span><br><span style="color: hsl(120, 100%, 40%);">+           log_error(aeap, "setup  codec '%s' unsupported", ast_format_get_codec_name(format));</span><br><span style="color: hsl(120, 100%, 40%);">+                return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct ast_aeap_message_handler response_handlers[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+  { "setup", handle_response_setup },</span><br><span style="color: hsl(120, 100%, 40%);">+ { "get", handle_response_get },</span><br><span style="color: hsl(120, 100%, 40%);">+     { "set", handle_response_set },</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int handle_request_set(struct ast_aeap *aeap, struct ast_aeap_message *message, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_json_iter *iter;</span><br><span style="color: hsl(120, 100%, 40%);">+   const char *error_msg = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       iter = ast_json_object_iter(ast_json_object_get(ast_aeap_message_data(message), "params"));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!iter) {</span><br><span style="color: hsl(120, 100%, 40%);">+          error_msg = "no parameter(s) requested";</span><br><span style="color: hsl(120, 100%, 40%);">+    } else if (!strcmp(ast_json_object_iter_key(iter), "results")) {</span><br><span style="color: hsl(120, 100%, 40%);">+            struct ast_speech *speech = ast_aeap_user_data_object_by_id(aeap, "speech");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              if (!speech) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        error_msg = "no associated speech object";</span><br><span style="color: hsl(120, 100%, 40%);">+          } else if (handle_results(aeap, iter, &speech->results)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     error_msg = "unable to handle results";</span><br><span style="color: hsl(120, 100%, 40%);">+             } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_speech_change_state(speech, AST_SPEECH_STATE_DONE);</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              error_msg = "can only set 'results'";</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (error_msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+              log_error(aeap, "set - %s", error_msg);</span><br><span style="color: hsl(120, 100%, 40%);">+             message = ast_aeap_message_create_error(ast_aeap_message_type_json,</span><br><span style="color: hsl(120, 100%, 40%);">+                   ast_aeap_message_name(message), ast_aeap_message_id(message), error_msg);</span><br><span style="color: hsl(120, 100%, 40%);">+     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              message = ast_aeap_message_create_response(ast_aeap_message_type_json,</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_aeap_message_name(message), ast_aeap_message_id(message), NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_aeap_send_msg(aeap, message);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct ast_aeap_message_handler request_handlers[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+   { "set", handle_request_set },</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_aeap_params speech_aeap_params = {</span><br><span style="color: hsl(120, 100%, 40%);">+  .response_handlers = response_handlers,</span><br><span style="color: hsl(120, 100%, 40%);">+       .response_handlers_size = ARRAY_LEN(response_handlers),</span><br><span style="color: hsl(120, 100%, 40%);">+       .request_handlers = request_handlers,</span><br><span style="color: hsl(120, 100%, 40%);">+ .request_handlers_size = ARRAY_LEN(request_handlers),</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create, and connect to an external application and send initial setup</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Basic structure of the JSON message to send:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *   {</span><br><span style="color: hsl(120, 100%, 40%);">+ *     "request": "setup"</span><br><span style="color: hsl(120, 100%, 40%);">+ *     "codecs": [</span><br><span style="color: hsl(120, 100%, 40%);">+ *         {</span><br><span style="color: hsl(120, 100%, 40%);">+ *             "name": <name>,</span><br><span style="color: hsl(120, 100%, 40%);">+ *             "attributes": { <name>: <value>, ..., }</span><br><span style="color: hsl(120, 100%, 40%);">+ *         },</span><br><span style="color: hsl(120, 100%, 40%);">+ *         ...,</span><br><span style="color: hsl(120, 100%, 40%);">+ *     ],</span><br><span style="color: hsl(120, 100%, 40%);">+ *     "params": { <name>: <value>, ..., }</span><br><span style="color: hsl(120, 100%, 40%);">+ *   }</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param speech The speech engine</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param format The format codec to use</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success, -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int speech_aeap_engine_create(struct ast_speech *speech, struct ast_format *format)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ast_aeap *aeap;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_variable *vars;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_json *json;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      aeap = ast_aeap_create_and_connect_by_id(</span><br><span style="color: hsl(120, 100%, 40%);">+             speech->engine->name, &speech_aeap_params, CONNECTION_TIMEOUT);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!aeap) {</span><br><span style="color: hsl(120, 100%, 40%);">+          return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   speech->data = aeap;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Don't allow unloading of this module while an external application is in use */</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_module_ref(ast_module_info->self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   vars = ast_aeap_custom_fields_get(speech->engine->name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* While the protocol allows sending of codec attributes, for now don't */</span><br><span style="color: hsl(120, 100%, 40%);">+        json = ast_json_pack("{s:s,s:[{s:s}],s:o*}", "version", SPEECH_AEAP_VERSION, "codecs",</span><br><span style="color: hsl(120, 100%, 40%);">+          "name", ast_format_get_codec_name(format), "params", custom_fields_to_params(vars));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_variables_destroy(vars);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (ast_aeap_user_data_register(aeap, "speech", speech, NULL)) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_module_unref(ast_module_info->self);</span><br><span style="color: hsl(120, 100%, 40%);">+           return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* send_request handles json ref */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (speech_aeap_send_request(speech->data, "setup", json, format)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_module_unref(ast_module_info->self);</span><br><span style="color: hsl(120, 100%, 40%);">+           return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * Add a reference to the engine here, so if it happens to get unregistered</span><br><span style="color: hsl(120, 100%, 40%);">+    * while executing it won't disappear.</span><br><span style="color: hsl(120, 100%, 40%);">+     */</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_ref(speech->engine, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int speech_aeap_engine_destroy(struct ast_speech *speech)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_ref(speech->engine, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+       ao2_cleanup(speech->data);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_module_unref(ast_module_info->self);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int speech_aeap_engine_write(struct ast_speech *speech, void *data, int len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        return ast_aeap_send_binary(speech->data, data, len);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int speech_aeap_engine_dtmf(struct ast_speech *speech, const char *dtmf)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     return speech_aeap_set(speech, "dtmf", dtmf);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int speech_aeap_engine_start(struct ast_speech *speech)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_speech_change_state(speech, AST_SPEECH_STATE_READY);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int speech_aeap_engine_change(struct ast_speech *speech, const char *name, const char *value)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       return speech_aeap_set(speech, name, value);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int speech_aeap_engine_get_setting(struct ast_speech *speech, const char *name,</span><br><span style="color: hsl(120, 100%, 40%);">+     char *buf, size_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct speech_setting setting = {</span><br><span style="color: hsl(120, 100%, 40%);">+             .param = name,</span><br><span style="color: hsl(120, 100%, 40%);">+                .len = len,</span><br><span style="color: hsl(120, 100%, 40%);">+           .buf = buf,</span><br><span style="color: hsl(120, 100%, 40%);">+   };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return speech_aeap_get(speech, name, &setting);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int speech_aeap_engine_change_results_type(struct ast_speech *speech,</span><br><span style="color: hsl(120, 100%, 40%);">+        enum ast_speech_results_type results_type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return speech_aeap_set(speech, "results_type",</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_speech_results_type_to_string(results_type));</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_speech_result *speech_aeap_engine_get(struct ast_speech *speech)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_speech_result *results = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (speech->results) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return speech->results;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (speech_aeap_get(speech, "results", &results)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return results;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void speech_engine_destroy(void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_speech_engine *engine = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     ao2_cleanup(engine->formats);</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_free(engine->name);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_speech_engine *speech_engine_alloc(const char *name)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_speech_engine *engine;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   engine = ao2_t_alloc_options(sizeof(*engine), speech_engine_destroy,</span><br><span style="color: hsl(120, 100%, 40%);">+          AO2_ALLOC_OPT_LOCK_NOLOCK, name);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!engine) {</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_log(LOG_ERROR, "AEAP speech: unable create engine '%s'\n", name);</span><br><span style="color: hsl(120, 100%, 40%);">+               return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   engine->name = ast_strdup(name);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!engine->name) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ao2_ref(engine, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+          return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   engine->create = speech_aeap_engine_create;</span><br><span style="color: hsl(120, 100%, 40%);">+        engine->destroy = speech_aeap_engine_destroy;</span><br><span style="color: hsl(120, 100%, 40%);">+      engine->write = speech_aeap_engine_write;</span><br><span style="color: hsl(120, 100%, 40%);">+  engine->dtmf = speech_aeap_engine_dtmf;</span><br><span style="color: hsl(120, 100%, 40%);">+    engine->start = speech_aeap_engine_start;</span><br><span style="color: hsl(120, 100%, 40%);">+  engine->change = speech_aeap_engine_change;</span><br><span style="color: hsl(120, 100%, 40%);">+        engine->get_setting = speech_aeap_engine_get_setting;</span><br><span style="color: hsl(120, 100%, 40%);">+      engine->change_results_type = speech_aeap_engine_change_results_type;</span><br><span style="color: hsl(120, 100%, 40%);">+      engine->get = speech_aeap_engine_get;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    engine->formats = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     return engine;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void speech_engine_alloc_and_register(const char *name, const struct ast_format_cap *formats)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_speech_engine *engine;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   engine = speech_engine_alloc(name);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!engine) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (formats && ast_format_cap_append_from_cap(engine->formats,</span><br><span style="color: hsl(120, 100%, 40%);">+                     formats, AST_MEDIA_TYPE_AUDIO)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_log(LOG_WARNING, "AEAP speech: Unable to add engine '%s' formats\n", name);</span><br><span style="color: hsl(120, 100%, 40%);">+             ao2_ref(engine, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+          return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_speech_register(engine)) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_log(LOG_WARNING, "AEAP speech: Unable to register engine '%s'\n", name);</span><br><span style="color: hsl(120, 100%, 40%);">+                ao2_ref(engine, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef TEST_FRAMEWORK</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void speech_engine_alloc_and_register2(const char *name, const char *codec_names)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_speech_engine *engine;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   engine = speech_engine_alloc(name);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!engine) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (codec_names && ast_format_cap_update_by_allow_disallow(engine->formats, codec_names, 1)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_log(LOG_WARNING, "AEAP speech: Unable to add engine '%s' codecs\n", name);</span><br><span style="color: hsl(120, 100%, 40%);">+              ao2_ref(engine, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+          return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_speech_register(engine)) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_log(LOG_WARNING, "AEAP speech: Unable to register engine '%s'\n", name);</span><br><span style="color: hsl(120, 100%, 40%);">+                ao2_ref(engine, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int unload_engine(void *obj, void *arg, int flags)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        if (ast_aeap_client_config_has_protocol(obj, SPEECH_PROTOCOL)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ao2_cleanup(ast_speech_unregister2(ast_sorcery_object_get_id(obj)));</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int load_engine(void *obj, void *arg, int flags)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    const char *id;</span><br><span style="color: hsl(120, 100%, 40%);">+       const struct ast_format_cap *formats;</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_speech_engine *engine;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!ast_aeap_client_config_has_protocol(obj, SPEECH_PROTOCOL)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   id = ast_sorcery_object_get_id(obj);</span><br><span style="color: hsl(120, 100%, 40%);">+  formats = ast_aeap_client_config_codecs(obj);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!formats) {</span><br><span style="color: hsl(120, 100%, 40%);">+               formats = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (!formats) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       ast_log(LOG_ERROR, "AEAP speech: unable to allocate default engine format for '%s'\n", id);</span><br><span style="color: hsl(120, 100%, 40%);">+                 return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   engine = ast_speech_find_engine(id);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!engine) {</span><br><span style="color: hsl(120, 100%, 40%);">+                speech_engine_alloc_and_register(id, formats);</span><br><span style="color: hsl(120, 100%, 40%);">+                return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_format_cap_identical(formats, engine->formats)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          /* Same name, same formats then nothing changed */</span><br><span style="color: hsl(120, 100%, 40%);">+            return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_ref(ast_speech_unregister2(engine->name), -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ speech_engine_alloc_and_register(id, formats);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int matches_engine(void *obj, void *arg, int flags)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_speech_engine *engine = arg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return strcmp(ast_sorcery_object_get_id(obj), engine->name) ? 0 : CMP_MATCH;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int should_unregister(const struct ast_speech_engine *engine, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     void *obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (engine->create != speech_aeap_engine_create) {</span><br><span style="color: hsl(120, 100%, 40%);">+         /* Only want to potentially unregister AEAP speech engines */</span><br><span style="color: hsl(120, 100%, 40%);">+         return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef TEST_FRAMEWORK</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!strcmp("_aeap_test_speech_", engine->name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+               /* Don't remove the test engine */</span><br><span style="color: hsl(120, 100%, 40%);">+                return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   obj = ao2_callback(data, 0, matches_engine, (void*)engine);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (obj) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ao2_ref(obj, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+             return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* If no match in given container then unregister engine */</span><br><span style="color: hsl(120, 100%, 40%);">+   return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void speech_observer_loaded(const char *object_type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ao2_container *container;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (strcmp(object_type, AEAP_CONFIG_CLIENT)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   container = ast_aeap_client_configs_get(SPEECH_PROTOCOL);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!container) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * An AEAP module reload has occurred. First</span><br><span style="color: hsl(120, 100%, 40%);">+   * remove all engines that no longer exist.</span><br><span style="color: hsl(120, 100%, 40%);">+    */</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_speech_unregister_engines(should_unregister, container, __ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Now add or update engines */</span><br><span style="color: hsl(120, 100%, 40%);">+       ao2_callback(container, 0, load_engine, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+        ao2_ref(container, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Observer for AEAP reloads */</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct ast_sorcery_observer speech_observer = {</span><br><span style="color: hsl(120, 100%, 40%);">+   .loaded = speech_observer_loaded,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int unload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ao2_container *container;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef TEST_FRAMEWORK</span><br><span style="color: hsl(120, 100%, 40%);">+     ao2_cleanup(ast_speech_unregister2("_aeap_test_speech_"));</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_sorcery_observer_remove(ast_aeap_sorcery(), AEAP_CONFIG_CLIENT, &speech_observer);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  container = ast_aeap_client_configs_get(SPEECH_PROTOCOL);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (container) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ao2_callback(container, 0, unload_engine, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+              ao2_ref(container, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ao2_container *container;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    speech_aeap_params.msg_type = ast_aeap_message_type_json;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   container = ast_aeap_client_configs_get(SPEECH_PROTOCOL);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (container) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ao2_callback(container, 0, load_engine, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+                ao2_ref(container, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * Add an observer since a named speech server must be created,</span><br><span style="color: hsl(120, 100%, 40%);">+        * registered, and eventually removed for all AEAP client</span><br><span style="color: hsl(120, 100%, 40%);">+      * configuration matching the "speech_to_text" protocol.</span><br><span style="color: hsl(120, 100%, 40%);">+    */</span><br><span style="color: hsl(120, 100%, 40%);">+    if (ast_sorcery_observer_add(ast_aeap_sorcery(), AEAP_CONFIG_CLIENT, &speech_observer)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         return AST_MODULE_LOAD_DECLINE;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef TEST_FRAMEWORK</span><br><span style="color: hsl(120, 100%, 40%);">+    speech_engine_alloc_and_register2("_aeap_test_speech_", "ulaw");</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return AST_MODULE_LOAD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Asterisk External Application Speech Engine",</span><br><span style="color: hsl(120, 100%, 40%);">+      .support_level = AST_MODULE_SUPPORT_CORE,</span><br><span style="color: hsl(120, 100%, 40%);">+     .load = load_module,</span><br><span style="color: hsl(120, 100%, 40%);">+  .unload = unload_module,</span><br><span style="color: hsl(120, 100%, 40%);">+      .load_pri = AST_MODPRI_CHANNEL_DEPEND,</span><br><span style="color: hsl(120, 100%, 40%);">+        .requires = "res_speech,res_aeap",</span><br><span style="color: hsl(120, 100%, 40%);">+);</span><br><span>diff --git a/tests/test_aeap.c b/tests/test_aeap.c</span><br><span>new file mode 100644</span><br><span>index 0000000..899da7a</span><br><span>--- /dev/null</span><br><span>+++ b/tests/test_aeap.c</span><br><span>@@ -0,0 +1,252 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+        <depend>TEST_FRAMEWORK</depend></span><br><span style="color: hsl(120, 100%, 40%);">+   <depend>res_aeap</depend></span><br><span style="color: hsl(120, 100%, 40%);">+ <support_level>core</support_level></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/test.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/file.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/http_websocket.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/json.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_aeap.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_aeap_message.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define CATEGORY "/res/aeap/"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define ADDR "127.0.0.1:8088"</span><br><span style="color: hsl(120, 100%, 40%);">+#define AEAP_TRANSPORT_TYPE "ws"</span><br><span style="color: hsl(120, 100%, 40%);">+#define AEAP_REMOTE_URL "ws://" ADDR "/ws"</span><br><span style="color: hsl(120, 100%, 40%);">+#define AEAP_REMOTE_PROTOCOL "echo"</span><br><span style="color: hsl(120, 100%, 40%);">+#define AEAP_MESSAGE_ID "foo"</span><br><span style="color: hsl(120, 100%, 40%);">+#define AEAP_CONNECTION_TIMEOUT 2000</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(create_and_connect)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct ast_aeap *, aeap, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+               info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+             info->explicit_only = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+           info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+         info->summary = "test creating and connecting to an AEAP application";</span><br><span style="color: hsl(120, 100%, 40%);">+           info->description = info->summary;</span><br><span style="color: hsl(120, 100%, 40%);">+              return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+      case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_test_validate(test, (aeap = ast_aeap_create_and_connect(AEAP_TRANSPORT_TYPE,</span><br><span style="color: hsl(120, 100%, 40%);">+              NULL, AEAP_REMOTE_URL, AEAP_REMOTE_PROTOCOL, AEAP_CONNECTION_TIMEOUT)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    return AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void handle_string(struct ast_aeap *aeap, const char *buf, intmax_t size)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       int *passed = ast_aeap_user_data_object_by_id(aeap, AEAP_MESSAGE_ID);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (strstr(buf, AEAP_MESSAGE_ID)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ++*passed;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void handle_timeout(struct ast_aeap *aeap, struct ast_aeap_message *message, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    int *passed = ast_aeap_user_data_object_by_id(aeap, AEAP_MESSAGE_ID);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       ++*passed;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(send_msg_handle_string)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   int passed = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+       RAII_VAR(struct ast_aeap *, aeap, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_aeap_tsx_params tsx_params = {0};</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_aeap_params aeap_params = {</span><br><span style="color: hsl(120, 100%, 40%);">+                .on_string = handle_string,</span><br><span style="color: hsl(120, 100%, 40%);">+   };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+               info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+             info->explicit_only = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+           info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+         info->summary = "test an AEAP application string handler";</span><br><span style="color: hsl(120, 100%, 40%);">+               info->description = info->summary;</span><br><span style="color: hsl(120, 100%, 40%);">+              return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+      case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   tsx_params.timeout = 2000; /* Test will end by timing out */</span><br><span style="color: hsl(120, 100%, 40%);">+  tsx_params.on_timeout = handle_timeout;</span><br><span style="color: hsl(120, 100%, 40%);">+       tsx_params.wait = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_test_validate(test, (aeap = ast_aeap_create_and_connect(AEAP_TRANSPORT_TYPE,</span><br><span style="color: hsl(120, 100%, 40%);">+              &aeap_params, AEAP_REMOTE_URL, AEAP_REMOTE_PROTOCOL, AEAP_CONNECTION_TIMEOUT)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_test_validate(test, (!ast_aeap_user_data_register(aeap, AEAP_MESSAGE_ID, &passed, NULL)));</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_test_validate(test, (tsx_params.msg = ast_aeap_message_create_request(</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_aeap_message_type_json, "foo", AEAP_MESSAGE_ID, NULL)));</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_test_validate(test, ast_aeap_send_msg_tsx(aeap, &tsx_params)); /* Returns fail on timeout */</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_aeap_user_data_unregister(aeap, AEAP_MESSAGE_ID);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return passed == 2 ? AST_TEST_PASS : AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int handle_msg(struct ast_aeap *aeap, struct ast_aeap_message *message, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       int *passed = ast_aeap_user_data_object_by_id(aeap, AEAP_MESSAGE_ID);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       *passed = !strcmp(ast_aeap_message_id(message), AEAP_MESSAGE_ID) &&</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_aeap_message_is_named(message, data);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!*passed) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "Name '%s' did not equal '%s' for message '%s'",</span><br><span style="color: hsl(120, 100%, 40%);">+                 ast_aeap_message_name(message), (char *)data, ast_aeap_message_id(message));</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct ast_aeap_message_handler handlers[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+   { "foo", handle_msg },</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(send_msg_handle_response)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  int passed = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+       RAII_VAR(struct ast_aeap *, aeap, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ char *name = "foo";</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_aeap_params aeap_params = {</span><br><span style="color: hsl(120, 100%, 40%);">+                .response_handlers = handlers,</span><br><span style="color: hsl(120, 100%, 40%);">+                .response_handlers_size = ARRAY_LEN(handlers),</span><br><span style="color: hsl(120, 100%, 40%);">+        };</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_aeap_tsx_params tsx_params = {0};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+               info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+             info->explicit_only = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+           info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+         info->summary = "test an AEAP application response handler";</span><br><span style="color: hsl(120, 100%, 40%);">+             info->description = info->summary;</span><br><span style="color: hsl(120, 100%, 40%);">+              return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+      case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   aeap_params.msg_type = ast_aeap_message_type_json;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  tsx_params.timeout = 2000;</span><br><span style="color: hsl(120, 100%, 40%);">+    tsx_params.wait = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+  tsx_params.obj = name;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_test_validate(test, (aeap = ast_aeap_create_and_connect(AEAP_TRANSPORT_TYPE,</span><br><span style="color: hsl(120, 100%, 40%);">+              &aeap_params, AEAP_REMOTE_URL, AEAP_REMOTE_PROTOCOL, AEAP_CONNECTION_TIMEOUT)));</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_test_validate(test, (!ast_aeap_user_data_register(aeap, AEAP_MESSAGE_ID, &passed, NULL)));</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_test_validate(test, (tsx_params.msg = ast_aeap_message_create_response(</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_aeap_message_type_json, name, AEAP_MESSAGE_ID, NULL)));</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_test_validate(test, !ast_aeap_send_msg_tsx(aeap, &tsx_params));</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_aeap_user_data_unregister(aeap, AEAP_MESSAGE_ID);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return passed ? AST_TEST_PASS : AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(send_msg_handle_request)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      int passed = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+       RAII_VAR(struct ast_aeap *, aeap, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ char *name = "foo";</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_aeap_params aeap_params = {</span><br><span style="color: hsl(120, 100%, 40%);">+                .request_handlers = handlers,</span><br><span style="color: hsl(120, 100%, 40%);">+         .request_handlers_size = ARRAY_LEN(handlers),</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_aeap_tsx_params tsx_params = {0};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+               info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+             info->explicit_only = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+           info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+         info->summary = "test an AEAP application request handler";</span><br><span style="color: hsl(120, 100%, 40%);">+              info->description = info->summary;</span><br><span style="color: hsl(120, 100%, 40%);">+              return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+      case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   aeap_params.msg_type = ast_aeap_message_type_json;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  tsx_params.timeout = 2000;</span><br><span style="color: hsl(120, 100%, 40%);">+    tsx_params.wait = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+  tsx_params.obj = name;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_test_validate(test, (aeap = ast_aeap_create_and_connect(AEAP_TRANSPORT_TYPE,</span><br><span style="color: hsl(120, 100%, 40%);">+              &aeap_params, AEAP_REMOTE_URL, AEAP_REMOTE_PROTOCOL, AEAP_CONNECTION_TIMEOUT)));</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_test_validate(test, (!ast_aeap_user_data_register(aeap, AEAP_MESSAGE_ID, &passed, NULL)));</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_test_validate(test, (tsx_params.msg = ast_aeap_message_create_request(</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_aeap_message_type_json, name, AEAP_MESSAGE_ID, NULL)));</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_test_validate(test, !ast_aeap_send_msg_tsx(aeap, &tsx_params));</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_aeap_user_data_unregister(aeap, AEAP_MESSAGE_ID);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return passed ? AST_TEST_PASS : AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_http_server *http_server;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!(http_server = ast_http_test_server_get("aeap transport http server", NULL))) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return AST_MODULE_LOAD_DECLINE;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   AST_TEST_REGISTER(create_and_connect);</span><br><span style="color: hsl(120, 100%, 40%);">+        AST_TEST_REGISTER(send_msg_handle_string);</span><br><span style="color: hsl(120, 100%, 40%);">+    AST_TEST_REGISTER(send_msg_handle_response);</span><br><span style="color: hsl(120, 100%, 40%);">+  AST_TEST_REGISTER(send_msg_handle_request);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_MODULE_LOAD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int unload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       AST_TEST_UNREGISTER(send_msg_handle_request);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_UNREGISTER(send_msg_handle_response);</span><br><span style="color: hsl(120, 100%, 40%);">+        AST_TEST_UNREGISTER(send_msg_handle_string);</span><br><span style="color: hsl(120, 100%, 40%);">+  AST_TEST_UNREGISTER(create_and_connect);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_http_test_server_discard(http_server);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk External Application Protocol Object Tests",</span><br><span style="color: hsl(120, 100%, 40%);">+       .support_level = AST_MODULE_SUPPORT_CORE,</span><br><span style="color: hsl(120, 100%, 40%);">+     .load = load_module,</span><br><span style="color: hsl(120, 100%, 40%);">+  .unload = unload_module,</span><br><span style="color: hsl(120, 100%, 40%);">+      .requires = "res_aeap",</span><br><span style="color: hsl(120, 100%, 40%);">+);</span><br><span>diff --git a/tests/test_aeap_speech.c b/tests/test_aeap_speech.c</span><br><span>new file mode 100644</span><br><span>index 0000000..2e658c1</span><br><span>--- /dev/null</span><br><span>+++ b/tests/test_aeap_speech.c</span><br><span>@@ -0,0 +1,287 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+      <depend>TEST_FRAMEWORK</depend></span><br><span style="color: hsl(120, 100%, 40%);">+   <depend>res_aeap</depend></span><br><span style="color: hsl(120, 100%, 40%);">+ <support_level>core</support_level></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/test.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/file.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/format_cap.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/http.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/http_websocket.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/json.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/speech.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_aeap.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_aeap_message.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define ADDR "127.0.0.1:8088"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int speech_test_server_setup(struct ast_json *req, struct ast_json *resp)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ast_json *params;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (ast_json_object_set(resp, "codecs", ast_json_ref(ast_json_object_get(req, "codecs")))) {</span><br><span style="color: hsl(120, 100%, 40%);">+              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   params = ast_json_object_get(req, "params"); /* Optional */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (params && ast_json_object_set(resp, "params", ast_json_ref(params))) {</span><br><span style="color: hsl(120, 100%, 40%);">+          return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define TEST_SPEECH_RESULTS_TEXT "foo"</span><br><span style="color: hsl(120, 100%, 40%);">+#define TEST_SPEECH_RESULTS_SCORE 7</span><br><span style="color: hsl(120, 100%, 40%);">+#define TEST_SPEECH_RESULTS_GRAMMAR "bar"</span><br><span style="color: hsl(120, 100%, 40%);">+#define TEST_SPEECH_RESULTS_BEST 1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int speech_test_server_get(struct ast_json *req, struct ast_json *resp)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   const char *param;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_json *json = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       param = ast_json_string_get(ast_json_array_get(ast_json_object_get(req, "params"), 0));</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!param) {</span><br><span style="color: hsl(120, 100%, 40%);">+         return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!strcmp(param, "results")) {</span><br><span style="color: hsl(120, 100%, 40%);">+            json = ast_json_pack("{s:[{s:s,s:i,s:s,s:i}]}",</span><br><span style="color: hsl(120, 100%, 40%);">+                     param,</span><br><span style="color: hsl(120, 100%, 40%);">+                        "text", TEST_SPEECH_RESULTS_TEXT,</span><br><span style="color: hsl(120, 100%, 40%);">+                   "score", TEST_SPEECH_RESULTS_SCORE,</span><br><span style="color: hsl(120, 100%, 40%);">+                 "grammar", TEST_SPEECH_RESULTS_GRAMMAR,</span><br><span style="color: hsl(120, 100%, 40%);">+                     "best", TEST_SPEECH_RESULTS_BEST);</span><br><span style="color: hsl(120, 100%, 40%);">+  } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* Assume setting */</span><br><span style="color: hsl(120, 100%, 40%);">+          json = ast_json_pack("{s:s}", param, "bar");</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!json || ast_json_object_set(resp, "params", json)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int speech_test_server_set(struct ast_json *req, struct ast_json *resp)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     if (ast_json_object_set(resp, "params", ast_json_ref(ast_json_object_get(req, "params")))) {</span><br><span style="color: hsl(120, 100%, 40%);">+              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int speech_test_server_handle_request(struct ast_websocket *ws, const void *buf, uint64_t size)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ast_json *req;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_json *resp;</span><br><span style="color: hsl(120, 100%, 40%);">+        const char *name;</span><br><span style="color: hsl(120, 100%, 40%);">+     char *resp_buf;</span><br><span style="color: hsl(120, 100%, 40%);">+       int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        req = ast_json_load_buf(buf, size, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!req) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_ERROR, "speech test handle request: unable to load json\n");</span><br><span style="color: hsl(120, 100%, 40%);">+            return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   name = ast_json_object_string_get(req, "request");</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!name) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_ERROR, "speech test handle request: no name\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_json_unref(req);</span><br><span style="color: hsl(120, 100%, 40%);">+          return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   resp = ast_json_pack("{s:s, s:s}", "response", name,</span><br><span style="color: hsl(120, 100%, 40%);">+              "id", ast_json_object_string_get(req, "id"));</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!resp) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_ERROR, "speech test handle request: unable to create response '%s'\n", name);</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_json_unref(req);</span><br><span style="color: hsl(120, 100%, 40%);">+          return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!strcmp(name, "setup")) {</span><br><span style="color: hsl(120, 100%, 40%);">+               res = speech_test_server_setup(req, resp);</span><br><span style="color: hsl(120, 100%, 40%);">+    } else if (!strcmp(name, "get")) {</span><br><span style="color: hsl(120, 100%, 40%);">+          res = speech_test_server_get(req, resp);</span><br><span style="color: hsl(120, 100%, 40%);">+      } else if (!strcmp(name, "set")) {</span><br><span style="color: hsl(120, 100%, 40%);">+          res = speech_test_server_set(req, resp);</span><br><span style="color: hsl(120, 100%, 40%);">+      } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_ERROR, "speech test handle request: unsupported request '%s'\n", name);</span><br><span style="color: hsl(120, 100%, 40%);">+         return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (res) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_log(LOG_ERROR, "speech test handle request: unable to build response '%s'\n", name);</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_json_unref(resp);</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_json_unref(req);</span><br><span style="color: hsl(120, 100%, 40%);">+          return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   resp_buf = ast_json_dump_string(resp);</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_json_unref(resp);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!resp_buf) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_ERROR, "speech test handle request: unable to dump response '%s'\n", name);</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_json_unref(req);</span><br><span style="color: hsl(120, 100%, 40%);">+          return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   res = ast_websocket_write_string(ws, resp_buf);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (res) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_log(LOG_ERROR, "speech test handle request: unable to write response '%s'\n", name);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_json_unref(req);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_free(resp_buf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void speech_test_server_cb(struct ast_websocket *ws, struct ast_variable *parameters,</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_variable *headers)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      int res;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (ast_fd_set_flags(ast_websocket_fd(ws), O_NONBLOCK)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_websocket_unref(ws);</span><br><span style="color: hsl(120, 100%, 40%);">+              return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   while ((res = ast_websocket_wait_for_input(ws, -1)) > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+         char *payload;</span><br><span style="color: hsl(120, 100%, 40%);">+                uint64_t payload_len;</span><br><span style="color: hsl(120, 100%, 40%);">+         enum ast_websocket_opcode opcode;</span><br><span style="color: hsl(120, 100%, 40%);">+             int fragmented;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             if (ast_websocket_read(ws, &payload, &payload_len, &opcode, &fragmented)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   ast_log(LOG_ERROR, "speech test: Read failure in server loop\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                   break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           switch (opcode) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     case AST_WEBSOCKET_OPCODE_CLOSE:</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_websocket_unref(ws);</span><br><span style="color: hsl(120, 100%, 40%);">+                              return;</span><br><span style="color: hsl(120, 100%, 40%);">+                       case AST_WEBSOCKET_OPCODE_BINARY:</span><br><span style="color: hsl(120, 100%, 40%);">+                             ast_websocket_write(ws, opcode, payload, payload_len);</span><br><span style="color: hsl(120, 100%, 40%);">+                                break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        case AST_WEBSOCKET_OPCODE_TEXT:</span><br><span style="color: hsl(120, 100%, 40%);">+                               ast_debug(3, "payload=%.*s\n", (int)payload_len, payload);</span><br><span style="color: hsl(120, 100%, 40%);">+                          if (speech_test_server_handle_request(ws, payload, payload_len)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                    ast_websocket_unref(ws);</span><br><span style="color: hsl(120, 100%, 40%);">+                                      return;</span><br><span style="color: hsl(120, 100%, 40%);">+                               }</span><br><span style="color: hsl(120, 100%, 40%);">+                             break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        default:</span><br><span style="color: hsl(120, 100%, 40%);">+                              break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_websocket_unref(ws);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(res_speech_aeap_test)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       RAII_VAR(struct ast_format_cap *, cap, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+    RAII_VAR(struct ast_speech_result *, results, NULL, ast_speech_results_free);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_speech *speech = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     enum ast_test_result_state res = AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+       char buf[8] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+               info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+             info->explicit_only = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+           info->category = "/res/aeap/speech/";</span><br><span style="color: hsl(120, 100%, 40%);">+            info->summary = "test the speech AEAP interface";</span><br><span style="color: hsl(120, 100%, 40%);">+                info->description = info->summary;</span><br><span style="color: hsl(120, 100%, 40%);">+              return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+      case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_test_validate(test, !ast_websocket_add_protocol("_aeap_test_speech_", speech_test_server_cb));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_test_validate(test, (cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)));</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_test_validate(test, !ast_format_cap_update_by_allow_disallow(cap, "ulaw", 1));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_test_validate_cleanup(test, (speech = ast_speech_new("_aeap_test_speech_", cap)), res, cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_speech_start(speech);</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_test_validate_cleanup(test, !ast_speech_dtmf(speech, "1"), res, cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_test_validate_cleanup(test, !ast_speech_change(speech, "foo", "bar"), res, cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_test_validate_cleanup(test, !ast_speech_change_results_type(</span><br><span style="color: hsl(120, 100%, 40%);">+              speech, AST_SPEECH_RESULTS_TYPE_NBEST), res, cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_test_validate_cleanup(test, !ast_speech_get_setting(</span><br><span style="color: hsl(120, 100%, 40%);">+              speech, "foo", buf, sizeof(buf)), res, cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_test_validate_cleanup(test, !strcmp(buf, "bar"), res, cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_test_validate_cleanup(test, (results = ast_speech_results_get(speech)), res, cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_test_validate_cleanup(test, !strcmp(results->text, TEST_SPEECH_RESULTS_TEXT), res, cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_test_validate_cleanup(test, results->score == TEST_SPEECH_RESULTS_SCORE, res, cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_test_validate_cleanup(test, !strcmp(results->grammar, TEST_SPEECH_RESULTS_GRAMMAR), res, cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_test_validate_cleanup(test, results->nbest_num == TEST_SPEECH_RESULTS_BEST, res, cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+cleanup:</span><br><span style="color: hsl(120, 100%, 40%);">+ if (speech) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_speech_destroy(speech);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_websocket_remove_protocol("_aeap_test_speech_", speech_test_server_cb);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_http_server *http_server;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!(http_server = ast_http_test_server_get("aeap transport http server", NULL))) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return AST_MODULE_LOAD_DECLINE;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   AST_TEST_REGISTER(res_speech_aeap_test);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    return AST_MODULE_LOAD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int unload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       AST_TEST_UNREGISTER(res_speech_aeap_test);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_http_test_server_discard(http_server);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk External Application Protocol Speech test(s)",</span><br><span style="color: hsl(120, 100%, 40%);">+     .support_level = AST_MODULE_SUPPORT_CORE,</span><br><span style="color: hsl(120, 100%, 40%);">+     .load = load_module,</span><br><span style="color: hsl(120, 100%, 40%);">+  .unload = unload_module,</span><br><span style="color: hsl(120, 100%, 40%);">+      .requires = "res_speech_aeap",</span><br><span style="color: hsl(120, 100%, 40%);">+);</span><br><span>diff --git a/tests/test_aeap_transaction.c b/tests/test_aeap_transaction.c</span><br><span>new file mode 100644</span><br><span>index 0000000..f1879ad</span><br><span>--- /dev/null</span><br><span>+++ b/tests/test_aeap_transaction.c</span><br><span>@@ -0,0 +1,179 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+        <depend>TEST_FRAMEWORK</depend></span><br><span style="color: hsl(120, 100%, 40%);">+   <depend>res_aeap</depend></span><br><span style="color: hsl(120, 100%, 40%);">+ <support_level>core</support_level></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <pthread.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/lock.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/test.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_aeap.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_aeap_message.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "../res/res_aeap/general.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "../res/res_aeap/transaction.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define CATEGORY "/res/aeap/transaction/"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define AEAP_TRANSACTION_ID "foo"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void handle_timeout(struct ast_aeap *aeap, struct ast_aeap_message *msg, void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        int *passed = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  ++*passed;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void *end_transaction(void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Delay a second before ending transaction */</span><br><span style="color: hsl(120, 100%, 40%);">+        struct timespec delay = { 1, 0 };</span><br><span style="color: hsl(120, 100%, 40%);">+     int *passed = aeap_transaction_user_obj(data);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      while (nanosleep(&delay, &delay));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  ++*passed;</span><br><span style="color: hsl(120, 100%, 40%);">+    aeap_transaction_end(data, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static enum ast_test_result_state exec(struct ast_test *test,</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_aeap_tsx_params *params)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        pthread_t thread_id = AST_PTHREADT_NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ao2_container *tsxs = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct aeap_transaction *tsx = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  enum ast_test_result_state res = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+       int passed = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     tsxs = aeap_transactions_create();</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!tsxs) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_test_status_update(test, "Failed to create transactions object\n");</span><br><span style="color: hsl(120, 100%, 40%);">+             goto exec_cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   params->wait = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+  params->obj = &passed;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       tsx = aeap_transaction_create_and_add(tsxs, AEAP_TRANSACTION_ID, params, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!tsx) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_test_status_update(test, "Failed to create transaction object\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              goto exec_cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_pthread_create(&thread_id, NULL, end_transaction, ao2_bump(tsx))) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_test_status_update(test, "Failed to create response thread\n");</span><br><span style="color: hsl(120, 100%, 40%);">+         ao2_ref(tsx, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+             goto exec_cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (aeap_transaction_start(tsx)) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_test_status_update(test, "Failed to start transaction request\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              goto exec_cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (passed == 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+            res = AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+exec_cleanup:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (thread_id != AST_PTHREADT_NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+         pthread_cancel(thread_id);</span><br><span style="color: hsl(120, 100%, 40%);">+            pthread_join(thread_id, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   aeap_transaction_end(tsx, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(tsxs);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(transaction_exec)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_aeap_tsx_params params = {</span><br><span style="color: hsl(120, 100%, 40%);">+         .timeout = 5000, /* Give plenty of time for test thread to end */</span><br><span style="color: hsl(120, 100%, 40%);">+     };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+               info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+             info->explicit_only = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+           info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+         info->summary = "test creating a basic AEAP transaction request";</span><br><span style="color: hsl(120, 100%, 40%);">+                info->description = info->summary;</span><br><span style="color: hsl(120, 100%, 40%);">+              return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+      case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return exec(test, &params);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(transaction_exec_timeout)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_aeap_tsx_params params = {</span><br><span style="color: hsl(120, 100%, 40%);">+         .timeout = 100, /* Ensure timeout occurs before test thread ends */</span><br><span style="color: hsl(120, 100%, 40%);">+           .on_timeout = handle_timeout,</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+               info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+             info->explicit_only = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+           info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+         info->summary = "test creating a AEAP transaction request that times out";</span><br><span style="color: hsl(120, 100%, 40%);">+               info->description = info->summary;</span><br><span style="color: hsl(120, 100%, 40%);">+              return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+      case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return exec(test, &params);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_REGISTER(transaction_exec);</span><br><span style="color: hsl(120, 100%, 40%);">+  AST_TEST_REGISTER(transaction_exec_timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return AST_MODULE_LOAD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int unload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       AST_TEST_UNREGISTER(transaction_exec_timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+        AST_TEST_UNREGISTER(transaction_exec);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk External Application Protocol Transaction Tests",</span><br><span style="color: hsl(120, 100%, 40%);">+  .support_level = AST_MODULE_SUPPORT_CORE,</span><br><span style="color: hsl(120, 100%, 40%);">+     .load = load_module,</span><br><span style="color: hsl(120, 100%, 40%);">+  .unload = unload_module,</span><br><span style="color: hsl(120, 100%, 40%);">+      .requires = "res_aeap",</span><br><span style="color: hsl(120, 100%, 40%);">+);</span><br><span>diff --git a/tests/test_aeap_transport.c b/tests/test_aeap_transport.c</span><br><span>new file mode 100644</span><br><span>index 0000000..675ecf2</span><br><span>--- /dev/null</span><br><span>+++ b/tests/test_aeap_transport.c</span><br><span>@@ -0,0 +1,249 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+     <depend>TEST_FRAMEWORK</depend></span><br><span style="color: hsl(120, 100%, 40%);">+   <depend>res_aeap</depend></span><br><span style="color: hsl(120, 100%, 40%);">+ <support_level>core</support_level></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/http.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/test.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "../res/res_aeap/transport.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define CATEGORY "/res/aeap/transport/"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define ADDR "127.0.0.1:8088"</span><br><span style="color: hsl(120, 100%, 40%);">+#define TRANSPORT_URL "ws://" ADDR "/ws"</span><br><span style="color: hsl(120, 100%, 40%);">+#define TRANSPORT_URL_INVALID "ws://" ADDR "/invalid"</span><br><span style="color: hsl(120, 100%, 40%);">+#define TRANSPORT_PROTOCOL "echo"</span><br><span style="color: hsl(120, 100%, 40%);">+#define TRANSPORT_PROTOCOL_INVALID "invalid"</span><br><span style="color: hsl(120, 100%, 40%);">+#define TRANSPORT_TIMEOUT 2000</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(transport_create_invalid)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     RAII_VAR(struct aeap_transport *, transport, NULL, aeap_transport_destroy);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+               info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+             info->explicit_only = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+           info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+         info->summary = "test creating an AEAP invalid transport type";</span><br><span style="color: hsl(120, 100%, 40%);">+          info->description = info->summary;</span><br><span style="color: hsl(120, 100%, 40%);">+              return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+      case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Transport is expected to be NULL here */</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_test_validate(test, !(transport = aeap_transport_create("invalid")));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(transport_create)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      RAII_VAR(struct aeap_transport *, transport, NULL, aeap_transport_destroy);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+               info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+             info->explicit_only = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+           info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+         info->summary = "test creating an AEAP transport";</span><br><span style="color: hsl(120, 100%, 40%);">+               info->description = info->summary;</span><br><span style="color: hsl(120, 100%, 40%);">+              return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+      case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Type is based off the scheme, so just pass in the URL here */</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_test_validate(test, (transport = aeap_transport_create(TRANSPORT_URL)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(transport_connect)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     RAII_VAR(struct aeap_transport *, transport, NULL, aeap_transport_destroy);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+               info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+             info->explicit_only = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+           info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+         info->summary = "test connecting to an AEAP transport";</span><br><span style="color: hsl(120, 100%, 40%);">+          info->description = info->summary;</span><br><span style="color: hsl(120, 100%, 40%);">+              return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+      case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Type is based off the scheme, so just pass in the URL for the type */</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_test_validate(test, (transport = aeap_transport_create_and_connect(</span><br><span style="color: hsl(120, 100%, 40%);">+               TRANSPORT_URL, TRANSPORT_URL, TRANSPORT_PROTOCOL, TRANSPORT_TIMEOUT)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_test_validate(test, aeap_transport_is_connected(transport));</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_test_validate(test, !aeap_transport_disconnect(transport));</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_test_validate(test, !aeap_transport_is_connected(transport));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(transport_connect_fail)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        RAII_VAR(struct aeap_transport *, transport, NULL, aeap_transport_destroy);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+               info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+             info->explicit_only = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+           info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+         info->summary = "test connecting failure for an AEAP transport";</span><br><span style="color: hsl(120, 100%, 40%);">+         info->description = info->summary;</span><br><span style="color: hsl(120, 100%, 40%);">+              return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+      case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Test invalid address */</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_test_validate(test, (transport = aeap_transport_create(TRANSPORT_URL)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_test_validate(test, aeap_transport_connect(transport,</span><br><span style="color: hsl(120, 100%, 40%);">+             TRANSPORT_URL_INVALID, TRANSPORT_PROTOCOL, TRANSPORT_TIMEOUT));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_test_validate(test, !aeap_transport_is_connected(transport));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   aeap_transport_destroy(transport);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Test invalid protocol */</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_test_validate(test, (transport = aeap_transport_create(TRANSPORT_URL)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_test_validate(test, aeap_transport_connect(transport,</span><br><span style="color: hsl(120, 100%, 40%);">+             TRANSPORT_URL, TRANSPORT_PROTOCOL_INVALID, TRANSPORT_TIMEOUT));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_test_validate(test, !aeap_transport_is_connected(transport));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(transport_binary)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      RAII_VAR(struct aeap_transport *, transport, NULL, aeap_transport_destroy);</span><br><span style="color: hsl(120, 100%, 40%);">+   int num = 38;</span><br><span style="color: hsl(120, 100%, 40%);">+ enum AST_AEAP_DATA_TYPE rtype;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+               info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+             info->explicit_only = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+           info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+         info->summary = "test binary I/O from an AEAP transport";</span><br><span style="color: hsl(120, 100%, 40%);">+                info->description = info->summary;</span><br><span style="color: hsl(120, 100%, 40%);">+              return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+      case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_test_validate(test, (transport = aeap_transport_create_and_connect(</span><br><span style="color: hsl(120, 100%, 40%);">+               TRANSPORT_URL, TRANSPORT_URL, TRANSPORT_PROTOCOL, TRANSPORT_TIMEOUT)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_test_validate(test, aeap_transport_write(transport, &num, sizeof(num),</span><br><span style="color: hsl(120, 100%, 40%);">+                AST_AEAP_DATA_TYPE_BINARY) == sizeof(num));</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_test_validate(test, aeap_transport_read(transport, &num,</span><br><span style="color: hsl(120, 100%, 40%);">+              sizeof(num), &rtype) == sizeof(num));</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_test_validate(test, rtype == AST_AEAP_DATA_TYPE_BINARY);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_test_validate(test, num == 38);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(transport_string)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      RAII_VAR(struct aeap_transport *, transport, NULL, aeap_transport_destroy);</span><br><span style="color: hsl(120, 100%, 40%);">+   char buf[16];</span><br><span style="color: hsl(120, 100%, 40%);">+ enum AST_AEAP_DATA_TYPE rtype;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+               info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+             info->explicit_only = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+           info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+         info->summary = "test string I/O from an AEAP transport";</span><br><span style="color: hsl(120, 100%, 40%);">+                info->description = info->summary;</span><br><span style="color: hsl(120, 100%, 40%);">+              return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+      case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_test_validate(test, (transport = aeap_transport_create_and_connect(</span><br><span style="color: hsl(120, 100%, 40%);">+               TRANSPORT_URL, TRANSPORT_URL, TRANSPORT_PROTOCOL, TRANSPORT_TIMEOUT)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_test_validate(test, aeap_transport_write(transport, "foo bar baz", 11,</span><br><span style="color: hsl(120, 100%, 40%);">+          AST_AEAP_DATA_TYPE_STRING) == 11);</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_test_validate(test, aeap_transport_read(transport, buf,</span><br><span style="color: hsl(120, 100%, 40%);">+           sizeof(buf) / sizeof(char), &rtype) == 11);</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_test_validate(test, rtype == AST_AEAP_DATA_TYPE_STRING);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_test_validate(test, !strcmp(buf, "foo bar baz"));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     return AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_http_server *http_server;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!(http_server = ast_http_test_server_get("aeap transport http server", NULL))) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return AST_MODULE_LOAD_DECLINE;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   AST_TEST_REGISTER(transport_string);</span><br><span style="color: hsl(120, 100%, 40%);">+  AST_TEST_REGISTER(transport_binary);</span><br><span style="color: hsl(120, 100%, 40%);">+  AST_TEST_REGISTER(transport_connect_fail);</span><br><span style="color: hsl(120, 100%, 40%);">+    AST_TEST_REGISTER(transport_connect);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_REGISTER(transport_create);</span><br><span style="color: hsl(120, 100%, 40%);">+  AST_TEST_REGISTER(transport_create_invalid);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return AST_MODULE_LOAD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int unload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       AST_TEST_UNREGISTER(transport_string);</span><br><span style="color: hsl(120, 100%, 40%);">+        AST_TEST_UNREGISTER(transport_binary);</span><br><span style="color: hsl(120, 100%, 40%);">+        AST_TEST_UNREGISTER(transport_connect_fail);</span><br><span style="color: hsl(120, 100%, 40%);">+  AST_TEST_UNREGISTER(transport_connect);</span><br><span style="color: hsl(120, 100%, 40%);">+       AST_TEST_UNREGISTER(transport_create);</span><br><span style="color: hsl(120, 100%, 40%);">+        AST_TEST_UNREGISTER(transport_create_invalid);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_http_test_server_discard(http_server);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk External Application Protocol Transport Tests",</span><br><span style="color: hsl(120, 100%, 40%);">+    .support_level = AST_MODULE_SUPPORT_CORE,</span><br><span style="color: hsl(120, 100%, 40%);">+     .load = load_module,</span><br><span style="color: hsl(120, 100%, 40%);">+  .unload = unload_module,</span><br><span style="color: hsl(120, 100%, 40%);">+      .requires = "res_aeap",</span><br><span style="color: hsl(120, 100%, 40%);">+);</span><br><span></span><br></pre><div style="white-space:pre-wrap"></div><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/18429">change 18429</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/18429"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 19 </div>
<div style="display:none"> Gerrit-Change-Id: Iaa4b259f84aa63501e5fd2a6fb107f900b4d4ed2 </div>
<div style="display:none"> Gerrit-Change-Number: 18429 </div>
<div style="display:none"> Gerrit-PatchSet: 2 </div>
<div style="display:none"> Gerrit-Owner: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>