Merge "bridge_softmix.c: Report not talking immediately when muted."
authorJoshua Colp <jcolp@digium.com>
Fri, 2 Feb 2018 12:22:59 +0000 (06:22 -0600)
committerGerrit Code Review <gerrit2@gerrit.digium.api>
Fri, 2 Feb 2018 12:22:59 +0000 (06:22 -0600)
21 files changed:
CHANGES
apps/app_voicemail.c
apps/confbridge/conf_config_parser.c
apps/confbridge/include/confbridge.h
bridges/bridge_softmix.c
configs/samples/confbridge.conf.sample
configure.ac
contrib/ast-db-manage/config/versions/d3e4284f8707_add_prune_on_boot_to_ps_subscription_.py [new file with mode: 0644]
include/asterisk/bridge_technology.h
include/asterisk/dsp.h
include/asterisk/lock.h
include/asterisk/res_pjsip.h
main/dsp.c
res/res_pjsip.c
res/res_pjsip/include/res_pjsip_private.h
res/res_pjsip/pjsip_transport_events.c
res/res_pjsip_outbound_registration.c
res/res_pjsip_pubsub.c
res/res_pjsip_registrar.c
res/res_pjsip_registrar_expire.c [deleted file]
res/res_pjsip_session.c

diff --git a/CHANGES b/CHANGES
index 50ed2ca..b9d8032 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -54,6 +54,11 @@ res_pjsip
  * A new AMI action, PJSIPShowContacts, has been added which displays information
    about all configured PJSIP Contacts.
 
+res_pjsip_registrar_expire
+------------------
+ * The res_pjsip_registrar_expire module has been removed.  The functionality has
+   been moved into res_pjsip_registrar.
+
 cdr_syslog
 ------------------
  * The cdr_syslog module is now deprecated and by default it is no longer
@@ -81,6 +86,10 @@ res_pjsip
    identifier method split into the "ip" and "header" endpoint identifier
    methods.
 
+ * The pjsip_transport_event feature introduced in 15.1.0 has been refactored.
+   Any external modules that may have used that feature (highly unlikey) will
+   need to be changed as the API has been altered slightly.
+
 res_pjsip_endpoint_identifier_ip
 ------------------
  * The endpoint identifier "ip" method previously recognized endpoints either
@@ -95,6 +104,17 @@ res_pjsip_endpoint_identifier_ip
    you can now predict which endpoint is matched when a request comes in that
    matches both.
 
+res_pjsip_pubsub
+------------------
+ * In an earlier release, inbound registrations on a reliable transport
+   were pruned on Asterisk restart since the TCP connection would have
+   been torn down and become unusable when Asterisk stopped.  This same
+   process is now also applied to inbound subscriptions.  Since this
+   required the addition of a new column to the ps_subscription_persistence
+   realtime table, users who store their subscriptions in a database will
+   need to run the "alembic upgrade head" process to add the column to
+   the schema.
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 15.1.0 to Asterisk 15.2.0 ------------
 ------------------------------------------------------------------------------
