<p>George Joseph has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/18784">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Geolocation:  chan_pjsip Capability Preview<br><br>This commit adds res_pjsip_geolocation which gives chan_pjsip<br>the ability to use the core geolocation capabilities.<br><br>This commit message is intentionally short because this isn't<br>a simple capability.  See the documentation at<br>https://wiki.asterisk.org/wiki/display/AST/Geolocation<br>for more information.<br><br>THE CAPABILITIES IMPLEMENTED HERE MAY CHANGE BASED ON<br>USER FEEDBACK!<br><br>ASTERISK-30128<br><br>Change-Id: Ie2e2bcd87243c2cfabc43eb823d4427c7086f4d9<br>---<br>M configs/samples/pjsip.conf.sample<br>A contrib/ast-db-manage/config/versions/7197536bb68d_geoloc_endpoint_params.py<br>A doc/CHANGES-staging/res_pjsip_geolocation.txt<br>M include/asterisk/res_pjsip.h<br>M res/res_pjsip.c<br>M res/res_pjsip/pjsip_config.xml<br>M res/res_pjsip/pjsip_configuration.c<br>A res/res_pjsip_geolocation.c<br>8 files changed, 843 insertions(+), 1 deletion(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/84/18784/1</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 9d73843..a74b50f 100644</span><br><span>--- a/configs/samples/pjsip.conf.sample</span><br><span>+++ b/configs/samples/pjsip.conf.sample</span><br><span>@@ -955,6 +955,17 @@</span><br><span>                            ; responses.</span><br><span>                            ; (default: no)</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+;geoloc_incoming_call_profile =</span><br><span style="color: hsl(120, 100%, 40%);">+                ; This geolocation profile will be applied to all calls received</span><br><span style="color: hsl(120, 100%, 40%);">+                ; by the channel driver from the remote endpoint before they're</span><br><span style="color: hsl(120, 100%, 40%);">+                ; forwarded to the dialplan.</span><br><span style="color: hsl(120, 100%, 40%);">+;geoloc_outgoing_call_profile =</span><br><span style="color: hsl(120, 100%, 40%);">+                ; This geolocation profile will be applied to all calls received</span><br><span style="color: hsl(120, 100%, 40%);">+                ; by the channel driver from the dialplan before they're forwarded</span><br><span style="color: hsl(120, 100%, 40%);">+                ; the remote endpoint.</span><br><span 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> ;==========================AUTH SECTION OPTIONS=========================</span><br><span> ;[auth]</span><br><span> ;  SYNOPSIS: Authentication type</span><br><span>diff --git a/contrib/ast-db-manage/config/versions/7197536bb68d_geoloc_endpoint_params.py b/contrib/ast-db-manage/config/versions/7197536bb68d_geoloc_endpoint_params.py</span><br><span>new file mode 100644</span><br><span>index 0000000..5159ff4</span><br><span>--- /dev/null</span><br><span>+++ b/contrib/ast-db-manage/config/versions/7197536bb68d_geoloc_endpoint_params.py</span><br><span>@@ -0,0 +1,22 @@</span><br><span style="color: hsl(120, 100%, 40%);">+"""Geoloc Endpoint Params</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Revision ID: 7197536bb68d</span><br><span style="color: hsl(120, 100%, 40%);">+Revises: 58e440314c2a</span><br><span style="color: hsl(120, 100%, 40%);">+Create Date: 2022-03-07 05:32:54.909429</span><br><span 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 = '7197536bb68d'</span><br><span style="color: hsl(120, 100%, 40%);">+down_revision = '58e440314c2a'</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def upgrade():</span><br><span style="color: hsl(120, 100%, 40%);">+    op.add_column('ps_endpoints', sa.Column('geoloc_incoming_call_profile', sa.String(80)))</span><br><span style="color: hsl(120, 100%, 40%);">+    op.add_column('ps_endpoints', sa.Column('geoloc_outgoing_call_profile', sa.String(80)))</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%);">+    op.drop_column('ps_endpoints', 'geoloc_outgoing_call_profile')</span><br><span style="color: hsl(120, 100%, 40%);">+    op.drop_column('ps_endpoints', 'geoloc_incoming_call_profile')</span><br><span>diff --git a/doc/CHANGES-staging/res_pjsip_geolocation.txt b/doc/CHANGES-staging/res_pjsip_geolocation.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..acc4906</span><br><span>--- /dev/null</span><br><span>+++ b/doc/CHANGES-staging/res_pjsip_geolocation.txt</span><br><span>@@ -0,0 +1,4 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: res_pjsip_geolocation</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Added res_pjsip_geolocation which gives chan_pjsip</span><br><span style="color: hsl(120, 100%, 40%);">+the ability to use the core geolocation capabilities.</span><br><span>diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h</span><br><span>index b5b5a72..b3717e7 100644</span><br><span>--- a/include/asterisk/res_pjsip.h</span><br><span>+++ b/include/asterisk/res_pjsip.h</span><br><span>@@ -63,6 +63,9 @@</span><br><span> #define PJSIP_EXPIRES_NOT_SPECIFIED      ((pj_uint32_t)-1)</span><br><span> #endif</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#define PJSTR_PRINTF_SPEC "%.*s"</span><br><span style="color: hsl(120, 100%, 40%);">+#define PJSTR_PRINTF_VAR(_v) ((int)(_v).slen), ((_v).ptr)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Response codes from RFC8224 */</span><br><span> #define AST_STIR_SHAKEN_RESPONSE_CODE_STALE_DATE 403</span><br><span> #define AST_STIR_SHAKEN_RESPONSE_CODE_USE_IDENTITY_HEADER 428</span><br><span>@@ -957,6 +960,10 @@</span><br><span>        unsigned int stir_shaken;</span><br><span>    /*! Should we authenticate OPTIONS requests per RFC 3261? */</span><br><span>         unsigned int allow_unauthenticated_options;</span><br><span style="color: hsl(120, 100%, 40%);">+   /*! The name of the geoloc profile to apply when Asterisk receives a call from this endpoint */</span><br><span style="color: hsl(120, 100%, 40%);">+       AST_STRING_FIELD_EXTENDED(geoloc_incoming_call_profile);</span><br><span style="color: hsl(120, 100%, 40%);">+      /*! The name of the geoloc profile to apply when Asterisk sends a call to this endpoint */</span><br><span style="color: hsl(120, 100%, 40%);">+    AST_STRING_FIELD_EXTENDED(geoloc_outgoing_call_profile);</span><br><span> };</span><br><span> </span><br><span> /*! URI parameter for symmetric transport */</span><br><span>@@ -2458,6 +2465,17 @@</span><br><span> int ast_sip_add_header(pjsip_tx_data *tdata, const char *name, const char *value);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Add a header to an outbound SIP message, returning a pointer to the header</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param tdata The message to add the header to</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name The header name</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param value The header value</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return The pjsip_generic_string_hdr * added.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+pjsip_generic_string_hdr *ast_sip_add_header2(pjsip_tx_data *tdata,</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%);">+/*!</span><br><span>  * \brief Add a body to an outbound SIP message</span><br><span>  *</span><br><span>  * If this is called multiple times, the latest body will replace the current</span><br><span>diff --git a/res/res_pjsip.c b/res/res_pjsip.c</span><br><span>index 7822645..d722bbe 100644</span><br><span>--- a/res/res_pjsip.c</span><br><span>+++ b/res/res_pjsip.c</span><br><span>@@ -53,6 +53,7 @@</span><br><span>  <depend>res_sorcery_memory</depend></span><br><span>      <depend>res_sorcery_astdb</depend></span><br><span>       <use type="module">res_statsd</use></span><br><span style="color: hsl(120, 100%, 40%);">+     <use type="module">res_geolocation</use></span><br><span>       <support_level>core</support_level></span><br><span>  ***/</span><br><span> </span><br><span>@@ -1870,6 +1871,22 @@</span><br><span>        return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+pjsip_generic_string_hdr *ast_sip_add_header2(pjsip_tx_data *tdata,</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_str_t hdr_name;</span><br><span style="color: hsl(120, 100%, 40%);">+    pj_str_t hdr_value;</span><br><span style="color: hsl(120, 100%, 40%);">+   pjsip_generic_string_hdr *hdr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      pj_cstr(&hdr_name, name);</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_cstr(&hdr_value, value);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     hdr = pjsip_generic_string_hdr_create(tdata->pool, &hdr_name, &hdr_value);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) hdr);</span><br><span style="color: hsl(120, 100%, 40%);">+  return hdr;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static pjsip_msg_body *ast_body_to_pjsip_body(pj_pool_t *pool, const struct ast_sip_body *body)</span><br><span> {</span><br><span>     pj_str_t type;</span><br><span>@@ -2810,5 +2827,5 @@</span><br><span>       .reload = reload_module,</span><br><span>     .load_pri = AST_MODPRI_CHANNEL_DEPEND - 5,</span><br><span>   .requires = "dnsmgr,res_pjproject,res_sorcery_config,res_sorcery_memory,res_sorcery_astdb",</span><br><span style="color: hsl(0, 100%, 40%);">-   .optional_modules = "res_statsd",</span><br><span style="color: hsl(120, 100%, 40%);">+   .optional_modules = "res_geolocation,res_statsd",</span><br><span> );</span><br><span>diff --git a/res/res_pjsip/pjsip_config.xml b/res/res_pjsip/pjsip_config.xml</span><br><span>index e6fca2e..63fd1a9 100644</span><br><span>--- a/res/res_pjsip/pjsip_config.xml</span><br><span>+++ b/res/res_pjsip/pjsip_config.xml</span><br><span>@@ -1448,6 +1448,24 @@</span><br><span>                                              responses.</para></span><br><span>                                      </description></span><br><span>                                 </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="geoloc_incoming_call_profile" default=""></span><br><span style="color: hsl(120, 100%, 40%);">+                                       <synopsis>Geolocation profile to apply to incoming calls</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                                       <description><para></span><br><span style="color: hsl(120, 100%, 40%);">+                                               This geolocation profile will be applied to all calls received</span><br><span style="color: hsl(120, 100%, 40%);">+                                                by the channel driver from the remote endpoint before they're</span><br><span style="color: hsl(120, 100%, 40%);">+                                             forwarded to the dialplan.</span><br><span style="color: hsl(120, 100%, 40%);">+                                            </para></span><br><span style="color: hsl(120, 100%, 40%);">+                                 </description></span><br><span style="color: hsl(120, 100%, 40%);">+                          </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="geoloc_outgoing_call_profile" default=""></span><br><span style="color: hsl(120, 100%, 40%);">+                                       <synopsis>Geolocation profile to apply to outgoing calls</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                                       <description><para></span><br><span style="color: hsl(120, 100%, 40%);">+                                               This geolocation profile will be applied to all calls received</span><br><span style="color: hsl(120, 100%, 40%);">+                                                by the channel driver from the dialplan before they're forwarded</span><br><span style="color: hsl(120, 100%, 40%);">+                                          the remote endpoint.</span><br><span style="color: hsl(120, 100%, 40%);">+                                          </para></span><br><span style="color: hsl(120, 100%, 40%);">+                                 </description></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 ace68d2..5f1f61e 100644</span><br><span>--- a/res/res_pjsip/pjsip_configuration.c</span><br><span>+++ b/res/res_pjsip/pjsip_configuration.c</span><br><span>@@ -21,6 +21,7 @@</span><br><span> #include <pjsip.h></span><br><span> #include <pjsip_ua.h></span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_geolocation.h"</span><br><span> #include "asterisk/res_pjsip.h"</span><br><span> #include "include/res_pjsip_private.h"</span><br><span> #include "asterisk/res_pjsip_cli.h"</span><br><span>@@ -1539,6 +1540,36 @@</span><br><span>                 }</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(endpoint->geoloc_incoming_call_profile) ||</span><br><span style="color: hsl(120, 100%, 40%);">+            !ast_strlen_zero(endpoint->geoloc_outgoing_call_profile)) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              if (!ast_geoloc_is_loaded()) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_log(LOG_ERROR, "A geoloc incoming and/or outgoing call_profile was specified on endpoint '%s'"</span><br><span style="color: hsl(120, 100%, 40%);">+                          " but res_geolocation is not loaded.\n", ast_sorcery_object_get_id(endpoint));</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_strlen_zero(endpoint->geoloc_incoming_call_profile)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    struct ast_geoloc_profile *profile = ast_geoloc_get_profile(endpoint->geoloc_incoming_call_profile);</span><br><span style="color: hsl(120, 100%, 40%);">+                       if (!profile) {</span><br><span style="color: hsl(120, 100%, 40%);">+                               ast_log(LOG_ERROR, "geoloc_incoming_call_profile '%s' on endpoint '%s' doesn't exist\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                        endpoint->geoloc_incoming_call_profile, ast_sorcery_object_get_id(endpoint));</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%);">+                     ao2_cleanup(profile);</span><br><span 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_strlen_zero(endpoint->geoloc_outgoing_call_profile)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    struct ast_geoloc_profile *profile = ast_geoloc_get_profile(endpoint->geoloc_outgoing_call_profile);</span><br><span style="color: hsl(120, 100%, 40%);">+                       if (!profile) {</span><br><span style="color: hsl(120, 100%, 40%);">+                               ast_log(LOG_ERROR, "geoloc_outgoing_call_profile '%s' on endpoint '%s' doesn't exist\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                        endpoint->geoloc_outgoing_call_profile, ast_sorcery_object_get_id(endpoint));</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%);">+                     ao2_cleanup(profile);</span><br><span 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>  return 0;</span><br><span> }</span><br><span> </span><br><span>@@ -2194,6 +2225,8 @@</span><br><span>   ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "stir_shaken", "off", stir_shaken_handler, stir_shaken_to_str, NULL, 0, 0);</span><br><span>  ast_sorcery_object_field_register(sip_sorcery, "endpoint", "stir_shaken_profile", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, stir_shaken_profile));</span><br><span>  ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow_unauthenticated_options", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, allow_unauthenticated_options));</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "geoloc_incoming_call_profile", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, geoloc_incoming_call_profile));</span><br><span style="color: hsl(120, 100%, 40%);">+ 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> </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>@@ -2354,6 +2387,12 @@</span><br><span>            return NULL;</span><br><span>         }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_string_field_init_extended(endpoint, geoloc_incoming_call_profile) ||</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_string_field_init_extended(endpoint, geoloc_outgoing_call_profile)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ao2_cleanup(endpoint);</span><br><span style="color: hsl(120, 100%, 40%);">+                return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  if (!(endpoint->media.codecs = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {</span><br><span>              ao2_cleanup(endpoint);</span><br><span>               return NULL;</span><br><span>diff --git a/res/res_pjsip_geolocation.c b/res/res_pjsip_geolocation.c</span><br><span>new file mode 100644</span><br><span>index 0000000..c4ba003</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_pjsip_geolocation.c</span><br><span>@@ -0,0 +1,713 @@</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, Sangoma Technologies Corporation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * George Joseph <gjoseph@sangoma.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+       <depend>res_geolocation</depend></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%);">+        <depend>res_pjsip_session</depend></span><br><span style="color: hsl(120, 100%, 40%);">+        <depend>chan_pjsip</depend></span><br><span style="color: hsl(120, 100%, 40%);">+       <depend>libxml2</depend></span><br><span style="color: hsl(120, 100%, 40%);">+  <support_level>core</support_level></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/xml.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_geolocation.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <pjsip_ua.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_str_t GEOLOCATION_HDR;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int find_pidf(const char *session_name, struct pjsip_rx_data *rdata, char *geoloc_uri,</span><br><span style="color: hsl(120, 100%, 40%);">+     char **pidf_body, unsigned int *pidf_len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  char *local_uri = ast_strdupa(geoloc_uri);</span><br><span style="color: hsl(120, 100%, 40%);">+    char *ra = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+      /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * If the URI is "cid" then we're going to search for a pidf document</span><br><span style="color: hsl(120, 100%, 40%);">+    * in the body of the message.  If there's no body, there's no point.</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!rdata->msg_info.msg->body) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_WARNING, "%s: There's no message body in which to search for '%s'.  Skipping\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                    session_name, geoloc_uri);</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 (local_uri[0] == '<') {</span><br><span style="color: hsl(120, 100%, 40%);">+         local_uri++;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     ra = strchr(local_uri, '>');</span><br><span style="color: hsl(120, 100%, 40%);">+       if (ra) {</span><br><span style="color: hsl(120, 100%, 40%);">+             *ra = '\0';</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * If the message content type is 'application/pidf+xml', then the pidf is</span><br><span style="color: hsl(120, 100%, 40%);">+     * the only document in the message and we'll just parse the entire body</span><br><span style="color: hsl(120, 100%, 40%);">+   * as xml.  If it's 'multipart/mixed' then we have to find the part that</span><br><span style="color: hsl(120, 100%, 40%);">+   * has a Content-ID header value matching the URI.</span><br><span style="color: hsl(120, 100%, 40%);">+     */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_sip_are_media_types_equal(&rdata->msg_info.ctype->media,</span><br><span style="color: hsl(120, 100%, 40%);">+            &pjsip_media_type_application_pidf_xml)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                *pidf_body = rdata->msg_info.msg->body->data;</span><br><span style="color: hsl(120, 100%, 40%);">+                *pidf_len = rdata->msg_info.msg->body->len;</span><br><span style="color: hsl(120, 100%, 40%);">+  } else if (ast_sip_are_media_types_equal(&rdata->msg_info.ctype->media,</span><br><span style="color: hsl(120, 100%, 40%);">+             &pjsip_media_type_multipart_mixed)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             pj_str_t cid = pj_str(local_uri);</span><br><span style="color: hsl(120, 100%, 40%);">+             pjsip_multipart_part *mp = pjsip_multipart_find_part_by_cid_str(</span><br><span style="color: hsl(120, 100%, 40%);">+                      rdata->tp_info.pool, rdata->msg_info.msg->body, &cid);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         if (!mp) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    ast_log(LOG_WARNING, "%s: A Geolocation header was found with URI '%s'"</span><br><span style="color: hsl(120, 100%, 40%);">+                             " but the associated multipart part was not found in the message body.  Skipping URI",</span><br><span style="color: hsl(120, 100%, 40%);">+                              session_name, geoloc_uri);</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%);">+             *pidf_body = mp->body->data;</span><br><span style="color: hsl(120, 100%, 40%);">+            *pidf_len = mp->body->len;</span><br><span style="color: hsl(120, 100%, 40%);">+      } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_WARNING, "%s: A Geolocation header was found with URI '%s'"</span><br><span style="color: hsl(120, 100%, 40%);">+                     " but no pidf document with that content id was found.  Skipping URI",</span><br><span style="color: hsl(120, 100%, 40%);">+                      session_name, geoloc_uri);</span><br><span style="color: hsl(120, 100%, 40%);">+            return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int handle_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *session_name = (session ? ast_sip_session_get_name(session) : "NULL_SESSION");</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_sip_endpoint *endpoint = (session ? session->endpoint : NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_channel *channel = (session ? session->channel : NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct ast_geoloc_profile *, config_profile, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+     RAII_VAR(struct ast_geoloc_eprofile *, eprofile, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+  RAII_VAR(struct ast_datastore *, ds, NULL, ast_datastore_free);</span><br><span style="color: hsl(120, 100%, 40%);">+       size_t eprofile_count = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+    char *geoloc_hdr_value = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+        char *geoloc_uri = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+      int rc = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+   RAII_VAR(struct ast_str *, buf, ast_str_create(1024), ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+      pjsip_generic_string_hdr *geoloc_hdr = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  SCOPE_ENTER(3, "%s\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!session) {</span><br><span style="color: hsl(120, 100%, 40%);">+               SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING, "%s: session is NULL!!!.  Skipping.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                    session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!endpoint) {</span><br><span style="color: hsl(120, 100%, 40%);">+              SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING, "%s: Session has no endpoint.  Skipping.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                       session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!channel) {</span><br><span style="color: hsl(120, 100%, 40%);">+               SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING, "%s: Session has no channel.  Skipping.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                        session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!rdata) {</span><br><span style="color: hsl(120, 100%, 40%);">+         SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING, "%s: Session has no rdata.  Skipping.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                  session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   geoloc_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &GEOLOCATION_HDR, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!geoloc_hdr) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_trace(4, "%s: Message has no Geolocation header\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+      } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_trace(4, "%s: Geolocation: " PJSTR_PRINTF_SPEC "\n", session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+                    PJSTR_PRINTF_VAR(geoloc_hdr->hvalue));</span><br><span 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_strlen_zero(endpoint->geoloc_incoming_call_profile)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (geoloc_hdr) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_NOTICE, "%s: Message has Geolocation header '"</span><br><span style="color: hsl(120, 100%, 40%);">+                          PJSTR_PRINTF_SPEC "' but endpoint has no geoloc_incoming_call_profile. "</span><br><span style="color: hsl(120, 100%, 40%);">+                                "Geolocation info discarded.\n", session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+                              PJSTR_PRINTF_VAR(geoloc_hdr->hvalue));</span><br><span style="color: hsl(120, 100%, 40%);">+             } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_NOTICE, "%s: Endpoint has no geoloc_incoming_call_profile. "</span><br><span style="color: hsl(120, 100%, 40%);">+                                "Skipping.\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   config_profile = ast_geoloc_get_profile(endpoint->geoloc_incoming_call_profile);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!config_profile) {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (geoloc_hdr) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_NOTICE, "%s: Message has Geolocation header '"</span><br><span style="color: hsl(120, 100%, 40%);">+                          PJSTR_PRINTF_SPEC "' but endpoint's geoloc_incoming_call_profile doesn't exist. "</span><br><span style="color: hsl(120, 100%, 40%);">+                               "Geolocation info discarded.\n", session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+                              PJSTR_PRINTF_VAR(geoloc_hdr->hvalue));</span><br><span style="color: hsl(120, 100%, 40%);">+             } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_NOTICE, "%s: Message has no Geolocation header and endpoint has "</span><br><span style="color: hsl(120, 100%, 40%);">+                           " an invalid geoloc_incoming_call_profile.  Nothing to do..\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+              }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ds = ast_geoloc_datastore_create(session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!ds) {</span><br><span style="color: hsl(120, 100%, 40%);">+            SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING,</span><br><span style="color: hsl(120, 100%, 40%);">+                      "%s: Couldn't allocate a geoloc datastore\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * We want the datastore to pass through the dialplan and the core</span><br><span style="color: hsl(120, 100%, 40%);">+     * so we need to turn inheritance on.</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_geoloc_datastore_set_inheritance(ds, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        switch (config_profile->action) {</span><br><span style="color: hsl(120, 100%, 40%);">+  case AST_GEOLOC_ACT_DISCARD_INCOMING:</span><br><span style="color: hsl(120, 100%, 40%);">+         if (geoloc_hdr) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_trace(4, "%s: Profile '%s' location_disposition is 'discard_incoming' so "</span><br><span style="color: hsl(120, 100%, 40%);">+                              "discarding Geolocation: " PJSTR_PRINTF_SPEC "\n", session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+                          ast_sorcery_object_get_id(config_profile),</span><br><span style="color: hsl(120, 100%, 40%);">+                            PJSTR_PRINTF_VAR(geoloc_hdr->hvalue));</span><br><span style="color: hsl(120, 100%, 40%);">+             } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_trace(4, "%s: Profile '%s' location_disposition is 'discard_incoming' but there was no Geolocation header"</span><br><span style="color: hsl(120, 100%, 40%);">+                              "so there's nothing to discard\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                              session_name, ast_sorcery_object_get_id(config_profile));</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           eprofile = ast_geoloc_eprofile_create_from_profile(config_profile);</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!eprofile) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING, "%s: Unable to create eprofile from "</span><br><span style="color: hsl(120, 100%, 40%);">+                              "profile '%s'\n", session_name, ast_sorcery_object_get_id(config_profile));</span><br><span 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 (!eprofile->effective_location) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_NOTICE, "%s: Configured profile has no effective location.  Skipping."</span><br><span style="color: hsl(120, 100%, 40%);">+                              "profile '%s'\n", session_name, ast_sorcery_object_get_id(eprofile));</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           rc = ast_geoloc_datastore_add_eprofile(ds, eprofile);</span><br><span style="color: hsl(120, 100%, 40%);">+         if (rc <= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING,</span><br><span style="color: hsl(120, 100%, 40%);">+                              "%s: Couldn't add eprofile '%s' to datastore\n", session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+                          eprofile->id);</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_channel_lock(channel);</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_channel_datastore_add(channel, ds);</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_channel_unlock(channel);</span><br><span style="color: hsl(120, 100%, 40%);">+          /* We gave the datastore to the channel so don't let RAII_VAR clean it up. */</span><br><span style="color: hsl(120, 100%, 40%);">+             ds = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_trace(4, "ep: '%s' EffectiveLoc: %s\n", eprofile->id, ast_str_buffer(</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_variable_list_join(eprofile->effective_location, ",", "=", NULL, &buf)));</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_str_reset(buf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         /* We discarded the Geolocation header so there's no need to go on. */</span><br><span style="color: hsl(120, 100%, 40%);">+            SCOPE_EXIT_RTN_VALUE(0, "%s: Added geoloc datastore with 1 eprofile from config\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                 session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      case AST_GEOLOC_ACT_DISCARD_CONFIG:</span><br><span style="color: hsl(120, 100%, 40%);">+           if (geoloc_hdr) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_trace(4, "%s: Profile '%s' location_disposition is 'discard_config' so "</span><br><span style="color: hsl(120, 100%, 40%);">+                                "discarding config profile\n", session_name, ast_sorcery_object_get_id(config_profile));</span><br><span style="color: hsl(120, 100%, 40%);">+                    /* We process the Geolocation header down below. */</span><br><span style="color: hsl(120, 100%, 40%);">+           } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      /* Discarded the config and there's no Geolocation header so we're done. */</span><br><span style="color: hsl(120, 100%, 40%);">+                   SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_NOTICE, "%s: Profile '%s' location_disposition is 'discard_config' but "</span><br><span style="color: hsl(120, 100%, 40%);">+                            "there was no Geolocation header so there's nothing left to process\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                         session_name, ast_sorcery_object_get_id(config_profile));</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      case AST_GEOLOC_ACT_PREFER_CONFIG:</span><br><span style="color: hsl(120, 100%, 40%);">+            eprofile = ast_geoloc_eprofile_create_from_profile(config_profile);</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!eprofile) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING, "%s: Unable to create eprofile from "</span><br><span style="color: hsl(120, 100%, 40%);">+                              "profile '%s'\n", session_name, ast_sorcery_object_get_id(config_profile));</span><br><span 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 (!eprofile->effective_location) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       if (geoloc_hdr) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             ast_trace(4, "%s: Profile '%s' location_disposition is 'prefer_config' but the configured"</span><br><span style="color: hsl(120, 100%, 40%);">+                                  "eprofile has no location information.  Falling back to Geolocation: "</span><br><span style="color: hsl(120, 100%, 40%);">+                                      PJSTR_PRINTF_SPEC "\n", session_name, ast_sorcery_object_get_id(config_profile),</span><br><span style="color: hsl(120, 100%, 40%);">+                            PJSTR_PRINTF_VAR(geoloc_hdr->hvalue));</span><br><span style="color: hsl(120, 100%, 40%);">+                             /* We process the Geolocation header down below. */</span><br><span style="color: hsl(120, 100%, 40%);">+                   } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                              SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_NOTICE, "%s: Configured profile '%s' has no effective location"</span><br><span style="color: hsl(120, 100%, 40%);">+                                     " and there was no Geolocation header.  Skipping.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                       session_name, ast_sorcery_object_get_id(eprofile));</span><br><span style="color: hsl(120, 100%, 40%);">+                   }</span><br><span style="color: hsl(120, 100%, 40%);">+                     break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           rc = ast_geoloc_datastore_add_eprofile(ds, eprofile);</span><br><span style="color: hsl(120, 100%, 40%);">+         if (rc <= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING,</span><br><span style="color: hsl(120, 100%, 40%);">+                              "%s: Couldn't add eprofile '%s' to datastore\n", session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+                          eprofile->id);</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_channel_lock(channel);</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_channel_datastore_add(channel, ds);</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_channel_unlock(channel);</span><br><span style="color: hsl(120, 100%, 40%);">+          /* We gave the datastore to the channel so don't let RAII_VAR clean it up. */</span><br><span style="color: hsl(120, 100%, 40%);">+             ds = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          if (geoloc_hdr) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_trace(4, "%s: Profile '%s' location_disposition is 'prefer_config' so "</span><br><span style="color: hsl(120, 100%, 40%);">+                         "discarding Geolocation: " PJSTR_PRINTF_SPEC "\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                session_name, ast_sorcery_object_get_id(config_profile), PJSTR_PRINTF_VAR(geoloc_hdr->hvalue));</span><br><span 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_trace(4, "ep: '%s' EffectiveLoc: %s\n", eprofile->id, ast_str_buffer(</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_variable_list_join(eprofile->effective_location, ",", "=", NULL, &buf)));</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_str_reset(buf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         /* We discarded the Geolocation header so there's no need to go on. */</span><br><span style="color: hsl(120, 100%, 40%);">+            SCOPE_EXIT_RTN_VALUE(0, "%s: Added geoloc datastore with 1 eprofile from config\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                 session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      case AST_GEOLOC_ACT_PREFER_INCOMING:</span><br><span style="color: hsl(120, 100%, 40%);">+          if (geoloc_hdr) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_trace(4, "%s: Profile '%s' location_disposition is 'replace' so "</span><br><span style="color: hsl(120, 100%, 40%);">+                               "we don't need to do anything with the configured profile", session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+                               ast_sorcery_object_get_id(config_profile));</span><br><span style="color: hsl(120, 100%, 40%);">+           } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_NOTICE,</span><br><span style="color: hsl(120, 100%, 40%);">+                               "%s: Profile '%s' location_disposition is 'replace' but there's "</span><br><span style="color: hsl(120, 100%, 40%);">+                               "no Geolocation header and therefore no location info to replace"</span><br><span style="color: hsl(120, 100%, 40%);">+                           "it with\n", session_name, ast_sorcery_object_get_id(config_profile));</span><br><span 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%);">+   geoloc_hdr_value = ast_alloca(geoloc_hdr->hvalue.slen + 1);</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_copy_pj_str(geoloc_hdr_value, &geoloc_hdr->hvalue, geoloc_hdr->hvalue.slen + 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%);">+     * From RFC-6442:</span><br><span style="color: hsl(120, 100%, 40%);">+      * Geolocation-header = "Geolocation" HCOLON locationValue</span><br><span style="color: hsl(120, 100%, 40%);">+   *                      *( COMMA locationValue )</span><br><span style="color: hsl(120, 100%, 40%);">+       * locationValue      = LAQUOT locationURI RAQUOT</span><br><span style="color: hsl(120, 100%, 40%);">+      *                      *(SEMI geoloc-param)</span><br><span style="color: hsl(120, 100%, 40%);">+   * locationURI        = sip-URI / sips-URI / pres-URI</span><br><span style="color: hsl(120, 100%, 40%);">+  *                        / http-URI / https-URI</span><br><span style="color: hsl(120, 100%, 40%);">+       *                            / cid-url ; (from RFC 2392)</span><br><span style="color: hsl(120, 100%, 40%);">+      *                        / absoluteURI ; (from RFC 3261)</span><br><span style="color: hsl(120, 100%, 40%);">+      */</span><br><span style="color: hsl(120, 100%, 40%);">+   while((geoloc_uri = ast_strsep(&geoloc_hdr_value, ',', AST_STRSEP_TRIM))) {</span><br><span style="color: hsl(120, 100%, 40%);">+               /* geoloc_uri should now be <scheme:location>[;loc-src=fqdn] */</span><br><span style="color: hsl(120, 100%, 40%);">+         char *pidf_body = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+               unsigned int pidf_len = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+            struct ast_xml_doc *incoming_doc = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+              struct ast_geoloc_eprofile *eprofile = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+          int rc = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_trace(4, "Processing URI '%s'\n", geoloc_uri);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                if (geoloc_uri[0] != '<' || strchr(geoloc_uri, '>') == NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  ast_log(LOG_WARNING, "%s: Geolocation header has bad URI '%s'.  Skipping\n", session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+                          geoloc_uri);</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 the URI isn't "cid" then we're just going to pass it through.</span><br><span style="color: hsl(120, 100%, 40%);">+          */</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!ast_begins_with(geoloc_uri, "<cid:")) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_trace(4, "Processing URI '%s'\n", geoloc_uri);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                        eprofile = ast_geoloc_eprofile_create_from_uri(geoloc_uri, session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (!eprofile) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_log(LOG_WARNING, "%s: Unable to create effective profile for URI '%s'.  Skipping\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                    session_name, geoloc_uri);</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%);">+             } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_trace(4, "Processing PIDF-LO '%s'\n", geoloc_uri);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                    rc = find_pidf(session_name, rdata, geoloc_uri, &pidf_body, &pidf_len);</span><br><span style="color: hsl(120, 100%, 40%);">+                       if (rc != 0 || !pidf_body || pidf_len == 0) {</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%);">+                     ast_trace(5, "Processing PIDF-LO "PJSTR_PRINTF_SPEC "\n", (int)pidf_len, pidf_body);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                    incoming_doc = ast_xml_read_memory(pidf_body, pidf_len);</span><br><span style="color: hsl(120, 100%, 40%);">+                      if (!incoming_doc) {</span><br><span style="color: hsl(120, 100%, 40%);">+                          ast_log(LOG_WARNING, "%s: Unable to parse pidf document for URI '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                    session_name, geoloc_uri);</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%);">+                   eprofile = ast_geoloc_eprofile_create_from_pidf(incoming_doc, geoloc_uri, session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+              }</span><br><span style="color: hsl(120, 100%, 40%);">+             eprofile->action = config_profile->action;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_trace(4, "Processing URI '%s'.  Adding to datastore\n", geoloc_uri);</span><br><span style="color: hsl(120, 100%, 40%);">+            rc = ast_geoloc_datastore_add_eprofile(ds, eprofile);</span><br><span style="color: hsl(120, 100%, 40%);">+         ao2_ref(eprofile, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+                if (rc <= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_log(LOG_WARNING, "%s: Unable to add effective profile for URI '%s' to datastore.  Skipping\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                          session_name, geoloc_uri);</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (config_profile->action == AST_GEOLOC_ACT_PREFER_CONFIG) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_trace(4, "%s: Profile '%s' location_disposition is 'prepend' so "</span><br><span style="color: hsl(120, 100%, 40%);">+                       "adding to datastore first", session_name, ast_sorcery_object_get_id(config_profile));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            eprofile = ast_geoloc_eprofile_create_from_profile(config_profile);</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!eprofile) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING, "%s: Unable to create eprofile from"</span><br><span style="color: hsl(120, 100%, 40%);">+                               " profile '%s'\n", session_name, ast_sorcery_object_get_id(config_profile));</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           rc = ast_geoloc_datastore_add_eprofile(ds, eprofile);</span><br><span style="color: hsl(120, 100%, 40%);">+         if (rc <= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING,</span><br><span style="color: hsl(120, 100%, 40%);">+                              "%s: Couldn't add eprofile '%s' to datastore\n", session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+                          eprofile->id);</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   eprofile_count = ast_geoloc_datastore_size(ds);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (eprofile_count == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+            SCOPE_EXIT_RTN_VALUE(0,</span><br><span style="color: hsl(120, 100%, 40%);">+                       "%s: Unable to add any effective profiles.  Not adding datastore to channel.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                    session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_channel_lock(channel);</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_channel_datastore_add(channel, ds);</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_channel_unlock(channel);</span><br><span style="color: hsl(120, 100%, 40%);">+  ds = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  SCOPE_EXIT_RTN_VALUE(0, "%s: Added geoloc datastore with %" PRIu64 " eprofiles\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                session_name, eprofile_count);</span><br><span 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 add_pidf_to_tdata(struct ast_datastore *tempds, struct ast_channel *channel,</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_vector_string *uris, int pidf_index, struct pjsip_tx_data *tdata, const char *session_name)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     static const pj_str_t from_name = { "From", 4};</span><br><span style="color: hsl(120, 100%, 40%);">+     static const pj_str_t cid_name = { "Content-ID", 10 };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    pjsip_sip_uri *sip_uri;</span><br><span style="color: hsl(120, 100%, 40%);">+       pjsip_generic_string_hdr *cid;</span><br><span style="color: hsl(120, 100%, 40%);">+        pj_str_t cid_value;</span><br><span style="color: hsl(120, 100%, 40%);">+   pjsip_from_hdr *from = pjsip_msg_find_hdr_by_name(tdata->msg, &from_name, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+       pjsip_sdp_info *tdata_sdp_info;</span><br><span style="color: hsl(120, 100%, 40%);">+       pjsip_msg_body *multipart_body = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+        pjsip_multipart_part *pidf_part;</span><br><span style="color: hsl(120, 100%, 40%);">+      pj_str_t pidf_body_text;</span><br><span style="color: hsl(120, 100%, 40%);">+      char id[6];</span><br><span style="color: hsl(120, 100%, 40%);">+   size_t alloc_size;</span><br><span style="color: hsl(120, 100%, 40%);">+    RAII_VAR(char *, base_cid, NULL, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+   const char *final;</span><br><span style="color: hsl(120, 100%, 40%);">+    int rc = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+   RAII_VAR(struct ast_str *, buf, ast_str_create(1024), ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+      SCOPE_ENTER(3, "%s\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * ast_geoloc_eprofiles_to_pidf() takes the datastore with all of the eprofiles</span><br><span style="color: hsl(120, 100%, 40%);">+        * in it, skips over the ones not needing PIDF processing and combines the</span><br><span style="color: hsl(120, 100%, 40%);">+     * rest into one document.</span><br><span style="color: hsl(120, 100%, 40%);">+     */</span><br><span style="color: hsl(120, 100%, 40%);">+   final = ast_geoloc_eprofiles_to_pidf(tempds, channel, &buf, session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_trace(5, "Final pidf: \n%s\n", final);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * There _should_ be an SDP already attached to the tdata at this point</span><br><span style="color: hsl(120, 100%, 40%);">+        * but maybe not.  If we can find an existing one, we'll convert the tdata</span><br><span style="color: hsl(120, 100%, 40%);">+         * body into a multipart body and add the SDP as the first part.  Then we'll</span><br><span style="color: hsl(120, 100%, 40%);">+       * create another part to hold the PIDF.</span><br><span style="color: hsl(120, 100%, 40%);">+       *</span><br><span style="color: hsl(120, 100%, 40%);">+     * If we don't find one, we're going to create an empty multipart body</span><br><span style="color: hsl(120, 100%, 40%);">+         * and add the PIDF part to it.</span><br><span style="color: hsl(120, 100%, 40%);">+        *</span><br><span style="color: hsl(120, 100%, 40%);">+     * Technically, if we only have the PIDF, we don't need a multipart</span><br><span style="color: hsl(120, 100%, 40%);">+        * body to hold it but that means we'd have to add the Content-ID header</span><br><span style="color: hsl(120, 100%, 40%);">+   * to the main SIP message.  Since it's unlikely, it's just better to</span><br><span style="color: hsl(120, 100%, 40%);">+  * add the multipart body and leave the rest of the processing unchanged.</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%);">+         ast_trace(4, "body: %p %u\n", tdata_sdp_info->sdp, (unsigned)tdata_sdp_info->sdp_err);</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, &multipart_body);</span><br><span style="color: hsl(120, 100%, 40%);">+            if (rc != PJ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_ERROR, "%s: Unable to create sdp multipart body\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                         session_name);</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, &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%);">+   pidf_part = pjsip_multipart_create_part(tdata->pool);</span><br><span style="color: hsl(120, 100%, 40%);">+      pj_cstr(&pidf_body_text, final);</span><br><span style="color: hsl(120, 100%, 40%);">+  pidf_part->body = pjsip_msg_body_create(tdata->pool, &pjsip_media_type_application_pidf_xml.type,</span><br><span style="color: hsl(120, 100%, 40%);">+           &pjsip_media_type_application_pidf_xml.subtype, &pidf_body_text);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    pjsip_multipart_add_part(tdata->pool, multipart_body, pidf_part);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       sip_uri = (pjsip_sip_uri *)pjsip_uri_get_uri(from->uri);</span><br><span style="color: hsl(120, 100%, 40%);">+   alloc_size = sizeof(id) + pj_strlen(&sip_uri->host) + 2;</span><br><span style="color: hsl(120, 100%, 40%);">+       base_cid = ast_malloc(alloc_size);</span><br><span style="color: hsl(120, 100%, 40%);">+    sprintf(base_cid, "%s@%.*s",</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_generate_random_string(id, sizeof(id)),</span><br><span style="color: hsl(120, 100%, 40%);">+                   (int) pj_strlen(&sip_uri->host), pj_strbuf(&sip_uri->host));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_str_set(&buf, 0, "cid:%s", base_cid);</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_trace(4, "cid: '%s' uri: '%s' pidf_index: %d\n", base_cid, ast_str_buffer(buf), pidf_index);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  AST_VECTOR_INSERT_AT(uris, pidf_index, ast_strdup(ast_str_buffer(buf)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    cid_value.ptr = pj_pool_alloc(tdata->pool, alloc_size);</span><br><span style="color: hsl(120, 100%, 40%);">+    cid_value.slen = sprintf(cid_value.ptr, "<%s>", base_cid);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  cid = pjsip_generic_string_hdr_create(tdata->pool, &cid_name, &cid_value);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       pj_list_insert_after(&pidf_part->hdr, cid);</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%);">+  SCOPE_EXIT_RTN_VALUE(0, "%s: PIDF-LO added with cid '%s'\n", session_name, base_cid);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void handle_outgoing_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    const char *session_name = ast_sip_session_get_name(session);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_endpoint *endpoint = session->endpoint;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ast_channel *channel = session->channel;</span><br><span style="color: hsl(120, 100%, 40%);">+    RAII_VAR(struct ast_geoloc_profile *, config_profile, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+     RAII_VAR(struct ast_geoloc_eprofile *, config_eprofile, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+   RAII_VAR(struct ast_str *, buf, ast_str_create(1024), ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+      RAII_VAR(struct ast_datastore *, tempds, NULL, ast_datastore_free);</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_datastore *ds = NULL;  /* The channel cleans up ds */</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_vector_string uris;</span><br><span style="color: hsl(120, 100%, 40%);">+        pjsip_msg_body *orig_body;</span><br><span style="color: hsl(120, 100%, 40%);">+    pjsip_generic_string_hdr *geoloc_hdr;</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        int eprofile_count = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+       int pidf_index = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+  int geoloc_routing = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+       int rc = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+   const char *final;</span><br><span style="color: hsl(120, 100%, 40%);">+    SCOPE_ENTER(3, "%s\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!buf) {</span><br><span style="color: hsl(120, 100%, 40%);">+           SCOPE_EXIT_LOG_RTN(LOG_WARNING, "%s: Unable to allocate buf\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                     session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!endpoint) {</span><br><span style="color: hsl(120, 100%, 40%);">+              SCOPE_EXIT_LOG_RTN(LOG_WARNING, "%s: Session has no endpoint.  Skipping.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                        session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!channel) {</span><br><span style="color: hsl(120, 100%, 40%);">+               SCOPE_EXIT_LOG_RTN(LOG_WARNING, "%s: Session has no channel.  Skipping.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                 session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_strlen_zero(endpoint->geoloc_outgoing_call_profile)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     SCOPE_EXIT_LOG_RTN(LOG_NOTICE, "%s: Endpoint has no geoloc_outgoing_call_profile. "</span><br><span style="color: hsl(120, 100%, 40%);">+                         "Skipping.\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   config_profile = ast_geoloc_get_profile(endpoint->geoloc_outgoing_call_profile);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!config_profile) {</span><br><span style="color: hsl(120, 100%, 40%);">+                SCOPE_EXIT_LOG_RTN(LOG_ERROR, "%s: Endpoint's geoloc_outgoing_call_profile doesn't exist. "</span><br><span style="color: hsl(120, 100%, 40%);">+                 "Geolocation info discarded.\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   config_eprofile = ast_geoloc_eprofile_create_from_profile(config_profile);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!config_eprofile) {</span><br><span style="color: hsl(120, 100%, 40%);">+               SCOPE_EXIT_LOG_RTN(LOG_WARNING, "%s: Unable to create eprofile from "</span><br><span style="color: hsl(120, 100%, 40%);">+                       "profile '%s'\n", session_name, ast_sorcery_object_get_id(config_profile));</span><br><span 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 (config_profile->action != AST_GEOLOC_ACT_PREFER_INCOMING) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ds = ast_geoloc_datastore_find(channel);</span><br><span style="color: hsl(120, 100%, 40%);">+              if (!ds) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    ast_trace(4, "%s: There was no geoloc datastore\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+          } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      eprofile_count = ast_geoloc_datastore_size(ds);</span><br><span style="color: hsl(120, 100%, 40%);">+                       ast_trace(4, "%s: There are %d geoloc profiles on this channel\n", session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+                            eprofile_count);</span><br><span 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%);">+   /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * We don't want to alter the datastore that may (or may not) be on</span><br><span style="color: hsl(120, 100%, 40%);">+        * the channel so we're going to create a temporary one to hold the</span><br><span style="color: hsl(120, 100%, 40%);">+        * config eprofile plus any in the channel datastore.  Technically</span><br><span style="color: hsl(120, 100%, 40%);">+     * we could just use a vector but the datastore already has the logic</span><br><span style="color: hsl(120, 100%, 40%);">+  * to release all the eprofile references and the datastore itself.</span><br><span style="color: hsl(120, 100%, 40%);">+    */</span><br><span style="color: hsl(120, 100%, 40%);">+   tempds = ast_geoloc_datastore_create("temp");</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!ds) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_trace(4, "%s: There are no geoloc profiles on this channel\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_geoloc_datastore_add_eprofile(tempds, config_eprofile);</span><br><span style="color: hsl(120, 100%, 40%);">+   } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (config_profile->action == AST_GEOLOC_ACT_PREFER_CONFIG) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_trace(4, "%s: prepending config_eprofile\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_geoloc_datastore_add_eprofile(tempds, config_eprofile);</span><br><span style="color: hsl(120, 100%, 40%);">+           }</span><br><span style="color: hsl(120, 100%, 40%);">+             for (i = 0; i < eprofile_count; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     struct ast_geoloc_eprofile *ep = ast_geoloc_datastore_get_eprofile(ds, i);</span><br><span style="color: hsl(120, 100%, 40%);">+                    ast_trace(4, "%s: adding eprofile '%s' from channel\n", session_name, ep->id);</span><br><span style="color: hsl(120, 100%, 40%);">+                   ast_geoloc_datastore_add_eprofile(tempds, ep);</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+             if (config_profile->action == AST_GEOLOC_ACT_PREFER_INCOMING) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    ast_trace(4, "%s: appending config_eprofile\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_geoloc_datastore_add_eprofile(tempds, config_eprofile);</span><br><span 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%);">+   eprofile_count = ast_geoloc_datastore_size(tempds);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (eprofile_count == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+            SCOPE_EXIT_RTN("%s: There are no profiles left to send\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_trace(4, "%s: There are now %d geoloc profiles to be sent\n", session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+             eprofile_count);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * This vector is going to accumulate all of the URIs that</span><br><span style="color: hsl(120, 100%, 40%);">+     * will need to go on the Geolocation header.</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+   rc = AST_VECTOR_INIT(&uris, 2);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (rc != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                SCOPE_EXIT_LOG_RTN(LOG_ERROR, "%s: Unable to allocate memory for vector\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * It's possible that we have a list of eprofiles that have both "pass-by-reference (external URI)"</span><br><span style="color: hsl(120, 100%, 40%);">+      * and "pass by value (to go in PIDF)" eprofiles.  The ones that just need a URI added to the</span><br><span style="color: hsl(120, 100%, 40%);">+        * Geolocation header get added to the "uris" vector in this loop. The ones that result in a</span><br><span style="color: hsl(120, 100%, 40%);">+         * PIDF though, need to be combined into a single PIDF-LO document so we're just going to</span><br><span style="color: hsl(120, 100%, 40%);">+  * save the first one's index so we can insert the "cid" header in the right place, then</span><br><span style="color: hsl(120, 100%, 40%);">+         * we'll send the whole list off to add_pidf_to_tdata() so they can be combined into a</span><br><span style="color: hsl(120, 100%, 40%);">+     * single document.</span><br><span style="color: hsl(120, 100%, 40%);">+    */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < eprofile_count; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+             struct ast_geoloc_eprofile *ep = ast_geoloc_datastore_get_eprofile(tempds, i);</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_geoloc_eprofile_refresh_location(ep);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_trace(4, "ep: '%s' EffectiveLoc: %s\n", ep->id, ast_str_buffer(</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_variable_list_join(ep->effective_location, ",", "=", NULL, &buf)));</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_str_reset(buf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         if (ep->format == AST_GEOLOC_FORMAT_URI) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 final = ast_geoloc_eprofile_to_uri(ep, channel, &buf, session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_trace(4, "URI: %s\n", final);</span><br><span style="color: hsl(120, 100%, 40%);">+                   AST_VECTOR_APPEND(&uris, ast_strdup(final));</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_str_reset(buf);</span><br><span style="color: hsl(120, 100%, 40%);">+           } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      /*</span><br><span style="color: hsl(120, 100%, 40%);">+                     * If there are GML or civicAddress eprofiles, we need to save the position</span><br><span style="color: hsl(120, 100%, 40%);">+                    * of the first one in relation to any URI ones so we can insert the "cid"</span><br><span style="color: hsl(120, 100%, 40%);">+                   * uri for it in the original position.</span><br><span style="color: hsl(120, 100%, 40%);">+                        */</span><br><span style="color: hsl(120, 100%, 40%);">+                   if (pidf_index < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              pidf_index = i;</span><br><span style="color: hsl(120, 100%, 40%);">+                       }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             /* The LAST eprofile determines routing */</span><br><span style="color: hsl(120, 100%, 40%);">+            geoloc_routing = ep->geolocation_routing;</span><br><span style="color: hsl(120, 100%, 40%);">+          ao2_ref(ep, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * If we found at least one eprofile needing PIDF processing, we'll</span><br><span style="color: hsl(120, 100%, 40%);">+        * send the entire list off to add_pidf_to_tdata().  We're going to save</span><br><span style="color: hsl(120, 100%, 40%);">+   * the pointer to the original tdata body in case we need to revert</span><br><span style="color: hsl(120, 100%, 40%);">+    * if we can't add the headers.</span><br><span style="color: hsl(120, 100%, 40%);">+    */</span><br><span style="color: hsl(120, 100%, 40%);">+   orig_body = tdata->msg->body;</span><br><span style="color: hsl(120, 100%, 40%);">+   if (pidf_index >= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             rc = add_pidf_to_tdata(tempds, channel, &uris, pidf_index, tdata, session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * Now that we have all the URIs in the vector, we'll string them together</span><br><span style="color: hsl(120, 100%, 40%);">+         * to create the data for the Geolocation header.</span><br><span style="color: hsl(120, 100%, 40%);">+      */</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_str_reset(buf);</span><br><span style="color: hsl(120, 100%, 40%);">+   for (i = 0; i < AST_VECTOR_SIZE(&uris); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+         char *uri = AST_VECTOR_GET(&uris, i);</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_trace(4, "ix: %d of %d LocRef: %s\n", i, (int)AST_VECTOR_SIZE(&uris), uri);</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_str_append(&buf, 0, "%s<%s>", (i > 0 ? "," : ""), uri);</span><br><span 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_VECTOR_RESET(&uris, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+        AST_VECTOR_FREE(&uris);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* It's almost impossible for add header to fail but you never know */</span><br><span style="color: hsl(120, 100%, 40%);">+    geoloc_hdr = ast_sip_add_header2(tdata, "Geolocation", ast_str_buffer(buf));</span><br><span style="color: hsl(120, 100%, 40%);">+        if (geoloc_hdr == NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+             tdata->msg->body = orig_body;</span><br><span style="color: hsl(120, 100%, 40%);">+           SCOPE_EXIT_LOG_RTN(LOG_ERROR, "%s: Unable to add Geolocation header\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+     rc = ast_sip_add_header(tdata, "Geolocation-Routing", geoloc_routing ? "yes" : "no");</span><br><span style="color: hsl(120, 100%, 40%);">+   if (rc != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                tdata->msg->body = orig_body;</span><br><span style="color: hsl(120, 100%, 40%);">+           pj_list_erase(geoloc_hdr);</span><br><span style="color: hsl(120, 100%, 40%);">+            SCOPE_EXIT_LOG_RTN(LOG_ERROR, "%s: Unable to add Geolocation-Routing header\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+     SCOPE_EXIT_RTN("%s: Geolocation: %s\n", session_name, ast_str_buffer(buf));</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_sip_session_supplement geolocation_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_CHANNEL + 10,</span><br><span style="color: hsl(120, 100%, 40%);">+ .incoming_request = handle_incoming_request,</span><br><span style="color: hsl(120, 100%, 40%);">+  .outgoing_request = handle_outgoing_request,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int reload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int unload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_sip_session_unregister_supplement(&geolocation_supplement);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+  GEOLOCATION_HDR = pj_str("Geolocation");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_sip_session_register_supplement(&geolocation_supplement);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "res_pjsip_geolocation Module for Asterisk",</span><br><span style="color: hsl(120, 100%, 40%);">+       .support_level = AST_MODULE_SUPPORT_CORE,</span><br><span style="color: hsl(120, 100%, 40%);">+     .load = load_module,</span><br><span style="color: hsl(120, 100%, 40%);">+  .unload = unload_module,</span><br><span style="color: hsl(120, 100%, 40%);">+      .reload = reload_module,</span><br><span style="color: hsl(120, 100%, 40%);">+      .load_pri = AST_MODPRI_CHANNEL_DEPEND - 1,</span><br><span style="color: hsl(120, 100%, 40%);">+    .requires = "res_geolocation,res_pjsip,res_pjsip_session,chan_pjsip",</span><br><span style="color: hsl(120, 100%, 40%);">+);</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/18784">change 18784</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/+/18784"/><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: Ie2e2bcd87243c2cfabc43eb823d4427c7086f4d9 </div>
<div style="display:none"> Gerrit-Change-Number: 18784 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>