<p>Michael Kuron has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/19460">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_pjsip_aoc: New module for sending advice-of-charge with chan_pjsip<br><br>chan_sip supported sending AOC-D and AOC-E information in SIP INFO<br>messages in an "AOC" header in a format that was originally defined by<br>Snom. In the meantime, ETSI TS 124 647 introduced an XML-based AOC<br>format that is supported by devices from multiple vendors, including<br>Snom phones with firmware >= 8.4.2 (released in 2010).<br><br>This commit adds a callback hook for AOC indications to chan_pjsip. It<br>also adds a new res_pjsip_aoc module that inserts AOC information into<br>outgoing messages or sends SIP INFO messages as follows:<br><br>* AOC-S in the 180/183/200 responses to an INVITE request<br>* AOC-S in SIP INFO (if a 200 response has already been sent)<br>* AOC-D in SIP INFO<br>* AOC-E in the 200 response to a BYE request (if the client hangs up)<br>* AOC-E in a BYE request (if Asterisk hangs up)<br><br>The SIP INFO requests are sent out immediately when the AOC indication<br>is received. The others are inserted into an appropriate outgoing<br>message whenever that is ready to be sent. In the latter case, the XML<br>is stored in a channel variable at the time the AOC indication is<br>received. Depending on where the AOC indications are coming from (e.g.<br>PRI or AMI), it may not always be possible to guarantee that the AOC-E<br>is available in time for the BYE.<br><br>Successfully tested AOC-D and both variants of AOC-E with a Snom D735<br>running firmware 10.1.127.10. It does not appear to properly support<br>AOC-S however, so that could only be tested by inspecting SIP traces.<br><br>ASTERISK-21502 #close<br>Reported-by: Matt Jordan <mjordan@digium.com><br><br>Change-Id: Iebb7ad0d5f88526bc6629d3a1f9f11665434d333<br>---<br>M channels/chan_pjsip.c<br>M include/asterisk/res_pjsip.h<br>M res/res_pjsip.c<br>A res/res_pjsip_aoc.c<br>4 files changed, 694 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/60/19460/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c</span><br><span>index 3e8abd2..3c5ea91 100644</span><br><span>--- a/channels/chan_pjsip.c</span><br><span>+++ b/channels/chan_pjsip.c</span><br><span>@@ -1831,6 +1831,9 @@</span><br><span>              break;</span><br><span>       case AST_CONTROL_STREAM_TOPOLOGY_SOURCE_CHANGED:</span><br><span>             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case AST_CONTROL_AOC:</span><br><span style="color: hsl(120, 100%, 40%);">+         res = ast_sip_indicate_aoc(ast, data, datalen);</span><br><span style="color: hsl(120, 100%, 40%);">+               break;</span><br><span>       case -1:</span><br><span>             res = -1;</span><br><span>            break;</span><br><span>diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h</span><br><span>index 743b19f..42c7100 100644</span><br><span>--- a/include/asterisk/res_pjsip.h</span><br><span>+++ b/include/asterisk/res_pjsip.h</span><br><span>@@ -1247,6 +1247,25 @@</span><br><span> };</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief an interchangeable way of sending out advice-of-charge indications</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * An AOC callback takes AOC indications and sends them out immediately in a new SIP request</span><br><span style="color: hsl(120, 100%, 40%);">+ * or stores them for later use in other SIP requests/responses.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_sip_aoc_callback {</span><br><span style="color: hsl(120, 100%, 40%);">+   /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Indicate that an AOC message has been posted for a channel</span><br><span style="color: hsl(120, 100%, 40%);">+   *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param ast An Asterisk channel</span><br><span style="color: hsl(120, 100%, 40%);">+      * \param data Pointer to AOC message, to be decoded with ast_aoc_decode</span><br><span style="color: hsl(120, 100%, 40%);">+       * \param datalen Length of the AOC message</span><br><span style="color: hsl(120, 100%, 40%);">+    * \retval 0 Successfully processed AOC message</span><br><span style="color: hsl(120, 100%, 40%);">+        * \retval -1 Failed to process AOC message</span><br><span style="color: hsl(120, 100%, 40%);">+    */</span><br><span style="color: hsl(120, 100%, 40%);">+   int (*indicate)(struct ast_channel *ast, const void *data, size_t datalen);</span><br><span 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>  * \brief An entity responsible for identifying the source of a SIP message</span><br><span>  */</span><br><span> struct ast_sip_endpoint_identifier {</span><br><span>@@ -1458,6 +1477,30 @@</span><br><span> pjsip_endpoint *ast_sip_get_pjsip_endpoint(void);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Register an AOC callback element.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 21</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param element What we are registering.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sip_register_aoc_callback(struct ast_sip_aoc_callback *element);</span><br><span 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 Unregister an AOC callback element.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 21</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param element What we are unregistering.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_sip_unregister_aoc_callback(struct ast_sip_aoc_callback *element);</span><br><span 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 Use a registered AOC callback to indicate that an AOC message has been received for a channel</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 21</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param element What we are registering.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sip_indicate_aoc(struct ast_channel *ast, const void *data, size_t datalen);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span>  * \brief Get a pointer to the SIP sorcery structure.</span><br><span>  *</span><br><span>  * \retval NULL sorcery has not been initialized</span><br><span>diff --git a/res/res_pjsip.c b/res/res_pjsip.c</span><br><span>index 0ad5ec2..98295d2 100644</span><br><span>--- a/res/res_pjsip.c</span><br><span>+++ b/res/res_pjsip.c</span><br><span>@@ -516,6 +516,40 @@</span><br><span>   return ast_pjsip_endpoint;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_sip_aoc_callback *registered_aoc_callback;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sip_register_aoc_callback(struct ast_sip_aoc_callback *element)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       if (registered_aoc_callback) {</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_log(LOG_WARNING, "AOC callback %p is already registered. Cannot register a new one\n", registered_aoc_callback);</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%);">+     registered_aoc_callback = element;</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_debug(1, "Registered SIP AOC callback %p\n", element);</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 ast_sip_unregister_aoc_callback(struct ast_sip_aoc_callback *element)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (registered_aoc_callback != element) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_log(LOG_WARNING, "Trying to unregister AOC callback %p but AOC callback %p registered\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                               element, registered_aoc_callback);</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%);">+     registered_aoc_callback = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_debug(1, "Unregistered SIP AOC callback %p\n", element);</span><br><span 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_sip_indicate_aoc(struct ast_channel *ast, const void *data, size_t datalen)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!registered_aoc_callback) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_WARNING, "No AOC callback registered. Ignoring AOC indicationl\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%);">+     return registered_aoc_callback->indicate(ast, data, datalen);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int ast_sip_will_uri_survive_restart(pjsip_sip_uri *uri, struct ast_sip_endpoint *endpoint,</span><br><span>         pjsip_rx_data *rdata)</span><br><span> {</span><br><span>diff --git a/res/res_pjsip_aoc.c b/res/res_pjsip_aoc.c</span><br><span>new file mode 100644</span><br><span>index 0000000..341e0ab</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_pjsip_aoc.c</span><br><span>@@ -0,0 +1,574 @@</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) 2022, Michael Kuron</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Michael Kuron <m.kuron@gmx.de></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>pjproject</depend></span><br><span style="color: hsl(120, 100%, 40%);">+        <depend>res_pjsip</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 <pjsip.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <pjlib.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/aoc.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/pbx.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_pjsip.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_pjsip_session.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static pj_xml_attr *aoc_xml_create_attr(pj_pool_t *pool, pj_xml_node *node,</span><br><span style="color: hsl(120, 100%, 40%);">+      const char *name, const char *value)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       pj_xml_attr *attr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  attr = PJ_POOL_ALLOC_T(pool, pj_xml_attr);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  pj_strdup2(pool, &attr->name, name);</span><br><span style="color: hsl(120, 100%, 40%);">+   pj_strdup2(pool, &attr->value, value);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       pj_xml_add_attr(node, attr);</span><br><span style="color: hsl(120, 100%, 40%);">+  return attr;</span><br><span 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 pj_xml_node *aoc_xml_create_node(pj_pool_t *pool, pj_xml_node *parent,</span><br><span style="color: hsl(120, 100%, 40%);">+      const char *name)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  pj_xml_node *node;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  node = PJ_POOL_ALLOC_T(pool, pj_xml_node);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  pj_list_init(&node->attr_head);</span><br><span style="color: hsl(120, 100%, 40%);">+        pj_list_init(&node->node_head);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      pj_strdup2(pool, &node->name, name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ node->content.ptr = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  node->content.slen = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (parent) {</span><br><span style="color: hsl(120, 100%, 40%);">+         pj_xml_add_node(parent, node);</span><br><span 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 node;</span><br><span 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 aoc_xml_set_node_content(pj_pool_t *pool, pj_xml_node *node,</span><br><span style="color: hsl(120, 100%, 40%);">+   const char *content)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       pj_strdup2(pool, &node->content, content);</span><br><span 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 char * aoc_format_amount(pj_pool_t *pool, unsigned int amount,</span><br><span style="color: hsl(120, 100%, 40%);">+         enum ast_aoc_currency_multiplier multiplier)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       const size_t amount_max_size = 16;</span><br><span style="color: hsl(120, 100%, 40%);">+    char *amount_str;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   amount_str = pj_pool_alloc(pool, amount_max_size);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  switch (multiplier) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case AST_AOC_MULT_ONETHOUSANDTH:</span><br><span style="color: hsl(120, 100%, 40%);">+              pj_ansi_snprintf(amount_str, amount_max_size, "%.3f", amount*0.001f);</span><br><span style="color: hsl(120, 100%, 40%);">+               break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case AST_AOC_MULT_ONEHUNDREDTH:</span><br><span style="color: hsl(120, 100%, 40%);">+               pj_ansi_snprintf(amount_str, amount_max_size, "%.2f", amount*0.01f);</span><br><span style="color: hsl(120, 100%, 40%);">+                break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case AST_AOC_MULT_ONETENTH:</span><br><span style="color: hsl(120, 100%, 40%);">+           pj_ansi_snprintf(amount_str, amount_max_size, "%.1f", amount*0.1f);</span><br><span style="color: hsl(120, 100%, 40%);">+         break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case AST_AOC_MULT_ONE:</span><br><span style="color: hsl(120, 100%, 40%);">+                pj_ansi_snprintf(amount_str, amount_max_size, "%d", amount);</span><br><span style="color: hsl(120, 100%, 40%);">+                break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case AST_AOC_MULT_TEN:</span><br><span style="color: hsl(120, 100%, 40%);">+                pj_ansi_snprintf(amount_str, amount_max_size, "%d", amount*10);</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case AST_AOC_MULT_HUNDRED:</span><br><span style="color: hsl(120, 100%, 40%);">+            pj_ansi_snprintf(amount_str, amount_max_size, "%d", amount*100);</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case AST_AOC_MULT_THOUSAND:</span><br><span style="color: hsl(120, 100%, 40%);">+           pj_ansi_snprintf(amount_str, amount_max_size, "%d", amount*1000);</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%);">+              pj_ansi_snprintf(amount_str, amount_max_size, "%d", amount);</span><br><span 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 amount_str;</span><br><span 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 *aoc_time_scale_str(enum ast_aoc_time_scale value)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      const char *str;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    switch (value) {</span><br><span style="color: hsl(120, 100%, 40%);">+      default:</span><br><span style="color: hsl(120, 100%, 40%);">+      case AST_AOC_TIME_SCALE_HUNDREDTH_SECOND:</span><br><span style="color: hsl(120, 100%, 40%);">+             str = "one-hundredth-second";</span><br><span style="color: hsl(120, 100%, 40%);">+               break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case AST_AOC_TIME_SCALE_TENTH_SECOND:</span><br><span style="color: hsl(120, 100%, 40%);">+         str = "one-tenth-second";</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case AST_AOC_TIME_SCALE_SECOND:</span><br><span style="color: hsl(120, 100%, 40%);">+               str = "one-second";</span><br><span style="color: hsl(120, 100%, 40%);">+         break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case AST_AOC_TIME_SCALE_TEN_SECOND:</span><br><span style="color: hsl(120, 100%, 40%);">+           str = "ten-seconds";</span><br><span style="color: hsl(120, 100%, 40%);">+                break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case AST_AOC_TIME_SCALE_MINUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+               str = "one-minute";</span><br><span style="color: hsl(120, 100%, 40%);">+         break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case AST_AOC_TIME_SCALE_HOUR:</span><br><span style="color: hsl(120, 100%, 40%);">+         str = "one-hour";</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case AST_AOC_TIME_SCALE_DAY:</span><br><span style="color: hsl(120, 100%, 40%);">+          str = "twenty-four-hours";</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%);">+     return str;</span><br><span 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 aoc_indicate(struct ast_channel *ast, const void *data, size_t datalen)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ast_aoc_decoded *decoded;</span><br><span style="color: hsl(120, 100%, 40%);">+      pj_pool_t *pool;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    decoded = ast_aoc_decode((struct ast_aoc_encoded *) data, datalen, ast);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!decoded) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "Error decoding indicated AOC data\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%);">+   pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "AOC",</span><br><span style="color: hsl(120, 100%, 40%);">+                 1024, 1024);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (ast_aoc_get_msg_type(decoded) == AST_AOC_D ||</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_aoc_get_msg_type(decoded) == AST_AOC_E) {</span><br><span style="color: hsl(120, 100%, 40%);">+         struct ast_sip_channel_pvt *channel;</span><br><span style="color: hsl(120, 100%, 40%);">+          struct ast_sip_session *session;</span><br><span style="color: hsl(120, 100%, 40%);">+              pj_xml_node *aoc;</span><br><span style="color: hsl(120, 100%, 40%);">+             pj_xml_node *aoc_type;</span><br><span style="color: hsl(120, 100%, 40%);">+                pj_xml_node *charging_info;</span><br><span style="color: hsl(120, 100%, 40%);">+           pj_xml_node *charges;</span><br><span style="color: hsl(120, 100%, 40%);">+         pj_xml_node *charge;</span><br><span style="color: hsl(120, 100%, 40%);">+          char *xml;</span><br><span style="color: hsl(120, 100%, 40%);">+            size_t size;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                channel = ast_channel_tech_pvt(ast);</span><br><span style="color: hsl(120, 100%, 40%);">+          session = channel->session;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              aoc = aoc_xml_create_node(pool, NULL, "aoc");</span><br><span style="color: hsl(120, 100%, 40%);">+               aoc_xml_create_attr(pool, aoc, "xmlns",</span><br><span style="color: hsl(120, 100%, 40%);">+                             "http://uri.etsi.org/ngn/params/xml/simservs/aoc");</span><br><span style="color: hsl(120, 100%, 40%);">+         aoc_type = aoc_xml_create_node(pool, aoc,</span><br><span style="color: hsl(120, 100%, 40%);">+                             ast_aoc_get_msg_type(decoded) == AST_AOC_D ? "aoc-d" : "aoc-e");</span><br><span style="color: hsl(120, 100%, 40%);">+          charging_info = aoc_xml_create_node(pool, aoc_type, "charging-info");</span><br><span style="color: hsl(120, 100%, 40%);">+               aoc_xml_set_node_content(pool, charging_info,</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_aoc_get_msg_type(decoded) == AST_AOC_D ? "subtotal" : "total");</span><br><span style="color: hsl(120, 100%, 40%);">+               charges = aoc_xml_create_node(pool, aoc_type, "recorded-charges");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                if (ast_aoc_get_charge_type(decoded) == AST_AOC_CHARGE_FREE) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        charge = aoc_xml_create_node(pool, charges, "free-charge");</span><br><span style="color: hsl(120, 100%, 40%);">+         } else if (ast_aoc_get_charge_type(decoded) == AST_AOC_CHARGE_CURRENCY ||</span><br><span style="color: hsl(120, 100%, 40%);">+                             ast_aoc_get_charge_type(decoded) == AST_AOC_CHARGE_UNIT) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    charge = aoc_xml_create_node(pool, charges, "recorded-currency-units");</span><br><span style="color: hsl(120, 100%, 40%);">+             } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      charge = aoc_xml_create_node(pool, charges, "not-available");</span><br><span 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_aoc_get_charge_type(decoded) == AST_AOC_CHARGE_CURRENCY) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    const char *currency;</span><br><span style="color: hsl(120, 100%, 40%);">+                 pj_xml_node *amount;</span><br><span style="color: hsl(120, 100%, 40%);">+                  char *amount_str;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                   currency = ast_aoc_get_currency_name(decoded);</span><br><span style="color: hsl(120, 100%, 40%);">+                        if (!ast_strlen_zero(currency)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             pj_xml_node *currency_id;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                           currency_id = aoc_xml_create_node(pool, charge, "currency-id");</span><br><span style="color: hsl(120, 100%, 40%);">+                             aoc_xml_set_node_content(pool, currency_id, currency);</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                   amount = aoc_xml_create_node(pool, charge, "currency-amount");</span><br><span style="color: hsl(120, 100%, 40%);">+                      amount_str = aoc_format_amount(pool, ast_aoc_get_currency_amount(decoded),</span><br><span style="color: hsl(120, 100%, 40%);">+                                    ast_aoc_get_currency_multiplier(decoded));</span><br><span style="color: hsl(120, 100%, 40%);">+                    aoc_xml_set_node_content(pool, amount, amount_str);</span><br><span style="color: hsl(120, 100%, 40%);">+           } else if (ast_aoc_get_charge_type(decoded) == AST_AOC_CHARGE_UNIT) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 pj_xml_node *currency_id;</span><br><span style="color: hsl(120, 100%, 40%);">+                     const struct ast_aoc_unit_entry *unit_entry;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                        currency_id = aoc_xml_create_node(pool, charge, "currency-id");</span><br><span style="color: hsl(120, 100%, 40%);">+                     aoc_xml_set_node_content(pool, currency_id, "UNIT");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                      unit_entry = ast_aoc_get_unit_info(decoded, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+                       if (unit_entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             pj_xml_node *amount;</span><br><span style="color: hsl(120, 100%, 40%);">+                          char *amount_str;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                           amount = aoc_xml_create_node(pool, charge, "currency-amount");</span><br><span style="color: hsl(120, 100%, 40%);">+                              amount_str = aoc_format_amount(pool, unit_entry->amount,</span><br><span style="color: hsl(120, 100%, 40%);">+                                           AST_AOC_MULT_ONE);</span><br><span style="color: hsl(120, 100%, 40%);">+                            aoc_xml_set_node_content(pool, amount, amount_str);</span><br><span 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%);">+           const size_t xml_max_size = 512;</span><br><span style="color: hsl(120, 100%, 40%);">+              xml = pj_pool_alloc(pool, xml_max_size);</span><br><span style="color: hsl(120, 100%, 40%);">+              size = pj_xml_print(aoc, xml, xml_max_size - 1, PJ_TRUE);</span><br><span style="color: hsl(120, 100%, 40%);">+             if (size >= xml_max_size) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_log(LOG_WARNING, "aoc+xml body text too large\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%);">+             xml[size] = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              if (ast_aoc_get_msg_type(decoded) == AST_AOC_D) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     struct pjsip_tx_data *tdata;</span><br><span style="color: hsl(120, 100%, 40%);">+                  struct ast_sip_body body = {</span><br><span style="color: hsl(120, 100%, 40%);">+                          .type = "application",</span><br><span style="color: hsl(120, 100%, 40%);">+                              .subtype = "vnd.etsi.aoc+xml",</span><br><span style="color: hsl(120, 100%, 40%);">+                              .body_text = xml</span><br><span 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_sip_create_request("INFO", session->inv_session->dlg,</span><br><span style="color: hsl(120, 100%, 40%);">+                                 session->endpoint, NULL, NULL, &tdata)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_log(LOG_ERROR, "Could not create AOC INFO request\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%);">+                     if (ast_sip_add_body(tdata, &body)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             ast_log(LOG_ERROR, "Could not add body to AOC INFO request\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                             pjsip_tx_data_dec_ref(tdata);</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_sip_session_send_request(session, tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+         } else if (ast_aoc_get_msg_type(decoded) == AST_AOC_E) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      pbx_builtin_setvar_helper(ast, "AOCE", xml);</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+     } else if (ast_aoc_get_msg_type(decoded) == AST_AOC_S) {</span><br><span style="color: hsl(120, 100%, 40%);">+              struct ast_sip_channel_pvt *channel;</span><br><span style="color: hsl(120, 100%, 40%);">+          struct ast_sip_session *session;</span><br><span style="color: hsl(120, 100%, 40%);">+              pj_xml_node *aoc;</span><br><span style="color: hsl(120, 100%, 40%);">+             pj_xml_node *aoc_type;</span><br><span style="color: hsl(120, 100%, 40%);">+                pj_xml_node *charged_items;</span><br><span style="color: hsl(120, 100%, 40%);">+           const struct ast_aoc_s_entry *entry;</span><br><span style="color: hsl(120, 100%, 40%);">+          int idx;</span><br><span style="color: hsl(120, 100%, 40%);">+              char *xml;</span><br><span style="color: hsl(120, 100%, 40%);">+            size_t size;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                channel = ast_channel_tech_pvt(ast);</span><br><span style="color: hsl(120, 100%, 40%);">+          session = channel->session;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              aoc = aoc_xml_create_node(pool, NULL, "aoc");</span><br><span style="color: hsl(120, 100%, 40%);">+               aoc_xml_create_attr(pool, aoc, "xmlns",</span><br><span style="color: hsl(120, 100%, 40%);">+                             "http://uri.etsi.org/ngn/params/xml/simservs/aoc");</span><br><span style="color: hsl(120, 100%, 40%);">+         aoc_type = aoc_xml_create_node(pool, aoc, "aoc-s");</span><br><span style="color: hsl(120, 100%, 40%);">+         charged_items = aoc_xml_create_node(pool, aoc_type, "charged-items");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             for (idx = 0; idx < ast_aoc_s_get_count(decoded); idx++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 pj_xml_node *charged_item;</span><br><span style="color: hsl(120, 100%, 40%);">+                    pj_xml_node *charge;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                        if (!(entry = ast_aoc_s_get_rate_info(decoded, idx))) {</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 (entry->charged_item == AST_AOC_CHARGED_ITEM_BASIC_COMMUNICATION) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             charged_item = aoc_xml_create_node(pool, charged_items, "basic");</span><br><span style="color: hsl(120, 100%, 40%);">+                   } else if (entry->charged_item == AST_AOC_CHARGED_ITEM_CALL_ATTEMPT) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             charged_item = aoc_xml_create_node(pool, charged_items,</span><br><span style="color: hsl(120, 100%, 40%);">+                                               "communication-attempt");</span><br><span style="color: hsl(120, 100%, 40%);">+                   } else if (entry->charged_item == AST_AOC_CHARGED_ITEM_CALL_SETUP) {</span><br><span style="color: hsl(120, 100%, 40%);">+                               charged_item = aoc_xml_create_node(pool, charged_items,</span><br><span style="color: hsl(120, 100%, 40%);">+                                               "communication-setup");</span><br><span style="color: hsl(120, 100%, 40%);">+                     } else {</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 (entry->rate_type == AST_AOC_RATE_TYPE_FREE) {</span><br><span style="color: hsl(120, 100%, 40%);">+                          charge = aoc_xml_create_node(pool, charged_item, "free-charge");</span><br><span style="color: hsl(120, 100%, 40%);">+                    } else if (entry->rate_type == AST_AOC_RATE_TYPE_FLAT) {</span><br><span style="color: hsl(120, 100%, 40%);">+                           charge = aoc_xml_create_node(pool, charged_item, "flat-rate");</span><br><span style="color: hsl(120, 100%, 40%);">+                      } else if (entry->rate_type == AST_AOC_RATE_TYPE_DURATION &&</span><br><span style="color: hsl(120, 100%, 40%);">+                                       entry->charged_item == AST_AOC_CHARGED_ITEM_BASIC_COMMUNICATION) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         charge = aoc_xml_create_node(pool, charged_item, "price-time");</span><br><span style="color: hsl(120, 100%, 40%);">+                     } else {</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 (entry->rate_type == AST_AOC_RATE_TYPE_DURATION ||</span><br><span style="color: hsl(120, 100%, 40%);">+                                      entry->rate_type == AST_AOC_RATE_TYPE_FLAT) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              const char *currency;</span><br><span style="color: hsl(120, 100%, 40%);">+                         pj_xml_node *amount;</span><br><span style="color: hsl(120, 100%, 40%);">+                          uint32_t amount_val;</span><br><span style="color: hsl(120, 100%, 40%);">+                          enum ast_aoc_currency_multiplier multiplier_val;</span><br><span style="color: hsl(120, 100%, 40%);">+                              char *amount_str;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                           currency = (AST_AOC_RATE_TYPE_DURATION ?</span><br><span style="color: hsl(120, 100%, 40%);">+                                              entry->rate.duration.currency_name :</span><br><span style="color: hsl(120, 100%, 40%);">+                                               entry->rate.flat.currency_name);</span><br><span style="color: hsl(120, 100%, 40%);">+                           if (!ast_strlen_zero(currency)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                     pj_xml_node *currency_id;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                                   currency_id = aoc_xml_create_node(pool, charge, "currency-id");</span><br><span style="color: hsl(120, 100%, 40%);">+                                     aoc_xml_set_node_content(pool, currency_id, currency);</span><br><span style="color: hsl(120, 100%, 40%);">+                                }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                           amount = aoc_xml_create_node(pool, charge, "currency-amount");</span><br><span style="color: hsl(120, 100%, 40%);">+                              amount_val = (AST_AOC_RATE_TYPE_DURATION ? entry->rate.duration.amount :</span><br><span style="color: hsl(120, 100%, 40%);">+                                           entry->rate.flat.amount);</span><br><span style="color: hsl(120, 100%, 40%);">+                          multiplier_val = (AST_AOC_RATE_TYPE_DURATION ?</span><br><span style="color: hsl(120, 100%, 40%);">+                                                entry->rate.duration.multiplier : entry->rate.flat.multiplier);</span><br><span style="color: hsl(120, 100%, 40%);">+                         amount_str = aoc_format_amount(pool, amount_val, multiplier_val);</span><br><span style="color: hsl(120, 100%, 40%);">+                             aoc_xml_set_node_content(pool, amount, amount_str);</span><br><span 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 (entry->rate_type == AST_AOC_RATE_TYPE_DURATION) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              pj_xml_node *length_time_unit;</span><br><span style="color: hsl(120, 100%, 40%);">+                                pj_xml_node *time_unit;</span><br><span style="color: hsl(120, 100%, 40%);">+                               char *time_str;</span><br><span style="color: hsl(120, 100%, 40%);">+                               pj_xml_node *scale;</span><br><span style="color: hsl(120, 100%, 40%);">+                           pj_xml_node *charging_type;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                         length_time_unit = aoc_xml_create_node(pool, charge, "length-time-unit");</span><br><span style="color: hsl(120, 100%, 40%);">+                           time_unit = aoc_xml_create_node(pool, length_time_unit, "time-unit");</span><br><span style="color: hsl(120, 100%, 40%);">+                               time_str = aoc_format_amount(pool, entry->rate.duration.time,</span><br><span style="color: hsl(120, 100%, 40%);">+                                              AST_AOC_MULT_ONE);</span><br><span style="color: hsl(120, 100%, 40%);">+                            aoc_xml_set_node_content(pool, time_unit, time_str);</span><br><span style="color: hsl(120, 100%, 40%);">+                          scale = aoc_xml_create_node(pool, length_time_unit, "scale");</span><br><span style="color: hsl(120, 100%, 40%);">+                               aoc_xml_set_node_content(pool, scale,</span><br><span style="color: hsl(120, 100%, 40%);">+                                         aoc_time_scale_str(entry->rate.duration.time_scale));</span><br><span style="color: hsl(120, 100%, 40%);">+                              charging_type = aoc_xml_create_node(pool, charge, "charging-type");</span><br><span style="color: hsl(120, 100%, 40%);">+                         aoc_xml_set_node_content(pool, charging_type,</span><br><span style="color: hsl(120, 100%, 40%);">+                                         entry->rate.duration.charging_type ? "step-function" :</span><br><span style="color: hsl(120, 100%, 40%);">+                                           "continuous");</span><br><span 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%);">+           const size_t xml_max_size = 1024;</span><br><span style="color: hsl(120, 100%, 40%);">+             xml = pj_pool_alloc(pool, xml_max_size);</span><br><span style="color: hsl(120, 100%, 40%);">+              size = pj_xml_print(aoc, xml, xml_max_size - 1, PJ_TRUE);</span><br><span style="color: hsl(120, 100%, 40%);">+             if (size >= xml_max_size) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_log(LOG_WARNING, "aoc+xml body text too large\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%);">+             xml[size] = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              if (ast_channel_state(ast) == AST_STATE_UP) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 struct pjsip_tx_data *tdata;</span><br><span style="color: hsl(120, 100%, 40%);">+                  struct ast_sip_body body = {</span><br><span style="color: hsl(120, 100%, 40%);">+                          .type = "application",</span><br><span style="color: hsl(120, 100%, 40%);">+                              .subtype = "vnd.etsi.aoc+xml",</span><br><span style="color: hsl(120, 100%, 40%);">+                              .body_text = xml</span><br><span 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_sip_create_request("INFO", session->inv_session->dlg,</span><br><span style="color: hsl(120, 100%, 40%);">+                                 session->endpoint, NULL, NULL, &tdata)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_log(LOG_ERROR, "Could not create AOC INFO request\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%);">+                     if (ast_sip_add_body(tdata, &body)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             ast_log(LOG_ERROR, "Could not add body to AOC INFO request\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                             pjsip_tx_data_dec_ref(tdata);</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_sip_session_send_request(session, tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+         } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      pbx_builtin_setvar_helper(ast, "AOCS", xml);</span><br><span 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%);">+   pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_aoc_destroy_decoded(decoded);</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 struct ast_sip_aoc_callback aoc_callback = {</span><br><span style="color: hsl(120, 100%, 40%);">+   .indicate = aoc_indicate,</span><br><span 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 aoc_bye_outgoing_response(struct ast_sip_session *session,</span><br><span style="color: hsl(120, 100%, 40%);">+               struct pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       const char *xml;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    xml = pbx_builtin_getvar_helper(session->channel, "AOCE");</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!xml) {</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%);">+   struct ast_sip_body body = {</span><br><span style="color: hsl(120, 100%, 40%);">+          .type = "application",</span><br><span style="color: hsl(120, 100%, 40%);">+              .subtype = "vnd.etsi.aoc+xml",</span><br><span style="color: hsl(120, 100%, 40%);">+              .body_text = xml</span><br><span 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_sip_add_body(tdata, &body)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_log(LOG_ERROR, "Could not add body to AOC INFO request\n");</span><br><span 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 aoc_bye_outgoing_request(struct ast_sip_session *session,</span><br><span style="color: hsl(120, 100%, 40%);">+         struct pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       const char *xml;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    xml = pbx_builtin_getvar_helper(session->channel, "AOCE");</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!xml) {</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%);">+   struct ast_sip_body body = {</span><br><span style="color: hsl(120, 100%, 40%);">+          .type = "application",</span><br><span style="color: hsl(120, 100%, 40%);">+              .subtype = "vnd.etsi.aoc+xml",</span><br><span style="color: hsl(120, 100%, 40%);">+              .body_text = xml</span><br><span 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_sip_add_body(tdata, &body)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_log(LOG_ERROR, "Could not add body to AOC INFO request\n");</span><br><span 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 aoc_invite_outgoing_response(struct ast_sip_session *session,</span><br><span style="color: hsl(120, 100%, 40%);">+             struct pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       const char *xml;</span><br><span style="color: hsl(120, 100%, 40%);">+      pjsip_sdp_info *tdata_sdp_info;</span><br><span style="color: hsl(120, 100%, 40%);">+       pjsip_msg_body *multipart_body;</span><br><span style="color: hsl(120, 100%, 40%);">+       pjsip_multipart_part *part;</span><br><span style="color: hsl(120, 100%, 40%);">+   pj_str_t body_text;</span><br><span style="color: hsl(120, 100%, 40%);">+   pj_str_t type;</span><br><span style="color: hsl(120, 100%, 40%);">+        pj_str_t subtype;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if(tdata->msg->line.status.code != 180 && tdata->msg->line.status.code != 183 &&</span><br><span style="color: hsl(120, 100%, 40%);">+                  tdata->msg->line.status.code != 200) {</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%);">+   xml = pbx_builtin_getvar_helper(session->channel, "AOCS");</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!xml) {</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%);">+   tdata_sdp_info = pjsip_tdata_get_sdp_info(tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (tdata_sdp_info->sdp) {</span><br><span style="color: hsl(120, 100%, 40%);">+         pj_status_t rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             rc = pjsip_create_multipart_sdp_body(tdata->pool, tdata_sdp_info->sdp,</span><br><span style="color: hsl(120, 100%, 40%);">+                          &multipart_body);</span><br><span style="color: hsl(120, 100%, 40%);">+         if (rc != PJ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       ast_log(LOG_ERROR, "Unable to create sdp multipart body\n");</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%);">+     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              multipart_body = pjsip_multipart_create(tdata->pool,</span><br><span style="color: hsl(120, 100%, 40%);">+                               &pjsip_media_type_multipart_mixed, 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%);">+   part = pjsip_multipart_create_part(tdata->pool);</span><br><span style="color: hsl(120, 100%, 40%);">+   pj_strdup2(tdata->pool, &body_text, xml);</span><br><span style="color: hsl(120, 100%, 40%);">+      pj_cstr(&type, "application");</span><br><span style="color: hsl(120, 100%, 40%);">+  pj_cstr(&subtype, "vnd.etsi.aoc+xml");</span><br><span style="color: hsl(120, 100%, 40%);">+  part->body = pjsip_msg_body_create(tdata->pool, &type, &subtype, &body_text);</span><br><span style="color: hsl(120, 100%, 40%);">+       pjsip_multipart_add_part(tdata->pool, multipart_body, part);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     tdata->msg->body = multipart_body;</span><br><span 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 pj_status_t aoc_outgoing_response(pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      pjsip_cseq_hdr *cseq;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, tdata->msg->hdr.next);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (cseq->method.id == PJSIP_BYE_METHOD) {</span><br><span style="color: hsl(120, 100%, 40%);">+         struct ast_channel_iterator *chan_iter;</span><br><span style="color: hsl(120, 100%, 40%);">+               struct ast_channel *chan;</span><br><span style="color: hsl(120, 100%, 40%);">+             pjsip_cid_hdr *cid;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         cid = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CALL_ID, tdata->msg->hdr.next);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               chan_iter = ast_channel_iterator_by_name_new("PJSIP", 5);</span><br><span style="color: hsl(120, 100%, 40%);">+           for (; (chan = ast_channel_iterator_next(chan_iter)); ast_channel_unref(chan)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      const char *pvtid;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                  ast_channel_lock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+                       pvtid = ast_channel_tech(chan)->get_pvt_uniqueid(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (pj_strcmp2(&cid->id, pvtid) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                struct ast_sip_channel_pvt *channel;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                                channel = ast_channel_tech_pvt(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+                         aoc_bye_outgoing_response(channel->session, tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+                                ast_channel_unlock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+                             ast_channel_unref(chan);</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%);">+                     ast_channel_unlock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_channel_iterator_destroy(chan_iter);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+     return PJ_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 struct ast_sip_session_supplement aoc_bye_supplement = {</span><br><span style="color: hsl(120, 100%, 40%);">+      .method = "BYE",</span><br><span style="color: hsl(120, 100%, 40%);">+    .priority = AST_SIP_SUPPLEMENT_PRIORITY_LAST,</span><br><span style="color: hsl(120, 100%, 40%);">+ .outgoing_request = aoc_bye_outgoing_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%);">+static struct ast_sip_session_supplement aoc_invite_supplement = {</span><br><span style="color: hsl(120, 100%, 40%);">+       .method = "INVITE",</span><br><span style="color: hsl(120, 100%, 40%);">+ .priority = AST_SIP_SUPPLEMENT_PRIORITY_LAST,</span><br><span style="color: hsl(120, 100%, 40%);">+ .outgoing_response = aoc_invite_outgoing_response,</span><br><span 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 pjsip_module aoc_module = {</span><br><span style="color: hsl(120, 100%, 40%);">+  .name = { "AOC", 3 },</span><br><span style="color: hsl(120, 100%, 40%);">+       .id = -1,</span><br><span style="color: hsl(120, 100%, 40%);">+     .priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 2,</span><br><span style="color: hsl(120, 100%, 40%);">+ .on_tx_response = aoc_outgoing_response,</span><br><span 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%);">+       if (ast_sip_register_aoc_callback(&aoc_callback)) {</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%);">+     if (ast_sip_register_service(&aoc_module)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_sip_unregister_aoc_callback(&aoc_callback);</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_ERROR, "Could not register AOC module\n");</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%);">+     ast_sip_session_register_supplement(&aoc_bye_supplement);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_register_supplement(&aoc_invite_supplement);</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_sip_unregister_aoc_callback(&aoc_callback);</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_sip_unregister_service(&aoc_module);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_sip_session_unregister_supplement(&aoc_bye_supplement);</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_sip_session_unregister_supplement(&aoc_invite_supplement);</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_LOAD_ORDER, "PJSIP AOC Support",</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_pjsip",</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/+/19460">change 19460</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/+/19460"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Iebb7ad0d5f88526bc6629d3a1f9f11665434d333 </div>
<div style="display:none"> Gerrit-Change-Number: 19460 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Michael Kuron <m.kuron@gmx.de> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>