<p>Kevin Harwell has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/18429">View Change</a></p><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;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/29/18429/1</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, ¶ms);</span><br><span 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, ¶ms);</span><br><span 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><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: 1 </div>
<div style="display:none"> Gerrit-Owner: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>