Merge "core: Remove 'Data Retrieval API'"
authorJenkins2 <jenkins2@gerrit.asterisk.org>
Fri, 7 Jul 2017 20:42:56 +0000 (15:42 -0500)
committerGerrit Code Review <gerrit2@gerrit.digium.api>
Fri, 7 Jul 2017 20:42:56 +0000 (15:42 -0500)
15 files changed:
CHANGES
bridges/bridge_native_rtp.c
channels/chan_pjsip.c
channels/chan_sip.c
configs/samples/pjsip.conf.sample
configure
contrib/ast-db-manage/config/versions/164abbd708c_add_auto_info_to_endpoint_dtmf_mode.py
include/asterisk/rtp_engine.h
main/channel.c
res/res_pjsip.c
res/res_pjsip/pjsip_distributor.c
third-party/configure.m4
third-party/pjproject/Makefile.rules
third-party/pjproject/configure.m4
third-party/pjproject/patches/0070-Set-PJSIP_INV_SUPPORT_UPDATE-correctly-in-pjsip_inv_.patch [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index ee7c128..f2760c3 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -63,6 +63,14 @@ chan_pjsip
    from the SDP, unless the remote side sends a different codec and we will
    switch to match.
 
+Build System
+------------------
+ * Added a new PJPROJECT_CONFIGURE_OPTS environment variable which can be used
+   to pass arbitrary options to the bundled pjproject configure.
+
+ * Automatically set the bundled pjproject configure --host and --build
+   options to match those supplied for the asterisk configure.
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 14.4.0 to Asterisk 14.5.0 ------------
 ------------------------------------------------------------------------------
index 4af93bf..a459c9c 100644 (file)
 #include "asterisk/frame.h"
 #include "asterisk/rtp_engine.h"
 
-/*! \brief Internal structure which contains information about bridged RTP channels */
-struct native_rtp_bridge_data {
+/*! \brief Internal structure which contains bridged RTP channel hook data */
+struct native_rtp_framehook_data {
        /*! \brief Framehook used to intercept certain control frames */
        int id;
        /*! \brief Set when this framehook has been detached */
        unsigned int detached;
 };
 
-/*! \brief Internal helper function which gets all RTP information (glue and instances) relating to the given channels */
-static enum ast_rtp_glue_result native_rtp_bridge_get(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp_glue **glue0,
-       struct ast_rtp_glue **glue1, struct ast_rtp_instance **instance0, struct ast_rtp_instance **instance1,
-       struct ast_rtp_instance **vinstance0, struct ast_rtp_instance **vinstance1)
+struct rtp_glue_stream {
+       /*! \brief RTP instance */
+       struct ast_rtp_instance *instance;
+       /*! \brief glue result */
+       enum ast_rtp_glue_result result;
+};
+
+struct rtp_glue_data {
+       /*!
+        * \brief glue callbacks
+        *
+        * \note The glue data is considered valid if cb is not NULL.
+        */
+       struct ast_rtp_glue *cb;
+       struct rtp_glue_stream audio;
+       struct rtp_glue_stream video;
+       /*! Combined glue result of both bridge channels. */
+       enum ast_rtp_glue_result result;
+};
+
+/*! \brief Internal structure which contains instance information about bridged RTP channels */
+struct native_rtp_bridge_channel_data {
+       /*! \brief Channel's hook data */
+       struct native_rtp_framehook_data *hook_data;
+       /*!
+        * \brief Glue callbacks to bring remote channel streams back to Asterisk.
+        * \note NULL if channel streams are local.
+        */
+       struct ast_rtp_glue *remote_cb;
+       /*! \brief Channel's cached RTP glue information */
+       struct rtp_glue_data glue;
+};
+
+static void rtp_glue_data_init(struct rtp_glue_data *glue)
 {
-       enum ast_rtp_glue_result audio_glue0_res;
-       enum ast_rtp_glue_result video_glue0_res;
-       enum ast_rtp_glue_result audio_glue1_res;
-       enum ast_rtp_glue_result video_glue1_res;
+       glue->cb = NULL;
+       glue->audio.instance = NULL;
+       glue->audio.result = AST_RTP_GLUE_RESULT_FORBID;
+       glue->video.instance = NULL;
+       glue->video.result = AST_RTP_GLUE_RESULT_FORBID;
+       glue->result = AST_RTP_GLUE_RESULT_FORBID;
+}
 
-       if (!(*glue0 = ast_rtp_instance_get_glue(ast_channel_tech(c0)->type)) ||
-               !(*glue1 = ast_rtp_instance_get_glue(ast_channel_tech(c1)->type))) {
-               return AST_RTP_GLUE_RESULT_FORBID;
+static void rtp_glue_data_destroy(struct rtp_glue_data *glue)
+{
+       if (!glue) {
+               return;
        }
+       ao2_cleanup(glue->audio.instance);
+       ao2_cleanup(glue->video.instance);
+}
+
+static void rtp_glue_data_reset(struct rtp_glue_data *glue)
+{
+       rtp_glue_data_destroy(glue);
+       rtp_glue_data_init(glue);
+}
+
+static void native_rtp_bridge_channel_data_free(struct native_rtp_bridge_channel_data *data)
+{
+       ast_debug(2, "Destroying channel tech_pvt data %p\n", data);
 
-       audio_glue0_res = (*glue0)->get_rtp_info(c0, instance0);
-       video_glue0_res = (*glue0)->get_vrtp_info ? (*glue0)->get_vrtp_info(c0, vinstance0) : AST_RTP_GLUE_RESULT_FORBID;
+       /*
+        * hook_data will probably already have been unreferenced by the framehook detach
+        * and the pointer set to null.
+        */
+       ao2_cleanup(data->hook_data);
 
-       audio_glue1_res = (*glue1)->get_rtp_info(c1, instance1);
-       video_glue1_res = (*glue1)->get_vrtp_info ? (*glue1)->get_vrtp_info(c1, vinstance1) : AST_RTP_GLUE_RESULT_FORBID;
+       rtp_glue_data_reset(&data->glue);
+       ast_free(data);
+}
+
+static struct native_rtp_bridge_channel_data *native_rtp_bridge_channel_data_alloc(void)
+{
+       struct native_rtp_bridge_channel_data *data;
+
+       data = ast_calloc(1, sizeof(*data));
+       if (data) {
+               rtp_glue_data_init(&data->glue);
+       }
+       return data;
+}
+
+/*!
+ * \internal
+ * \brief Helper function which gets all RTP information (glue and instances) relating to the given channels
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int rtp_glue_data_get(struct ast_channel *c0, struct rtp_glue_data *glue0,
+       struct ast_channel *c1, struct rtp_glue_data *glue1)
+{
+       struct ast_rtp_glue *cb0;
+       struct ast_rtp_glue *cb1;
+       enum ast_rtp_glue_result combined_result;
+
+       cb0 = ast_rtp_instance_get_glue(ast_channel_tech(c0)->type);
+       cb1 = ast_rtp_instance_get_glue(ast_channel_tech(c1)->type);
+       if (!cb0 || !cb1) {
+               /* One or both channels doesn't have any RTP glue registered. */
+               return -1;
+       }
+
+       /* The glue callbacks bump the RTP instance refcounts for us. */
+
+       glue0->cb = cb0;
+       glue0->audio.result = cb0->get_rtp_info(c0, &glue0->audio.instance);
+       glue0->video.result = cb0->get_vrtp_info
+               ? cb0->get_vrtp_info(c0, &glue0->video.instance) : AST_RTP_GLUE_RESULT_FORBID;
+
+       glue1->cb = cb1;
+       glue1->audio.result = cb1->get_rtp_info(c1, &glue1->audio.instance);
+       glue1->video.result = cb1->get_vrtp_info
+               ? cb1->get_vrtp_info(c1, &glue1->video.instance) : AST_RTP_GLUE_RESULT_FORBID;
+
+       /*
+        * Now determine the combined glue result.
+        */
 
        /* Apply any limitations on direct media bridging that may be present */
-       if (audio_glue0_res == audio_glue1_res && audio_glue1_res == AST_RTP_GLUE_RESULT_REMOTE) {
-               if ((*glue0)->allow_rtp_remote && !((*glue0)->allow_rtp_remote(c0, *instance1))) {
+       if (glue0->audio.result == glue1->audio.result && glue1->audio.result == AST_RTP_GLUE_RESULT_REMOTE) {
+               if (glue0->cb->allow_rtp_remote && !glue0->cb->allow_rtp_remote(c0, glue1->audio.instance)) {
                        /* If the allow_rtp_remote indicates that remote isn't allowed, revert to local bridge */
-                       audio_glue0_res = audio_glue1_res = AST_RTP_GLUE_RESULT_LOCAL;
-               } else if ((*glue1)->allow_rtp_remote && !((*glue1)->allow_rtp_remote(c1, *instance0))) {
-                       audio_glue0_res = audio_glue1_res = AST_RTP_GLUE_RESULT_LOCAL;
+                       glue0->audio.result = glue1->audio.result = AST_RTP_GLUE_RESULT_LOCAL;
+               } else if (glue1->cb->allow_rtp_remote && !glue1->cb->allow_rtp_remote(c1, glue0->audio.instance)) {
+                       glue0->audio.result = glue1->audio.result = AST_RTP_GLUE_RESULT_LOCAL;
                }
        }
-       if (video_glue0_res == video_glue1_res && video_glue1_res == AST_RTP_GLUE_RESULT_REMOTE) {
-               if ((*glue0)->allow_vrtp_remote && !((*glue0)->allow_vrtp_remote(c0, *instance1))) {
+       if (glue0->video.result == glue1->video.result && glue1->video.result == AST_RTP_GLUE_RESULT_REMOTE) {
+               if (glue0->cb->allow_vrtp_remote && !glue0->cb->allow_vrtp_remote(c0, glue1->audio.instance)) {
                        /* if the allow_vrtp_remote indicates that remote isn't allowed, revert to local bridge */
-                       video_glue0_res = video_glue1_res = AST_RTP_GLUE_RESULT_LOCAL;
-               } else if ((*glue1)->allow_vrtp_remote && !((*glue1)->allow_vrtp_remote(c1, *instance0))) {
-                       video_glue0_res = video_glue1_res = AST_RTP_GLUE_RESULT_LOCAL;
+                       glue0->video.result = glue1->video.result = AST_RTP_GLUE_RESULT_LOCAL;
+               } else if (glue1->cb->allow_vrtp_remote && !glue1->cb->allow_vrtp_remote(c1, glue0->audio.instance)) {
+                       glue0->video.result = glue1->video.result = AST_RTP_GLUE_RESULT_LOCAL;
                }
        }
 
        /* If we are carrying video, and both sides are not going to remotely bridge... fail the native bridge */
-       if (video_glue0_res != AST_RTP_GLUE_RESULT_FORBID
-               && (audio_glue0_res != AST_RTP_GLUE_RESULT_REMOTE
-                       || video_glue0_res != AST_RTP_GLUE_RESULT_REMOTE)) {
-               audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID;
+       if (glue0->video.result != AST_RTP_GLUE_RESULT_FORBID
+               && (glue0->audio.result != AST_RTP_GLUE_RESULT_REMOTE
+                       || glue0->video.result != AST_RTP_GLUE_RESULT_REMOTE)) {
+               glue0->audio.result = AST_RTP_GLUE_RESULT_FORBID;
        }
-       if (video_glue1_res != AST_RTP_GLUE_RESULT_FORBID
-               && (audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE
-                       || video_glue1_res != AST_RTP_GLUE_RESULT_REMOTE)) {
-               audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID;
+       if (glue1->video.result != AST_RTP_GLUE_RESULT_FORBID
+               && (glue1->audio.result != AST_RTP_GLUE_RESULT_REMOTE
+                       || glue1->video.result != AST_RTP_GLUE_RESULT_REMOTE)) {
+               glue1->audio.result = AST_RTP_GLUE_RESULT_FORBID;
        }
 
        /* The order of preference is: forbid, local, and remote. */
-       if (audio_glue0_res == AST_RTP_GLUE_RESULT_FORBID ||
-               audio_glue1_res == AST_RTP_GLUE_RESULT_FORBID) {
+       if (glue0->audio.result == AST_RTP_GLUE_RESULT_FORBID
+               || glue1->audio.result == AST_RTP_GLUE_RESULT_FORBID) {
                /* If any sort of bridge is forbidden just completely bail out and go back to generic bridging */
-               return AST_RTP_GLUE_RESULT_FORBID;
-       } else if (audio_glue0_res == AST_RTP_GLUE_RESULT_LOCAL ||
-               audio_glue1_res == AST_RTP_GLUE_RESULT_LOCAL) {
-               return AST_RTP_GLUE_RESULT_LOCAL;
+               combined_result = AST_RTP_GLUE_RESULT_FORBID;
+       } else if (glue0->audio.result == AST_RTP_GLUE_RESULT_LOCAL
+               || glue1->audio.result == AST_RTP_GLUE_RESULT_LOCAL) {
+               combined_result = AST_RTP_GLUE_RESULT_LOCAL;
        } else {
-               return AST_RTP_GLUE_RESULT_REMOTE;
+               combined_result = AST_RTP_GLUE_RESULT_REMOTE;
+       }
+       glue0->result = combined_result;
+       glue1->result = combined_result;
+
+       return 0;
+}
+
+/*!
+ * \internal
+ * \brief Get the current RTP native bridge combined glue result.
+ * \since 15.0.0
+ *
+ * \param c0 First bridge channel
+ * \param c1 Second bridge channel
+ *
+ * \note Both channels must be locked when calling this function.
+ *
+ * \return Current combined glue result.
+ */
+static enum ast_rtp_glue_result rtp_glue_get_current_combined_result(struct ast_channel *c0,
+       struct ast_channel *c1)
+{
+       struct rtp_glue_data glue_a;
+       struct rtp_glue_data glue_b;
+       struct rtp_glue_data *glue0;
+       struct rtp_glue_data *glue1;
+       enum ast_rtp_glue_result combined_result;
+
+       rtp_glue_data_init(&glue_a);
+       glue0 = &glue_a;
+       rtp_glue_data_init(&glue_b);
+       glue1 = &glue_b;
+       if (rtp_glue_data_get(c0, glue0, c1, glue1)) {
+               return AST_RTP_GLUE_RESULT_FORBID;
        }
+
+       combined_result = glue0->result;
+       rtp_glue_data_destroy(glue0);
+       rtp_glue_data_destroy(glue1);
+       return combined_result;
 }
 
 /*!
@@ -129,52 +267,91 @@ static void native_rtp_bridge_start(struct ast_bridge *bridge, struct ast_channe
 {
        struct ast_bridge_channel *bc0 = AST_LIST_FIRST(&bridge->channels);
        struct ast_bridge_channel *bc1 = AST_LIST_LAST(&bridge->channels);
-       enum ast_rtp_glue_result native_type = AST_RTP_GLUE_RESULT_FORBID;
-       struct ast_rtp_glue *glue0, *glue1;
-       RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup);
-       RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup);
-       RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup);
-       RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup);
-       RAII_VAR(struct ast_rtp_instance *, tinstance0, NULL, ao2_cleanup);
-       RAII_VAR(struct ast_rtp_instance *, tinstance1, NULL, ao2_cleanup);
-       RAII_VAR(struct ast_format_cap *, cap0, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup);
-       RAII_VAR(struct ast_format_cap *, cap1, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup);
+       struct native_rtp_bridge_channel_data *data0;
+       struct native_rtp_bridge_channel_data *data1;
+       struct rtp_glue_data *glue0;
+       struct rtp_glue_data *glue1;
+       struct ast_format_cap *cap0;
+       struct ast_format_cap *cap1;
+       enum ast_rtp_glue_result native_type;
 
        if (bc0 == bc1) {
                return;
        }
+       data0 = bc0->tech_pvt;
+       data1 = bc1->tech_pvt;
+       if (!data0 || !data1) {
+               /* Not all channels are joined with the bridge tech yet */
+               return;
+       }
+       glue0 = &data0->glue;
+       glue1 = &data1->glue;
 
        ast_channel_lock_both(bc0->chan, bc1->chan);
-       if (!bc0->suspended && !bc1->suspended) {
-               native_type = native_rtp_bridge_get(bc0->chan, bc1->chan, &glue0, &glue1, &instance0, &instance1, &vinstance0, &vinstance1);
+
+       if (!glue0->cb || !glue1->cb) {
+               /*
+                * Somebody doesn't have glue data so the bridge isn't running
+                *
+                * Actually neither side should have glue data.
+                */
+               ast_assert(!glue0->cb && !glue1->cb);
+
+               if (rtp_glue_data_get(bc0->chan, glue0, bc1->chan, glue1)) {
+                       /*
+                        * This might happen if one of the channels got masqueraded
+                        * at a critical time.  It's a bit of a stretch even then
+                        * since the channel is in a bridge.
+                        */
+                       goto done;
+               }
        }
 
+       ast_debug(2, "Bridge '%s'.  Tech starting '%s' and '%s' with target '%s'\n",
+               bridge->uniqueid, ast_channel_name(bc0->chan), ast_channel_name(bc1->chan),
+               target ? ast_channel_name(target) : "none");
+
+       native_type = glue0->result;
+
        switch (native_type) {
        case AST_RTP_GLUE_RESULT_LOCAL:
-               if (ast_rtp_instance_get_engine(instance0)->local_bridge) {
-                       ast_rtp_instance_get_engine(instance0)->local_bridge(instance0, instance1);
+               if (ast_rtp_instance_get_engine(glue0->audio.instance)->local_bridge) {
+                       ast_rtp_instance_get_engine(glue0->audio.instance)->local_bridge(glue0->audio.instance, glue1->audio.instance);
                }
-               if (ast_rtp_instance_get_engine(instance1)->local_bridge) {
-                       ast_rtp_instance_get_engine(instance1)->local_bridge(instance1, instance0);
+               if (ast_rtp_instance_get_engine(glue1->audio.instance)->local_bridge) {
+                       ast_rtp_instance_get_engine(glue1->audio.instance)->local_bridge(glue1->audio.instance, glue0->audio.instance);
                }
-               ast_rtp_instance_set_bridged(instance0, instance1);
-               ast_rtp_instance_set_bridged(instance1, instance0);
+               ast_rtp_instance_set_bridged(glue0->audio.instance, glue1->audio.instance);
+               ast_rtp_instance_set_bridged(glue1->audio.instance, glue0->audio.instance);
                ast_verb(4, "Locally RTP bridged '%s' and '%s' in stack\n",
                        ast_channel_name(bc0->chan), ast_channel_name(bc1->chan));
                break;
-
        case AST_RTP_GLUE_RESULT_REMOTE:
-               if (glue0->get_codec) {
-                       glue0->get_codec(bc0->chan, cap0);
+               cap0 = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+               cap1 = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+               if (!cap0 || !cap1) {
+                       ao2_cleanup(cap0);
+                       ao2_cleanup(cap1);
+                       break;
                }
-               if (glue1->get_codec) {
-                       glue1->get_codec(bc1->chan, cap1);
+
+               if (glue0->cb->get_codec) {
+                       glue0->cb->get_codec(bc0->chan, cap0);
+               }
+               if (glue1->cb->get_codec) {
+                       glue1->cb->get_codec(bc1->chan, cap1);
                }
 
-               /* If we have a target, it's the channel that received the UNHOLD or UPDATE_RTP_PEER frame and was told to resume */
+               /*
+                * If we have a target, it's the channel that received the UNHOLD or
+                * UPDATE_RTP_PEER frame and was told to resume
+                */
                if (!target) {
-                       glue0->update_peer(bc0->chan, instance1, vinstance1, tinstance1, cap1, 0);
-                       glue1->update_peer(bc1->chan, instance0, vinstance0, tinstance0, cap0, 0);
+                       /* Send both channels to remote */
+                       data0->remote_cb = glue0->cb;
+                       data1->remote_cb = glue1->cb;
+                       glue0->cb->update_peer(bc0->chan, glue1->audio.instance, glue1->video.instance, NULL, cap1, 0);
+                       glue1->cb->update_peer(bc1->chan, glue0->audio.instance, glue0->video.instance, NULL, cap0, 0);
                        ast_verb(4, "Remotely bridged '%s' and '%s' - media will flow directly between them\n",
                                ast_channel_name(bc0->chan), ast_channel_name(bc1->chan));
                } else {
@@ -184,51 +361,121 @@ static void native_rtp_bridge_start(struct ast_bridge *bridge, struct ast_channe
                         * already set up to handle the new media path or will have its own set of updates independent
                         * of this pass.
                         */
+                       ast_debug(2, "Bridge '%s'.  Sending '%s' back to remote\n",
+                               bridge->uniqueid, ast_channel_name(target));
                        if (bc0->chan == target) {
-                               glue0->update_peer(bc0->chan, instance1, vinstance1, tinstance1, cap1, 0);
+                               data0->remote_cb = glue0->cb;
+                               glue0->cb->update_peer(bc0->chan, glue1->audio.instance, glue1->video.instance, NULL, cap1, 0);
                        } else {
-                               glue1->update_peer(bc1->chan, instance0, vinstance0, tinstance0, cap0, 0);
+                               data1->remote_cb = glue1->cb;
+                               glue1->cb->update_peer(bc1->chan, glue0->audio.instance, glue0->video.instance, NULL, cap0, 0);
                        }
                }
+
+               ao2_cleanup(cap0);
+               ao2_cleanup(cap1);
                break;
        case AST_RTP_GLUE_RESULT_FORBID:
                break;
        }
 
+       if (native_type != AST_RTP_GLUE_RESULT_REMOTE) {
+               /* Bring any remaining channels back to us. */
+               if (data0->remote_cb) {
+                       ast_debug(2, "Bridge '%s'.  Bringing back '%s' to us\n",
+                               bridge->uniqueid, ast_channel_name(bc0->chan));
+                       data0->remote_cb->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0);
+                       data0->remote_cb = NULL;
+               }
+               if (data1->remote_cb) {
+                       ast_debug(2, "Bridge '%s'.  Bringing back '%s' to us\n",
+                               bridge->uniqueid, ast_channel_name(bc1->chan));
+                       data1->remote_cb->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0);
+                       data1->remote_cb = NULL;
+               }
+       }
+
+done:
        ast_channel_unlock(bc0->chan);
        ast_channel_unlock(bc1->chan);
 }
 
+/*!
+ * \internal
+ * \brief Stop native RTP bridging of two channels
+ *
+ * \param bridge The bridge that had native RTP bridging happening on it
+ * \param target If remote RTP bridging, the channel that is held.
+ *
+ * \note The first channel to leave the bridge triggers the cleanup for both channels
+ */
 static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel *target)
 {
        struct ast_bridge_channel *bc0 = AST_LIST_FIRST(&bridge->channels);
        struct ast_bridge_channel *bc1 = AST_LIST_LAST(&bridge->channels);
-       enum ast_rtp_glue_result native_type;
-       struct ast_rtp_glue *glue0, *glue1 = NULL;
-       RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup);
-       RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup);
-       RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup);
-       RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup);
+       struct native_rtp_bridge_channel_data *data0;
+       struct native_rtp_bridge_channel_data *data1;
+       struct rtp_glue_data *glue0;
+       struct rtp_glue_data *glue1;
 
        if (bc0 == bc1) {
                return;
        }
+       data0 = bc0->tech_pvt;
+       data1 = bc1->tech_pvt;
+       if (!data0 || !data1) {
+               /* Not all channels are joined with the bridge tech */
+               return;
+       }
+       glue0 = &data0->glue;
+       glue1 = &data1->glue;
+
+       ast_debug(2, "Bridge '%s'.  Tech stopping '%s' and '%s' with target '%s'\n",
+               bridge->uniqueid, ast_channel_name(bc0->chan), ast_channel_name(bc1->chan),
+               target ? ast_channel_name(target) : "none");
+
+       if (!glue0->cb || !glue1->cb) {
+               /*
+                * Somebody doesn't have glue data so the bridge isn't running
+                *
+                * Actually neither side should have glue data.
+                */
+               ast_assert(!glue0->cb && !glue1->cb);
+               /* At most one channel can be left at the remote endpoint here. */
+               ast_assert(!data0->remote_cb || !data1->remote_cb);
+
+               /* Bring selected channel streams back to us */
+               if (data0->remote_cb && (!target || target == bc0->chan)) {
+                       ast_channel_lock(bc0->chan);
+                       ast_debug(2, "Bridge '%s'.  Bringing back '%s' to us\n",
+                               bridge->uniqueid, ast_channel_name(bc0->chan));
+                       data0->remote_cb->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0);
+                       data0->remote_cb = NULL;
+                       ast_channel_unlock(bc0->chan);
+               }
+               if (data1->remote_cb && (!target || target == bc1->chan)) {
+                       ast_channel_lock(bc1->chan);
+                       ast_debug(2, "Bridge '%s'.  Bringing back '%s' to us\n",
+                               bridge->uniqueid, ast_channel_name(bc1->chan));
+                       data1->remote_cb->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0);
+                       data1->remote_cb = NULL;
+                       ast_channel_unlock(bc1->chan);
+               }
+               return;
+       }
 
        ast_channel_lock_both(bc0->chan, bc1->chan);
-       native_type = native_rtp_bridge_get(bc0->chan, bc1->chan, &glue0, &glue1, &instance0, &instance1, &vinstance0, &vinstance1);
 
-       switch (native_type) {
+       switch (glue0->result) {
        case AST_RTP_GLUE_RESULT_LOCAL:
-               if (ast_rtp_instance_get_engine(instance0)->local_bridge) {
-                       ast_rtp_instance_get_engine(instance0)->local_bridge(instance0, NULL);
-               }
-               if (instance1 && ast_rtp_instance_get_engine(instance1)->local_bridge) {
-                       ast_rtp_instance_get_engine(instance1)->local_bridge(instance1, NULL);
+               if (ast_rtp_instance_get_engine(glue0->audio.instance)->local_bridge) {
+                       ast_rtp_instance_get_engine(glue0->audio.instance)->local_bridge(glue0->audio.instance, NULL);
                }
-               ast_rtp_instance_set_bridged(instance0, NULL);
-               if (instance1) {
-                       ast_rtp_instance_set_bridged(instance1, NULL);
+               if (ast_rtp_instance_get_engine(glue1->audio.instance)->local_bridge) {
+                       ast_rtp_instance_get_engine(glue1->audio.instance)->local_bridge(glue1->audio.instance, NULL);
                }
+               ast_rtp_instance_set_bridged(glue0->audio.instance, NULL);
+               ast_rtp_instance_set_bridged(glue1->audio.instance, NULL);
                break;
        case AST_RTP_GLUE_RESULT_REMOTE:
                if (target) {
@@ -236,10 +483,38 @@ static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel
                         * If a target was provided, it is being put on hold and should expect to
                         * receive media from Asterisk instead of what it was previously connected to.
                         */
+                       ast_debug(2, "Bridge '%s'.  Bringing back '%s' to us\n",
+                               bridge->uniqueid, ast_channel_name(target));
                        if (bc0->chan == target) {
-                               glue0->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0);
+                               data0->remote_cb = NULL;
+                               glue0->cb->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0);
+                       } else {
+                               data1->remote_cb = NULL;
+                               glue1->cb->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0);
+                       }
+               } else {
+                       data0->remote_cb = NULL;
+                       data1->remote_cb = NULL;
+                       /*
+                        * XXX We don't want to bring back the channels if we are
+                        * switching to T.38.  We have received a reinvite on one channel
+                        * and we will be sending a reinvite on the other to start T.38.
+                        * If we bring the streams back now we confuse the chan_pjsip
+                        * channel driver processing the incoming T.38 reinvite with
+                        * reinvite glare.  I think this is really a bug in chan_pjsip
+                        * that this exception case is working around.
+                        */
+                       if (rtp_glue_get_current_combined_result(bc0->chan, bc1->chan)
+                               != AST_RTP_GLUE_RESULT_FORBID) {
+                               ast_debug(2, "Bridge '%s'.  Bringing back '%s' and '%s' to us\n",
+                                       bridge->uniqueid, ast_channel_name(bc0->chan),
+                                       ast_channel_name(bc1->chan));
+                               glue0->cb->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0);
+                               glue1->cb->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0);
                        } else {
-                               glue1->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0);
+                               ast_debug(2, "Bridge '%s'.  Skip bringing back '%s' and '%s' to us\n",
+                                       bridge->uniqueid, ast_channel_name(bc0->chan),
+                                       ast_channel_name(bc1->chan));
                        }
                }
                break;
@@ -247,10 +522,8 @@ static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel
                break;
        }
 
-       if (!target && native_type != AST_RTP_GLUE_RESULT_FORBID) {
-               glue0->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0);
-               glue1->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0);
-       }
+       rtp_glue_data_reset(glue0);
+       rtp_glue_data_reset(glue1);
 
        ast_debug(2, "Discontinued RTP bridging of '%s' and '%s' - media will flow through Asterisk core\n",
                ast_channel_name(bc0->chan), ast_channel_name(bc1->chan));
