<p>Nick French has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/9505">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_pjsip: Implement additional SIP RFCs for Google Voice trunk compatability<br><br>Implement the following additional SIP features that are required<br>by the Google Voice SIP registrar:<br><br>- Service-Routes (RFC 3608) and P-Preferred-Identity (RFC 3325), by storing<br>  headers returned from a REGISTER response and using them in subsequent<br>  INVITE/CANCELs<br>- OAuth / Bearer token authentication (draft-ietf-sipcore-sip-authn-02), by<br>  using similar token requests as currently found in chan_motif but piping<br>  them through to pjsip via REGISTER Authorization header<br>- Mechanisms to use separate TLS transports for separate registrations and<br>  their associated message dialog, by creating a transport without going<br>  through the pjsip transport pool and storing it in the client_state for reuse<br>- User-configurable additions to SIP Contact header<br><br>All functionality changes are controlled by pjsip.conf configuration options<br>and do not affect non-configured pjsip endpoints otherwise.<br><br>Change-Id: Id214c2d1c550a41fcf564b7df8f3da7be565bd58<br>---<br>M include/asterisk/res_pjsip.h<br>M res/res_pjsip.c<br>M res/res_pjsip/config_auth.c<br>M res/res_pjsip/pjsip_configuration.c<br>M res/res_pjsip_outbound_authenticator_digest.c<br>M res/res_pjsip_outbound_registration.c<br>A third-party/pjproject/patches/0110-oauth.patch<br>A third-party/pjproject/patches/0120-contact-params.patch<br>8 files changed, 789 insertions(+), 22 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/05/9505/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h</span><br><span>index 849f087..493fb94 100644</span><br><span>--- a/include/asterisk/res_pjsip.h</span><br><span>+++ b/include/asterisk/res_pjsip.h</span><br><span>@@ -401,6 +401,8 @@</span><br><span>        AST_SIP_AUTH_TYPE_USER_PASS,</span><br><span>         /*! Credentials stored as an MD5 sum */</span><br><span>      AST_SIP_AUTH_TYPE_MD5,</span><br><span style="color: hsl(120, 100%, 40%);">+        /*! Oauth */</span><br><span style="color: hsl(120, 100%, 40%);">+  AST_SIP_AUTH_TYPE_OAUTH,</span><br><span>     /*! Credentials not stored this is a fake auth */</span><br><span>    AST_SIP_AUTH_TYPE_ARTIFICIAL</span><br><span> };</span><br><span>@@ -419,6 +421,12 @@</span><br><span>            AST_STRING_FIELD(auth_pass);</span><br><span>                 /*! Authentication credentials in MD5 format (hash of user:realm:pass) */</span><br><span>            AST_STRING_FIELD(md5_creds);</span><br><span style="color: hsl(120, 100%, 40%);">+          /*! Refresh token to use for OAuth authentication */</span><br><span style="color: hsl(120, 100%, 40%);">+          AST_STRING_FIELD(refresh_token);</span><br><span style="color: hsl(120, 100%, 40%);">+              /*! Client ID to use for OAuth authentication */</span><br><span style="color: hsl(120, 100%, 40%);">+              AST_STRING_FIELD(oauth_clientid);</span><br><span style="color: hsl(120, 100%, 40%);">+             /*! Secret to use for OAuth authentication */</span><br><span style="color: hsl(120, 100%, 40%);">+         AST_STRING_FIELD(oauth_secret);</span><br><span>      );</span><br><span>   /*! The time period (in seconds) that a nonce may be reused */</span><br><span>       unsigned int nonce_lifetime;</span><br><span>@@ -734,6 +742,8 @@</span><br><span>           AST_STRING_FIELD(transport);</span><br><span>                 /*! Outbound proxy to use */</span><br><span>                 AST_STRING_FIELD(outbound_proxy);</span><br><span style="color: hsl(120, 100%, 40%);">+             /*! Outbound registration associated with this endpoint */</span><br><span style="color: hsl(120, 100%, 40%);">+            AST_STRING_FIELD(outbound_registration);</span><br><span>             /*! Explicit AORs to dial if none are specified */</span><br><span>           AST_STRING_FIELD(aors);</span><br><span>              /*! Musiconhold class to suggest that the other side use when placing on hold */</span><br><span>@@ -3186,4 +3196,13 @@</span><br><span>  */</span><br><span> void ast_sip_transport_state_unregister(struct ast_sip_tpmgr_state_callback *element);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Register an override to the default selection of transports based on endpoint name</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since gvsip</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param callback Callback to evoke when determining the transport when creating a new dialog</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+typedef int (*transport_from_endpoint_callback)(const struct ast_sip_endpoint *endpoint, pjsip_transport** transport);</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_sip_set_transport_from_endpoint_override(transport_from_endpoint_callback callback);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #endif /* _RES_PJSIP_H */</span><br><span>diff --git a/res/res_pjsip.c b/res/res_pjsip.c</span><br><span>index 507267a..e0686d7 100644</span><br><span>--- a/res/res_pjsip.c</span><br><span>+++ b/res/res_pjsip.c</span><br><span>@@ -398,6 +398,9 @@</span><br><span>                             <configOption name="outbound_proxy"></span><br><span>                                         <synopsis>Full SIP URI of the outbound proxy used to send requests</synopsis></span><br><span>                            </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="outbound_registration"></span><br><span style="color: hsl(120, 100%, 40%);">+                                   <synopsis>Name of the registration config associated with this endpoint</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                                </configOption></span><br><span>                                <configOption name="rewrite_contact"></span><br><span>                                        <synopsis>Allow Contact header to be rewritten with the source IP address-port</synopsis></span><br><span>                                        <description><para></span><br><span>@@ -1133,11 +1136,12 @@</span><br><span>                                            This option specifies which of the password style config options should be read</span><br><span>                                              when trying to authenticate an endpoint inbound request. If set to <literal>userpass</literal></span><br><span>                                           then we'll read from the 'password' option. For <literal>md5</literal> we'll read</span><br><span style="color: hsl(0, 100%, 40%);">-                                           from 'md5_cred'.</span><br><span style="color: hsl(120, 100%, 40%);">+                                              from 'md5_cred'. If set to <literal>oauth</literal> then we'll read from the refresh_toke/oauth_client_id/oauth_secret fields.</span><br><span>                                               </para></span><br><span>                                                <enumlist></span><br><span>                                                     <enum name="md5"/></span><br><span>                                                   <enum name="userpass"/></span><br><span style="color: hsl(120, 100%, 40%);">+                                                       <enum name="oauth"/></span><br><span>                                                 </enumlist></span><br><span>                                    </description></span><br><span>                                 </configOption></span><br><span>@@ -1152,6 +1156,15 @@</span><br><span>                                       <synopsis>Plain text password used for authentication.</synopsis></span><br><span>                                        <description><para>Only used when auth_type is <literal>userpass</literal>.</para></description></span><br><span>                                 </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="refresh_token"></span><br><span style="color: hsl(120, 100%, 40%);">+                                   <synopsis>Google OAuth 2.0 refresh token</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                               </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="oauth_clientid"></span><br><span style="color: hsl(120, 100%, 40%);">+                                  <synopsis>Google OAuth 2.0 application's client id</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                         </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="oauth_secret"></span><br><span style="color: hsl(120, 100%, 40%);">+                                    <synopsis>Google OAuth 2.0 application's secret</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                            </configOption></span><br><span>                                <configOption name="realm"></span><br><span>                                  <synopsis>SIP realm for endpoint</synopsis></span><br><span>                                      <description><para></span><br><span>@@ -2134,6 +2147,9 @@</span><br><span>                              <parameter name="OutboundProxy"></span><br><span>                                     <para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='endpoint']/configOption[@name='outbound_proxy']/synopsis/node())"/></para></span><br><span>                           </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+                            <parameter name="OutboundRegistration"></span><br><span style="color: hsl(120, 100%, 40%);">+                                       <para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='endpoint']/configOption[@name='outbound_registration']/synopsis/node())"/></para></span><br><span style="color: hsl(120, 100%, 40%);">+                             </parameter></span><br><span>                           <parameter name="MohSuggest"></span><br><span>                                        <para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='endpoint']/configOption[@name='moh_suggest']/synopsis/node())"/></para></span><br><span>                              </parameter></span><br><span>@@ -2792,6 +2808,14 @@</span><br><span> /*! Local host address for IPv6 (string form) */</span><br><span> static char host_ip_ipv6_string[PJ_INET6_ADDRSTRLEN];</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static transport_from_endpoint_callback transport_from_endpoint_override_callback;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_sip_set_transport_from_endpoint_override(transport_from_endpoint_callback callback)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_log(LOG_DEBUG, "Transport override set!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+    transport_from_endpoint_override_callback = callback;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int register_service(void *data)</span><br><span> {</span><br><span>   pjsip_module **module = data;</span><br><span>@@ -3266,6 +3290,7 @@</span><br><span>        pjsip_tpselector *selector)</span><br><span> {</span><br><span>     pjsip_sip_uri *uri;</span><br><span style="color: hsl(120, 100%, 40%);">+   pjsip_transport* transport;</span><br><span>  pjsip_tpselector sel = { .type = PJSIP_TPSELECTOR_NONE, };</span><br><span> </span><br><span>       uri = pjsip_uri_get_uri(dlg->target);</span><br><span>@@ -3274,6 +3299,14 @@</span><br><span>    }</span><br><span> </span><br><span>        ast_sip_set_tpselector_from_ep_or_uri(endpoint, uri, selector);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (transport_from_endpoint_override_callback && transport_from_endpoint_override_callback(endpoint, &transport)) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_DEBUG, "Overriding endpoint transport to use %p\n", (void*)transport);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                selector->type = PJSIP_TPSELECTOR_TRANSPORT;</span><br><span style="color: hsl(120, 100%, 40%);">+               selector->u.transport = transport;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  pjsip_dlg_set_transport(dlg, selector);</span><br><span> </span><br><span>  return 0;</span><br><span>diff --git a/res/res_pjsip/config_auth.c b/res/res_pjsip/config_auth.c</span><br><span>index b1bf9c4..9d87f4d 100644</span><br><span>--- a/res/res_pjsip/config_auth.c</span><br><span>+++ b/res/res_pjsip/config_auth.c</span><br><span>@@ -56,6 +56,8 @@</span><br><span>               auth->type = AST_SIP_AUTH_TYPE_USER_PASS;</span><br><span>         } else if (!strcasecmp(var->value, "md5")) {</span><br><span>            auth->type = AST_SIP_AUTH_TYPE_MD5;</span><br><span style="color: hsl(120, 100%, 40%);">+        } else if (!strcasecmp(var->value, "oauth")) {</span><br><span style="color: hsl(120, 100%, 40%);">+           auth->type = AST_SIP_AUTH_TYPE_OAUTH;</span><br><span>     } else {</span><br><span>             ast_log(LOG_WARNING, "Unknown authentication storage type '%s' specified for %s\n",</span><br><span>                                var->value, var->name);</span><br><span>@@ -66,7 +68,8 @@</span><br><span> </span><br><span> static const char *auth_types_map[] = {</span><br><span>     [AST_SIP_AUTH_TYPE_USER_PASS] = "userpass",</span><br><span style="color: hsl(0, 100%, 40%);">-   [AST_SIP_AUTH_TYPE_MD5] = "md5"</span><br><span style="color: hsl(120, 100%, 40%);">+     [AST_SIP_AUTH_TYPE_MD5] = "md5",</span><br><span style="color: hsl(120, 100%, 40%);">+    [AST_SIP_AUTH_TYPE_OAUTH] = "oauth"</span><br><span> };</span><br><span> </span><br><span> const char *ast_sip_auth_type_to_str(enum ast_sip_auth_type type)</span><br><span>@@ -106,6 +109,13 @@</span><br><span>                  res = -1;</span><br><span>            }</span><br><span>            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case AST_SIP_AUTH_TYPE_OAUTH:</span><br><span style="color: hsl(120, 100%, 40%);">+         if (ast_strlen_zero(auth->refresh_token) || ast_strlen_zero(auth->oauth_clientid) || ast_strlen_zero(auth->oauth_secret)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  ast_log(LOG_ERROR, "'oauth' authentication specified but refresh_token, oauth_clientid, or "</span><br><span style="color: hsl(120, 100%, 40%);">+                                        "oauth_secret not specified for auth '%s'\n", ast_sorcery_object_get_id(auth));</span><br><span style="color: hsl(120, 100%, 40%);">+                     res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span>       case AST_SIP_AUTH_TYPE_USER_PASS:</span><br><span>    case AST_SIP_AUTH_TYPE_ARTIFICIAL:</span><br><span>           break;</span><br><span>@@ -365,6 +375,12 @@</span><br><span>                        "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, auth_user));</span><br><span>      ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "password",</span><br><span>                      "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, auth_pass));</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "refresh_token",</span><br><span style="color: hsl(120, 100%, 40%);">+                  "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, refresh_token));</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "oauth_clientid",</span><br><span style="color: hsl(120, 100%, 40%);">+                 "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, oauth_clientid));</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "oauth_secret",</span><br><span style="color: hsl(120, 100%, 40%);">+                   "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, oauth_secret));</span><br><span>   ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "md5_cred",</span><br><span>                      "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, md5_creds));</span><br><span>      ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "realm",</span><br><span>diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c</span><br><span>index f4a9ecb..8e09bb5 100644</span><br><span>--- a/res/res_pjsip/pjsip_configuration.c</span><br><span>+++ b/res/res_pjsip/pjsip_configuration.c</span><br><span>@@ -1797,6 +1797,7 @@</span><br><span>        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rewrite_contact", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, nat.rewrite_contact));</span><br><span>      ast_sorcery_object_field_register(sip_sorcery, "endpoint", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, transport));</span><br><span>      ast_sorcery_object_field_register(sip_sorcery, "endpoint", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, outbound_proxy));</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_sorcery_object_field_register(sip_sorcery, "endpoint", "outbound_registration", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, outbound_registration));</span><br><span>      ast_sorcery_object_field_register(sip_sorcery, "endpoint", "moh_suggest", "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, mohsuggest));</span><br><span>    ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "100rel", "yes", prack_handler, prack_to_str, NULL, 0, 0);</span><br><span>   ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "timers", "yes", timers_handler, timers_to_str, NULL, 0, 0);</span><br><span>diff --git a/res/res_pjsip_outbound_authenticator_digest.c b/res/res_pjsip_outbound_authenticator_digest.c</span><br><span>index 063b4d3..49b3884 100644</span><br><span>--- a/res/res_pjsip_outbound_authenticator_digest.c</span><br><span>+++ b/res/res_pjsip_outbound_authenticator_digest.c</span><br><span>@@ -83,6 +83,9 @@</span><br><span>                    pj_cstr(&auth_creds[i].data, auths[i]->md5_creds);</span><br><span>                    auth_creds[i].data_type = PJSIP_CRED_DATA_DIGEST;</span><br><span>                    break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case AST_SIP_AUTH_TYPE_OAUTH:</span><br><span style="color: hsl(120, 100%, 40%);">+                 /* nothing to do. handled seperately in res_pjsip_outbound_registration */</span><br><span style="color: hsl(120, 100%, 40%);">+                    break;</span><br><span>               case AST_SIP_AUTH_TYPE_ARTIFICIAL:</span><br><span>                   ast_log(LOG_ERROR, "Trying to set artificial outbound auth credentials shouldn't happen.\n");</span><br><span>                  break;</span><br><span>diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c</span><br><span>index 0d815ad..40f070a 100644</span><br><span>--- a/res/res_pjsip_outbound_registration.c</span><br><span>+++ b/res/res_pjsip_outbound_registration.c</span><br><span>@@ -27,6 +27,7 @@</span><br><span> </span><br><span> #include <pjsip.h></span><br><span> #include <pjsip_ua.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <pjsip/sip_dialog.h></span><br><span> </span><br><span> #include "asterisk/res_pjsip.h"</span><br><span> #include "asterisk/res_pjsip_cli.h"</span><br><span>@@ -37,7 +38,11 @@</span><br><span> #include "asterisk/threadstorage.h"</span><br><span> #include "asterisk/threadpool.h"</span><br><span> #include "asterisk/statsd.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/pbx.h"</span><br><span> #include "res_pjsip/include/res_pjsip_private.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%);">+#include "asterisk/vector.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_pjproject.h"</span><br><span> </span><br><span> /*** DOCUMENTATION</span><br><span>       <configInfo name="res_pjsip_outbound_registration" language="en_US"></span><br><span>@@ -76,6 +81,9 @@</span><br><span>                           <configOption name="contact_user"></span><br><span>                                   <synopsis>Contact User to use in request</synopsis></span><br><span>                              </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="contact_additional_params"></span><br><span style="color: hsl(120, 100%, 40%);">+                                       <synopsis>Additional parameters for contact</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                            </configOption></span><br><span>                                <configOption name="expiration" default="3600"></span><br><span>                                    <synopsis>Expiration time for registrations in seconds</synopsis></span><br><span>                                </configOption></span><br><span>@@ -144,7 +152,11 @@</span><br><span>                                                 <literal>pjsip.conf</literal>. As with other <literal>res_pjsip</literal> modules, this will use the first available transport of the appropriate type if unconfigured.</para></note></span><br><span>                                    </description></span><br><span>                                 </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="transport_reuse" default="yes"></span><br><span style="color: hsl(120, 100%, 40%);">+                                 <synopsis>Determine if same transport can be re-used by different registrations</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                                </configOption></span><br><span>                                <configOption name="line"></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>                                         <synopsis>Whether to add a 'line' parameter to the Contact for inbound call matching</synopsis></span><br><span>                                  <description><para></span><br><span>                                              When enabled this option will cause a 'line' parameter to be added to the Contact</span><br><span>@@ -171,6 +183,9 @@</span><br><span>                                              header as necessary.</span><br><span>                                         </para></description></span><br><span>                            </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="support_outbound"></span><br><span style="color: hsl(120, 100%, 40%);">+                                        <synopsis>Enables Outbound support for outbound REGISTER requests.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                             </configOption></span><br><span>                        </configObject></span><br><span>                </configFile></span><br><span>  </configInfo></span><br><span>@@ -224,6 +239,11 @@</span><br><span>   </manager></span><br><span>  ***/</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* forward declarations */</span><br><span style="color: hsl(120, 100%, 40%);">+static int set_outbound_initial_authentication_credentials(pjsip_regc *regc,</span><br><span style="color: hsl(120, 100%, 40%);">+               const struct ast_sip_auth_vector *auth_vector);</span><br><span style="color: hsl(120, 100%, 40%);">+static int transport_from_endpoint_override(const struct ast_sip_endpoint *endpoint, pjsip_transport** transport);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \brief Some thread local storage used to determine if the running thread invoked the callback */</span><br><span> AST_THREADSTORAGE(register_callback_invoked);</span><br><span> </span><br><span>@@ -291,6 +311,8 @@</span><br><span>             AST_STRING_FIELD(client_uri);</span><br><span>                /*! \brief Optional user for contact header */</span><br><span>               AST_STRING_FIELD(contact_user);</span><br><span style="color: hsl(120, 100%, 40%);">+               /*! \bried Optional additional parameters for contact */</span><br><span style="color: hsl(120, 100%, 40%);">+              AST_STRING_FIELD(contact_additional_params);</span><br><span>                 /*! \brief Explicit transport to use for registration */</span><br><span>             AST_STRING_FIELD(transport);</span><br><span>                 /*! \brief Outbound proxy to use */</span><br><span>@@ -316,8 +338,21 @@</span><br><span>   struct ast_sip_auth_vector outbound_auths;</span><br><span>   /*! \brief Whether Path support is enabled */</span><br><span>        unsigned int support_path;</span><br><span style="color: hsl(120, 100%, 40%);">+    /*! \brief Whether Outbound support is enabled */</span><br><span style="color: hsl(120, 100%, 40%);">+     unsigned int support_outbound;</span><br><span style="color: hsl(120, 100%, 40%);">+        /*! \brief Determine if same transport can be re-used by different registrations */</span><br><span style="color: hsl(120, 100%, 40%);">+   unsigned int transport_reuse;</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* \brief Vector type to store service routes */</span><br><span style="color: hsl(120, 100%, 40%);">+AST_VECTOR(service_route_vector_type, pj_str_t);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* \brief Caching pool to use to create pool to store saved pjsip strings */</span><br><span style="color: hsl(120, 100%, 40%);">+static pj_caching_pool cachingpool;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* \brief Pool to use to store saved pjsip strings */</span><br><span style="color: hsl(120, 100%, 40%);">+static pj_pool_t *reg_pool;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \brief Outbound registration client state information (persists for lifetime of regc) */</span><br><span> struct sip_outbound_registration_client_state {</span><br><span>  /*! \brief Current state of this registration */</span><br><span>@@ -347,18 +382,28 @@</span><br><span>     unsigned int auth_rejection_permanent;</span><br><span>       /*! \brief Determines whether SIP Path support should be advertised */</span><br><span>       unsigned int support_path;</span><br><span style="color: hsl(120, 100%, 40%);">+    /*! \brief Determines whether SIP Outbound support should be advertised */</span><br><span style="color: hsl(120, 100%, 40%);">+    unsigned int support_outbound;</span><br><span>       /*! CSeq number of last sent auth request. */</span><br><span>        unsigned int auth_cseq;</span><br><span>      /*! \brief Serializer for stuff and things */</span><br><span>        struct ast_taskprocessor *serializer;</span><br><span>        /*! \brief Configured authentication credentials */</span><br><span>  struct ast_sip_auth_vector outbound_auths;</span><br><span style="color: hsl(120, 100%, 40%);">+    /*! \brief List of service-routes in register response */</span><br><span style="color: hsl(120, 100%, 40%);">+     struct service_route_vector_type service_route_vector;</span><br><span style="color: hsl(120, 100%, 40%);">+        /*! \brief P-Associated-URI from register response */</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_str_t associated_uri;</span><br><span>     /*! \brief Registration should be destroyed after completion of transaction */</span><br><span>       unsigned int destroy:1;</span><br><span>      /*! \brief Non-zero if we have attempted sending a REGISTER with authentication */</span><br><span>   unsigned int auth_attempted:1;</span><br><span>       /*! \brief The name of the transport to be used for the registration */</span><br><span>      char *transport_name;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief The transport used by the registration */</span><br><span style="color: hsl(120, 100%, 40%);">+  pjsip_transport *transport;</span><br><span style="color: hsl(120, 100%, 40%);">+   /*! \brief Determine if same transport can be re-used by different registrations */</span><br><span style="color: hsl(120, 100%, 40%);">+   unsigned int transport_reuse;</span><br><span>        /*! \brief The name of the registration sorcery object */</span><br><span>    char *registration_name;</span><br><span> };</span><br><span>@@ -520,9 +565,112 @@</span><br><span> }</span><br><span> </span><br><span> static pj_str_t PATH_NAME = { "path", 4 };</span><br><span style="color: hsl(120, 100%, 40%);">+static pj_str_t OUTBOUND_NAME = { "outbound", 8 };</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/*! \brief Helper function which sends a message and cleans up, if needed, on failure */</span><br><span style="color: hsl(0, 100%, 40%);">-static pj_status_t registration_client_send(struct sip_outbound_registration_client_state *client_state,</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Helper to send message on specified transport */</span><br><span style="color: hsl(120, 100%, 40%);">+static pj_status_t send_on_transport(struct sip_outbound_registration_client_state *client_state, </span><br><span style="color: hsl(120, 100%, 40%);">+    pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_TRANSPORT, };</span><br><span style="color: hsl(120, 100%, 40%);">+  selector.u.transport = client_state->transport;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  pjsip_regc_set_transport(client_state->client, &selector);</span><br><span style="color: hsl(120, 100%, 40%);">+     return pjsip_regc_send(client_state->client, 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%);">+struct stateless_send_resolver_callback_data</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct sip_outbound_registration_client_state *client_state;</span><br><span style="color: hsl(120, 100%, 40%);">+  pjsip_tx_data *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%);">+/*! \brief Callback used to manually select transport when transport_reuse is off */</span><br><span style="color: hsl(120, 100%, 40%);">+static void</span><br><span style="color: hsl(120, 100%, 40%);">+stateless_send_resolver_callback( pj_status_t status, void *token, const struct pjsip_server_addresses *addr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      RAII_VAR(struct stateless_send_resolver_callback_data *, data, token, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+      pjsip_tpselector orig_selector = { .type = PJSIP_TPSELECTOR_NONE, };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        struct sip_outbound_registration_client_state *client_state = data->client_state;</span><br><span style="color: hsl(120, 100%, 40%);">+  pjsip_tx_data *tdata = data->tdata;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (status != PJ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_ERROR, "Resolver failed. Cannot send message\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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_sip_set_tpselector_from_transport_name(client_state->transport_name, &orig_selector);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (orig_selector.type != PJSIP_TPSELECTOR_LISTENER)</span><br><span style="color: hsl(120, 100%, 40%);">+  {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_log(LOG_ERROR, "transport_reuse requires setting a transport\n");</span><br><span style="color: hsl(120, 100%, 40%);">+               status = PJ_EUNKNOWN;</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%);">+   /* Copy server addresses */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (addr && addr != &tdata->dest_info.addr) {</span><br><span style="color: hsl(120, 100%, 40%);">+          pj_memcpy( &tdata->dest_info.addr, addr, sizeof(pjsip_server_addresses));</span><br><span 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 (orig_selector.u.listener->create_transport2) {</span><br><span style="color: hsl(120, 100%, 40%);">+         orig_selector.u.listener->create_transport2(orig_selector.u.listener,</span><br><span style="color: hsl(120, 100%, 40%);">+                      pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()),</span><br><span style="color: hsl(120, 100%, 40%);">+                  ast_sip_get_pjsip_endpoint(),</span><br><span style="color: hsl(120, 100%, 40%);">+                 &tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr,</span><br><span style="color: hsl(120, 100%, 40%);">+                       tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr_len,</span><br><span style="color: hsl(120, 100%, 40%);">+                        tdata,</span><br><span style="color: hsl(120, 100%, 40%);">+                        &client_state->transport);</span><br><span style="color: hsl(120, 100%, 40%);">+     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              orig_selector.u.listener->create_transport(orig_selector.u.listener,</span><br><span style="color: hsl(120, 100%, 40%);">+                       pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()),</span><br><span style="color: hsl(120, 100%, 40%);">+                  ast_sip_get_pjsip_endpoint(),</span><br><span style="color: hsl(120, 100%, 40%);">+                 &tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr,</span><br><span style="color: hsl(120, 100%, 40%);">+                       tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr_len,</span><br><span style="color: hsl(120, 100%, 40%);">+                        &client_state->transport);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_log(LOG_DEBUG, "Registration using newly created transport %p\n", (void*)client_state->transport);</span><br><span style="color: hsl(120, 100%, 40%);">+   send_on_transport(client_state, 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%);">+/*! \brief Send a message using a manually-built transport */</span><br><span style="color: hsl(120, 100%, 40%);">+static pj_status_t registration_client_send_manual(struct sip_outbound_registration_client_state *client_state,</span><br><span style="color: hsl(120, 100%, 40%);">+  pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      pj_status_t status;</span><br><span style="color: hsl(120, 100%, 40%);">+   pjsip_host_info dest_info;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct stateless_send_resolver_callback_data* data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Due to the message going out the callback may now be invoked, so bump the count */</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(client_state, +1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* If we already have a transport, just use it. */</span><br><span style="color: hsl(120, 100%, 40%);">+    if (client_state->transport && !client_state->transport->is_shutdown) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_DEBUG, "Registration re-using transport %p\n", (void*)client_state->transport);</span><br><span style="color: hsl(120, 100%, 40%);">+              return send_on_transport(client_state, 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%);">+   /* If not, then create a new one. First, resolve the endpoint's host */</span><br><span style="color: hsl(120, 100%, 40%);">+   status = pjsip_process_route_set(tdata, &dest_info);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (status != PJ_SUCCESS)</span><br><span style="color: hsl(120, 100%, 40%);">+             return status;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      data = ast_malloc(sizeof(struct stateless_send_resolver_callback_data));</span><br><span style="color: hsl(120, 100%, 40%);">+      data->client_state = client_state;</span><br><span style="color: hsl(120, 100%, 40%);">+ data->tdata = tdata;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_strdup(tdata->pool, &tdata->dest_info.name, &dest_info.addr.host);</span><br><span style="color: hsl(120, 100%, 40%);">+   pjsip_endpt_resolve( ast_sip_get_pjsip_endpoint(), tdata->pool, &dest_info, data,</span><br><span style="color: hsl(120, 100%, 40%);">+                              &stateless_send_resolver_callback);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     return status;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Send a message using a transport from the normal pjsip transport factory */</span><br><span style="color: hsl(120, 100%, 40%);">+static pj_status_t registration_client_send_normal(struct sip_outbound_registration_client_state *client_state,</span><br><span>       pjsip_tx_data *tdata)</span><br><span> {</span><br><span>   pj_status_t status;</span><br><span>@@ -545,6 +693,8 @@</span><br><span>     */</span><br><span>  ast_sip_set_tpselector_from_transport_name(client_state->transport_name, &selector);</span><br><span>  pjsip_regc_set_transport(client_state->client, &selector);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_log(LOG_DEBUG, "Registration using factory-chosen transport\n");</span><br><span>       status = pjsip_regc_send(client_state->client, tdata);</span><br><span> </span><br><span>        /* If the attempt to send the message failed and the callback was not invoked we need to</span><br><span>@@ -557,12 +707,69 @@</span><br><span>     return status;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Helper function which sends a message and cleans up, if needed, on failure */</span><br><span style="color: hsl(120, 100%, 40%);">+static pj_status_t registration_client_send(struct sip_outbound_registration_client_state *client_state,</span><br><span style="color: hsl(120, 100%, 40%);">+   pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!client_state->transport_reuse) {</span><br><span style="color: hsl(120, 100%, 40%);">+              return registration_client_send_manual(client_state, tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+  } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              return registration_client_send_normal(client_state, 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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Helper function to add string to Supported header */</span><br><span style="color: hsl(120, 100%, 40%);">+static int add_to_supported_header(pjsip_tx_data *tdata, pj_str_t *name)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  pjsip_supported_hdr *hdr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_SUPPORTED, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!hdr) {</span><br><span style="color: hsl(120, 100%, 40%);">+           /* insert a new Supported header */</span><br><span style="color: hsl(120, 100%, 40%);">+           hdr = pjsip_supported_hdr_create(tdata->pool);</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!hdr) {</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 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%);">+           pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* add on to the existing Supported header */</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_strassign(&hdr->values[hdr->count++], name);</span><br><span style="color: hsl(120, 100%, 40%);">+</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%);">+/*! \brief Helper function to add configured supported headers */</span><br><span style="color: hsl(120, 100%, 40%);">+static int add_configured_supported_headers(struct sip_outbound_registration_client_state *client_state, pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  if (client_state->support_path) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (!add_to_supported_header(tdata, &PATH_NAME)) {</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%);">+   if (client_state->support_outbound) {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (!add_to_supported_header(tdata, &OUTBOUND_NAME)) {</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%);">+   return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \brief Callback function for registering */</span><br><span> static int handle_client_registration(void *data)</span><br><span> {</span><br><span>  RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);</span><br><span>  pjsip_tx_data *tdata;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+     if (set_outbound_initial_authentication_credentials(client_state->client, &client_state->outbound_auths)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_WARNING, "Failed to set initial authentication credentials\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>  if (client_state->status == SIP_REGISTRATION_STOPPED</span><br><span>              || pjsip_regc_register(client_state->client, PJ_FALSE, &tdata) != PJ_SUCCESS) {</span><br><span>               return 0;</span><br><span>@@ -578,23 +785,9 @@</span><br><span>                     (int) info.client_uri.slen, info.client_uri.ptr);</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (client_state->support_path) {</span><br><span style="color: hsl(0, 100%, 40%);">-            pjsip_supported_hdr *hdr;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_SUPPORTED, NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-               if (!hdr) {</span><br><span style="color: hsl(0, 100%, 40%);">-                     /* insert a new Supported header */</span><br><span style="color: hsl(0, 100%, 40%);">-                     hdr = pjsip_supported_hdr_create(tdata->pool);</span><br><span style="color: hsl(0, 100%, 40%);">-                       if (!hdr) {</span><br><span style="color: hsl(0, 100%, 40%);">-                             pjsip_tx_data_dec_ref(tdata);</span><br><span style="color: hsl(0, 100%, 40%);">-                           return -1;</span><br><span style="color: hsl(0, 100%, 40%);">-                      }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);</span><br><span style="color: hsl(0, 100%, 40%);">-             }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               /* add on to the existing Supported header */</span><br><span style="color: hsl(0, 100%, 40%);">-           pj_strassign(&hdr->values[hdr->count++], &PATH_NAME);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!add_configured_supported_headers(client_state, tdata)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_WARNING, "Failed to set supported headers\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          return -1;</span><br><span>   }</span><br><span> </span><br><span>        registration_client_send(client_state, tdata);</span><br><span>@@ -707,6 +900,7 @@</span><br><span>                         update_client_state_status(client_state, SIP_REGISTRATION_STOPPING);</span><br><span>                         client_state->destroy = 1;</span><br><span>                        if (pjsip_regc_unregister(client_state->client, &tdata) == PJ_SUCCESS</span><br><span style="color: hsl(120, 100%, 40%);">+                          && add_configured_supported_headers(client_state, tdata)</span><br><span>                             && registration_client_send(client_state, tdata) == PJ_SUCCESS) {</span><br><span>                            ao2_ref(client_state, -1);</span><br><span>                           return 0;</span><br><span>@@ -885,6 +1079,32 @@</span><br><span>    ao2_ref(monitor, -1);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void save_response_fields_to_client_state(struct registration_response *response)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   static const pj_str_t associated_uri_str = { "P-Associated-URI", 16 };</span><br><span style="color: hsl(120, 100%, 40%);">+      static const pj_str_t service_route_str = { "Service-Route", 13 };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        pjsip_hdr *service_route_hdr = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  pjsip_msg *msg = response->rdata->msg_info.msg;</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsip_hdr *associated_uri_hdr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      AST_VECTOR_INIT(&response->client_state->service_route_vector, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+  while((service_route_hdr = (pjsip_hdr*)pjsip_msg_find_hdr_by_name(msg, &service_route_str, service_route_hdr ? service_route_hdr->next : NULL))) {</span><br><span style="color: hsl(120, 100%, 40%);">+             pj_str_t value = ((pjsip_generic_string_hdr*)service_route_hdr)->hvalue;</span><br><span style="color: hsl(120, 100%, 40%);">+           pj_str_t copy;</span><br><span style="color: hsl(120, 100%, 40%);">+                pj_strdup_with_null(reg_pool, &copy, &value);</span><br><span style="color: hsl(120, 100%, 40%);">+         AST_VECTOR_APPEND(&response->client_state->service_route_vector, copy);</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_log(LOG_DEBUG, "Stored service-route: %s\n", copy.ptr);</span><br><span 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 ((associated_uri_hdr = (pjsip_hdr*)pjsip_msg_find_hdr_by_name(msg, &associated_uri_str, NULL))) {</span><br><span style="color: hsl(120, 100%, 40%);">+              pj_str_t value = ((pjsip_generic_string_hdr*)associated_uri_hdr)->hvalue;</span><br><span style="color: hsl(120, 100%, 40%);">+          pj_strdup_with_null(reg_pool, &response->client_state->associated_uri, &value);</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_DEBUG, "Stored associated uri: %s\n", response->client_state->associated_uri.ptr);</span><br><span 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> /*! \brief Callback function for handling a response to a registration attempt */</span><br><span> static int handle_registration_response(void *data)</span><br><span> {</span><br><span>@@ -964,6 +1184,9 @@</span><br><span>                             registration_transport_shutdown_cb, response->client_state->registration_name,</span><br><span>                                 monitor_matcher);</span><br><span>            }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           save_response_fields_to_client_state(response);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>    } else if (response->client_state->destroy) {</span><br><span>          /* We need to deal with the pending destruction instead. */</span><br><span>  } else if (response->retry_after) {</span><br><span>@@ -1283,6 +1506,132 @@</span><br><span>     return rc;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* \brief Get google oauth2 access token using refresh token */</span><br><span style="color: hsl(120, 100%, 40%);">+static int fetch_access_token(struct ast_sip_auth *auth)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     RAII_VAR(char *, cmd, NULL, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+        char cBuf[4096] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+       const char *url = "https://www.googleapis.com/oauth2/v3/token";</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ast_json_error error;</span><br><span style="color: hsl(120, 100%, 40%);">+  RAII_VAR(struct ast_json *, jobj, NULL, ast_json_unref);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /* set timeout to be shorter than default 180s (also checks func_curl is available) */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (ast_func_write(NULL, "CURLOPT(conntimeout)", "10")) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_ERROR, "CURL is unavailable. This is required for OAuth 2.0 authentication. Please ensure it is loaded.\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%);">+   ast_asprintf(&cmd, "CURL(%s,client_id=%s&client_secret=%s&refresh_token=%s&grant_type=refresh_token)",</span><br><span style="color: hsl(120, 100%, 40%);">+                   url, auth->oauth_clientid, auth->oauth_secret, auth->refresh_token);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_debug(2, "Performing OAuth 2.0 authentication using command: %s\n", cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (ast_func_read(NULL, cmd, cBuf, sizeof(cBuf) - 1)) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "An error occurred while retreiving OAuth 2.0 access token\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%);">+   ast_debug(2, "OAuth 2.0 authentication returned: %s\n", cBuf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    jobj = ast_json_load_string(cBuf, &error);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (jobj) {</span><br><span style="color: hsl(120, 100%, 40%);">+           const char *token = ast_json_string_get(ast_json_object_get(jobj, "access_token"));</span><br><span style="color: hsl(120, 100%, 40%);">+         if (token) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  ast_string_field_set(auth, auth_pass, token);</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%);">+   ast_log(LOG_ERROR, "An error occurred while performing OAuth 2.0 authentication: %s\n", cBuf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Set pjsip registration context with any authentication credientials that need to be</span><br><span style="color: hsl(120, 100%, 40%);">+ * sent in the initial registration request</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param regc The pjsip registration context</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param auth_vector The vector of configured authentication credientials</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int set_outbound_initial_authentication_credentials(pjsip_regc *regc,</span><br><span style="color: hsl(120, 100%, 40%);">+              const struct ast_sip_auth_vector *auth_vector)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     size_t auth_size = AST_VECTOR_SIZE(auth_vector);</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_sip_auth **auths = ast_alloca(auth_size * sizeof(*auths));</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsip_cred_info *auth_creds = ast_alloca(1 * sizeof(*auth_creds));</span><br><span style="color: hsl(120, 100%, 40%);">+    pjsip_auth_clt_pref prefs;</span><br><span style="color: hsl(120, 100%, 40%);">+    int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+  int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (ast_sip_retrieve_auths(auth_vector, auths)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+             goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   for (i = 0; i < auth_size; ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+          switch (auths[i]->type) {</span><br><span style="color: hsl(120, 100%, 40%);">+          case AST_SIP_AUTH_TYPE_OAUTH:</span><br><span style="color: hsl(120, 100%, 40%);">+                 pj_cstr(&auth_creds[0].username, auths[i]->auth_user);</span><br><span style="color: hsl(120, 100%, 40%);">+                 pj_cstr(&auth_creds[0].scheme, "Bearer");</span><br><span style="color: hsl(120, 100%, 40%);">+                       pj_cstr(&auth_creds[0].realm, auths[i]->realm);</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_debug(2, "Obtaining OAuth access token\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (fetch_access_token(auths[i])) {</span><br><span style="color: hsl(120, 100%, 40%);">+                           ast_log(LOG_WARNING, "Obtaining OAuth access token failed\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                              res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_debug(2, "Setting data to %s\n", auths[i]->auth_pass);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                     pj_cstr(&auth_creds[0].data, auths[i]->auth_pass);</span><br><span style="color: hsl(120, 100%, 40%);">+                     auth_creds[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                     pjsip_regc_set_credentials(regc, 1, auth_creds);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                    /* for oauth, send auth without waiting for unauthorized response */</span><br><span style="color: hsl(120, 100%, 40%);">+                  prefs.initial_auth = PJ_TRUE;</span><br><span style="color: hsl(120, 100%, 40%);">+                 pj_cstr(&prefs.algorithm, "oauth");</span><br><span style="color: hsl(120, 100%, 40%);">+                     pjsip_regc_set_prefs(regc, &prefs);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                     break;</span><br><span style="color: hsl(120, 100%, 40%);">+                default:</span><br><span style="color: hsl(120, 100%, 40%);">+                      /* other cases handled after receiving auth rejection */</span><br><span style="color: hsl(120, 100%, 40%);">+                      break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+cleanup:</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_cleanup_auths(auths, auth_size);</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%);">+/*! \brief Helper to convert ; seperated list to pjsip_param list */</span><br><span style="color: hsl(120, 100%, 40%);">+static pjsip_param* get_params_list_from_string(const char* param_string)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  pjsip_param *params;</span><br><span style="color: hsl(120, 100%, 40%);">+  char *buf;</span><br><span style="color: hsl(120, 100%, 40%);">+    char *word;</span><br><span style="color: hsl(120, 100%, 40%);">+   char *next;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ params = PJ_POOL_ALLOC_T(reg_pool, pjsip_param);</span><br><span style="color: hsl(120, 100%, 40%);">+      pj_list_init(params);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       buf = ast_strdupa(param_string);</span><br><span style="color: hsl(120, 100%, 40%);">+      next = buf;</span><br><span style="color: hsl(120, 100%, 40%);">+   while ((word = strsep(&next, ";"))) {</span><br><span style="color: hsl(120, 100%, 40%);">+           char name[31];</span><br><span style="color: hsl(120, 100%, 40%);">+                char value[31];</span><br><span style="color: hsl(120, 100%, 40%);">+               if (sscanf(word, "%30[^=]=%30[^=]", name, value) == 2) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    pjsip_param *param = PJ_POOL_ALLOC_T(reg_pool, pjsip_param);</span><br><span style="color: hsl(120, 100%, 40%);">+                  pj_strdup2_with_null(reg_pool, &param->name, name);</span><br><span style="color: hsl(120, 100%, 40%);">+                    pj_strdup2_with_null(reg_pool, &param->value, value);</span><br><span style="color: hsl(120, 100%, 40%);">+                  pj_list_insert_after(params, param);</span><br><span 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 params;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \brief Helper function that allocates a pjsip registration client and configures it */</span><br><span> static int sip_outbound_registration_regc_alloc(void *data)</span><br><span> {</span><br><span>@@ -1382,6 +1731,15 @@</span><br><span>               return -1;</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(registration->contact_additional_params)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           pjsip_param *params = get_params_list_from_string(registration->contact_additional_params);</span><br><span style="color: hsl(120, 100%, 40%);">+                pjsip_regc_update_contact(state->client_state->client, 1, &contact_uri, params);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!registration->transport_reuse) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_sip_set_transport_from_endpoint_override(&transport_from_endpoint_override);</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>@@ -1409,6 +1767,8 @@</span><br><span>   state->client_state->max_retries = registration->max_retries;</span><br><span>       state->client_state->retries = 0;</span><br><span>      state->client_state->support_path = registration->support_path;</span><br><span style="color: hsl(120, 100%, 40%);">+      state->client_state->support_outbound = registration->support_outbound;</span><br><span style="color: hsl(120, 100%, 40%);">+      state->client_state->transport_reuse = registration->transport_reuse;</span><br><span>       state->client_state->auth_rejection_permanent = registration->auth_rejection_permanent;</span><br><span> </span><br><span>         pjsip_regc_update_expires(state->client_state->client, registration->expiration);</span><br><span>@@ -1550,7 +1910,8 @@</span><br><span> </span><br><span>       cancel_registration(state->client_state);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        if (pjsip_regc_unregister(client, &tdata) == PJ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">+        if (pjsip_regc_unregister(client, &tdata) == PJ_SUCCESS</span><br><span style="color: hsl(120, 100%, 40%);">+           && add_configured_supported_headers(state->client_state, tdata)) {</span><br><span>                registration_client_send(state->client_state, tdata);</span><br><span>     }</span><br><span> </span><br><span>@@ -2137,6 +2498,107 @@</span><br><span>      reregister_all();</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Callback function for matching an outbound registration based on name */</span><br><span style="color: hsl(120, 100%, 40%);">+static int find_registration(void *obj, void *arg, int flags)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct sip_outbound_registration_state *state = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+  const char* target_name = arg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      const char* registration_name = ast_sorcery_object_get_id(state->registration);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return !strcmp(target_name, registration_name) ? CMP_MATCH : 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 transport_from_endpoint_override(const struct ast_sip_endpoint *endpoint, pjsip_transport** transport)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    RAII_VAR(struct ao2_container *, states, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+  RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!endpoint || ast_strlen_zero(endpoint->outbound_registration)) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_DEBUG, "Outgoing request not associated with a registration. No mangling necessary.\n");</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%);">+   states = ao2_global_obj_ref(current_states);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!states) {</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_log(LOG_ERROR, "Cannot find outbound registration states\n");</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%);">+   state = ao2_callback(states, 0, find_registration, (void*)endpoint->outbound_registration);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!state) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_ERROR, "Cannot find matching outbound registration state: %s\n", endpoint->outbound_registration);</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_log(LOG_DEBUG, "Setting transport to %p\n", (void*)state->client_state->transport);</span><br><span style="color: hsl(120, 100%, 40%);">+       *transport = state->client_state->transport;</span><br><span style="color: hsl(120, 100%, 40%);">+</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%);">+/*! \brief Mangle outgoing INVITEs by adding headers based on the response to the associated registration request */</span><br><span style="color: hsl(120, 100%, 40%);">+static void handle_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   static const pj_str_t route_str = { "Route", 5 };</span><br><span style="color: hsl(120, 100%, 40%);">+   static const pj_str_t pj_pai_name = { "P-Preferred-Identity", 20 };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       RAII_VAR(struct ao2_container *, states, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+  RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct service_route_vector_type service_routes;</span><br><span style="color: hsl(120, 100%, 40%);">+      pjsip_generic_string_hdr *pai_hdr;</span><br><span style="color: hsl(120, 100%, 40%);">+    int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!session || !session->endpoint || ast_strlen_zero(session->endpoint->outbound_registration)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_DEBUG, "Outgoing request not associated with a registration. No mangling necessary.\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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   states = ao2_global_obj_ref(current_states);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!states) {</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_log(LOG_ERROR, "Cannot find outbound registration states\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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   state = ao2_callback(states, 0, find_registration, (void*)session->endpoint->outbound_registration);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!state) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_ERROR, "Cannot find matching outbound registration state\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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_log(LOG_DEBUG, "Found matching outbound registration state\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* add Route for every Service-Route in associated registration response */</span><br><span style="color: hsl(120, 100%, 40%);">+   service_routes = state->client_state->service_route_vector;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   for (i = 0; i < AST_VECTOR_SIZE(&service_routes); ++i)</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+             pjsip_generic_string_hdr* route_hdr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                pj_str_t service_route_str = AST_VECTOR_GET(&service_routes, i);</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_DEBUG, "Found service-route. Adding route header for %s\n", service_route_str.ptr);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           route_hdr = pjsip_generic_string_hdr_create(tdata->pool, &route_str, &service_route_str);</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*)route_hdr);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* add ppi header for first Associated-URI in associated registration response */</span><br><span style="color: hsl(120, 100%, 40%);">+     pai_hdr = pjsip_generic_string_hdr_create(tdata->pool, &pj_pai_name, &state->client_state->associated_uri);</span><br><span style="color: hsl(120, 100%, 40%);">+  pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)pai_hdr);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* add outbound & path to supported header */</span><br><span style="color: hsl(120, 100%, 40%);">+     add_configured_supported_headers(state->client_state, 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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_sip_session_supplement gvsip_supplement = {</span><br><span style="color: hsl(120, 100%, 40%);">+        .method = "INVITE, CANCEL",</span><br><span style="color: hsl(120, 100%, 40%);">+ .outgoing_request = handle_outgoing_request,</span><br><span style="color: hsl(120, 100%, 40%);">+  .priority = AST_SIP_SUPPLEMENT_PRIORITY_LAST,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int unload_module(void)</span><br><span> {</span><br><span>   int remaining;</span><br><span>@@ -2178,6 +2640,8 @@</span><br><span> </span><br><span>   ast_debug(2, "Successful shutdown.\n");</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ ast_pjproject_caching_pool_destroy(&cachingpool);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>      ao2_cleanup(shutdown_group);</span><br><span>         shutdown_group = NULL;</span><br><span> </span><br><span>@@ -2204,6 +2668,9 @@</span><br><span>   ao2_global_obj_replace_unref(current_states, new_states);</span><br><span>    ao2_ref(new_states, -1);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+  ast_pjproject_caching_pool_init(&cachingpool, &pj_pool_factory_default_policy, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+    reg_pool = pj_pool_create(&cachingpool.factory, "registration", 4096, 4096, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>   /*</span><br><span>    * Register sorcery object descriptions.</span><br><span>      */</span><br><span>@@ -2219,6 +2686,7 @@</span><br><span>  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "server_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, server_uri));</span><br><span>     ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "client_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, client_uri));</span><br><span>     ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "contact_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, contact_user));</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "contact_additional_params", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, contact_additional_params));</span><br><span>       ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, transport));</span><br><span>       ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, outbound_proxy));</span><br><span>     ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "expiration", "3600", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, expiration));</span><br><span>@@ -2229,6 +2697,8 @@</span><br><span>   ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "auth_rejection_permanent", "yes", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, auth_rejection_permanent));</span><br><span>        ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "registration", "outbound_auth", "", outbound_auth_handler, outbound_auths_to_str, outbound_auths_to_var_list, 0, 0);</span><br><span>  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "support_path", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, support_path));</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "support_outbound", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, support_outbound));</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "transport_reuse", "yes", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, transport_reuse));</span><br><span>  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "line", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, line));</span><br><span>         ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, endpoint));</span><br><span> </span><br><span>@@ -2249,6 +2719,8 @@</span><br><span>     /* Register how this module identifies endpoints. */</span><br><span>         ast_sip_register_endpoint_identifier(&line_identifier);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+       ast_sip_session_register_supplement(&gvsip_supplement);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>        /* Register CLI commands. */</span><br><span>         cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);</span><br><span>         if (!cli_formatter) {</span><br><span>diff --git a/third-party/pjproject/patches/0110-oauth.patch b/third-party/pjproject/patches/0110-oauth.patch</span><br><span>new file mode 100644</span><br><span>index 0000000..ac1f2d5</span><br><span>--- /dev/null</span><br><span>+++ b/third-party/pjproject/patches/0110-oauth.patch</span><br><span>@@ -0,0 +1,127 @@</span><br><span style="color: hsl(120, 100%, 40%);">+diff -x '*.o' -x '*.a' -ru a/pjsip/include/pjsip/sip_auth_msg.h b/pjsip/include/pjsip/sip_auth_msg.h</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjsip/include/pjsip/sip_auth_msg.h 2011-05-05 01:14:19.000000000 -0500</span><br><span>++++ b/pjsip/include/pjsip/sip_auth_msg.h 2018-06-11 21:48:01.497904085 -0500</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -89,6 +89,23 @@</span><br><span style="color: hsl(120, 100%, 40%);">+ typedef struct pjsip_pgp_credential pjsip_pgp_credential;</span><br><span 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 structure describe credential used in Authorization and</span><br><span style="color: hsl(120, 100%, 40%);">++ * Proxy-Authorization header for oauth authentication scheme.</span><br><span style="color: hsl(120, 100%, 40%);">++ */</span><br><span style="color: hsl(120, 100%, 40%);">++struct pjsip_oauth_credential</span><br><span style="color: hsl(120, 100%, 40%);">++{</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_str_t    realm;          /**< Realm of the credential    */</span><br><span style="color: hsl(120, 100%, 40%);">++    pjsip_param other_param;    /**< Other parameters.          */</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_str_t    username;       /**< Username parameter.        */</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_str_t    token;          /**< Token parameter.           */</span><br><span 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%);">++ * @see pjsip_oauth_credential</span><br><span style="color: hsl(120, 100%, 40%);">++ */</span><br><span style="color: hsl(120, 100%, 40%);">++typedef struct pjsip_oauth_credential pjsip_oauth_credential;</span><br><span 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 structure describes SIP Authorization header (and also SIP</span><br><span style="color: hsl(120, 100%, 40%);">+  * Proxy-Authorization header).</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -106,6 +123,8 @@</span><br><span style="color: hsl(120, 100%, 40%);">+     pjsip_common_credential common; /**< Common fields.      */</span><br><span style="color: hsl(120, 100%, 40%);">+        pjsip_digest_credential digest; /**< Digest credentials.    */</span><br><span style="color: hsl(120, 100%, 40%);">+     pjsip_pgp_credential    pgp;    /**< PGP credentials.            */</span><br><span style="color: hsl(120, 100%, 40%);">++       pjsip_oauth_credential  oauth;  /**< OAuth credentials.     */</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">+     } credential;</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+diff -x '*.o' -x '*.a' -ru a/pjsip/include/pjsip/sip_auth_parser.h b/pjsip/include/pjsip/sip_auth_parser.h</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjsip/include/pjsip/sip_auth_parser.h       2011-05-05 01:14:19.000000000 -0500</span><br><span>++++ b/pjsip/include/pjsip/sip_auth_parser.h      2018-06-11 21:37:18.105232901 -0500</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -64,6 +64,7 @@</span><br><span style="color: hsl(120, 100%, 40%);">+                        pjsip_FALSE_STR,    /**< "false" string const.         */</span><br><span style="color: hsl(120, 100%, 40%);">+                        pjsip_DIGEST_STR,   /**< "digest" string const.        */</span><br><span style="color: hsl(120, 100%, 40%);">+                        pjsip_PGP_STR,      /**< "pgp" string const.           */</span><br><span style="color: hsl(120, 100%, 40%);">++                       pjsip_BEARER_STR,   /**< "bearer" string const.     */</span><br><span style="color: hsl(120, 100%, 40%);">+                   pjsip_MD5_STR,      /**< "md5" string const.           */</span><br><span style="color: hsl(120, 100%, 40%);">+                        pjsip_AUTH_STR;     /**< "auth" string const.          */</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+diff -x '*.o' -x '*.a' -ru a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjsip/src/pjsip/sip_auth_client.c  2017-03-31 01:02:48.000000000 -0500</span><br><span>++++ b/pjsip/src/pjsip/sip_auth_client.c  2018-06-11 22:26:04.728266296 -0500</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -959,13 +959,25 @@</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+                 hs = pjsip_authorization_hdr_create(tdata->pool);</span><br><span style="color: hsl(120, 100%, 40%);">+          pj_strdup(tdata->pool, &hs->scheme, &c->scheme);</span><br><span style="color: hsl(120, 100%, 40%);">+-            pj_strdup(tdata->pool, &hs->credential.digest.username,</span><br><span style="color: hsl(120, 100%, 40%);">+-                      &c->username);</span><br><span style="color: hsl(120, 100%, 40%);">+-              pj_strdup(tdata->pool, &hs->credential.digest.realm,</span><br><span style="color: hsl(120, 100%, 40%);">+-                         &c->realm);</span><br><span style="color: hsl(120, 100%, 40%);">+-         pj_strdup(tdata->pool, &hs->credential.digest.uri, &uri);</span><br><span style="color: hsl(120, 100%, 40%);">+-              pj_strdup(tdata->pool, &hs->credential.digest.algorithm,</span><br><span style="color: hsl(120, 100%, 40%);">++           if (pj_stricmp(&c->scheme, &pjsip_BEARER_STR)==0)</span><br><span style="color: hsl(120, 100%, 40%);">++         {</span><br><span style="color: hsl(120, 100%, 40%);">++                    pj_strdup(tdata->pool, &hs->credential.oauth.username,</span><br><span style="color: hsl(120, 100%, 40%);">++                                  &c->username);</span><br><span style="color: hsl(120, 100%, 40%);">++                        pj_strdup(tdata->pool, &hs->credential.oauth.realm,</span><br><span style="color: hsl(120, 100%, 40%);">++                                  &c->realm);</span><br><span style="color: hsl(120, 100%, 40%);">++                        pj_strdup(tdata->pool, &hs->credential.oauth.token,</span><br><span style="color: hsl(120, 100%, 40%);">++                                  &c->data);</span><br><span style="color: hsl(120, 100%, 40%);">++              }</span><br><span style="color: hsl(120, 100%, 40%);">++            else //if (pj_stricmp(&c->scheme, &pjsip_DIGEST_STR)==0)</span><br><span style="color: hsl(120, 100%, 40%);">++          {</span><br><span style="color: hsl(120, 100%, 40%);">++                    pj_strdup(tdata->pool, &hs->credential.digest.username,</span><br><span style="color: hsl(120, 100%, 40%);">++                              &c->username);</span><br><span style="color: hsl(120, 100%, 40%);">++                      pj_strdup(tdata->pool, &hs->credential.digest.realm,</span><br><span style="color: hsl(120, 100%, 40%);">++                                 &c->realm);</span><br><span style="color: hsl(120, 100%, 40%);">++                 pj_strdup(tdata->pool, &hs->credential.digest.uri, &uri);</span><br><span style="color: hsl(120, 100%, 40%);">++                      pj_strdup(tdata->pool, &hs->credential.digest.algorithm,</span><br><span style="color: hsl(120, 100%, 40%);">+                      &sess->pref.algorithm);</span><br><span style="color: hsl(120, 100%, 40%);">++             }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+          pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hs);</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+diff -x '*.o' -x '*.a' -ru a/pjsip/src/pjsip/sip_auth_msg.c b/pjsip/src/pjsip/sip_auth_msg.c</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjsip/src/pjsip/sip_auth_msg.c       2016-01-26 23:42:20.000000000 -0600</span><br><span>++++ b/pjsip/src/pjsip/sip_auth_msg.c     2018-06-11 22:31:08.414019962 -0500</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -103,6 +103,19 @@</span><br><span style="color: hsl(120, 100%, 40%);">+     return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">++static int print_oauth_credential(pjsip_oauth_credential *cred, char *buf, pj_size_t size)</span><br><span style="color: hsl(120, 100%, 40%);">++{</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_ssize_t printed;</span><br><span style="color: hsl(120, 100%, 40%);">++    char *startbuf = buf;</span><br><span style="color: hsl(120, 100%, 40%);">++    char *endbuf = buf + size;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++    copy_advance_pair_quote_cond_always(buf, "token=", 6, cred->token, '"', '"');</span><br><span style="color: hsl(120, 100%, 40%);">++    copy_advance_pair_quote_cond_always(buf, ", username=", 11, cred->username, '"', '"');</span><br><span style="color: hsl(120, 100%, 40%);">++    copy_advance_pair_quote_cond_always(buf, ", realm=", 8, cred->realm, '"', '"');</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++    return (int) (buf-startbuf);</span><br><span 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 pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr,</span><br><span style="color: hsl(120, 100%, 40%);">+                                         char *buf, pj_size_t size)</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -125,6 +138,10 @@</span><br><span style="color: hsl(120, 100%, 40%);">+     {</span><br><span style="color: hsl(120, 100%, 40%);">+        printed = print_pgp_credential(&hdr->credential.pgp, buf, endbuf - buf);</span><br><span style="color: hsl(120, 100%, 40%);">+     } </span><br><span style="color: hsl(120, 100%, 40%);">++    else if (pj_stricmp(&hdr->scheme, &pjsip_BEARER_STR) == 0)</span><br><span style="color: hsl(120, 100%, 40%);">++    {</span><br><span style="color: hsl(120, 100%, 40%);">++        printed = print_oauth_credential(&hdr->credential.oauth, buf, endbuf - buf);</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%);">+       pj_assert(0);</span><br><span style="color: hsl(120, 100%, 40%);">+         return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+diff -x '*.o' -x '*.a' -ru a/pjsip/src/pjsip/sip_auth_parser.c b/pjsip/src/pjsip/sip_auth_parser.c</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjsip/src/pjsip/sip_auth_parser.c 2014-06-09 21:56:56.000000000 -0500</span><br><span>++++ b/pjsip/src/pjsip/sip_auth_parser.c  2018-06-11 21:53:03.831715838 -0500</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -59,6 +59,7 @@</span><br><span style="color: hsl(120, 100%, 40%);">+                pjsip_QUOTED_DIGEST_STR =   { "\"Digest\"", 8},</span><br><span style="color: hsl(120, 100%, 40%);">+           pjsip_PGP_STR =             { "PGP", 3 },</span><br><span style="color: hsl(120, 100%, 40%);">+           pjsip_QUOTED_PGP_STR =      { "\"PGP\"", 5 },</span><br><span style="color: hsl(120, 100%, 40%);">++            pjsip_BEARER_STR =          { "Bearer", 6 },</span><br><span style="color: hsl(120, 100%, 40%);">+                pjsip_MD5_STR =             { "md5", 3 },</span><br><span style="color: hsl(120, 100%, 40%);">+           pjsip_QUOTED_MD5_STR =      { "\"md5\"", 5},</span><br><span style="color: hsl(120, 100%, 40%);">+              pjsip_AUTH_STR =            { "auth", 4},</span><br><span>diff --git a/third-party/pjproject/patches/0120-contact-params.patch b/third-party/pjproject/patches/0120-contact-params.patch</span><br><span>new file mode 100644</span><br><span>index 0000000..8fcc45b</span><br><span>--- /dev/null</span><br><span>+++ b/third-party/pjproject/patches/0120-contact-params.patch</span><br><span>@@ -0,0 +1,96 @@</span><br><span style="color: hsl(120, 100%, 40%);">+diff -ru a/pjsip/include/pjsip-ua/sip_regc.h b/pjsip/include/pjsip-ua/sip_regc.h</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjsip/include/pjsip-ua/sip_regc.h      2016-06-24 08:03:25.000000000 -0500</span><br><span>++++ b/pjsip/include/pjsip-ua/sip_regc.h  2018-07-04 13:29:26.165775909 -0500</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -413,7 +413,8 @@</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+ PJ_DECL(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,</span><br><span style="color: hsl(120, 100%, 40%);">+                                            int ccnt,</span><br><span style="color: hsl(120, 100%, 40%);">+-                                            const pj_str_t contact[] );</span><br><span style="color: hsl(120, 100%, 40%);">++                                          const pj_str_t contact[],</span><br><span style="color: hsl(120, 100%, 40%);">++                                            const pjsip_param* params );</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ /**</span><br><span style="color: hsl(120, 100%, 40%);">+  * Update the expires value. The next REGISTER request will contain</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Transaction settings */</span><br><span style="color: hsl(120, 100%, 40%);">+diff -ru a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjsip/src/pjsip-ua/sip_reg.c        2016-06-30 03:23:08.000000000 -0500</span><br><span>++++ b/pjsip/src/pjsip-ua/sip_reg.c       2018-07-04 13:30:23.440884417 -0500</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -250,7 +250,8 @@</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ static pj_status_t set_contact( pjsip_regc *regc,</span><br><span style="color: hsl(120, 100%, 40%);">+                               int contact_cnt,</span><br><span style="color: hsl(120, 100%, 40%);">+-                             const pj_str_t contact[] )</span><br><span style="color: hsl(120, 100%, 40%);">++                           const pj_str_t contact[],</span><br><span style="color: hsl(120, 100%, 40%);">++                            const pjsip_param *params )</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+     const pj_str_t CONTACT = { "Contact", 7 };</span><br><span style="color: hsl(120, 100%, 40%);">+     pjsip_contact_hdr *h;</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -321,6 +322,20 @@</span><br><span style="color: hsl(120, 100%, 40%);">+      pj_list_push_back(&sip_uri->other_param, xuid_param);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">++ /* Add additional contact params */</span><br><span style="color: hsl(120, 100%, 40%);">++  if (params)</span><br><span style="color: hsl(120, 100%, 40%);">++  {</span><br><span style="color: hsl(120, 100%, 40%);">++        pjsip_param* param = params->next;</span><br><span style="color: hsl(120, 100%, 40%);">++        while (param != params) {</span><br><span style="color: hsl(120, 100%, 40%);">++                pjsip_param *param_copy;</span><br><span style="color: hsl(120, 100%, 40%);">++             param_copy = PJ_POOL_ZALLOC_T(regc->pool, pjsip_param);</span><br><span style="color: hsl(120, 100%, 40%);">++           param_copy->name = param->name;</span><br><span style="color: hsl(120, 100%, 40%);">++                param_copy->value = param->value;</span><br><span style="color: hsl(120, 100%, 40%);">++              pj_list_push_back(&hdr->other_param, param_copy);</span><br><span style="color: hsl(120, 100%, 40%);">++             param = param->next;</span><br><span 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%);">+  pj_list_push_back(&regc->contact_hdr_list, hdr);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+@@ -376,7 +391,7 @@</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     /* Set "Contact" header. */</span><br><span style="color: hsl(120, 100%, 40%);">+-    status = set_contact( regc, contact_cnt, contact);</span><br><span style="color: hsl(120, 100%, 40%);">++    status = set_contact( regc, contact_cnt, contact, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (status != PJ_SUCCESS)</span><br><span style="color: hsl(120, 100%, 40%);">+     return status;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+@@ -709,14 +724,15 @@</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ PJ_DEF(pj_status_t) pjsip_regc_update_contact(  pjsip_regc *regc,</span><br><span style="color: hsl(120, 100%, 40%);">+                                               int contact_cnt,</span><br><span style="color: hsl(120, 100%, 40%);">+-                                             const pj_str_t contact[] )</span><br><span style="color: hsl(120, 100%, 40%);">++                                           const pj_str_t contact[],</span><br><span style="color: hsl(120, 100%, 40%);">++                                            const pjsip_param *params )</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_status_t status;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     PJ_ASSERT_RETURN(regc, PJ_EINVAL);</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     pj_lock_acquire(regc->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+-    status = set_contact( regc, contact_cnt, contact );</span><br><span style="color: hsl(120, 100%, 40%);">++    status = set_contact( regc, contact_cnt, contact, params );</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_lock_release(regc->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     return status;</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -1125,7 +1141,7 @@</span><br><span style="color: hsl(120, 100%, 40%);">+           }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+             /* Update contact address */</span><br><span style="color: hsl(120, 100%, 40%);">+-            pjsip_regc_update_contact(regc, param.contact_cnt, param.contact);</span><br><span style="color: hsl(120, 100%, 40%);">++            pjsip_regc_update_contact(regc, param.contact_cnt, param.contact, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+             update_contact = PJ_TRUE;</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+diff -ru a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjsip/src/pjsua-lib/pjsua_acc.c    2017-09-15 00:32:08.000000000 -0500</span><br><span>++++ b/pjsip/src/pjsua-lib/pjsua_acc.c    2018-07-04 13:30:55.098286217 -0500</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -1865,7 +1865,7 @@</span><br><span style="color: hsl(120, 100%, 40%);">+     if (contact_rewrite_method == PJSUA_CONTACT_REWRITE_NO_UNREG &&</span><br><span style="color: hsl(120, 100%, 40%);">+         acc->regc != NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+     {</span><br><span style="color: hsl(120, 100%, 40%);">+-     pjsip_regc_update_contact(acc->regc, 1, &acc->reg_contact);</span><br><span style="color: hsl(120, 100%, 40%);">++        pjsip_regc_update_contact(acc->regc, 1, &acc->reg_contact, 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%);">+     /* Perform new registration */</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/9505">change 9505</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/9505"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: Id214c2d1c550a41fcf564b7df8f3da7be565bd58 </div>
<div style="display:none"> Gerrit-Change-Number: 9505 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Nick French <naf@ou.edu> </div>