<p>Friendly Automation <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/19533">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
Friendly Automation: 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/+/19533">change 19533</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/+/19533"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 18 </div>
<div style="display:none"> Gerrit-Change-Id: Iebb7ad0d5f88526bc6629d3a1f9f11665434d333 </div>
<div style="display:none"> Gerrit-Change-Number: 19533 </div>
<div style="display:none"> Gerrit-PatchSet: 6 </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-MessageType: merged </div>