@@ -259,11 +532,15 @@ static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel
        ast_channel_unlock(bc1->chan);
 }
 
-/*! \brief Frame hook that is called to intercept hold/unhold */
-static struct ast_frame *native_rtp_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data)
+/*!
+ * \internal
+ * \brief Frame hook that is called to intercept hold/unhold
+ */
+static struct ast_frame *native_rtp_framehook(struct ast_channel *chan,
+       struct ast_frame *f, enum ast_framehook_event event, void *data)
 {
        RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
-       struct native_rtp_bridge_data *native_data = data;
+       struct native_rtp_framehook_data *native_data = data;
 
        if (!f || (event != AST_FRAMEHOOK_EVENT_WRITE)) {
                return f;
@@ -293,39 +570,49 @@ static struct ast_frame *native_rtp_framehook(struct ast_channel *chan, struct a
                }
                ast_bridge_unlock(bridge);
                ast_channel_lock(chan);
-
        }
 
        return f;
 }
 
-/*! \brief Callback function which informs upstream if we are consuming a frame of a specific type */
+/*!
+ * \internal
+ * \brief Callback function which informs upstream if we are consuming a frame of a specific type
+ */
 static int native_rtp_framehook_consume(void *data, enum ast_frame_type type)
 {
        return (type == AST_FRAME_CONTROL ? 1 : 0);
 }
 
