<p>George Joseph <strong>submitted</strong> this change.</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;"><span></span><br></pre><div style="white-space:pre-wrap">Approvals:
  Joshua Colp: Looks good to me, but someone else must approve
  George Joseph: Looks good to me, approved; Approved for Submit

</div><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 new res_pjsip_aoc module that inserts AOC information<br>into outgoing messages or sends SIP INFO messages as described below.<br>It also fixes a small issue in res_pjsip_session which didn't always<br>call session supplements on outgoing_response.<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 or if the<br>  INVITE was sent by Asterisk)<br>* AOC-D in SIP INFO<br>* AOC-D in the 200 response to a BYE request (if the client hangs up)<br>* AOC-D in a BYE request (if Asterisk hangs up)<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 specification defines one more, AOC-S in an INVITE request, which<br>is not implemented here because it is not currently possible in<br>Asterisk to have AOC data ready at this point in call setup. Once<br>specifying AOC-S via the dialplan or passing it through from another<br>SIP channel's INVITE is possible, that might be added.<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 configs/samples/pjsip.conf.sample<br>A contrib/ast-db-manage/config/versions/5a2247c957d2_add_aoc_option.py<br>A doc/CHANGES-staging/res_pjsip_aoc.txt<br>M include/asterisk/res_pjsip.h<br>M res/res_pjsip/pjsip_config.xml<br>M res/res_pjsip/pjsip_configuration.c<br>A res/res_pjsip_aoc.c<br>M res/res_pjsip_session.c<br>8 files changed, 822 insertions(+), 1 deletion(-)<br><br></pre>
<pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample</span><br><span>index b6a9cd0..0a93cbe 100644</span><br><span>--- a/configs/samples/pjsip.conf.sample</span><br><span>+++ b/configs/samples/pjsip.conf.sample</span><br><span>@@ -964,6 +964,11 @@</span><br><span>                 ; by the channel driver from the dialplan before they're forwarded</span><br><span>                 ; the remote endpoint.</span><br><span> ;</span><br><span style="color: hsl(120, 100%, 40%);">+; send_aoc =</span><br><span style="color: hsl(120, 100%, 40%);">+                ; This options turns on and off support for sending AOC to endpoints.</span><br><span style="color: hsl(120, 100%, 40%);">+                ; AOC updates can be sent using the AOCMessage AMI action or come</span><br><span style="color: hsl(120, 100%, 40%);">+                ; from PRI channels.</span><br><span style="color: hsl(120, 100%, 40%);">+                ; (default: no)</span><br><span> </span><br><span> </span><br><span> ;==========================AUTH SECTION OPTIONS=========================</span><br><span>diff --git a/contrib/ast-db-manage/config/versions/5a2247c957d2_add_aoc_option.py b/contrib/ast-db-manage/config/versions/5a2247c957d2_add_aoc_option.py</span><br><span>new file mode 100644</span><br><span>index 0000000..a603e7f</span><br><span>--- /dev/null</span><br><span>+++ b/contrib/ast-db-manage/config/versions/5a2247c957d2_add_aoc_option.py</span><br><span>@@ -0,0 +1,38 @@</span><br><span style="color: hsl(120, 100%, 40%);">+"""add aoc option</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Revision ID: 5a2247c957d2</span><br><span style="color: hsl(120, 100%, 40%);">+Revises: ccf795ee535f</span><br><span style="color: hsl(120, 100%, 40%);">+Create Date: 2022-10-30 12:47:56.173511</span><br><span 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%);">+# revision identifiers, used by Alembic.</span><br><span style="color: hsl(120, 100%, 40%);">+revision = '5a2247c957d2'</span><br><span style="color: hsl(120, 100%, 40%);">+down_revision = 'ccf795ee535f'</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from alembic import op</span><br><span style="color: hsl(120, 100%, 40%);">+import sqlalchemy as sa</span><br><span style="color: hsl(120, 100%, 40%);">+from sqlalchemy.dialects.postgresql import ENUM</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_BOOL_NAME = 'ast_bool_values'</span><br><span style="color: hsl(120, 100%, 40%);">+# We'll just ignore the n/y and f/t abbreviations as Asterisk does not write</span><br><span style="color: hsl(120, 100%, 40%);">+# those aliases.</span><br><span style="color: hsl(120, 100%, 40%);">+AST_BOOL_VALUES = [ '0', '1',</span><br><span style="color: hsl(120, 100%, 40%);">+                    'off', 'on',</span><br><span style="color: hsl(120, 100%, 40%);">+                    'false', 'true',</span><br><span style="color: hsl(120, 100%, 40%);">+                    'no', 'yes' ]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def upgrade():</span><br><span style="color: hsl(120, 100%, 40%);">+    ############################# Enums ##############################</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    # ast_bool_values has already been created, so use postgres enum object</span><br><span style="color: hsl(120, 100%, 40%);">+    # type to get around "already created" issue - works okay with mysql</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_bool_values = ENUM(*AST_BOOL_VALUES, name=AST_BOOL_NAME, create_type=False)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    op.add_column('ps_endpoints', sa.Column('send_aoc', ast_bool_values))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def downgrade():</span><br><span style="color: hsl(120, 100%, 40%);">+    if op.get_context().bind.dialect.name == 'mssql':</span><br><span style="color: hsl(120, 100%, 40%);">+        op.drop_constraint('ck_ps_endpoints_send_aoc_ast_bool_values', 'ps_endpoints')</span><br><span style="color: hsl(120, 100%, 40%);">+    op.drop_column('ps_endpoints', 'send_aoc')</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span>diff --git a/doc/CHANGES-staging/res_pjsip_aoc.txt b/doc/CHANGES-staging/res_pjsip_aoc.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..496bd0b</span><br><span>--- /dev/null</span><br><span>+++ b/doc/CHANGES-staging/res_pjsip_aoc.txt</span><br><span>@@ -0,0 +1,4 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: res_pjsip_aoc</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Added res_pjsip_aoc which gives chan_pjsip the ability to send Advice-of-Charge messages.</span><br><span style="color: hsl(120, 100%, 40%);">+A new endpoint option, send_aoc, controls this.</span><br><span>diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h</span><br><span>index f31cd10..35c7339 100644</span><br><span>--- a/include/asterisk/res_pjsip.h</span><br><span>+++ b/include/asterisk/res_pjsip.h</span><br><span>@@ -1067,6 +1067,8 @@</span><br><span>        AST_STRING_FIELD_EXTENDED(geoloc_outgoing_call_profile);</span><br><span>     /*! 100rel mode to use with this endpoint */</span><br><span>         enum ast_sip_100rel_mode rel100;</span><br><span style="color: hsl(120, 100%, 40%);">+      /*! Send Advice-of-Charge messages */</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int send_aoc;</span><br><span> };</span><br><span> </span><br><span> /*! URI parameter for symmetric transport */</span><br><span>diff --git a/res/res_pjsip/pjsip_config.xml b/res/res_pjsip/pjsip_config.xml</span><br><span>index ba8ba91..99ca645 100644</span><br><span>--- a/res/res_pjsip/pjsip_config.xml</span><br><span>+++ b/res/res_pjsip/pjsip_config.xml</span><br><span>@@ -1506,6 +1506,9 @@</span><br><span>                                                </para></span><br><span>                                        </description></span><br><span>                                 </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="send_aoc" default="no"></span><br><span style="color: hsl(120, 100%, 40%);">+                                 <synopsis>Send Advice-of-Charge messages</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                               </configOption></span><br><span>                        </configObject></span><br><span>                        <configObject name="auth"></span><br><span>                           <synopsis>Authentication type</synopsis></span><br><span>diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c</span><br><span>index e63018e..bb015d0 100644</span><br><span>--- a/res/res_pjsip/pjsip_configuration.c</span><br><span>+++ b/res/res_pjsip/pjsip_configuration.c</span><br><span>@@ -2263,6 +2263,7 @@</span><br><span>        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "geoloc_outgoing_call_profile", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, geoloc_outgoing_call_profile));</span><br><span>        ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "security_mechanisms", "", security_mechanism_handler, NULL, NULL, 0, 0);</span><br><span>    ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "security_negotiation", "no", security_negotiation_handler, NULL, NULL, 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_aoc", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, send_aoc));</span><br><span> </span><br><span>    if (ast_sip_initialize_sorcery_transport()) {</span><br><span>                ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");</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..dba4e26</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_pjsip_aoc.c</span><br><span>@@ -0,0 +1,698 @@</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>extended</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_ZALLOC_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%);">+ 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 void aoc_datastore_destroy(void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     char *xml = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_free(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%);">+static const struct ast_datastore_info aoc_s_datastore = {</span><br><span style="color: hsl(120, 100%, 40%);">+       .type = "AOC-S",</span><br><span style="color: hsl(120, 100%, 40%);">+    .destroy = aoc_datastore_destroy,</span><br><span 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_datastore_info aoc_d_datastore = {</span><br><span style="color: hsl(120, 100%, 40%);">+   .type = "AOC-D",</span><br><span style="color: hsl(120, 100%, 40%);">+    .destroy = aoc_datastore_destroy,</span><br><span 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_datastore_info aoc_e_datastore = {</span><br><span style="color: hsl(120, 100%, 40%);">+   .type = "AOC-E",</span><br><span style="color: hsl(120, 100%, 40%);">+    .destroy = aoc_datastore_destroy,</span><br><span 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 aoc_data {</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_sip_session *session;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_aoc_decoded *decoded;</span><br><span style="color: hsl(120, 100%, 40%);">+      enum ast_channel_state channel_state;</span><br><span 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_release_pool(void * data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_pool_t *pool = data;</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%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int aoc_send_as_xml(void * data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        RAII_VAR(struct aoc_data *, adata, data, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+        RAII_VAR(pj_pool_t *, pool, NULL, aoc_release_pool);</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", 2048, 512);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!pool) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_ERROR, "Could not create a memory pool for AOC XML\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%);">+   if (ast_aoc_get_msg_type(adata->decoded) == AST_AOC_D ||</span><br><span style="color: hsl(120, 100%, 40%);">+                   ast_aoc_get_msg_type(adata->decoded) == AST_AOC_E) {</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 = NULL;</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%);">+          const size_t xml_max_size = 512;</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(adata->decoded) == AST_AOC_D ? "aoc-d" : "aoc-e");</span><br><span style="color: hsl(120, 100%, 40%);">+                if (ast_aoc_get_msg_type(adata->decoded) == AST_AOC_D) {</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_total_type(adata->decoded) == AST_AOC_SUBTOTAL ? "subtotal" : "total");</span><br><span style="color: hsl(120, 100%, 40%);">+            }</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(adata->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(adata->decoded) == AST_AOC_CHARGE_CURRENCY ||</span><br><span style="color: hsl(120, 100%, 40%);">+                           ast_aoc_get_charge_type(adata->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(adata->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(adata->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(adata->decoded),</span><br><span style="color: hsl(120, 100%, 40%);">+                                  ast_aoc_get_currency_multiplier(adata->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(adata->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(adata->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%);">+           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_ERROR, "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(adata->decoded) == AST_AOC_D) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   RAII_VAR(struct ast_datastore *, datastore,</span><br><span style="color: hsl(120, 100%, 40%);">+                                   ast_sip_session_get_datastore(adata->session, aoc_d_datastore.type),</span><br><span style="color: hsl(120, 100%, 40%);">+                                       ao2_cleanup);</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", adata->session->inv_session->dlg,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       adata->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(adata->session, tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (!datastore) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             datastore = ast_sip_session_alloc_datastore(&aoc_d_datastore, aoc_d_datastore.type);</span><br><span style="color: hsl(120, 100%, 40%);">+                              if (!datastore) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                     ast_log(LOG_ERROR, "Unable to create datastore for AOC-D.\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%);">+                             datastore->data = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+                            if (ast_sip_session_add_datastore(adata->session, datastore)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                    ast_log(LOG_ERROR, "Unable to create datastore for AOC-D.\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%);">+                     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_free(datastore->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%);">+                   aoc_xml_set_node_content(pool, charging_info, "total");</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%);">+                     xml[size] = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                        datastore->data = ast_strdup(xml);</span><br><span style="color: hsl(120, 100%, 40%);">+         } else if (ast_aoc_get_msg_type(adata->decoded) == AST_AOC_E) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    RAII_VAR(struct ast_datastore *, datastore,</span><br><span style="color: hsl(120, 100%, 40%);">+                                   ast_sip_session_get_datastore(adata->session, aoc_e_datastore.type),</span><br><span style="color: hsl(120, 100%, 40%);">+                                       ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (!datastore) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             datastore = ast_sip_session_alloc_datastore(&aoc_e_datastore, aoc_e_datastore.type);</span><br><span style="color: hsl(120, 100%, 40%);">+                              if (!datastore) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                     ast_log(LOG_ERROR, "Unable to create datastore for AOC-E.\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%);">+                             datastore->data = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+                            if (ast_sip_session_add_datastore(adata->session, datastore)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                    ast_log(LOG_ERROR, "Unable to create datastore for AOC-E.\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%);">+                     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_free(datastore->data);</span><br><span style="color: hsl(120, 100%, 40%);">+                 }</span><br><span style="color: hsl(120, 100%, 40%);">+                     datastore->data = ast_strdup(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(adata->decoded) == AST_AOC_S) {</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%);">+          const size_t xml_max_size = 1024;</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(adata->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(adata->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 = (entry->rate_type == 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 = (entry->rate_type == AST_AOC_RATE_TYPE_DURATION ?</span><br><span style="color: hsl(120, 100%, 40%);">+                                             entry->rate.duration.amount : entry->rate.flat.amount);</span><br><span style="color: hsl(120, 100%, 40%);">+                         multiplier_val = (entry->rate_type == 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%);">+           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_ERROR, "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 (adata->channel_state == AST_STATE_UP ||</span><br><span style="color: hsl(120, 100%, 40%);">+                                adata->session->call_direction == AST_SIP_SESSION_OUTGOING_CALL) {</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", adata->session->inv_session->dlg,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       adata->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(adata->session, tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+               } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      RAII_VAR(struct ast_datastore *, datastore,</span><br><span style="color: hsl(120, 100%, 40%);">+                                   ast_sip_session_get_datastore(adata->session, aoc_s_datastore.type),</span><br><span style="color: hsl(120, 100%, 40%);">+                                       ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (!datastore) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             datastore = ast_sip_session_alloc_datastore(&aoc_s_datastore, aoc_s_datastore.type);</span><br><span style="color: hsl(120, 100%, 40%);">+                              if (!datastore) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                     ast_log(LOG_ERROR, "Unable to create datastore for AOC-S.\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_session_add_datastore(adata->session, datastore)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                    ast_log(LOG_ERROR, "Unable to create datastore for AOC-S.\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%);">+                     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_free(datastore->data);</span><br><span style="color: hsl(120, 100%, 40%);">+                 }</span><br><span style="color: hsl(120, 100%, 40%);">+                     datastore->data = ast_strdup(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%);">+   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 aoc_data_destroy(void * data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct aoc_data *adata = data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_aoc_destroy_decoded(adata->decoded);</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_cleanup(adata->session);</span><br><span 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_frame *aoc_framehook(struct ast_channel *ast, struct ast_frame *f,</span><br><span style="color: hsl(120, 100%, 40%);">+            enum ast_framehook_event event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</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 aoc_data *adata;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!f || f->frametype != AST_FRAME_CONTROL || event != AST_FRAMEHOOK_EVENT_WRITE ||</span><br><span style="color: hsl(120, 100%, 40%);">+                       f->subclass.integer != AST_CONTROL_AOC) {</span><br><span style="color: hsl(120, 100%, 40%);">+          return f;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   adata = ao2_alloc(sizeof(struct aoc_data), aoc_data_destroy);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!adata) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_ERROR, "Failed to allocate AOC data\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                return f;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   adata->decoded = ast_aoc_decode((struct ast_aoc_encoded *) f->data.ptr, f->datalen, ast);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!adata->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%);">+          ao2_ref(adata, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+           return f;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</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%);">+  adata->session = ao2_bump(channel->session);</span><br><span style="color: hsl(120, 100%, 40%);">+    adata->channel_state = ast_channel_state(ast);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_sip_push_task(adata->session->serializer, aoc_send_as_xml, adata)) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_log(LOG_ERROR, "Unable to send AOC XML for channel %s\n", ast_channel_name(ast));</span><br><span style="color: hsl(120, 100%, 40%);">+               ao2_ref(adata, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     return &ast_null_frame;</span><br><span 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_consume(void *data, enum ast_frame_type type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     return (type == AST_FRAME_CONTROL) ? 1 : 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 aoc_attach_framehook(struct ast_sip_session *session)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        int framehook_id;</span><br><span style="color: hsl(120, 100%, 40%);">+     static struct ast_framehook_interface hook = {</span><br><span style="color: hsl(120, 100%, 40%);">+                .version = AST_FRAMEHOOK_INTERFACE_VERSION,</span><br><span style="color: hsl(120, 100%, 40%);">+           .event_cb = aoc_framehook,</span><br><span style="color: hsl(120, 100%, 40%);">+            .consume_cb = aoc_consume,</span><br><span 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 (!session->channel || !session->endpoint->send_aoc) {</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_channel_lock(session->channel);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      framehook_id = ast_framehook_attach(session->channel, &hook);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (framehook_id < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_log(LOG_WARNING, "Could not attach AOC Frame hook, AOC will be unavailable on '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                  ast_channel_name(session->channel));</span><br><span 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_channel_unlock(session->channel);</span><br><span 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_incoming_invite_request(struct ast_sip_session *session,</span><br><span style="color: hsl(120, 100%, 40%);">+                struct pjsip_rx_data *rdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       aoc_attach_framehook(session);</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 aoc_outgoing_invite_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%);">+       aoc_attach_framehook(session);</span><br><span 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%);">+       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%);">+      };</span><br><span style="color: hsl(120, 100%, 40%);">+    RAII_VAR(struct ast_datastore *, datastore_d, ast_sip_session_get_datastore(session,</span><br><span style="color: hsl(120, 100%, 40%);">+                  aoc_d_datastore.type), ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+  RAII_VAR(struct ast_datastore *, datastore_e, ast_sip_session_get_datastore(session,</span><br><span style="color: hsl(120, 100%, 40%);">+                  aoc_e_datastore.type), ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (datastore_e) {</span><br><span style="color: hsl(120, 100%, 40%);">+            body.body_text = datastore_e->data;</span><br><span style="color: hsl(120, 100%, 40%);">+        } else if (datastore_d) {</span><br><span style="color: hsl(120, 100%, 40%);">+             body.body_text = datastore_d->data;</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%);">+                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_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%);">+       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%);">+      };</span><br><span style="color: hsl(120, 100%, 40%);">+    RAII_VAR(struct ast_datastore *, datastore_d, ast_sip_session_get_datastore(session,</span><br><span style="color: hsl(120, 100%, 40%);">+                  aoc_d_datastore.type), ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+  RAII_VAR(struct ast_datastore *, datastore_e, ast_sip_session_get_datastore(session,</span><br><span style="color: hsl(120, 100%, 40%);">+                  aoc_e_datastore.type), ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (datastore_e) {</span><br><span style="color: hsl(120, 100%, 40%);">+            body.body_text = datastore_e->data;</span><br><span style="color: hsl(120, 100%, 40%);">+        } else if (datastore_d) {</span><br><span style="color: hsl(120, 100%, 40%);">+             body.body_text = datastore_d->data;</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%);">+                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_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%);">+       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%);">+     RAII_VAR(struct ast_datastore *, datastore, ast_sip_session_get_datastore(session,</span><br><span style="color: hsl(120, 100%, 40%);">+                    aoc_s_datastore.type), ao2_cleanup);</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%);">+   if (!datastore) {</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 (pjsip_media_type_cmp(&tdata->msg->body->content_type,</span><br><span style="color: hsl(120, 100%, 40%);">+                        &pjsip_media_type_multipart_mixed, 0) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             multipart_body = tdata->msg->body;</span><br><span style="color: hsl(120, 100%, 40%);">+      } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              pjsip_sdp_info *tdata_sdp_info;</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%);">+</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, datastore->data);</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 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%);">+ .outgoing_response = aoc_bye_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 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%);">+ .incoming_request = aoc_incoming_invite_request,</span><br><span style="color: hsl(120, 100%, 40%);">+      .outgoing_request = aoc_outgoing_invite_request,</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 int load_module(void)</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_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_EXTENDED,</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>diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c</span><br><span>index 67dcdf5..652b735 100644</span><br><span>--- a/res/res_pjsip_session.c</span><br><span>+++ b/res/res_pjsip_session.c</span><br><span>@@ -2568,13 +2568,20 @@</span><br><span> </span><br><span> void ast_sip_session_send_response(struct ast_sip_session *session, pjsip_tx_data *tdata)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- handle_outgoing_response(session, tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+     pjsip_dialog *dlg = pjsip_tdata_get_dlg(tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+       RAII_VAR(struct ast_sip_session *, dlg_session, dlg ? ast_sip_dialog_get_session(dlg) : NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!dlg_session) {</span><br><span style="color: hsl(120, 100%, 40%);">+           /* If the dialog has a session, handle_outgoing_response will be called</span><br><span style="color: hsl(120, 100%, 40%);">+                  from session_on_tx_response. If it does not, call it from here. */</span><br><span style="color: hsl(120, 100%, 40%);">+         handle_outgoing_response(session, tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span>    pjsip_inv_send_msg(session->inv_session, tdata);</span><br><span>  return;</span><br><span> }</span><br><span> </span><br><span> static pj_bool_t session_on_rx_request(pjsip_rx_data *rdata);</span><br><span> static pj_bool_t session_on_rx_response(pjsip_rx_data *rdata);</span><br><span style="color: hsl(120, 100%, 40%);">+static pj_status_t session_on_tx_response(pjsip_tx_data *tdata);</span><br><span> static void session_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e);</span><br><span> </span><br><span> static pjsip_module session_module = {</span><br><span>@@ -2583,6 +2590,7 @@</span><br><span>     .on_rx_request = session_on_rx_request,</span><br><span>      .on_rx_response = session_on_rx_response,</span><br><span>    .on_tsx_state = session_on_tsx_state,</span><br><span style="color: hsl(120, 100%, 40%);">+ .on_tx_response = session_on_tx_response,</span><br><span> };</span><br><span> </span><br><span> /*! \brief Determine whether the SDP provided requires deferral of negotiating or not</span><br><span>@@ -4266,6 +4274,18 @@</span><br><span>                handled == PJ_TRUE ? "yes" : "no");</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static pj_bool_t session_on_tx_response(pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  pjsip_dialog *dlg = pjsip_tdata_get_dlg(tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+       RAII_VAR(struct ast_sip_session *, session, dlg ? ast_sip_dialog_get_session(dlg) : NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (session) {</span><br><span style="color: hsl(120, 100%, 40%);">+                handle_outgoing_response(session, tdata);</span><br><span 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 PJ_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void resend_reinvite(pj_timer_heap_t *timer, pj_timer_entry *entry)</span><br><span> {</span><br><span>   struct ast_sip_session *session = entry->user_data;</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: 16 </div>
<div style="display:none"> Gerrit-Owner: Michael Kuron <m.kuron@gmx.de> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@sangoma.com> </div>
<div style="display:none"> Gerrit-Reviewer: N A <asterisk@phreaknet.org> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>