index 4853a53..72e3e59 100644 (file)
@@ -3499,7 +3499,7 @@ static struct vm_state *get_vm_state_by_imapuser(const char *user, int interacti
        if (interactive) {
                struct vm_state *vms;
                pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
-               if ((vms = pthread_getspecific(ts_vmstate.key)) && vms->imapuser && !strcmp(vms->imapuser, user)) {
+               if ((vms = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms->imapuser, user)) {
                        return vms;
                }
        }
@@ -3513,10 +3513,6 @@ static struct vm_state *get_vm_state_by_imapuser(const char *user, int interacti
                if (vlist->vms->imapversion != imapversion) {
                        continue;
                }
-               if (!vlist->vms->imapuser) {
-                       ast_debug(3, "error: imapuser is NULL for %s\n", user);
-                       continue;
-               }
 
                if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
                        AST_LIST_UNLOCK(&vmstates);
@@ -3539,7 +3535,7 @@ static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char
        if (interactive) {
                struct vm_state *vms;
                pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
-               if ((vms = pthread_getspecific(ts_vmstate.key)) && vms->username && vms->context &&
+               if ((vms = pthread_getspecific(ts_vmstate.key)) &&
                    !strcmp(vms->username,mailbox) && !strcmp(vms->context, local_context)) {
                        return vms;
                }
@@ -3554,10 +3550,6 @@ static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char
                if (vlist->vms->imapversion != imapversion) {
                        continue;
                }
-               if (!vlist->vms->username || !vlist->vms->context) {
-                       ast_debug(3, "error: username is NULL for %s\n", mailbox);
-                       continue;
-               }
 
                ast_debug(3, "comparing mailbox %s@%s (i=%d) to vmstate mailbox %s@%s (i=%d)\n", mailbox, local_context, interactive, vlist->vms->username, vlist->vms->context, vlist->vms->interactive);
 
index 99eea0a..71da802 100644 (file)
                                        </para></description>
                                </configOption>
                                <configOption name="dsp_silence_threshold">
-                                       <synopsis>The number of milliseconds of detected silence necessary to trigger silence detection</synopsis>
-                                       <description><para>
-                                       The time in milliseconds of sound falling within the what
-                                       the dsp has established as baseline silence before a user
-                                       is considered be silent.  This value affects several
-                                       operations and should not be changed unless the impact
-                                       on call quality is fully understood.</para>
-                                       <para>What this value affects internally:</para>
-                                       <para>
-                                               1. When talk detection AMI events are enabled, this value
+                                       <synopsis>The number of milliseconds of silence necessary to declare talking stopped.</synopsis>
+                                       <description>
+                                               <para>The time in milliseconds of sound falling below the
+                                               <replaceable>dsp_talking_threshold</replaceable> option when
+                                               a user is considered to stop talking.  This value affects several
+                                               operations and should not be changed unless the impact on call
+                                               quality is fully understood.
+                                               </para>
+                                               <para>What this value affects internally:
+                                               </para>
+                                               <para>1. When talk detection AMI events are enabled, this value
                                                determines when the user has stopped talking after a
                                                period of talking.  If this value is set too low
                                                AMI events indicating the user has stopped talking
                                                may get falsely sent out when the user briefly pauses
                                                during mid sentence.
-                                       </para>
-                                       <para>
-                                               2. The <replaceable>drop_silence</replaceable> option depends on this value to
-                                               determine when the user's audio should begin to be
-                                               dropped from the conference bridge after the user
+                                               </para>
+                                               <para>2. The <replaceable>drop_silence</replaceable> option
+                                               depends on this value to determine when the user's audio should
+                                               begin to be dropped from the conference bridge after the user
                                                stops talking.  If this value is set too low the user's
-                                               audio stream may sound choppy to the other participants.
-                                               This is caused by the user transitioning constantly from
-                                               silence to talking during mid sentence.
-                                       </para>
-                                       <para>
-                                               The best way to approach this option is to set it slightly above
-                                               the maximum amount of ms of silence a user may generate during
-                                               natural speech.
-                                       </para>
-                                       <para>By default this value is 2500ms. Valid values are 1 through 2^31.</para>
+                                               audio stream may sound choppy to the other participants.  This
+                                               is caused by the user transitioning constantly from silence to
+                                               talking during mid sentence.
+                                               </para>
+                                               <para>The best way to approach this option is to set it slightly
+                                               above the maximum amount of milliseconds of silence a user may
+                                               generate during natural speech.
+                                               </para>
+                                               <para>Valid values are 1 through 2^31.</para>
                                        </description>
                                </configOption>
                                <configOption name="dsp_talking_threshold">
-                                       <synopsis>The number of milliseconds of detected non-silence necessary to triger talk detection</synopsis>
-                                       <description><para>
-                                               The time in milliseconds of sound above what the dsp has
-                                               established as base line silence for a user before a user
-                                               is considered to be talking.  This value affects several
-                                               operations and should not be changed unless the impact on
-                                               call quality is fully understood.</para>
-                                               <para>
-                                               What this value affects internally:
+                                       <synopsis>Average magnitude threshold to determine talking.</synopsis>
+                                       <description>
+                                               <para>The minimum average magnitude per sample in a frame
+                                               for the DSP to consider talking/noise present.  A value below
+                                               this level is considered silence.  This value affects several
+                                               operations and should not be changed unless the impact on call
+                                               quality is fully understood.
+                                               </para>
+                                               <para>What this value affects internally:
                                                </para>
-                                               <para>
-                                               1. Audio is only mixed out of a user's incoming audio stream
-                                               if talking is detected.  If this value is set too
-                                               loose the user will hear themselves briefly each
-                                               time they begin talking until the dsp has time to
-                                               establish that they are in fact talking.
+                                               <para>1. Audio is only mixed out of a user's incoming audio
+                                               stream if talking is detected.  If this value is set too
+                                               high the user will hear himself talking.
                                                </para>
-                                               <para>
-                                               2. When talk detection AMI events are enabled, this value
+                                               <para>2. When talk detection AMI events are enabled, this value
                                                determines when talking has begun which results in
-                                               an AMI event to fire.  If this value is set too tight
+                                               an AMI event to fire.  If this value is set too low
                                                AMI events may be falsely triggered by variants in
                                                room noise.
                                                </para>
-                                               <para>
-                                               3. The <replaceable>drop_silence</replaceable> option depends on this value to determine
-                                               when the user's audio should be mixed into the bridge
-                                               after periods of silence.  If this value is too loose
-                                               the beginning of a user's speech will get cut off as they
-                                               transition from silence to talking.
+                                               <para>3. The <replaceable>drop_silence</replaceable> option
+                                               depends on this value to determine when the user's audio should
+                                               be mixed into the bridge after periods of silence.  If this value
+                                               is too high the user's speech will get discarded as they will
+                                               be considered silent.
                                                </para>
-                                               <para>By default this value is 160 ms. Valid values are 1 through 2^31</para>
+                                               <para>Valid values are 1 through 2^15.</para>
                                        </description>
                                </configOption>
                                <configOption name="jitterbuffer">
@@ -1479,7 +1473,7 @@ static char *handle_cli_confbridge_show_user_profile(struct ast_cli_entry *e, in
                "enabled" : "disabled");
        ast_cli(a->fd,"Silence Threshold:       %ums\n",
                u_profile.silence_threshold);
-       ast_cli(a->fd,"Talking Threshold:       %ums\n",
+       ast_cli(a->fd,"Talking Threshold:       %u\n",
                u_profile.talking_threshold);
        ast_cli(a->fd,"Denoise:                 %s\n",
                u_profile.flags & USER_OPT_DENOISE ?
index 162ff22..044ab40 100644 (file)
 #define DEFAULT_BRIDGE_PROFILE "default_bridge"
 #define DEFAULT_MENU_PROFILE "default_menu"
 
+/*! Default minimum average magnitude threshold to determine talking by the DSP. */
 #define DEFAULT_TALKING_THRESHOLD 160
+
+/*! Default time in ms of silence necessary to declare talking stopped by the bridge. */
 #define DEFAULT_SILENCE_THRESHOLD 2500
 
 enum user_profile_flags {
@@ -140,9 +143,9 @@ struct user_profile {
        char announcement[PATH_MAX];
        unsigned int flags;
        unsigned int announce_user_count_all_after;
-       /*! The time in ms of talking before a user is considered to be talking by the dsp. */
+       /*! Minimum average magnitude threshold to determine talking by the DSP. */
        unsigned int talking_threshold;
-       /*! The time in ms of silence before a user is considered to be silent by the dsp. */
+       /*! Time in ms of silence necessary to declare talking stopped by the bridge. */
        unsigned int silence_threshold;
        /*! The time in ms the user may stay in the confbridge */
        unsigned int timeout;
index 461b6e5..9986d9c 100644 (file)
 /*! \brief Number of mixing iterations to perform between gathering statistics. */
 #define SOFTMIX_STAT_INTERVAL 100
 
-/* This is the threshold in ms at which a channel's own audio will stop getting
- * mixed out its own write audio stream because it is not talking. */
+/*!
+ * \brief Default time in ms of silence necessary to declare talking stopped by the bridge.
+ *
+ * \details
+ * This is the time at which a channel's own audio will stop getting
+ * mixed out of its own write audio stream because it is no longer talking.
+ */
 #define DEFAULT_SOFTMIX_SILENCE_THRESHOLD 2500
+
+/*! Default minimum average magnitude threshold to determine talking by the DSP. */
 #define DEFAULT_SOFTMIX_TALKING_THRESHOLD 160
 
 #define SOFTBRIDGE_VIDEO_DEST_PREFIX "softbridge_dest"
index c8f30bc..e2d8a62 100644 (file)
@@ -49,59 +49,67 @@ type=user
                        ; noise from the conference. Highly recommended for large conferences
                        ; due to its performance enhancements.
 
-;dsp_talking_threshold=128  ; The time in milliseconds of sound above what the dsp has
-                            ; established as base line silence for a user before a user
-                            ; is considered to be talking.  This value affects several
+;dsp_talking_threshold=128  ; Average magnitude threshold to determine talking.
+                            ;
+                            ; The minimum average magnitude per sample in a frame for the
+                            ; DSP to consider talking/noise present.  A value below this
+                            ; level is considered silence.  This value affects several
                             ; operations and should not be changed unless the impact on
                             ; call quality is fully understood.
                             ;
                             ; What this value affects internally:
                             ;
-                            ; 1. Audio is only mixed out of a user's incoming audio stream
-                            ;    if talking is detected.  If this value is set too
-                            ;    loose the user will hear themselves briefly each
-                            ;    time they begin talking until the dsp has time to
-                            ;    establish that they are in fact talking.
+                            ; 1. Audio is only mixed out of a user's incoming audio
+                            ;    stream if talking is detected.  If this value is set too
+                            ;    high the user will hear himself talking.
+                            ;
                             ; 2. When talk detection AMI events are enabled, this value
-                            ;    determines when talking has begun which results in
-                            ;    an AMI event to fire.  If this value is set too tight
-                            ;    AMI events may be falsely triggered by variants in
-                            ;    room noise.
-                            ; 3. The drop_silence option depends on this value to determine
-                            ;    when the user's audio should be mixed into the bridge
-                            ;    after periods of silence.  If this value is too loose
-                            ;    the beginning of a user's speech will get cut off as they
-                            ;    transition from silence to talking.
+                            ;    determines when talking has begun which results in an
+                            ;    AMI event to fire.  If this value is set too low AMI
+                            ;    events may be falsely triggered by variants in room
+                            ;    noise.
+                            ;
+                            ; 3. The 'drop_silence' option depends on this value to
+                            ;    determine when the user's audio should be mixed into the
+                            ;    bridge after periods of silence.  If this value is too
+                            ;    high the user's speech will get discarded as they will
+                            ;    be considered silent.
                             ;
-                            ; By default this value is 160 ms. Valid values are 1 through 2^31
+                            ; Valid values are 1 through 2^15.
+                            ; By default this value is 160.
 
-;dsp_silence_threshold=2000 ; The time in milliseconds of sound falling within the what
-                            ; the dsp has established as baseline silence before a user
-                            ; is considered be silent.  This value affects several
-                            ; operations and should not be changed unless the impact
-                            ; on call quality is fully understood.
+;dsp_silence_threshold=2000 ; The number of milliseconds of silence necessary to declare
+                            ; talking stopped.
+                            ;
+                            ; The time in milliseconds of sound falling below the
+                            ; 'dsp_talking_threshold' option when a user is considered to
+                            ; stop talking.  This value affects several operations and
+                            ; should not be changed unless the impact on call quality is
+                            ; fully understood.
                             ;
                             ; What this value affects internally:
                             ;
                             ; 1. When talk detection AMI events are enabled, this value
                             ;    determines when the user has stopped talking after a
-                            ;    period of talking.  If this value is set too low
-                            ;    AMI events indicating the user has stopped talking
-                            ;    may get falsely sent out when the user briefly pauses
-                            ;    during mid sentence.
-                            ; 2. The drop_silence option depends on this value to
+                            ;    period of talking.  If this value is set too low AMI
+                            ;    events indicating the user has stopped talking may get
+                            ;    falsely sent out when the user briefly pauses during mid
+                            ;    sentence.
+                            ;
+                            ; 2. The 'drop_silence' option depends on this value to
                             ;    determine when the user's audio should begin to be
-                            ;    dropped from the conference bridge after the user
-                            ;    stops talking.  If this value is set too low the user's
-                            ;    audio stream may sound choppy to the other participants.
-                            ;    This is caused by the user transitioning constantly from
+                            ;    dropped from the conference bridge after the user stops
+                            ;    talking.  If this value is set too low the user's audio
+                            ;    stream may sound choppy to the other participants.  This
+                            ;    is caused by the user transitioning constantly from
                             ;    silence to talking during mid sentence.
                             ;
-                            ; The best way to approach this option is to set it slightly above
-                            ; the maximum amount of ms of silence a user may generate during
-                            ; natural speech.
+                            ; The best way to approach this option is to set it slightly
+                            ; above the maximum amount of milliseconds of silence a user
+                            ; may generate during natural speech.
                             ;
-                            ; By default this value is 2500ms. Valid values are 1 through 2^31
+                            ; Valid values are 1 through 2^31.
+                            ; By default this value is 2500ms.
 
 ;talk_detection_events=yes ; This option sets whether or not notifications of when a user
                            ; begins and ends talking should be sent out as events over AMI.
index 6e54f3c..4b91883 100644 (file)
@@ -1,12 +1,4 @@
-# Process this file with autoconf to produce a configure script.
-#
-# Make sure we use autoconf 2.60 to generate the "configure" script,
-# in case we want to commit it. Other than that, version 2.59 is
-# perfectly fine for our purposes, so people who want to modify
-# this file just have to remember to set the AC_PREREQ argument
-# to something that suits their needs.
-
-AC_PREREQ(2.60)
+AC_PREREQ(2.60a)
 
 AC_INIT([asterisk], [trunk], [https://issues.asterisk.org])
 
diff --git a/contrib/ast-db-manage/config/versions/d3e4284f8707_add_prune_on_boot_to_ps_subscription_.py b/contrib/ast-db-manage/config/versions/d3e4284f8707_add_prune_on_boot_to_ps_subscription_.py
new file mode 100644 (file)
index 0000000..aa780be
--- /dev/null
@@ -0,0 +1,33 @@
+"""add prune_on_boot to ps_subscription_persistence
+
+Revision ID: d3e4284f8707
+Revises: 52798ad97bdf
+Create Date: 2018-01-28 17:45:36.218123
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'd3e4284f8707'
+down_revision = '52798ad97bdf'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
+
+YESNO_NAME = 'yesno_values'
+YESNO_VALUES = ['yes', 'no']
+
+
+def upgrade():
+    ############################# Enums ##############################
+
+    # yesno_values have already been created, so use postgres enum object
+    # type to get around "already created" issue - works okay with mysql
+    yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
+
+    op.add_column('ps_subscription_persistence', sa.Column('prune_on_boot', yesno_values))
+
+def downgrade():
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_subscription_persistence_prune_on_boot_yesno_values','ps_subscription_persistence')
+    op.drop_column('ps_subscription_persistence', 'prune_on_boot')
index eaea28d..12fd499 100644 (file)
@@ -46,11 +46,9 @@ enum ast_bridge_preference {
  * performing talking optimizations.
  */
 struct ast_bridge_tech_optimizations {
-       /*! The amount of time in ms that talking must be detected before
-        *  the dsp determines that talking has occurred */
+       /*! Minimum average magnitude threshold to determine talking by the DSP. */
        unsigned int talking_threshold;
-       /*! The amount of time in ms that silence must be detected before
-        *  the dsp determines that talking has stopped */
+       /*! Time in ms of silence necessary to declare talking stopped by the bridge. */
        unsigned int silence_threshold;
        /*! Whether or not the bridging technology should drop audio
         *  detected as silence from the mix. */
index 7e84ebe..d092e6b 100644 (file)
@@ -87,7 +87,7 @@ void ast_dsp_free(struct ast_dsp *dsp);
  * created with */
 unsigned int ast_dsp_get_sample_rate(const struct ast_dsp *dsp);
 
-/*! \brief Set threshold value for silence */
+/*! \brief Set the minimum average magnitude threshold to determine talking by the DSP. */
 void ast_dsp_set_threshold(struct ast_dsp *dsp, int threshold);
 
 /*! \brief Set number of required cadences for busy */
@@ -106,19 +106,41 @@ int ast_dsp_set_call_progress_zone(struct ast_dsp *dsp, char *zone);
    busies, and call progress, all dependent upon which features are enabled */
 struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp, struct ast_frame *inf);
 
-/*! \brief Return non-zero if this is silence.  Updates "totalsilence" with the total
-   number of seconds of silence  */
+/*!
+ * \brief Process the audio frame for silence.
+ *
+ * \param dsp DSP processing audio media.
+ * \param f Audio frame to process.
+ * \param totalsilence Variable to set to the total accumulated silence in ms
+ * seen by the DSP since the last noise.
+ *
+ * \return Non-zero if the frame is silence.
+ */
 int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence);
 
-/*! \brief Return non-zero if this is silence.  Updates "totalsilence" with the total
-   number of seconds of silence. Returns the average energy of the samples in the frame
-   in frames_energy variable. */
+/*!
+ * \brief Process the audio frame for silence.
+ *
+ * \param dsp DSP processing audio media.
+ * \param f Audio frame to process.
+ * \param totalsilence Variable to set to the total accumulated silence in ms
+ * seen by the DSP since the last noise.
+ * \param frames_energy Variable to set to the average energy of the samples in the frame.
+ *
+ * \return Non-zero if the frame is silence.
+ */
 int ast_dsp_silence_with_energy(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence, int *frames_energy);
 
 /*!
- * \brief Return non-zero if this is noise.  Updates "totalnoise" with the total
- * number of seconds of noise
+ * \brief Process the audio frame for noise.
  * \since 1.6.1
+ *
+ * \param dsp DSP processing audio media.
+ * \param f Audio frame to process.
+ * \param totalnoise Variable to set to the total accumulated noise in ms
+ * seen by the DSP since the last silence.
+ *
+ * \return Non-zero if the frame is silence.
  */
 int ast_dsp_noise(struct ast_dsp *dsp, struct ast_frame *f, int *totalnoise);
 
index c3dd691..a46d047 100644 (file)
@@ -617,20 +617,109 @@ static void  __attribute__((destructor)) fini_##rwlock(void) \
 #define pthread_create __use_ast_pthread_create_instead__
 #endif
 
-/* Support for atomic instructions. */
+/*!
+ * \brief Support for atomic instructions.
+ *
+ * These macros implement a uniform interface to use built-in atomic functionality.
+ * If available __atomic built-ins are prefered.  Legacy __sync built-ins are used
+ * as a fallback for older compilers.
+ *
+ * Detailed documentation can be found in the GCC manual, all API's are modeled after
+ * the __atomic interfaces but using the namespace ast_atomic.
+ *
+ * The memorder argument is always ignored by legacy __sync functions.  Invalid
+ * memorder arguments do not produce errors unless __atomic functions are supported
+ * as the argument is erased by the preprocessor.
+ *
+ * \note ast_atomic_fetch_nand and ast_atomic_nand_fetch purposely do not exist.
+ *       It's implementation was broken prior to gcc-4.4.
+ *
+ * @{
+ */
 
 #include "asterisk/inline_api.h"
 
 #if defined(HAVE_C_ATOMICS)
-#define ast_atomic_fetch_add(p, v, memorder)  __atomic_fetch_add(p, v, memorder)
-#define ast_atomic_sub_fetch(p, v, memorder)  __atomic_sub_fetch(p, v, memorder)
+/*! Atomic += */
+#define ast_atomic_fetch_add(ptr, val, memorder)  __atomic_fetch_add((ptr), (val), (memorder))
+#define ast_atomic_add_fetch(ptr, val, memorder)  __atomic_add_fetch((ptr), (val), (memorder))
+
+/*! Atomic -= */
+#define ast_atomic_fetch_sub(ptr, val, memorder)  __atomic_fetch_sub((ptr), (val), (memorder))
+#define ast_atomic_sub_fetch(ptr, val, memorder)  __atomic_sub_fetch((ptr), (val), (memorder))
+
+/*! Atomic &= */
+#define ast_atomic_fetch_and(ptr, val, memorder)  __atomic_fetch_and((ptr), (val), (memorder))
+#define ast_atomic_and_fetch(ptr, val, memorder)  __atomic_and_fetch((ptr), (val), (memorder))
+
+/*! Atomic |= */
+#define ast_atomic_fetch_or(ptr, val, memorder)   __atomic_fetch_or((ptr), (val), (memorder))
+#define ast_atomic_or_fetch(ptr, val, memorder)   __atomic_or_fetch((ptr), (val), (memorder))
+
+/*! Atomic xor = */
+#define ast_atomic_fetch_xor(ptr, val, memorder)  __atomic_fetch_xor((ptr), (val), (memorder))
+#define ast_atomic_xor_fetch(ptr, val, memorder)  __atomic_xor_fetch((ptr), (val), (memorder))
+
+#if 0
+/* Atomic compare and swap
+ *
+ * See comments near the __atomic implementation for why this is disabled.
+ */
+#define ast_atomic_compare_exchange_n(ptr, expected, desired, success_memorder, failure_memorder) \
+       __atomic_compare_exchange_n((ptr), (expected), (desired), 0, success_memorder, failure_memorder)
+
+#define ast_atomic_compare_exchange(ptr, expected, desired, success_memorder, failure_memorder) \
+       __atomic_compare_exchange((ptr), (expected), (desired), 0, success_memorder, failure_memorder)
+#endif
+
 #elif defined(HAVE_GCC_ATOMICS)
-#define ast_atomic_fetch_add(p, v, memorder)  __sync_fetch_and_add(p, v)
-#define ast_atomic_sub_fetch(p, v, memorder)  __sync_sub_and_fetch(p, v)
+/*! Atomic += */
+#define ast_atomic_fetch_add(ptr, val, memorder)  __sync_fetch_and_add((ptr), (val))
+#define ast_atomic_add_fetch(ptr, val, memorder)  __sync_add_and_fetch((ptr), (val))
+
+/*! Atomic -= */
+#define ast_atomic_fetch_sub(ptr, val, memorder)  __sync_fetch_and_sub((ptr), (val))
+#define ast_atomic_sub_fetch(ptr, val, memorder)  __sync_sub_and_fetch((ptr), (val))
+
+/*! Atomic &= */
+#define ast_atomic_fetch_and(ptr, val, memorder)  __sync_fetch_and_and((ptr), (val))
+#define ast_atomic_and_fetch(ptr, val, memorder)  __sync_and_and_fetch((ptr), (val))
+
+/*! Atomic |= */
+#define ast_atomic_fetch_or(ptr, val, memorder)  __sync_fetch_and_or((ptr), (val))
+#define ast_atomic_or_fetch(ptr, val, memorder)  __sync_or_and_fetch((ptr), (val))
+
+/*! Atomic xor = */
+#define ast_atomic_fetch_xor(ptr, val, memorder)  __sync_fetch_and_xor((ptr), (val))
+#define ast_atomic_xor_fetch(ptr, val, memorder)  __sync_xor_and_fetch((ptr), (val))
+
+#if 0
+/* Atomic compare and swap
+ *
+ * The \a expected argument is a pointer, I'm guessing __atomic built-ins
+ * perform all memory reads/writes in a single atomic operation.  I don't
+ * believe this is possible to exactly replicate using __sync built-ins.
+ * Will need to determine potential use cases of this feature and write a
+ * wrapper which provides consistant behavior between __sync and __atomic
+ * implementations.
+ */
+#define ast_atomic_compare_exchange_n(ptr, expected, desired, success_memorder, failure_memorder) \
+       __sync_bool_compare_and_swap((ptr), *(expected), (desired))
+
+#define ast_atomic_compare_exchange(ptr, expected, desired, success_memorder, failure_memorder) \
+       __sync_bool_compare_and_swap((ptr), *(expected), *(desired))
+#endif
+
 #else
 #error "Atomics not available."
 #endif
 
+/*! Atomic flag set */
+#define ast_atomic_flag_set(ptr, val, memorder)   ast_atomic_fetch_or((ptr), (val), (memorder))
+
+/*! Atomic flag clear */
+#define ast_atomic_flag_clear(ptr, val, memorder) ast_atomic_fetch_and((ptr), ~(val), (memorder))
+
 /*!
  * \brief Atomically add v to *p and return the previous value of *p.
  *
@@ -652,4 +741,6 @@ AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
        return ast_atomic_sub_fetch(p, 1, __ATOMIC_RELAXED) == 0;
 })
 
+/*! @} */
+
 #endif /* _ASTERISK_LOCK_H */
index a3bd782..c017e62 100644 (file)
@@ -2985,6 +2985,18 @@ int ast_sip_str_to_dtmf(const char *dtmf_mode);
  */
 typedef void (*ast_transport_monitor_shutdown_cb)(void *data);
 
+/*!
+ * \brief Transport shutdown monitor data matcher
+ * \since 13.20.0
+ *
+ * \param a User data to compare.
+ * \param b User data to compare.
+ *
+ * \retval 1 The data objects match
+ * \retval 0 The data objects don't match
+ */
+typedef int (*ast_transport_monitor_data_matcher)(void *a, void *b);
+
 enum ast_transport_monitor_reg {
        /*! \brief Successfully registered the transport monitor */
        AST_TRANSPORT_MONITOR_REG_SUCCESS,
@@ -3001,37 +3013,59 @@ enum ast_transport_monitor_reg {
 
 /*!
  * \brief Register a reliable transport shutdown monitor callback.
- * \since 13.18.0
+ * \since 13.20.0
  *
  * \param transport Transport to monitor for shutdown.
  * \param cb Who to call when transport is shutdown.
  * \param ao2_data Data to pass with the callback.
  *
+ * \note The data object passed will have its reference count automatically
+ * incremented by this call and automatically decremented after the callback
+ * runs or when the callback is unregistered.
+ *
+ * There is no checking for duplicate registrations.
+ *
  * \return enum ast_transport_monitor_reg
  */
 enum ast_transport_monitor_reg ast_sip_transport_monitor_register(pjsip_transport *transport,
        ast_transport_monitor_shutdown_cb cb, void *ao2_data);
 
 /*!
- * \brief Unregister a reliable transport shutdown monitor callback.
- * \since 13.18.0
+ * \brief Unregister a reliable transport shutdown monitor
+ * \since 13.20.0
  *
  * \param transport Transport to monitor for shutdown.
- * \param cb Who to call when transport is shutdown.
+ * \param cb The callback that was used for the original register.
+ * \param data Data to pass to the matcher. May be NULL and does NOT need to be an ao2 object.
+ *             If NULL, all monitors with the provided callbck are unregistered.
+ * \param matches Matcher function that returns true if data matches the previously
+ *                registered data object.  If NULL, a simple pointer comparison is done.
+ *
+ * \note The data object passed into the original register will have its reference count
+ * automatically decremeneted.
  *
  * \return Nothing
  */
-void ast_sip_transport_monitor_unregister(pjsip_transport *transport, ast_transport_monitor_shutdown_cb cb);
+void ast_sip_transport_monitor_unregister(pjsip_transport *transport,
+       ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches);
 
 /*!
- * \brief Unregister monitor callback from all reliable transports.
- * \since 13.18.0
+ * \brief Unregister a transport shutdown monitor from all reliable transports
+ * \since 13.20.0
+ *
+ * \param cb The callback that was used for the original register.
+ * \param data Data to pass to the matcher. May be NULL and does NOT need to be an ao2 object.
+ *             If NULL, all monitors with the provided callbck are unregistered.
+ * \param matches Matcher function that returns true if ao2_data matches the previously
+ *                registered data object.  If NULL, a simple pointer comparison is done.
  *
- * \param cb Who to call when a transport is shutdown.
+ * \note The data object passed into the original register will have its reference count
+ * automatically decremeneted.
  *
  * \return Nothing
  */
-void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb);
+void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb,
+       void *data, ast_transport_monitor_data_matcher matches);
 
 /*! Transport state notification registration element.  */
 struct ast_sip_tpmgr_state_callback {
index e4e7fd3..8b39fe5 100644 (file)
@@ -122,12 +122,19 @@ static struct progress {
        { GSAMP_SIZE_UK, { 350, 400, 440 } },                           /*!< UK */
 };
 
-/*!\brief This value is the minimum threshold, calculated by averaging all
- * of the samples within a frame, for which a frame is determined to either
- * be silence (below the threshold) or noise (above the threshold).  Please
- * note that while the default threshold is an even exponent of 2, there is
- * no requirement that it be so.  The threshold will accept any value between
- * 0 and 32767.
+/*!
+ * \brief Default minimum average magnitude threshold to determine talking/noise by the DSP.
+ *
+ * \details
+ * The magnitude calculated for this threshold is determined by
+ * averaging the absolute value of all samples within a frame.
+ *
+ * This value is the threshold for which a frame's average magnitude
+ * is determined to either be silence (below the threshold) or
+ * noise/talking (at or above the threshold).  Please note that while
+ * the default threshold is an even exponent of 2, there is no
+ * requirement that it be so.  The threshold will work for any value
+ * between 1 and 2^15.
  */
 #define DEFAULT_THRESHOLD      512
 
@@ -397,7 +404,9 @@ typedef struct {
 struct ast_dsp {
        struct ast_frame f;
        int threshold;
+       /*! Accumulated total silence in ms since last talking/noise. */
        int totalsilence;
+       /*! Accumulated total talking/noise in ms since last silence. */
        int totalnoise;
        int features;
        int ringtimeout;
index 6a7d918..bf859fe 100644 (file)
@@ -3118,6 +3118,45 @@ pjsip_endpoint *ast_sip_get_pjsip_endpoint(void)
        return ast_pjsip_endpoint;
 }
 
+int ast_sip_will_uri_survive_restart(pjsip_sip_uri *uri, struct ast_sip_endpoint *endpoint,
+       pjsip_rx_data *rdata)
+{
+       pj_str_t host_name;
+       int result = 1;
+
+       /* Determine if the contact cannot survive a restart/boot. */
+       if (uri->port == rdata->pkt_info.src_port
+               && !pj_strcmp(&uri->host,
+                       pj_cstr(&host_name, rdata->pkt_info.src_name))
+               /* We have already checked if the URI scheme is sip: or sips: */
+               && PJSIP_TRANSPORT_IS_RELIABLE(rdata->tp_info.transport)) {
+               pj_str_t type_name;
+
+               /* Determine the transport parameter value */
+               if (!strcasecmp("WSS", rdata->tp_info.transport->type_name)) {
+                       /* WSS is special, as it needs to be ws. */
+                       pj_cstr(&type_name, "ws");
+               } else {
+                       pj_cstr(&type_name, rdata->tp_info.transport->type_name);
+               }
+
+               if (!pj_stricmp(&uri->transport_param, &type_name)
+                       && (endpoint->nat.rewrite_contact
+                               /* Websockets are always rewritten */
+                               || !pj_stricmp(&uri->transport_param,
+                                       pj_cstr(&type_name, "ws")))) {
+                       /*
+                        * The contact was rewritten to the reliable transport's
+                        * source address.  Disconnecting the transport for any
+                        * reason invalidates the contact.
+                        */
+                       result = 0;
+               }
+       }
+
+       return result;
+}
+
 int ast_sip_get_transport_name(const struct ast_sip_endpoint *endpoint,
        pjsip_sip_uri *sip_uri, char *buf, size_t buf_len)
 {
index 7fafd80..7d434aa 100644 (file)
@@ -347,4 +347,18 @@ int ast_sip_initialize_scheduler(void);
  */
 int ast_sip_destroy_scheduler(void);
 
+/*!
+ * \internal
+ * \brief Determines if a uri will still be valid after an asterisk restart
+ * \since 13.20.0
+ *
+ * \param uri uri to test
+ * \param endpoint The associated endpoint
+ * \param rdata The rdata to get transport information from
+ *
+ * \retval 1 Yes, 0 No
+ */
+int ast_sip_will_uri_survive_restart(pjsip_sip_uri *uri, struct ast_sip_endpoint *endpoint,
+       pjsip_rx_data *rdata);
+
 #endif /* RES_PJSIP_PRIVATE_H_ */
index 0f57303..c701b84 100644 (file)
@@ -135,7 +135,7 @@ static void transport_state_callback(pjsip_transport *transport,
                                break;
                        }
                        monitored->transport = transport;
-                       if (AST_VECTOR_INIT(&monitored->monitors, 2)) {
+                       if (AST_VECTOR_INIT(&monitored->monitors, 5)) {
                                ao2_ref(monitored, -1);
                                break;
                        }
@@ -166,6 +166,8 @@ static void transport_state_callback(pjsip_transport *transport,
                                        struct transport_monitor_notifier *notifier;
 
                                        notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
+                                       ast_debug(3, "running callback %p(%p) for transport %s\n",
+                                               notifier->cb, notifier->data, transport->obj_name);
                                        notifier->cb(notifier->data);
                                }
                                ao2_ref(monitored, -1);
@@ -195,43 +197,66 @@ static void transport_state_callback(pjsip_transport *transport,
        }
 }
 
-static int transport_monitor_unregister_all(void *obj, void *arg, int flags)
+struct callback_data {
+       ast_transport_monitor_shutdown_cb cb;
+       void *data;
+       ast_transport_monitor_data_matcher matches;
+};
+
+static int transport_monitor_unregister_cb(void *obj, void *arg, int flags)
 {
        struct transport_monitor *monitored = obj;
-       ast_transport_monitor_shutdown_cb cb = arg;
+       struct callback_data *cb_data = arg;
        int idx;
 
        for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
                struct transport_monitor_notifier *notifier;
 
                notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
-               if (notifier->cb == cb) {
+               if (notifier->cb == cb_data->cb && (!cb_data->data
+                       || cb_data->matches(cb_data->data, notifier->data))) {
                        ao2_cleanup(notifier->data);
                        AST_VECTOR_REMOVE_UNORDERED(&monitored->monitors, idx);
-                       break;
+                       ast_debug(3, "Unregistered monitor %p(%p) from transport %s\n",
+                               notifier->cb, notifier->data, monitored->transport->obj_name);
                }
        }
        return 0;
 }
 
-void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb)
+static int ptr_matcher(void *a, void *b)
+{
+       return a == b;
+}
+
+void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb,
+       void *data, ast_transport_monitor_data_matcher matches)
 {
        struct ao2_container *transports;
+       struct callback_data cb_data = {
+               .cb = cb,
+               .data = data,
+               .matches = matches ?: ptr_matcher,
+       };
+
+       ast_assert(cb != NULL);
 
        transports = ao2_global_obj_ref(active_transports);
        if (!transports) {
                return;
        }
-       ao2_callback(transports, OBJ_MULTIPLE | OBJ_NODATA, transport_monitor_unregister_all,
-               cb);
+       ao2_callback(transports, OBJ_MULTIPLE | OBJ_NODATA, transport_monitor_unregister_cb, &cb_data);
        ao2_ref(transports, -1);
 }
 
-void ast_sip_transport_monitor_unregister(pjsip_transport *transport, ast_transport_monitor_shutdown_cb cb)
+void ast_sip_transport_monitor_unregister(pjsip_transport *transport,
+       ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches)
 {
        struct ao2_container *transports;
        struct transport_monitor *monitored;
 
+       ast_assert(transport != NULL && cb != NULL);
+
        transports = ao2_global_obj_ref(active_transports);
        if (!transports) {
                return;
@@ -240,18 +265,13 @@ void ast_sip_transport_monitor_unregister(pjsip_transport *transport, ast_transp
        ao2_lock(transports);
        monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
        if (monitored) {
-               int idx;
+               struct callback_data cb_data = {
+                       .cb = cb,
+                       .data = data,
+                       .matches = matches ?: ptr_matcher,
+               };
 
-               for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
-                       struct transport_monitor_notifier *notifier;
-
-                       notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
-                       if (notifier->cb == cb) {
-                               ao2_cleanup(notifier->data);
-                               AST_VECTOR_REMOVE_UNORDERED(&monitored->monitors, idx);
-                               break;
-                       }
-               }
+               transport_monitor_unregister_cb(monitored, &cb_data, 0);
                ao2_ref(monitored, -1);
        }
        ao2_unlock(transports);
@@ -265,6 +285,8 @@ enum ast_transport_monitor_reg ast_sip_transport_monitor_register(pjsip_transpor
        struct transport_monitor *monitored;
        enum ast_transport_monitor_reg res = AST_TRANSPORT_MONITOR_REG_NOT_FOUND;
 
+       ast_assert(transport != NULL && cb != NULL);
+
        transports = ao2_global_obj_ref(active_transports);
        if (!transports) {
                return res;
@@ -273,31 +295,22 @@ enum ast_transport_monitor_reg ast_sip_transport_monitor_register(pjsip_transpor
        ao2_lock(transports);
        monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
        if (monitored) {
-               int idx;
                struct transport_monitor_notifier new_monitor;
 
-               /* Check if the callback monitor already exists */
-               for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
-                       struct transport_monitor_notifier *notifier;
-
-                       notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
-                       if (notifier->cb == cb) {
-                               /* The monitor is already in the vector replace with new ao2_data. */
-                               ao2_replace(notifier->data, ao2_data);
-                               res = AST_TRANSPORT_MONITOR_REG_REPLACED;
-                               goto register_done;
-                       }
-               }
-
                /* Add new monitor to vector */
                new_monitor.cb = cb;
                new_monitor.data = ao2_bump(ao2_data);
                if (AST_VECTOR_APPEND(&monitored->monitors, new_monitor)) {
                        ao2_cleanup(ao2_data);
                        res = AST_TRANSPORT_MONITOR_REG_FAILED;
+                       ast_debug(3, "Register monitor %p(%p) to transport %s FAILED\n",
+                               cb, ao2_data, transport->obj_name);
+               } else {
+                       res = AST_TRANSPORT_MONITOR_REG_SUCCESS;
+                       ast_debug(3, "Registered monitor %p(%p) to transport %s\n",
+                               cb, ao2_data, transport->obj_name);
                }
 
-register_done:
                ao2_ref(monitored, -1);
        }
        ao2_unlock(transports);
index 4baf23c..d0f7546 100644 (file)
@@ -850,6 +850,14 @@ static void registration_transport_shutdown_cb(void *obj)
        }
 }
 
+static int monitor_matcher(void *a, void *b)
+{
+       char *ma = a;
+       char *mb = b;
+
+       return strcmp(ma, mb) == 0;
+}
+
 static void registration_transport_monitor_setup(pjsip_transport *transport, const char *registration_name)
 {
        char *monitor;
@@ -950,7 +958,8 @@ static int handle_registration_response(void *data)
                        ast_debug(1, "Outbound unregistration to '%s' with client '%s' successful\n", server_uri, client_uri);
                        update_client_state_status(response->client_state, SIP_REGISTRATION_UNREGISTERED);
                        ast_sip_transport_monitor_unregister(response->rdata->tp_info.transport,
-                               registration_transport_shutdown_cb);
+                               registration_transport_shutdown_cb, response->client_state->registration_name,
+                               monitor_matcher);
                }
        } else if (response->client_state->destroy) {
                /* We need to deal with the pending destruction instead. */
@@ -2149,7 +2158,7 @@ static int unload_module(void)
 
        ao2_global_obj_release(current_states);
 
-       ast_sip_transport_monitor_unregister_all(registration_transport_shutdown_cb);
+       ast_sip_transport_monitor_unregister_all(registration_transport_shutdown_cb, NULL, NULL);
 
        /* Wait for registration serializers to get destroyed. */
        ast_debug(2, "Waiting for registration transactions to complete for unload.\n");
index 369e06d..c78f20c 100644 (file)
                                <configOption name="contact_uri">
                                        <synopsis>The Contact URI of the dialog for the subscription</synopsis>
                                </configOption>
+                               <configOption name="prune_on_boot">
+                                       <synopsis>If set, indicates that the contact used a reliable transport
+                                       and therefore the subscription must be deleted after an asterisk restart.
+                                       </synopsis>
+                               </configOption>
                        </configObject>
                        <configObject name="resource_list">
                                <synopsis>Resource list configuration parameters.</synopsis>
@@ -382,6 +387,8 @@ struct subscription_persistence {
        struct timeval expires;
        /*! Contact URI */
        char contact_uri[PJSIP_MAX_URL_SIZE];
+       /*! Prune subscription on restart */
+       int prune_on_boot;
 };
 
 /*!
@@ -446,6 +453,10 @@ struct sip_subscription_tree {
         * capable of restarting the timer.
         */
        struct ast_sip_sched_task *expiration_task;
+       /*! The transport the subscription was received on.
+        * Only used for reliable transports.
+        */
+       pjsip_transport *transport;
 };
 
 /*!
@@ -549,6 +560,17 @@ static void *publication_resource_alloc(const char *name)
        return ast_sorcery_generic_alloc(sizeof(struct ast_sip_publication_resource), publication_resource_destroy);
 }
 
+static void sub_tree_transport_cb(void *data) {
+       struct sip_subscription_tree *sub_tree = data;
+
+       ast_debug(3, "Transport destroyed.  Removing subscription '%s->%s'  prune on restart: %d\n",
+               sub_tree->persistence->endpoint, sub_tree->root->resource,
+               sub_tree->persistence->prune_on_boot);
+
+       sub_tree->state = SIP_SUB_TREE_TERMINATE_IN_PROGRESS;
+       pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
+}
+
 /*! \brief Destructor for subscription persistence */
 static void subscription_persistence_destroy(void *obj)
 {
@@ -599,8 +621,9 @@ static void subscription_persistence_update(struct sip_subscription_tree *sub_tr
                return;
        }
 
-       ast_debug(3, "Updating persistence for '%s->%s'\n", sub_tree->persistence->endpoint,
-               sub_tree->root->resource);
+       ast_debug(3, "Updating persistence for '%s->%s'  prune on restart: %s\n",
+               sub_tree->persistence->endpoint, sub_tree->root->resource,
+               sub_tree->persistence->prune_on_boot ? "yes" : "no");
 
        dlg = sub_tree->dlg;
        sub_tree->persistence->cseq = dlg->local.cseq;
@@ -614,6 +637,28 @@ static void subscription_persistence_update(struct sip_subscription_tree *sub_tr
                sub_tree->persistence->expires = ast_tvadd(ast_tvnow(), ast_samp2tv(expires, 1));
 
                if (contact_hdr) {
+                       if (contact_hdr) {
+                               if (type == SUBSCRIPTION_PERSISTENCE_CREATED) {
+                                       sub_tree->persistence->prune_on_boot =
+                                               !ast_sip_will_uri_survive_restart(
+                                                       (pjsip_sip_uri *)pjsip_uri_get_uri(contact_hdr->uri),
+                                                       sub_tree->endpoint, rdata);
+
+                                       if (sub_tree->persistence->prune_on_boot) {
+                                               ast_debug(3, "adding transport monitor on %s for '%s->%s'  prune on restart: %d\n",
+                                                       rdata->tp_info.transport->obj_name,
+                                                       sub_tree->persistence->endpoint, sub_tree->root->resource,
+                                                       sub_tree->persistence->prune_on_boot);
+                                               sub_tree->transport = rdata->tp_info.transport;
+                                               ast_sip_transport_monitor_register(rdata->tp_info.transport,
+                                                       sub_tree_transport_cb, sub_tree);
+                                               /*
+                                                * FYI: ast_sip_transport_monitor_register holds a reference to the sub_tree
+                                                */
+                                       }
+                               }
+                       }
+
                        pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, contact_hdr->uri,
                                        sub_tree->persistence->contact_uri, sizeof(sub_tree->persistence->contact_uri));
                } else {
@@ -656,6 +701,15 @@ static void subscription_persistence_remove(struct sip_subscription_tree *sub_tr
                return;
        }
 
+       if (sub_tree->persistence->prune_on_boot && sub_tree->transport) {
+               ast_debug(3, "Unregistering transport monitor on %s '%s->%s'\n",
+                       sub_tree->transport->obj_name,
+                       sub_tree->endpoint ? ast_sorcery_object_get_id(sub_tree->endpoint) : "Unknown",
+                       sub_tree->root ? sub_tree->root->resource : "Unknown");
+               ast_sip_transport_monitor_unregister(sub_tree->transport,
+                       sub_tree_transport_cb, sub_tree, NULL);
+       }
+
        ast_sorcery_delete(ast_sip_get_sorcery(), sub_tree->persistence);
        ao2_ref(sub_tree->persistence, -1);
        sub_tree->persistence = NULL;
@@ -1564,6 +1618,14 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags)
        pjsip_rx_data rdata;
        struct persistence_recreate_data recreate_data;
 
+       /* If this subscription used a reliable transport it can't be reestablished so remove it */
+       if (persistence->prune_on_boot) {
+               ast_debug(3, "Deleting subscription marked as 'prune' from persistent store '%s' %s\n",
+                       persistence->endpoint, persistence->tag);
+               ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
+               return 0;
+       }
+
        /* If this subscription has already expired remove it */
        if (ast_tvdiff_ms(persistence->expires, ast_tvnow()) <= 0) {
                ast_debug(3, "Expired subscription retrived from persistent store '%s' %s\n",
@@ -5416,6 +5478,8 @@ static int load_module(void)
                persistence_expires_str2struct, persistence_expires_struct2str, NULL, 0, 0);
        ast_sorcery_object_field_register(sorcery, "subscription_persistence", "contact_uri", "", OPT_CHAR_ARRAY_T, 0,
                CHARFLDSET(struct subscription_persistence, contact_uri));
+       ast_sorcery_object_field_register(sorcery, "subscription_persistence", "prune_on_boot", "0", OPT_UINT_T, 0,
+               FLDSET(struct subscription_persistence, prune_on_boot));
 
        if (apply_list_configuration(sorcery)) {
                ast_sched_context_destroy(sched);
@@ -5492,6 +5556,8 @@ static int unload_module(void)
        AST_TEST_UNREGISTER(loop);
        AST_TEST_UNREGISTER(bad_event);
 
+       ast_sip_transport_monitor_unregister_all(sub_tree_transport_cb, NULL, NULL);
+
        ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
 
        ast_manager_unregister(AMI_SHOW_SUBSCRIPTIONS_OUTBOUND);
index 76d8b04..bdee91f 100644 (file)
@@ -328,6 +328,15 @@ struct contact_transport_monitor {
        char aor_name[0];
 };
 
+static int contact_transport_monitor_matcher(void *a, void *b)
+{
+       struct contact_transport_monitor *ma = a;
+       struct contact_transport_monitor *mb = b;
+
+       return strcmp(ma->aor_name, mb->aor_name) == 0
+               && strcmp(ma->contact_name, mb->contact_name) == 0;
+}
+
 static void register_contact_transport_shutdown_cb(void *data)
 {
        struct contact_transport_monitor *monitor = data;
@@ -579,8 +588,7 @@ static void register_aor_core(pjsip_rx_data *rdata,
 
                contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details);
                if (!contact) {
-                       int prune_on_boot = 0;
-                       pj_str_t host_name;
+                       int prune_on_boot;
 
                        /* If they are actually trying to delete a contact that does not exist... be forgiving */
                        if (!expiration) {
@@ -589,35 +597,7 @@ static void register_aor_core(pjsip_rx_data *rdata,
                                continue;
                        }
 
-                       /* Determine if the contact cannot survive a restart/boot. */
-                       if (details.uri->port == rdata->pkt_info.src_port
-                               && !pj_strcmp(&details.uri->host,
-                                       pj_cstr(&host_name, rdata->pkt_info.src_name))
-                               /* We have already checked if the URI scheme is sip: or sips: */
-                               && PJSIP_TRANSPORT_IS_RELIABLE(rdata->tp_info.transport)) {
-                               pj_str_t type_name;
-
-                               /* Determine the transport parameter value */
-                               if (!strcasecmp("WSS", rdata->tp_info.transport->type_name)) {
-                                       /* WSS is special, as it needs to be ws. */
-                                       pj_cstr(&type_name, "ws");
-                               } else {
-                                       pj_cstr(&type_name, rdata->tp_info.transport->type_name);
-                               }
-
-                               if (!pj_stricmp(&details.uri->transport_param, &type_name)
-                                       && (endpoint->nat.rewrite_contact
-                                               /* Websockets are always rewritten */
-                                               || !pj_stricmp(&details.uri->transport_param,
-                                                       pj_cstr(&type_name, "ws")))) {
-                                       /*
-                                        * The contact was rewritten to the reliable transport's
-                                        * source address.  Disconnecting the transport for any
-                                        * reason invalidates the contact.
-                                        */
-                                       prune_on_boot = 1;
-                               }
-                       }
+                       prune_on_boot = !ast_sip_will_uri_survive_restart(details.uri, endpoint, rdata);
 
                        contact = ast_sip_location_create_contact(aor, contact_uri,
                                ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)),
@@ -704,6 +684,21 @@ static void register_aor_core(pjsip_rx_data *rdata,
                                        contact_update->user_agent);
                        ao2_cleanup(contact_update);
                } else {
+                       if (contact->prune_on_boot) {
+                               struct contact_transport_monitor *monitor;
+                               const char *contact_name =
+                                       ast_sorcery_object_get_id(contact);
+
+                               monitor = ast_alloca(sizeof(*monitor) + 2 + strlen(aor_name)
+                                       + strlen(contact_name));
+                               strcpy(monitor->aor_name, aor_name);/* Safe */
+                               monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1;
+                               strcpy(monitor->contact_name, contact_name);/* Safe */
+
+                               ast_sip_transport_monitor_unregister(rdata->tp_info.transport,
+                                       register_contact_transport_shutdown_cb, monitor, contact_transport_monitor_matcher);
+                       }
+
                        /* We want to report the user agent that was actually in the removed contact */
                        ast_sip_location_delete_contact(contact);
                        ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name);
@@ -1115,6 +1110,19 @@ static int expire_contact(void *obj, void *arg, int flags)
         */
        ao2_lock(lock);
        if (ast_tvdiff_ms(ast_tvnow(), contact->expiration_time) > 0) {
+               if (contact->prune_on_boot) {
+                       struct contact_transport_monitor *monitor;
+                       const char *contact_name = ast_sorcery_object_get_id(contact);
+
+                       monitor = ast_alloca(sizeof(*monitor) + 2 + strlen(contact->aor)
+                               + strlen(contact_name));
+                       strcpy(monitor->aor_name, contact->aor);/* Safe */
+                       monitor->contact_name = monitor->aor_name + strlen(contact->aor) + 1;
+                       strcpy(monitor->contact_name, contact_name);/* Safe */
+
+                       ast_sip_transport_monitor_unregister_all(register_contact_transport_shutdown_cb,
+                               monitor, contact_transport_monitor_matcher);
+               }
                ast_sip_location_delete_contact(contact);
        }
        ao2_unlock(lock);
@@ -1222,7 +1230,7 @@ static int unload_module(void)
        ast_manager_unregister(AMI_SHOW_REGISTRATIONS);
        ast_manager_unregister(AMI_SHOW_REGISTRATION_CONTACT_STATUSES);
        ast_sip_unregister_service(&registrar_module);
-       ast_sip_transport_monitor_unregister_all(register_contact_transport_shutdown_cb);
+       ast_sip_transport_monitor_unregister_all(register_contact_transport_shutdown_cb, NULL, NULL);
        return 0;
 }
 
diff --git a/res/res_pjsip_registrar_expire.c b/res/res_pjsip_registrar_expire.c
deleted file mode 100644 (file)
index 4c21895..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2013, Digium, Inc.
- *
- * Joshua Colp <jcolp@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*** MODULEINFO
-       <depend>pjproject</depend>
-       <depend>res_pjsip</depend>
-       <support_level>extended</support_level>
- ***/
-
-/*
- * This module has been refactored into res_pjsip_registrar
- */
-
-#include "asterisk.h"
-
-#include "asterisk/module.h"
-
-static int unload_module(void)
-{
-       return 0;
-}
-
-static int load_module(void)
-{
-       return AST_MODULE_LOAD_SUCCESS;
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "OBSOLETE PJSIP Contact Auto-Expiration",
-       .support_level = AST_MODULE_SUPPORT_EXTENDED,
-       .load = load_module,
-       .unload = unload_module,
-       .load_pri = AST_MODPRI_APP_DEPEND,
-       .requires = "res_pjsip",
-);
index 4d4ed47..5a9134b 100644 (file)
@@ -3425,9 +3425,13 @@ static void handle_incoming_before_media(pjsip_inv_session *inv,
 
 static void session_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
 {
-       struct ast_sip_session *session = inv->mod_data[session_module.id];
+       struct ast_sip_session *session;
        pjsip_event_id_e type;
 
+       if (ast_shutdown_final()) {
+               return;
+       }
+
        if (e) {
                print_debug_details(inv, NULL, e);
                type = e->type;
@@ -3435,6 +3439,7 @@ static void session_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
                type = PJSIP_EVENT_UNKNOWN;
        }
 
+       session = inv->mod_data[session_module.id];
        if (!session) {
                return;
        }
@@ -3536,13 +3541,7 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
        struct ast_sip_session *session;
        pjsip_tx_data *tdata;
 
-       /*
-        * A race condition exists at shutdown where the res_pjsip_session can be
-        * unloaded but this callback may still get called afterwards. In this case
-        * the id may end up being -1 which is useless to us. To work around this
-        * we store the current value and check/use it.
-        */
-       if (id < 0) {
+       if (ast_shutdown_final()) {
                return;
        }
 
@@ -3991,9 +3990,14 @@ static struct pjmedia_sdp_session *create_local_sdp(pjsip_inv_session *inv, stru
 
 static void session_inv_on_rx_offer(pjsip_inv_session *inv, const pjmedia_sdp_session *offer)
 {
-       struct ast_sip_session *session = inv->mod_data[session_module.id];
+       struct ast_sip_session *session;
        pjmedia_sdp_session *answer;
 
+       if (ast_shutdown_final()) {
+               return;
+       }
+
+       session = inv->mod_data[session_module.id];
        if (handle_incoming_sdp(session, offer)) {
                return;
        }
@@ -4012,9 +4016,14 @@ static void session_inv_on_create_offer(pjsip_inv_session *inv, pjmedia_sdp_sess
 
 static void session_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status)
 {
-       struct ast_sip_session *session = inv->mod_data[session_module.id];
+       struct ast_sip_session *session;
        const pjmedia_sdp_session *local, *remote;
 
+       if (ast_shutdown_final()) {
+               return;
+       }
+
+       session = inv->mod_data[session_module.id];
        if (!session || !session->channel) {
                /*
                 * If we don't have a session or channel then we really
@@ -4037,10 +4046,15 @@ static void session_inv_on_media_update(pjsip_inv_session *inv, pj_status_t stat
 
 static pjsip_redirect_op session_inv_on_redirected(pjsip_inv_session *inv, const pjsip_uri *target, const pjsip_event *e)
 {
-       struct ast_sip_session *session = inv->mod_data[session_module.id];
+       struct ast_sip_session *session;
        const pjsip_sip_uri *uri;
 
-       if (!session->channel) {
+       if (ast_shutdown_final()) {
+               return PJSIP_REDIRECT_STOP;
+       }
+
+       session = inv->mod_data[session_module.id];
+       if (!session || !session->channel) {
                return PJSIP_REDIRECT_STOP;
        }