-/*! \brief Internal helper function which checks whether the channels are compatible with our native bridging */
+/*!
+ * \internal
+ * \brief Internal helper function which checks whether a channel is compatible with our native bridging
+ */
 static int native_rtp_bridge_capable(struct ast_channel *chan)
 {
        return !ast_channel_has_hook_requiring_audio(chan);
 }
 
+/*!
+ * \internal
+ * \brief Internal helper function which checks whether both channels are compatible with our native bridging
+ */
 static int native_rtp_bridge_compatible_check(struct ast_bridge *bridge, struct ast_bridge_channel *bc0, struct ast_bridge_channel *bc1)
 {
        enum ast_rtp_glue_result native_type;
-       struct ast_rtp_glue *glue0;
-       struct ast_rtp_glue *glue1;
-       RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup);
-       RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup);
-       RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup);
-       RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup);
-       RAII_VAR(struct ast_format_cap *, cap0, NULL, ao2_cleanup);
-       RAII_VAR(struct ast_format_cap *, cap1, NULL, ao2_cleanup);
        int read_ptime0;
        int read_ptime1;
        int write_ptime0;
        int write_ptime1;
+       struct rtp_glue_data glue_a;
+       struct rtp_glue_data glue_b;
+       RAII_VAR(struct ast_format_cap *, cap0, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_format_cap *, cap1, NULL, ao2_cleanup);
+       RAII_VAR(struct rtp_glue_data *, glue0, NULL, rtp_glue_data_destroy);
+       RAII_VAR(struct rtp_glue_data *, glue1, NULL, rtp_glue_data_destroy);
+
+       ast_debug(1, "Bridge '%s'.  Checking compatability for channels '%s' and '%s'\n",
+               bridge->uniqueid, ast_channel_name(bc0->chan), ast_channel_name(bc1->chan));
 
        if (!native_rtp_bridge_capable(bc0->chan)) {
                ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has features which prevent it\n",
@@ -339,8 +626,17 @@ static int native_rtp_bridge_compatible_check(struct ast_bridge *bridge, struct
                return 0;
        }
 
-       native_type = native_rtp_bridge_get(bc0->chan, bc1->chan, &glue0, &glue1,
-               &instance0, &instance1, &vinstance0, &vinstance1);
+       rtp_glue_data_init(&glue_a);
+       glue0 = &glue_a;
+       rtp_glue_data_init(&glue_b);
+       glue1 = &glue_b;
+       if (rtp_glue_data_get(bc0->chan, glue0, bc1->chan, glue1)) {
+               ast_debug(1, "Bridge '%s' can not use native RTP bridge as could not get details\n",
+                       bridge->uniqueid);
+               return 0;
+       }
+       native_type = glue0->result;
+
        if (native_type == AST_RTP_GLUE_RESULT_FORBID) {
                ast_debug(1, "Bridge '%s' can not use native RTP bridge as it was forbidden while getting details\n",
                        bridge->uniqueid);
@@ -348,25 +644,25 @@ static int native_rtp_bridge_compatible_check(struct ast_bridge *bridge, struct
        }
 
        if (ao2_container_count(bc0->features->dtmf_hooks)
-               && ast_rtp_instance_dtmf_mode_get(instance0)) {
+               && ast_rtp_instance_dtmf_mode_get(glue0->audio.instance)) {
                ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has DTMF hooks\n",
                        bridge->uniqueid, ast_channel_name(bc0->chan));
                return 0;
        }
 
        if (ao2_container_count(bc1->features->dtmf_hooks)
-               && ast_rtp_instance_dtmf_mode_get(instance1)) {
+               && ast_rtp_instance_dtmf_mode_get(glue1->audio.instance)) {
                ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has DTMF hooks\n",
                        bridge->uniqueid, ast_channel_name(bc1->chan));
                return 0;
        }
 
        if (native_type == AST_RTP_GLUE_RESULT_LOCAL
-               && (ast_rtp_instance_get_engine(instance0)->local_bridge
-                       != ast_rtp_instance_get_engine(instance1)->local_bridge
-                       || (ast_rtp_instance_get_engine(instance0)->dtmf_compatible
-                               && !ast_rtp_instance_get_engine(instance0)->dtmf_compatible(bc0->chan,
-                                       instance0, bc1->chan, instance1)))) {
+               && (ast_rtp_instance_get_engine(glue0->audio.instance)->local_bridge
+                       != ast_rtp_instance_get_engine(glue1->audio.instance)->local_bridge
+                       || (ast_rtp_instance_get_engine(glue0->audio.instance)->dtmf_compatible
+                               && !ast_rtp_instance_get_engine(glue0->audio.instance)->dtmf_compatible(bc0->chan,
+                                       glue0->audio.instance, bc1->chan, glue1->audio.instance)))) {
                ast_debug(1, "Bridge '%s' can not use local native RTP bridge as local bridge or DTMF is not compatible\n",
                        bridge->uniqueid);
                return 0;
@@ -379,11 +675,11 @@ static int native_rtp_bridge_compatible_check(struct ast_bridge *bridge, struct
        }
 
        /* Make sure that codecs match */
-       if (glue0->get_codec) {
-               glue0->get_codec(bc0->chan, cap0);
+       if (glue0->cb->get_codec) {
+               glue0->cb->get_codec(bc0->chan, cap0);
        }
-       if (glue1->get_codec) {
-               glue1->get_codec(bc1->chan, cap1);
+       if (glue1->cb->get_codec) {
+               glue1->cb->get_codec(bc1->chan, cap1);
        }
        if (ast_format_cap_count(cap0) != 0
                && ast_format_cap_count(cap1) != 0
@@ -413,6 +709,10 @@ static int native_rtp_bridge_compatible_check(struct ast_bridge *bridge, struct
        return 1;
 }
 
+/*!
+ * \internal
+ * \brief Called by the bridge core "compatible' callback
+ */
 static int native_rtp_bridge_compatible(struct ast_bridge *bridge)
 {
        struct ast_bridge_channel *bc0;
@@ -437,10 +737,13 @@ static int native_rtp_bridge_compatible(struct ast_bridge *bridge)
        return is_compatible;
 }
 
-/*! \brief Helper function which adds frame hook to bridge channel */
+/*!
+ * \internal
+ * \brief Helper function which adds frame hook to bridge channel
+ */
 static int native_rtp_bridge_framehook_attach(struct ast_bridge_channel *bridge_channel)
 {
-       struct native_rtp_bridge_data *data = ao2_alloc(sizeof(*data), NULL);
+       struct native_rtp_bridge_channel_data *data = bridge_channel->tech_pvt;
        static struct ast_framehook_interface hook = {
                .version = AST_FRAMEHOOK_INTERFACE_VERSION,
                .event_cb = native_rtp_framehook,
@@ -449,45 +752,82 @@ static int native_rtp_bridge_framehook_attach(struct ast_bridge_channel *bridge_
                .disable_inheritance = 1,
        };
 
-       if (!data) {
+       ast_assert(data->hook_data == NULL);
+       data->hook_data = ao2_alloc_options(sizeof(*data->hook_data), NULL,
+               AO2_ALLOC_OPT_LOCK_NOLOCK);
+       if (!data->hook_data) {
                return -1;
        }
 
+       ast_debug(2, "Bridge '%s'.  Attaching hook data %p to '%s'\n",
+               bridge_channel->bridge->uniqueid, data, ast_channel_name(bridge_channel->chan));
+
        ast_channel_lock(bridge_channel->chan);
-       hook.data = ao2_bump(data);
-       data->id = ast_framehook_attach(bridge_channel->chan, &hook);
+       /* We're giving 1 ref to the framehook and keeping the one from the alloc for ourselves */
+       hook.data = ao2_bump(data->hook_data);
+       data->hook_data->id = ast_framehook_attach(bridge_channel->chan, &hook);
        ast_channel_unlock(bridge_channel->chan);
-       if (data->id < 0) {
-               /* We need to drop both the reference we hold, and the one the framehook would hold */
-               ao2_ref(data, -2);
+       if (data->hook_data->id < 0) {
+               /*
+                * We need to drop both the reference we hold in data,
+                * and the one the framehook would hold.
+                */
+               ao2_ref(data->hook_data, -2);
+               data->hook_data = NULL;
+
                return -1;
        }
 
-       bridge_channel->tech_pvt = data;
-
        return 0;
 }
 
-/*! \brief Helper function which removes frame hook from bridge channel */
+/*!
+ * \internal
+ * \brief Helper function which removes frame hook from bridge channel
+ */
 static void native_rtp_bridge_framehook_detach(struct ast_bridge_channel *bridge_channel)
 {
-       RAII_VAR(struct native_rtp_bridge_data *, data, bridge_channel->tech_pvt, ao2_cleanup);
+       struct native_rtp_bridge_channel_data *data = bridge_channel->tech_pvt;
 
-       if (!data) {
+       if (!data || !data->hook_data) {
                return;
        }
 
+       ast_debug(2, "Bridge '%s'.  Detaching hook data %p from '%s'\n",
+               bridge_channel->bridge->uniqueid, data->hook_data, ast_channel_name(bridge_channel->chan));
+
        ast_channel_lock(bridge_channel->chan);
-       ast_framehook_detach(bridge_channel->chan, data->id);
-       data->detached = 1;
+       ast_framehook_detach(bridge_channel->chan, data->hook_data->id);
+       data->hook_data->detached = 1;
        ast_channel_unlock(bridge_channel->chan);
-       bridge_channel->tech_pvt = NULL;
+       ao2_cleanup(data->hook_data);
+       data->hook_data = NULL;
 }
 
+/*!
+ * \internal
+ * \brief Called by the bridge core 'join' callback for each channel joining he bridge
+ */
 static int native_rtp_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
-       native_rtp_bridge_framehook_detach(bridge_channel);
+       ast_debug(2, "Bridge '%s'.  Channel '%s' is joining bridge tech\n",
+               bridge->uniqueid, ast_channel_name(bridge_channel->chan));
+
+       ast_assert(bridge_channel->tech_pvt == NULL);
+
+       if (bridge_channel->suspended) {
+               /* The channel will rejoin when it is unsuspended */
+               return 0;
+       }
+
+       bridge_channel->tech_pvt = native_rtp_bridge_channel_data_alloc();
+       if (!bridge_channel->tech_pvt) {
+               return -1;
+       }
+
        if (native_rtp_bridge_framehook_attach(bridge_channel)) {
+               native_rtp_bridge_channel_data_free(bridge_channel->tech_pvt);
+               bridge_channel->tech_pvt = NULL;
                return -1;
        }
 
@@ -495,15 +835,46 @@ static int native_rtp_bridge_join(struct ast_bridge *bridge, struct ast_bridge_c
        return 0;
 }
 
+/*!
+ * \internal
+ * \brief Add the channel back into the bridge
+ */
 static void native_rtp_bridge_unsuspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
+       ast_debug(2, "Bridge '%s'.  Channel '%s' is unsuspended back to bridge tech\n",
+               bridge->uniqueid, ast_channel_name(bridge_channel->chan));
        native_rtp_bridge_join(bridge, bridge_channel);
 }
 
+/*!
+ * \internal
+ * \brief Leave the bridge
+ */
 static void native_rtp_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
+       ast_debug(2, "Bridge '%s'.  Channel '%s' is leaving bridge tech\n",
+               bridge->uniqueid, ast_channel_name(bridge_channel->chan));
+
+       if (!bridge_channel->tech_pvt) {
+               return;
+       }
+
        native_rtp_bridge_framehook_detach(bridge_channel);
        native_rtp_bridge_stop(bridge, NULL);
+
+       native_rtp_bridge_channel_data_free(bridge_channel->tech_pvt);
+       bridge_channel->tech_pvt = NULL;
+}
+
+/*!
+ * \internal
+ * \brief Suspend the channel from the bridge
+ */
+static void native_rtp_bridge_suspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+       ast_debug(2, "Bridge '%s'.  Channel '%s' is suspending from bridge tech\n",
+               bridge->uniqueid, ast_channel_name(bridge_channel->chan));
+       native_rtp_bridge_leave(bridge, bridge_channel);
 }
 
 static int native_rtp_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
@@ -548,7 +919,7 @@ static struct ast_bridge_technology native_rtp_bridge = {
        .join = native_rtp_bridge_join,
        .unsuspend = native_rtp_bridge_unsuspend,
        .leave = native_rtp_bridge_leave,
-       .suspend = native_rtp_bridge_leave,
+       .suspend = native_rtp_bridge_suspend,
        .write = native_rtp_bridge_write,
        .compatible = native_rtp_bridge_compatible,
 };
index 0e4468c..fc59c90 100644 (file)
@@ -1372,8 +1372,7 @@ static int update_connected_line_information(void *data)
                        int generate_new_sdp;
 
                        method = session->endpoint->id.refresh_method;
-                       if (session->inv_session->invite_tsx
-                               && (session->inv_session->options & PJSIP_INV_SUPPORT_UPDATE)) {
+                       if (session->inv_session->options & PJSIP_INV_SUPPORT_UPDATE) {
                                method = AST_SIP_SESSION_REFRESH_METHOD_UPDATE;
                        }
 
index af82678..f2daf2b 100644 (file)
@@ -33277,17 +33277,17 @@ static int reload_config(enum channelreloadreason reason)
 
                /* If TCP is running on a different IP than UDP, then add it too */
                if (!ast_sockaddr_isnull(&sip_tcp_desc.local_address) &&
-                   !ast_sockaddr_cmp(&bindaddr, &sip_tcp_desc.local_address)) {
+                   ast_sockaddr_cmp_addr(&bindaddr, &sip_tcp_desc.local_address)) {
                        add_sip_domain(ast_sockaddr_stringify_addr(&sip_tcp_desc.local_address),
                                       SIP_DOMAIN_AUTO, NULL);
                }
 
                /* If TLS is running on a different IP than UDP and TCP, then add that too */
                if (!ast_sockaddr_isnull(&sip_tls_desc.local_address) &&
-                   !ast_sockaddr_cmp(&bindaddr, &sip_tls_desc.local_address) &&
-                   !ast_sockaddr_cmp(&sip_tcp_desc.local_address,
+                   ast_sockaddr_cmp_addr(&bindaddr, &sip_tls_desc.local_address) &&
+                   ast_sockaddr_cmp_addr(&sip_tcp_desc.local_address,
                                      &sip_tls_desc.local_address)) {
-                       add_sip_domain(ast_sockaddr_stringify_addr(&sip_tcp_desc.local_address),
+                       add_sip_domain(ast_sockaddr_stringify_addr(&sip_tls_desc.local_address),
                                       SIP_DOMAIN_AUTO, NULL);
                }
 
index ed5f93e..c05938e 100644 (file)
 ;direct_media_glare_mitigation=none     ; Mitigation of direct media re INVITE
                                         ; glare (default: "none")
 ;direct_media_method=invite     ; Direct Media method type (default: "invite")
-;connected_line_method=invite   ; Connected line method type (default:
-                                ; "invite")
+;connected_line_method=invite   ; Connected line method type.
+                                ; When set to "invite", check the remote's
+                                ; Allow header and if UPDATE is allowed, send
+                                ; UPDATE instead of INVITE to avoid SDP
+                                ; renegotiation.  If UPDATE is not Allowed,
+                                ; send INVITE.
+                                ; If set to "update", send UPDATE regardless
+                                ; of what the remote Allows.
+                                ; (default: "invite")
 ;direct_media=yes       ; Determines whether media may flow directly between
                         ; endpoints (default: "yes")
 ;disable_direct_media_on_nat=no ; Disable direct media session refreshes when
index 361af88..1808633 100755 (executable)
--- a/configure
+++ b/configure
@@ -1219,6 +1219,7 @@ PJPROJECT_LIB
 PBX_PJPROJECT
 PJPROJECT_DIR
 PJPROJECT_BUNDLED
+PJPROJECT_CONFIGURE_OPTS
 AST_C_COMPILER_FAMILY
 AST_CLANG_BLOCKS
 AST_CLANG_BLOCKS_LIBS
@@ -1355,7 +1356,6 @@ infodir
 docdir
 oldincludedir
 includedir
-runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -1484,6 +1484,7 @@ CXX
 CXXFLAGS
 CCC
 CXXCPP
+PJPROJECT_CONFIGURE_OPTS
 PKG_CONFIG
 PKG_CONFIG_PATH
 PKG_CONFIG_LIBDIR
@@ -1539,7 +1540,6 @@ datadir='${datarootdir}'
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
-runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1792,15 +1792,6 @@ do
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
-  -runstatedir | --runstatedir | --runstatedi | --runstated \
-  | --runstate | --runstat | --runsta | --runst | --runs \
-  | --run | --ru | --r)
-    ac_prev=runstatedir ;;
-  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
-  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
-  | --run=* | --ru=* | --r=*)
-    runstatedir=$ac_optarg ;;
-
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1938,7 +1929,7 @@ fi
 for ac_var in  exec_prefix prefix bindir sbindir libexecdir datarootdir \
                datadir sysconfdir sharedstatedir localstatedir includedir \
                oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-               libdir localedir mandir runstatedir
+               libdir localedir mandir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -2091,7 +2082,6 @@ Fine tuning of the installation directories:
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
-  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
@@ -2242,6 +2232,8 @@ Some influential environment variables:
   CXX         C++ compiler command
   CXXFLAGS    C++ compiler flags
   CXXCPP      C++ preprocessor
+  PJPROJECT_CONFIGURE_OPTS
+              Additional configure options to pass to bundled pjproject
   PKG_CONFIG  path to pkg-config utility
   PKG_CONFIG_PATH
               directories to add to pkg-config's search path
@@ -9315,20 +9307,33 @@ $as_echo "configuring" >&6; }
                as_fn_error $? "cat is required to build bundled pjproject" "$LINENO" 5
        fi
 
+
+       this_host=$(./config.sub $(./config.guess))
+       if test "$build" != "$this_host" ; then
+               PJPROJECT_CONFIGURE_OPTS+=" --build=$build"
+       fi
+       if test "$host" != "$this_host" ; then
+               PJPROJECT_CONFIGURE_OPTS+=" --host=$host"
+       fi
+
        export TAR PATCH SED NM EXTERNALS_CACHE_DIR DOWNLOAD_TO_STDOUT DOWNLOAD_TIMEOUT DOWNLOAD MD5 CAT
-       ${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} EXTERNALS_CACHE_DIR=${EXTERNALS_CACHE_DIR} configure
+       export NOISY_BUILD
+       ${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} \
+               PJPROJECT_CONFIGURE_OPTS="$PJPROJECT_CONFIGURE_OPTS" \
+               EXTERNALS_CACHE_DIR="${EXTERNALS_CACHE_DIR}" \
+               configure
        if test $? -ne 0 ; then
                { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
 $as_echo "failed" >&6; }
                { $as_echo "$as_me:${as_lineno-$LINENO}: Unable to configure ${PJPROJECT_DIR}" >&5
 $as_echo "$as_me: Unable to configure ${PJPROJECT_DIR}" >&6;}
-               as_fn_error $? "Run \"${GNU_MAKE} -C ${PJPROJECT_DIR} NOISY_BUILD=yes configure\" to see error details." "$LINENO" 5
+               as_fn_error $? "Re-run the ./configure command with 'NOISY_BUILD=yes' appended to see error details." "$LINENO" 5
        fi
 
        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for bundled pjproject" >&5
 $as_echo_n "checking for bundled pjproject... " >&6; }
 
-       PJPROJECT_INCLUDE=$(${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} EXTERNALS_CACHE_DIR=${EXTERNALS_CACHE_DIR} echo_cflags)
+       PJPROJECT_INCLUDE=$(${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} PJPROJECT_CONFIGURE_OPTS="$PJPROJECT_CONFIGURE_OPTS" EXTERNALS_CACHE_DIR="${EXTERNALS_CACHE_DIR}" echo_cflags)
        PJPROJECT_CFLAGS="$PJPROJECT_INCLUDE"
        PBX_PJPROJECT=1
 
@@ -14910,7 +14915,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
                       && LARGE_OFF_T % 2147483647 == 1)
                      ? 1 : -1];
@@ -14956,7 +14961,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
                       && LARGE_OFF_T % 2147483647 == 1)
                      ? 1 : -1];
@@ -14980,7 +14985,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
                       && LARGE_OFF_T % 2147483647 == 1)
                      ? 1 : -1];
@@ -15025,7 +15030,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
                       && LARGE_OFF_T % 2147483647 == 1)
                      ? 1 : -1];
@@ -15049,7 +15054,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
                       && LARGE_OFF_T % 2147483647 == 1)
                      ? 1 : -1];
index dbc8ce9..20cab2f 100644 (file)
@@ -1,17 +1,18 @@
 """Add auto_info to endpoint dtmf_mode
 
 Revision ID: 164abbd708c
-Revises: 86bb1efa278d
+Revises: 39959b9c2566
 Create Date: 2017-06-19 13:55:15.354706
 
 """
 
 # revision identifiers, used by Alembic.
 revision = '164abbd708c'
-down_revision = '86bb1efa278d'
+down_revision = '39959b9c2566'
 
 from alembic import op
 import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
 
 OLD_ENUM = ['rfc4733', 'inband', 'info', 'auto']
 NEW_ENUM = ['rfc4733', 'inband', 'info', 'auto', 'auto_info']
index 5f43916..36f499b 100644 (file)
@@ -640,12 +640,13 @@ struct ast_rtp_glue {
        /*!
         * \brief Used to prevent two channels from remotely bridging audio rtp if the channel tech has a
         *        reason for prohibiting it based on qualities that need to be compared from both channels.
-        * \note This function may be NULL for a given channel driver. This should be accounted for and if that is the case, function this is not used.
+        * \note This function may be NULL for a given channel driver. This should be accounted for and if that is the case, this function is not used.
         */
        int (*allow_rtp_remote)(struct ast_channel *chan1, struct ast_rtp_instance *instance);
        /*!
         * \brief Callback for retrieving the RTP instance carrying video
         * \note This function increases the reference count on the returned RTP instance.
+        * \note This function may be NULL for a given channel driver. This should be accounted for and if that is the case, this function is not used.
         */
        enum ast_rtp_glue_result (*get_vrtp_info)(struct ast_channel *chan, struct ast_rtp_instance **instance);
        /*!
@@ -658,11 +659,15 @@ struct ast_rtp_glue {
        /*!
         * \brief Callback for retrieving the RTP instance carrying text
         * \note This function increases the reference count on the returned RTP instance.
+        * \note This function may be NULL for a given channel driver. This should be accounted for and if that is the case, this function is not used.
         */
        enum ast_rtp_glue_result (*get_trtp_info)(struct ast_channel *chan, struct ast_rtp_instance **instance);
        /*! Callback for updating the destination that the remote side should send RTP to */
        int (*update_peer)(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_rtp_instance *vinstance, struct ast_rtp_instance *tinstance, const struct ast_format_cap *cap, int nat_active);
-       /*! Callback for retrieving codecs that the channel can do.  Result returned in result_cap. */
+       /*!
+        * \brief Callback for retrieving codecs that the channel can do.  Result returned in result_cap.
+        * \note This function may be NULL for a given channel driver. This should be accounted for and if that is the case, this function is not used.
+        */
        void (*get_codec)(struct ast_channel *chan, struct ast_format_cap *result_cap);
        /*! Linked list information */
        AST_RWLIST_ENTRY(ast_rtp_glue) entry;
index 1134d01..811826f 100644 (file)
@@ -3261,8 +3261,12 @@ int ast_waitfordigit_full(struct ast_channel *c, int timeout_ms, int audiofd, in
                } else if (rchan) {
                        int res;
                        struct ast_frame *f = ast_read(c);
-                       if (!f)
+
+                       if (!f) {
+                               ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+
                                return -1;
+                       }
 
                        switch (f->frametype) {
                        case AST_FRAME_DTMF_BEGIN:
index 0cf0343..8923540 100644 (file)
                                        <description>
                                                <para>Method used when updating connected line information.</para>
                                                <enumlist>
-                                                       <enum name="invite" />
+                                                       <enum name="invite">
+                                                       <para>When set to <literal>invite</literal>, check the remote's Allow header and
+                                                       if UPDATE is allowed, send UPDATE instead of INVITE to avoid SDP
+                                                       renegotiation.  If UPDATE is not Allowed, send INVITE.</para>
+                                                       </enum>
                                                        <enum name="reinvite">
                                                                <para>Alias for the <literal>invite</literal> value.</para>
                                                        </enum>
-                                                       <enum name="update" />
+                                                       <enum name="update">
+                                                       <para>If set to <literal>update</literal>, send UPDATE regardless of what the remote
+                                                       Allows. </para>
+                                                       </enum>
                                                </enumlist>
                                        </description>
                                </configOption>
index cca26a8..cf1b04a 100644 (file)
@@ -150,62 +150,189 @@ static struct ast_taskprocessor *find_request_serializer(pjsip_rx_data *rdata)
 
 /*! Dialog-specific information the distributor uses */
 struct distributor_dialog_data {
+       /*! dialog_associations ao2 container key */
+       pjsip_dialog *dlg;
        /*! Serializer to distribute tasks to for this dialog */
        struct ast_taskprocessor *serializer;
        /*! Endpoint associated with this dialog */
        struct ast_sip_endpoint *endpoint;
 };
 
+#define DIALOG_ASSOCIATIONS_BUCKETS 251
+
+static struct ao2_container *dialog_associations;
+
 /*!
  * \internal
+ * \brief Compute a hash value on an arbitrary buffer.
+ * \since 13.17.0
  *
- * \note Call this with the dialog locked
+ * \param[in] pos The buffer to add to the hash
+ * \param[in] len The buffer length to add to the hash
+ * \param[in] hash The hash value to add to
+ *
+ * \details
+ * This version of the function is for when you need to compute a
+ * hash of more than one buffer.
+ *
+ * This famous hash algorithm was written by Dan Bernstein and is
+ * commonly used.
+ *
+ * \sa http://www.cse.yorku.ca/~oz/hash.html
  */
-static struct distributor_dialog_data *distributor_dialog_data_alloc(pjsip_dialog *dlg)
+static int buf_hash_add(const char *pos, size_t len, int hash)
 {
-       struct distributor_dialog_data *dist;
+       while (len--) {
+               hash = hash * 33 ^ *pos++;
+       }
+
+       return hash;
+}
+
+/*!
+ * \internal
+ * \brief Compute a hash value on an arbitrary buffer.
+ * \since 13.17.0
+ *
+ * \param[in] pos The buffer to add to the hash
+ * \param[in] len The buffer length to add to the hash
+ *
+ * \details
+ * This version of the function is for when you need to compute a
+ * hash of more than one buffer.
+ *
+ * This famous hash algorithm was written by Dan Bernstein and is
+ * commonly used.
+ *
+ * \sa http://www.cse.yorku.ca/~oz/hash.html
+ */
+static int buf_hash(const char *pos, size_t len)
+{
+       return buf_hash_add(pos, len, 5381);
+}
 
-       dist = PJ_POOL_ZALLOC_T(dlg->pool, struct distributor_dialog_data);
-       pjsip_dlg_set_mod_data(dlg, distributor_mod.id, dist);
+static int dialog_associations_hash(const void *obj, int flags)
+{
+       const struct distributor_dialog_data *object;
+       union {
+               const pjsip_dialog *dlg;
+               const char buf[sizeof(pjsip_dialog *)];
+       } key;
 
-       return dist;
+       switch (flags & OBJ_SEARCH_MASK) {
+       case OBJ_SEARCH_KEY:
+               key.dlg = obj;
+               break;
+       case OBJ_SEARCH_OBJECT:
+               object = obj;
+               key.dlg = object->dlg;
+               break;
+       default:
+               /* Hash can only work on something with a full key. */
+               ast_assert(0);
+               return 0;
+       }
+       return ast_str_hash_restrict(buf_hash(key.buf, sizeof(key.buf)));
+}
+
+static int dialog_associations_cmp(void *obj, void *arg, int flags)
+{
+       const struct distributor_dialog_data *object_left = obj;
+       const struct distributor_dialog_data *object_right = arg;
+       const pjsip_dialog *right_key = arg;
+       int cmp = 0;
+
+       switch (flags & OBJ_SEARCH_MASK) {
+       case OBJ_SEARCH_OBJECT:
+               right_key = object_right->dlg;
+               /* Fall through */
+       case OBJ_SEARCH_KEY:
+               if (object_left->dlg == right_key) {
+                       cmp = CMP_MATCH;
+               }
+               break;
+       case OBJ_SEARCH_PARTIAL_KEY:
+               /* There is no such thing for this container. */
+               ast_assert(0);
+               break;
+       default:
+               cmp = 0;
+               break;
+       }
+       return cmp;
 }
 
 void ast_sip_dialog_set_serializer(pjsip_dialog *dlg, struct ast_taskprocessor *serializer)
 {
        struct distributor_dialog_data *dist;
-       SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock);
 
-       dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id);
+       ao2_wrlock(dialog_associations);
+       dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY | OBJ_NOLOCK);
        if (!dist) {
-               dist = distributor_dialog_data_alloc(dlg);
+               if (serializer) {
+                       dist = ao2_alloc(sizeof(*dist), NULL);
+                       if (dist) {
+                               dist->dlg = dlg;
+                               dist->serializer = serializer;
+                               ao2_link_flags(dialog_associations, dist, OBJ_NOLOCK);
+                               ao2_ref(dist, -1);
+                       }
+               }
+       } else {
+               ao2_lock(dist);
+               dist->serializer = serializer;
+               if (!dist->serializer && !dist->endpoint) {
+                       ao2_unlink_flags(dialog_associations, dist, OBJ_NOLOCK);
+               }
+               ao2_unlock(dist);
+               ao2_ref(dist, -1);
        }
-       dist->serializer = serializer;
+       ao2_unlock(dialog_associations);
 }
 
 void ast_sip_dialog_set_endpoint(pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint)
 {
        struct distributor_dialog_data *dist;
-       SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock);
 
-       dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id);
+       ao2_wrlock(dialog_associations);
+       dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY | OBJ_NOLOCK);
        if (!dist) {
-               dist = distributor_dialog_data_alloc(dlg);
+               if (endpoint) {
+                       dist = ao2_alloc(sizeof(*dist), NULL);
+                       if (dist) {
+                               dist->dlg = dlg;
+                               dist->endpoint = endpoint;
+                               ao2_link_flags(dialog_associations, dist, OBJ_NOLOCK);
+                               ao2_ref(dist, -1);
+                       }
+               }
+       } else {
+               ao2_lock(dist);
+               dist->endpoint = endpoint;
+               if (!dist->serializer && !dist->endpoint) {
+                       ao2_unlink_flags(dialog_associations, dist, OBJ_NOLOCK);
+               }
+               ao2_unlock(dist);
+               ao2_ref(dist, -1);
        }
-       dist->endpoint = endpoint;
+       ao2_unlock(dialog_associations);
 }
 
 struct ast_sip_endpoint *ast_sip_dialog_get_endpoint(pjsip_dialog *dlg)
 {
        struct distributor_dialog_data *dist;
-       SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock);
+       struct ast_sip_endpoint *endpoint;
 
-       dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id);
-       if (!dist || !dist->endpoint) {
-               return NULL;
+       dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY);
+       if (dist) {
+               ao2_lock(dist);
+               endpoint = ao2_bump(dist->endpoint);
+               ao2_unlock(dist);
+               ao2_ref(dist, -1);
+       } else {
+               endpoint = NULL;
        }
-       ao2_ref(dist->endpoint, +1);
-       return dist->endpoint;
+       return endpoint;
 }
 
 static pjsip_dialog *find_dialog(pjsip_rx_data *rdata)
@@ -237,7 +364,7 @@ static pjsip_dialog *find_dialog(pjsip_rx_data *rdata)
                        pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method) ||
                        rdata->msg_info.to->tag.slen != 0) {
                dlg = pjsip_ua_find_dialog(&rdata->msg_info.cid->id, local_tag,
-                               remote_tag, PJ_TRUE);
+                               remote_tag, PJ_FALSE);
                if (dlg) {
                        return dlg;
                }
@@ -275,11 +402,6 @@ static pjsip_dialog *find_dialog(pjsip_rx_data *rdata)
        pj_mutex_unlock(tsx->mutex);
 #endif
 
-       if (!dlg) {
-               return NULL;
-       }
-
-       pjsip_dlg_inc_lock(dlg);
        return dlg;
 }
 
@@ -302,16 +424,7 @@ static pjsip_dialog *find_dialog(pjsip_rx_data *rdata)
  */
 static int pjstr_hash_add(pj_str_t *str, int hash)
 {
-       size_t len;
-       const char *pos;
-
-       len = pj_strlen(str);
-       pos = pj_strbuf(str);
-       while (len--) {
-               hash = hash * 33 ^ *pos++;
-       }
-
-       return hash;
+       return buf_hash_add(pj_strbuf(str), pj_strlen(str), hash);
 }
 
 /*!
@@ -350,7 +463,7 @@ struct ast_taskprocessor *ast_sip_get_distributor_serializer(pjsip_rx_data *rdat
        /* Compute the hash from the SIP message call-id and remote-tag */
        hash = pjstr_hash(&rdata->msg_info.cid->id);
        hash = pjstr_hash_add(remote_tag, hash);
-       hash = abs(hash);
+       hash = ast_str_hash_restrict(hash);
 
        serializer = ao2_bump(distributor_pool[hash % ARRAY_LEN(distributor_pool)]);
        if (serializer) {
@@ -385,17 +498,18 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
 
        dlg = find_dialog(rdata);
        if (dlg) {
-               ast_debug(3, "Searching for serializer on dialog %s for %s\n",
+               ast_debug(3, "Searching for serializer associated with dialog %s for %s\n",
                        dlg->obj_name, pjsip_rx_data_get_info(rdata));
-               dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id);
+               dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY);
                if (dist) {
+                       ao2_lock(dist);
                        serializer = ao2_bump(dist->serializer);
+                       ao2_unlock(dist);
                        if (serializer) {
-                               ast_debug(3, "Found serializer %s on dialog %s\n",
+                               ast_debug(3, "Found serializer %s associated with dialog %s\n",
                                        ast_taskprocessor_name(serializer), dlg->obj_name);
                        }
                }
-               pjsip_dlg_dec_lock(dlg);
        }
 
        if (serializer) {
@@ -417,6 +531,7 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
                /* We have a BYE or CANCEL request without a serializer. */
                pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata,
                        PJSIP_SC_CALL_TSX_DOES_NOT_EXIST, NULL, NULL, NULL);
+               ao2_cleanup(dist);
                return PJ_TRUE;
        } else {
                if (ast_taskprocessor_alert_get()) {
@@ -431,6 +546,7 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
                         */
                        ast_debug(3, "Taskprocessor overload alert: Ignoring '%s'.\n",
                                pjsip_rx_data_get_info(rdata));
+                       ao2_cleanup(dist);
                        return PJ_TRUE;
                }
 
@@ -438,10 +554,17 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
                serializer = ast_sip_get_distributor_serializer(rdata);
        }
 
-       pjsip_rx_data_clone(rdata, 0, &clone);
+       if (pjsip_rx_data_clone(rdata, 0, &clone) != PJ_SUCCESS) {
+               ast_taskprocessor_unreference(serializer);
+               ao2_cleanup(dist);
+               return PJ_TRUE;
+       }
 
        if (dist) {
+               ao2_lock(dist);
                clone->endpt_info.mod_data[endpoint_mod.id] = ao2_bump(dist->endpoint);
+               ao2_unlock(dist);
+               ao2_cleanup(dist);
        }
 
        if (ast_sip_push_task(serializer, distribute, clone)) {
@@ -837,7 +960,7 @@ static int suspects_compare(void *obj, void *arg, int flags)
                /* Fall through */
        case OBJ_SEARCH_KEY:
                if (strcmp(object_left->src_name, right_key) == 0) {
-                       cmp = CMP_MATCH | CMP_STOP;
+                       cmp = CMP_MATCH;
                }
                break;
        case OBJ_SEARCH_PARTIAL_KEY:
@@ -852,15 +975,25 @@ static int suspects_compare(void *obj, void *arg, int flags)
        return cmp;
 }
 
-static int suspects_hash(const void *obj, int flags) {
-       const struct unidentified_request *object_left = obj;
+static int suspects_hash(const void *obj, int flags)
+{
+       const struct unidentified_request *object;
+       const char *key;
 
-       if (flags & OBJ_SEARCH_OBJECT) {
-               return ast_str_hash(object_left->src_name);
-       } else if (flags & OBJ_SEARCH_KEY) {
-               return ast_str_hash(obj);
+       switch (flags & OBJ_SEARCH_MASK) {
+       case OBJ_SEARCH_KEY:
+               key = obj;
+               break;
+       case OBJ_SEARCH_OBJECT:
+               object = obj;
+               key = object->src_name;
+               break;
+       default:
+               /* Hash can only work on something with a full key. */
+               ast_assert(0);
+               return 0;
        }
-       return -1;
+       return ast_str_hash(key);
 }
 
 static struct ao2_container *cli_unid_get_container(const char *regex)
@@ -1078,6 +1211,14 @@ int ast_sip_initialize_distributor(void)
                return -1;
        }
 
+       dialog_associations = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_RWLOCK, 0,
+               DIALOG_ASSOCIATIONS_BUCKETS, dialog_associations_hash, NULL,
+               dialog_associations_cmp);
+       if (!dialog_associations) {
+               ast_sip_destroy_distributor();
+               return -1;
+       }
+
        if (distributor_pool_setup()) {
                ast_sip_destroy_distributor();
                return -1;
@@ -1156,5 +1297,6 @@ void ast_sip_destroy_distributor(void)
 
        distributor_pool_shutdown();
 
+       ao2_cleanup(dialog_associations);
        ao2_cleanup(unidentified_requests);
 }
index 6354466..55b72da 100644 (file)
@@ -1,4 +1,7 @@
-
+#
+# If this file is changed, be sure to run ASTTOPDIR/bootstrap.sh
+# before committing.
+#
 
 AC_DEFUN([THIRD_PARTY_CONFIGURE],
 [
index 3f99c8a..acd7662 100644 (file)
@@ -1,8 +1,11 @@
 PJPROJECT_URL ?= https://raw.githubusercontent.com/asterisk/third-party/master/pjproject/$(PJPROJECT_VERSION)
 
+# PJPROJECT_CONFIGURE_OPTS could come from the command line or could be
+# set/modified by configure.m4 if the build or host tuples aren't the same
+# as the current build environment (cross-compile).
 # Even though we're not installing pjproject, we're setting prefix to /opt/pjproject to be safe
 
-PJPROJECT_CONFIG_OPTS = --prefix=/opt/pjproject \
+PJPROJECT_CONFIG_OPTS = $(PJPROJECT_CONFIGURE_OPTS) --prefix=/opt/pjproject \
        --disable-speex-codec \
        --disable-speex-aec \
        --disable-speex-aec \
index a5e9fca..709a706 100644 (file)
@@ -1,3 +1,8 @@
+#
+# If this file is changed, be sure to run ASTTOPDIR/bootstrap.sh
+# before committing.
+#
+
 AC_DEFUN([_PJPROJECT_CONFIGURE],
 [
        if test "${ac_mandatory_list#*PJPROJECT*}" != "$ac_mandatory_list" ; then
@@ -35,17 +40,30 @@ AC_DEFUN([_PJPROJECT_CONFIGURE],
                AC_MSG_ERROR(cat is required to build bundled pjproject)
        fi
 
+       AC_ARG_VAR([PJPROJECT_CONFIGURE_OPTS],[Additional configure options to pass to bundled pjproject])
+       this_host=$(./config.sub $(./config.guess))
+       if test "$build" != "$this_host" ; then
+               PJPROJECT_CONFIGURE_OPTS+=" --build=$build"
+       fi
+       if test "$host" != "$this_host" ; then
+               PJPROJECT_CONFIGURE_OPTS+=" --host=$host"
+       fi
+
        export TAR PATCH SED NM EXTERNALS_CACHE_DIR DOWNLOAD_TO_STDOUT DOWNLOAD_TIMEOUT DOWNLOAD MD5 CAT
-       ${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} EXTERNALS_CACHE_DIR=${EXTERNALS_CACHE_DIR} configure
+       export NOISY_BUILD
+       ${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} \
+               PJPROJECT_CONFIGURE_OPTS="$PJPROJECT_CONFIGURE_OPTS" \
+               EXTERNALS_CACHE_DIR="${EXTERNALS_CACHE_DIR}" \
+               configure
        if test $? -ne 0 ; then
                AC_MSG_RESULT(failed)
                AC_MSG_NOTICE(Unable to configure ${PJPROJECT_DIR})
-               AC_MSG_ERROR(Run "${GNU_MAKE} -C ${PJPROJECT_DIR} NOISY_BUILD=yes configure" to see error details.)
+               AC_MSG_ERROR(Re-run the ./configure command with 'NOISY_BUILD=yes' appended to see error details.)
        fi
 
        AC_MSG_CHECKING(for bundled pjproject)
 
-       PJPROJECT_INCLUDE=$(${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} EXTERNALS_CACHE_DIR=${EXTERNALS_CACHE_DIR} echo_cflags)
+       PJPROJECT_INCLUDE=$(${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} PJPROJECT_CONFIGURE_OPTS="$PJPROJECT_CONFIGURE_OPTS" EXTERNALS_CACHE_DIR="${EXTERNALS_CACHE_DIR}" echo_cflags)
        PJPROJECT_CFLAGS="$PJPROJECT_INCLUDE"
        PBX_PJPROJECT=1
 
diff --git a/third-party/pjproject/patches/0070-Set-PJSIP_INV_SUPPORT_UPDATE-correctly-in-pjsip_inv_.patch b/third-party/pjproject/patches/0070-Set-PJSIP_INV_SUPPORT_UPDATE-correctly-in-pjsip_inv_.patch
new file mode 100644 (file)
index 0000000..9238e3e
--- /dev/null
@@ -0,0 +1,29 @@
+From 1193681959816effa121c4470748d5faa3a59272 Mon Sep 17 00:00:00 2001
+From: George Joseph <gjoseph@digium.com>
+Date: Thu, 29 Jun 2017 13:42:10 -0600
+Subject: [PATCH] Set PJSIP_INV_SUPPORT_UPDATE correctly in
+ pjsip_inv_verify_request3
+
+pjsip_inv_verify_request3 was setting rem_options when UPDATE was
+detected in the Allow header.  That's just an internal variable and
+doesn't go anywhere.  It's '*options' that needs to be set.
+---
+ pjsip/src/pjsip-ua/sip_inv.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c
+index fbc8ebe..6db7e6b 100644
+--- a/pjsip/src/pjsip-ua/sip_inv.c
++++ b/pjsip/src/pjsip-ua/sip_inv.c
+@@ -1237,7 +1237,7 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request3(pjsip_rx_data *rdata,
+       if (i != allow->count) {
+           /* UPDATE is present in Allow */
+-          rem_option |= PJSIP_INV_SUPPORT_UPDATE;
++          *options |= PJSIP_INV_SUPPORT_UPDATE;
+       }
+     }
+-- 
+2.9.4
+