[Pkg-voip-commits] [asterisk] 03/08: Imported Upstream version 13.7.0~rc2~dfsg

tzafrir at debian.org tzafrir at debian.org
Sun Apr 24 13:49:07 UTC 2016


This is an automated email from the git hooks/post-receive script.

tzafrir pushed a commit to branch master
in repository asterisk.

commit 604480739567edb8fb3971b23fb9626229cb1a61
Author: Tzafrir Cohen <tzafrir at debian.org>
Date:   Mon Jan 4 16:56:48 2016 +0200

    Imported Upstream version 13.7.0~rc2~dfsg
---
 .version                                           |    2 +-
 CHANGES                                            |   90 +
 ChangeLog                                          | 2035 +++++++++++++++++++-
 Makefile                                           |   64 +-
 Makefile.rules                                     |   20 +
 addons/chan_mobile.c                               |    2 +-
 addons/chan_ooh323.c                               |   14 +-
 apps/app_chanisavail.c                             |    6 +-
 apps/app_confbridge.c                              |   18 +
 apps/app_dial.c                                    |    8 +-
 apps/app_dumpchan.c                                |    2 +-
 apps/app_meetme.c                                  |    3 +
 apps/app_page.c                                    |    2 +-
 apps/app_queue.c                                   |    1 -
 apps/confbridge/conf_config_parser.c               |    4 +
 apps/confbridge/include/confbridge.h               |    2 +
 asterisk-13.6.0-summary.html                       |  322 ----
 asterisk-13.6.0-summary.txt                        |  839 --------
 asterisk-13.7.0-rc2-summary.html                   |   46 +
 asterisk-13.7.0-rc2-summary.txt                    |  149 ++
 bridges/bridge_native_rtp.c                        |    4 +-
 build_tools/cflags.xml                             |   28 +
 build_tools/make_version_c                         |   25 +
 channels/chan_alsa.c                               |    2 +-
 channels/chan_console.c                            |    2 +-
 channels/chan_dahdi.c                              |    5 +
 channels/chan_iax2.c                               |   65 +-
 channels/chan_mgcp.c                               |   10 +-
 channels/chan_motif.c                              |    2 +-
 channels/chan_nbs.c                                |    2 +-
 channels/chan_oss.c                                |    2 +-
 channels/chan_phone.c                              |    2 +-
 channels/chan_pjsip.c                              |   32 +-
 channels/chan_sip.c                                |  165 +-
 channels/chan_skinny.c                             |   53 +-
 channels/chan_unistim.c                            |   18 +-
 channels/chan_vpb.cc                               |    2 +-
 channels/sig_pri.c                                 |    2 +-
 channels/sip/config_parser.c                       |    9 +-
 channels/sip/include/sip.h                         |    1 +
 channels/sip/reqresp_parser.c                      |    2 +-
 codecs/codec_resample.c                            |    8 +-
 configs/samples/confbridge.conf.sample             |    6 +
 configs/samples/pjsip.conf.sample                  |    8 +
 configs/samples/sip.conf.sample                    |    4 +
 .../189a235b3fd7_add_keep_alive_interval.py        |   22 +
 .../28ce1e718f05_add_fatal_response_interval.py    |   22 +
 .../2d078ec071b7_increaes_contact_column_size.py   |   22 +
 contrib/realtime/mssql/mssql_config.sql            |   20 +-
 contrib/realtime/mysql/mysql_config.sql            |   18 +
 contrib/realtime/oracle/oracle_config.sql          |   20 +-
 contrib/realtime/postgresql/postgresql_config.sql  |   14 +-
 contrib/scripts/autosupport                        |   95 +-
 funcs/func_callerid.c                              |   39 +-
 funcs/func_channel.c                               |    4 +-
 funcs/func_holdintercept.c                         |  236 +++
 funcs/func_presencestate.c                         |    4 +-
 include/asterisk/ast_version.h                     |    3 +
 include/asterisk/bridge.h                          |   12 +
 include/asterisk/format_cap.h                      |    5 +
 include/asterisk/res_pjsip.h                       |   17 +
 include/asterisk/res_pjsip_cli.h                   |    2 +-
 include/asterisk/res_pjsip_pubsub.h                |    9 +
 include/asterisk/statsd.h                          |   71 +-
 include/asterisk/taskprocessor.h                   |    8 +
 include/asterisk/term.h                            |    4 +-
 include/asterisk/threadpool.h                      |    6 +
 include/asterisk/translate.h                       |    8 +
 include/asterisk/utils.h                           |   23 +
 main/aoc.c                                         |   20 +-
 main/asterisk.c                                    |  124 +-
 main/audiohook.c                                   |   15 +-
 main/bridge.c                                      |   10 +-
 main/bridge_channel.c                              |    4 +
 main/channel.c                                     |   13 +-
 main/cli.c                                         |    8 +-
 main/codec_builtin.c                               |    2 +-
 main/config.c                                      |   44 +-
 main/dial.c                                        |    7 +-
 main/file.c                                        |    2 +-
 main/format.c                                      |   22 +-
 main/format_cap.c                                  |   42 +-
 main/hashtab.c                                     |    2 +
 main/loader.c                                      |    4 +-
 main/manager.c                                     |   22 +-
 main/rtp_engine.c                                  |   38 +-
 main/sched.c                                       |   22 +-
 main/sorcery.c                                     |    8 +-
 main/stasis.c                                      |    4 +-
 main/stasis_channels.c                             |   24 +-
 main/stasis_endpoints.c                            |    2 +-
 main/strings.c                                     |   91 +-
 main/taskprocessor.c                               |   18 +-
 main/term.c                                        |   27 +-
 main/threadpool.c                                  |   72 +-
 main/translate.c                                   |   41 +-
 main/utils.c                                       |   51 +-
 main/xmldoc.c                                      |  170 +-
 pbx/pbx_dundi.c                                    |    1 +
 res/res_agi.c                                      |    6 +-
 res/res_chan_stats.c                               |    4 +-
 res/res_config_pgsql.c                             |    8 +-
 res/res_endpoint_stats.c                           |  157 ++
 res/res_fax.c                                      |    4 +-
 res/res_format_attr_h264.c                         |   16 +-
 res/res_format_attr_opus.c                         |  210 +-
 res/res_format_attr_vp8.c                          |  228 +++
 res/res_pjsip.c                                    |   63 +-
 res/res_pjsip/config_auth.c                        |   15 +-
 res/res_pjsip/config_system.c                      |    2 +-
 res/res_pjsip/config_transport.c                   |   50 +-
 res/res_pjsip/location.c                           |  147 +-
 res/res_pjsip/pjsip_cli.c                          |   15 +-
 res/res_pjsip/pjsip_configuration.c                |  270 ++-
 res/res_pjsip/pjsip_distributor.c                  |   16 +-
 res/res_pjsip/pjsip_options.c                      |  129 +-
 res/res_pjsip_caller_id.c                          |   14 +-
 res/res_pjsip_endpoint_identifier_ip.c             |   58 +-
 res/res_pjsip_exten_state.c                        |    4 +-
 res/res_pjsip_mwi.c                                |    6 +-
 res/res_pjsip_notify.c                             |    2 +-
 res/res_pjsip_outbound_registration.c              |  125 +-
 res/res_pjsip_pubsub.c                             |  338 +++-
 res/res_pjsip_pubsub.exports.in                    |    1 +
 res/res_pjsip_rfc3326.c                            |   17 +-
 res/res_pjsip_sdp_rtp.c                            |   11 +-
 res/res_pjsip_t38.c                                |   67 +-
 res/res_rtp_asterisk.c                             |   36 +-
 res/res_sorcery_memory_cache.c                     | 1143 ++++++++++-
 res/res_sorcery_realtime.c                         |    2 +-
 res/res_stasis.c                                   |    6 +-
 res/res_stasis_playback.c                          |    4 +-
 res/res_stasis_recording.c                         |    4 +-
 res/res_statsd.c                                   |   88 +-
 res/res_statsd.exports.in                          |    1 +
 res/stasis/app.c                                   |    4 +-
 rest-api-templates/api.wiki.mustache               |    2 +-
 rest-api-templates/asterisk_processor.py           |    2 +
 tests/test_config.c                                |    4 +-
 tests/test_format_cap.c                            |    4 +-
 tests/test_threadpool.c                            |   83 +
 141 files changed, 6586 insertions(+), 2462 deletions(-)

diff --git a/.version b/.version
index 649e128..51e739d 100644
--- a/.version
+++ b/.version
@@ -1 +1 @@
-13.6.0
\ No newline at end of file
+13.7.0-rc2
\ No newline at end of file
diff --git a/CHANGES b/CHANGES
index 231ef2a..40dbfab 100644
--- a/CHANGES
+++ b/CHANGES
@@ -9,6 +9,96 @@
 ==============================================================================
 
 ------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 13.6.0 to Asterisk 13.7.0 ------------
+------------------------------------------------------------------------------
+
+Codecs
+------------------
+ * Added format attribute negotiation for the VP8 video codec. Format attribute
+   negotiation is provided by the res_format_attr_vp8 module.
+
+ConfBridge
+------------------
+ * A new "timeout" user profile option has been added. This configures the number
+   of seconds that a participant may stay in the ConfBridge after joining. When
+   the time expires, the user is ejected from the conference and CONFBRIDGE_RESULT
+   is set to "TIMEOUT" on the channel.
+
+chan_sip
+------------------
+ * The websockets_enabled option has been added to the general section of
+   sip.conf.  The option is enabled by default to match the previous behavior.
+   The option should be disabled when using res_pjsip_transport_websockets to
+   ensure chan_sip will not conflict with PJSIP websockets.
+
+Dialplan Functions
+------------------
+ * The HOLD_INTERCEPT dialplan function now actually exists in the source tree.
+   While support for the events was added in Asterisk 13.4.0, the function
+   accidentally never made it in. That function is now present, and will cause
+   the 'hold' raised by a channel to be intercepted and converted into an
+   event instead.
+
+res_pjsip_outbound_registration
+-------------------------------
+ * A new 'fatal_retry_interval' option has been added to outbound registration.
+   When set (default is zero), and upon receiving a failure response to an
+   outbound registration, registration is retried at the given interval up to
+   'max_retries'.
+
+ * If res_statsd is loaded and a StatsD server is configured, basic statistics
+   regarding the state of outbound registrations will now be emitted. This
+   includes:
+   - A GAUGE statistic for the overall number of outbound registrations, i.e.:
+       PJSIP.registrations.count
+   - A GAUGE statistic for the overall number of outbound registrations in a
+     particular state, e.g.:
+       PJSIP.registrations.state.Registered
+
+res_pjsip
+------------------
+ * The ability to use "like" has been added to the pjsip list and show
+   CLI commands.  For instance: CLI> pjsip list endpoints like abc
+
+ * If res_statsd is loaded and a StatsD server is configured, basic statistics
+   regarding the state of PJSIP contacts will now be emitted. This includes:
+   - A GAUGE statistic for the overall number of contacts in a particular
+     state, e.g.:
+       PJSIP.contacts.states.Reachable
+   - A TIMER statistic for the RTT time for each qualified contact, e.g.:
+       PJSIP.contacts.alice@@127.0.0.1:5061.rtt
+
+res_sorcery_memory_cache
+------------------------
+ * A new caching strategy, full_backend_cache, has been added which caches
+   all stored objects in the backend. When enabled all objects will be
+   expired or go stale according to the configuration. As well when enabled
+   all retrieval operations will be performed against the cache instead of
+   the backend.
+
+func_callerid
+-------------------
+ * CALLERID(pres) is now documented as a valid alternative to setting both
+   CALLERID(name-pres) and CALLERID(num-pres) at once.  Some channel drivers,
+   like chan_sip, don't make a distinction between the two: they take the
+   least public value from name-pres and num-pres.  By using CALLERID(pres)
+   for reading and writing, you touch the same combined value in the dialplan.
+   The same applies to CONNECTEDLINE(pres), REDIRECTING(orig-pres),
+   REDIRECTING(to-pres) and REDIRECTING(from-pres).
+
+res_endpoint_stats
+-------------------
+ * A new module that emits StatsD statistics regarding Asterisk endpoints.
+   This includes a total count of the number of endpoints, the count of the
+   number of endpoints in the technology agnostic state of the endpoint -
+   online or offline - as well as the number of channels associated with each
+   endpoint. These are recorded as three different GAUGE statistics:
+    - endpoints.count
+    - endpoints.state.{unknown|offline|online}
+    - endpoints.{tech}.{resource}.channels
+
+
+------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 13.5.0 to Asterisk 13.6.0 ------------
 ------------------------------------------------------------------------------
 
diff --git a/ChangeLog b/ChangeLog
index 4d431b3..496e64f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,52 +1,1905 @@
-2015-10-09 21:48 +0000  Asterisk Development Team <asteriskteam at digium.com>
+2015-12-18 18:18 +0000  Asterisk Development Team <asteriskteam at digium.com>
 
-	* asterisk 13.6.0 Released.
+	* asterisk 13.7.0-rc2 Released.
 
-2015-10-09 16:48 +0000 [68d2a14e1a]  Kevin Harwell <kharwell at lunkwill>
+2015-12-18 12:18 +0000 [a93a5387d4]  Kevin Harwell <kharwell at lunkwill>
 
-	* Release summaries: Add summaries for 13.6.0
+	* Release summaries: Add summaries for 13.7.0-rc2
 
-2015-10-09 16:45 +0000 [cc0eff5651]  Kevin Harwell <kharwell at lunkwill.digium.internal>
+2015-12-18 12:18 +0000 [8e201b742a]  Kevin Harwell <kharwell at lunkwill.digium.internal>
 
 	* Release summaries: Remove previous versions
 
-2015-10-09 16:45 +0000 [8cd191b885]  Kevin Harwell <kharwell at lunkwill>
+2015-12-18 12:18 +0000 [5a164c70f2]  Kevin Harwell <kharwell at lunkwill>
 
-	* .version: Update for 13.6.0
+	* .version: Update for 13.7.0-rc2
 
-2015-10-09 16:45 +0000 [a3777c24fd]  Kevin Harwell <kharwell at lunkwill>
+2015-12-18 12:18 +0000 [e039eca0a7]  Kevin Harwell <kharwell at lunkwill>
 
-	* .lastclean: Update for 13.6.0
+	* .lastclean: Update for 13.7.0-rc2
 
-2015-10-09 16:45 +0000 [68121cef21]  Kevin Harwell <kharwell at lunkwill>
+2015-12-18 12:18 +0000 [bfe2eb8751]  Kevin Harwell <kharwell at lunkwill>
 
-	* realtime: Add database scripts for 13.6.0
+	* realtime: Add database scripts for 13.7.0-rc2
 
-2015-10-07 18:42 +0000  Asterisk Development Team <asteriskteam at digium.com>
+2015-12-16 11:25 +0000 [805297783d]  Mark Michelson <mmichelson at digium.com>
 
-	* asterisk 13.6.0-rc3 Released.
+	* Alembic: Add PJSIP global keep_alive_interval.
 
-2015-10-07 13:41 +0000 [9da83dbd15]  Kevin Harwell <kharwell at lunkwill>
+	  The keep_alive_interval option was added about a year ago, but no
+	  alembic revision was created to add the appropriate column to the
+	  database.
 
-	* Release summaries: Add summaries for 13.6.0-rc3
+	  This commit fixes the problem and adds the column. This was discovered
+	  by running the testsuite with automatic conversion to realtime enabled.
 
-2015-10-07 13:41 +0000 [8c60f9326c]  Kevin Harwell <kharwell at lunkwill.digium.internal>
+	  Change-Id: If3ef92a7c4f4844d08f8aae170d2178aec5c4c1a
 
-	* Release summaries: Remove previous versions
+2015-12-16 11:28 +0000 [63df9bb560]  Mark Michelson <mmichelson at digium.com>
+
+	* Alembic: Increase column size of PJSIP AOR "contact".
+
+	  When running the PJSIP AMI "show_endpoint" test with automatic
+	  conversion to realtime, the test would fail. This was because the AOR
+	  "contact" column was sized at 40, and the configured contact was larger
+	  than that.
+
+	  This commit increases the size of the contact column to 255 characters.
+
+	  Change-Id: Ia65bc7fd37699b7c0eaef9629a1a31eab9a24ba1
+
+2015-12-14 12:04 +0000 [acd19d5f1f]  Joshua Colp <jcolp at digium.com>
+
+	* json: Audit ast_json_* usage for thread safety.
+
+	  The JSON library Asterisk uses, jansson, is not thread
+	  safe for us in a few ways. To help with this wrappers for JSON
+	  object reference count increasing and decreasing were added
+	  which use a global lock to ensure they don't clobber over
+	  each other. This does not extend to reference count manipulation
+	  within the jansson library itself. This means you can't safely
+	  use the object borrowing specifier (O) in ast_json_pack and
+	  you can't share JSON instances between objects.
+
+	  This change removes uses of the O specifier and replaces them
+	  with the o specifier and an explicit ast_json_ref. Some cases
+	  of instance sharing have also been removed.
+
+	  ASTERISK-25601 #close
+
+	  Change-Id: I06550d8b0cc1bfeb56cab580a4e608ae4f1ec7d1
+
+2015-12-05 10:01 +0000 [11ff4e6b3f]  Joshua Colp <jcolp at digium.com>
+
+	* res_sorcery_memory_cache: Add support for a full backend cache.
+
+	  This change introduces the configuration option 'full_backend_cache'
+	  which changes the cache to be a full mirror of the backend instead
+	  of a per-object cache. This allows all sorcery retrieval operations
+	  to be carried out against it and is useful for object types which
+	  are used in a "retrieve all" or "retrieve some" pattern.
+
+	  ASTERISK-25625 #close
+
+	  Change-Id: Ie2993487e9c19de563413ad5561c7403b48caab5
+	  (cherry picked from commit 59d5bb0613810418f2a618b9a6dee5bcfd45767e)
+
+2015-12-17 10:25 +0000 [9bc1e49325]  Joshua Colp <jcolp at digium.com>
+
+	* rtp_engine: Ignore empty filenames in DTLS configuration.
+
+	  When applying an empty DTLS configuration the filenames in the
+	  configuration will be empty. This is actually valid to do and
+	  each filename should simply be ignored.
+
+	  Change-Id: Ib761dc235638a3fb701df337952f831fc3e69539
+
+2015-12-17 08:10 +0000 [c78eb1e82b]  Joshua Colp <jcolp at digium.com>
+
+	* chan_sip: Enable WebSocket support by default.
+
+	  Per the documentation the WebSocket support in chan_sip is
+	  supposed to be enabled by default but is not. This change
+	  corrects that.
+
+	  Change-Id: Icb02bbcad47b11a795c14ce20a9bf29649a54423
+
+2015-12-15 18:01 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.7.0-rc1 Released.
+
+2015-12-15 11:57 +0000 [0370acecfc]  Kevin Harwell <kharwell at lunkwill>
+
+	* Release summaries: Add summaries for 13.7.0-rc1
+
+2015-12-15 11:54 +0000 [d1bb33fe0b]  Kevin Harwell <kharwell at lunkwill>
+
+	* .version: Update for 13.7.0-rc1
+
+2015-12-15 11:54 +0000 [d06a65de01]  Kevin Harwell <kharwell at lunkwill>
+
+	* .lastclean: Update for 13.7.0-rc1
+
+2015-12-15 11:54 +0000 [fb37b44660]  Kevin Harwell <kharwell at lunkwill>
+
+	* realtime: Add database scripts for 13.7.0-rc1
+
+2015-12-15 11:48 +0000 [20b7164b8c]  Kevin Harwell <kharwell at lunkwill>
+
+	* .version: Update for 13.7.0-rc1
+
+2015-12-15 11:48 +0000 [6cbf2414c3]  Kevin Harwell <kharwell at lunkwill>
+
+	* .lastclean: Update for 13.7.0-rc1
+
+2015-12-15 11:48 +0000 [ba1794464d]  Kevin Harwell <kharwell at lunkwill>
+
+	* realtime: Add database scripts for 13.7.0-rc1
+
+2015-12-15 11:39 +0000 [b3e9753a23]  Kevin Harwell <kharwell at lunkwill>
+
+	* .version: Update for 13.7.0-rc1
+
+2015-12-15 11:39 +0000 [b0df64b5f0]  Kevin Harwell <kharwell at lunkwill>
+
+	* .lastclean: Update for 13.7.0-rc1
+
+2015-12-15 11:39 +0000 [ce9a59faf6]  Kevin Harwell <kharwell at lunkwill>
+
+	* realtime: Add database scripts for 13.7.0-rc1
+
+2015-12-15 11:28 +0000 [2e26bef5bb]  Kevin Harwell <kharwell at lunkwill>
+
+	* .version: Update for 13.7.0-rc1
+
+2015-12-15 11:28 +0000 [5e9b47516d]  Kevin Harwell <kharwell at lunkwill>
+
+	* .lastclean: Update for 13.7.0-rc1
+
+2015-12-15 11:28 +0000 [034112c574]  Kevin Harwell <kharwell at lunkwill>
+
+	* realtime: Add database scripts for 13.7.0-rc1
+
+2015-12-15 11:19 +0000 [d1f8ff1789]  Kevin Harwell <kharwell at lunkwill>
+
+	* .version: Update for 13.7.0-rc1
+
+2015-12-15 11:19 +0000 [9376488bef]  Kevin Harwell <kharwell at lunkwill>
+
+	* .lastclean: Update for 13.7.0-rc1
+
+2015-12-15 11:19 +0000 [a894c9e7a9]  Kevin Harwell <kharwell at lunkwill>
+
+	* realtime: Add database scripts for 13.7.0-rc1
+
+2015-12-15 11:12 +0000 [52afb0f112]  Kevin Harwell <kharwell at lunkwill>
+
+	* .version: Update for 13.7.0-rc1
+
+2015-12-15 11:12 +0000 [2de343eb85]  Kevin Harwell <kharwell at lunkwill>
+
+	* .lastclean: Update for 13.7.0-rc1
+
+2015-12-15 11:12 +0000 [184de2a160]  Kevin Harwell <kharwell at lunkwill>
+
+	* realtime: Add database scripts for 13.7.0-rc1
+
+2015-12-14 13:53 +0000 [24ae124e4f]  server-pandora <server-pandora at xencall.com>
+
+	* res_rtp_asterisk.c: Fix DTLS negotiation delays.
+
+	  - Trigger pending DTLS packets to send out, once the RTP instance's remote
+	    address is set.
+	  - Avoids locking the DTLS structure unnecessarily by only doing this if
+	    DTLS is passive.
+	  - Add DTLS locks around the structurally sensitive calls in the SSL
+	    portion of __rtp_recvfrom, since dtls_srtp_check_pending does not lock
+	    inside of itself, and we're dealing with the SSL BIO in at least two
+	    threads.
+
+	  WebRTC channels may receive a DTLS handshake before
+	  ast_rtp_remote_address_set is called, which causes there to be a pending
+	  response to send out.   Previous to 1ad827, this was handled by calling
+	  dtls_srtp_check_pending on receipt of any RTP packet - a STUN or RTP
+	  packet could trigger the pending handshake response.  Since that was
+	  rightfully removed, whenever the DTLS handshake is received before the
+	  remote address is set, we would have to wait until another SSL packet
+	  arrives.
+
+	  As of Chrome M47's optimizations to their handshake process, WebRTC
+	  conversations between Chrome M47+ and Asterisk, where Asterisk is passive,
+	  experience a 1 second delay without this patch, because the SSL handshake
+	  is received before ICE negotation stores the remote_address, and the next
+	  SSL packet isn't received until after a 1 second timeout in Chrome, which
+	  causes a new handshake request.
+
+	  ASTERISK-25614 #close
+
+	  Change-Id: I547f1be7e302dbf71f6553dd8cbc0657b1d0b908
+
+2015-12-14 15:25 +0000 [36097a185d]  Richard Mudgett <rmudgett at digium.com>
+
+	* Fix sscanf() format string type mismatch.
+
+	  ASTERISK-25615
+	  Reported by: George Joseph
+
+	  Change-Id: Ieff35307254ca193f3d473cff2e396ca57c7ce0b
+
+2015-12-13 13:13 +0000 [94f9927784]  Matt Jordan <mjordan at digium.com>
+
+	* main/utils: Don't emit an ERROR message if the read end of a pipe closes
+
+	  An ERROR or WARNING message should generally indicate that something has gone
+	  wrong in Asterisk. In the case of writing to a file descriptor, Asterisk is not
+	  in control of when the far end closes its reading on a file descriptor. If the
+	  far end does close the file descriptor in an unclean fashion, this isn't a bug
+	  or error in Asterisk, particularly when the situation can be gracefully
+	  handled in Asterisk.
+
+	  Currently, when this happens, a user would see the following somewhat cryptic
+	  ERROR message:
+
+	    "utils.c: write() returned error: Broken pipe"
+
+	  There's a few problems with this:
+	  (1) It doesn't provide any context, other than 'something broke a pipe'
+	  (2) As noted, it isn't actually an error in Asterisk
+	  (3) It can get rather spammy if the thing breaking the pipe occurs often, such
+	      as a FastAGI server
+	  (4) Spammy ERROR messages make Asterisk appear to be having issues, or can even
+	      mask legitimate issues
+
+	  This patch changes ast_carefulwrite to only log an ERROR if we actually had one
+	  that was reasonably under our control. For debugging purposes, we still emit
+	  a debug message if we detect that the far side has stopped reading.
+
+	  Change-Id: Ia503bb1efcec685fa6f3017bedf98061f8e1b566
+
+2015-12-12 11:08 +0000 [5b867fa904]  gtjoseph <george.joseph at fairview5.com>
+
+	* pjsip/config_transport: Check pjproject version at runtime for async ops
+
+	  pjproject < 2.5.0 will segfault on a tls transport if async_operations
+	  is greater than 1.  A runtime version check has been added to throw
+	  an error if the version is < 2.5.0 and async_operations > 1.
+
+	  To assist in the check, a new api "ast_compare_versions" was added
+	  to utils which compares 2 major.minor.patch.extra version strings.
+
+	  ASTERISK-25615 #close
+
+	  Change-Id: I8e88bb49cbcfbca88d9de705496d6f6a8c938a98
+	  Reported-by: George Joseph
+	  Tested-by: George Joseph
+
+2015-12-10 11:44 +0000 [14b41115e3]  Jonathan Rose <jrose at digium.com>
+
+	* chan_sip: Add TCP/TLS keepalive to TCP/TLS server
+
+	  Adds the TCP Keep Alive option to TCP and TLS server sockets. Previously
+	  this option was only being set on session sockets.
+	  http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/
+	  According to the link above, the SO_KEEPALIVE option is useful for knowing
+	  when a TCP connected endpoint has severed communication without indicating
+	  it or has become unreachable for some reason. Without this patch, keep
+	  alive is not set on the socket listening for incoming TCP sessions and
+	  in Komatsu's report this resulted in the thread listening for TCP becoming
+	  stuck in a waiting state.
+
+	  ASTERISK-25364 #close
+	  Reported by: Hiroaki Komatsu
+
+	  Change-Id: I7ed7bcfa982b367dc64b4b73fbd962da49b9af36
+2015-12-09 09:48 +0000 [cd119ed4a2]  Tyler Cambron <tcambron at digium.com>
+
+	* res_chan_stats: Fix bug to send correct statistics to StatsD
+
+	  Fixed a bug that originally would show a negative number of
+	  active calls occuring in Asterisk. A gauge is persistent so
+	  incrementing and decrementing it results in a more consistent
+	  performance. Also changed to the call to StatsD to use
+	  ast_statsd_log_string() so that a "+" could be sent to StatsD.
+
+	  ASTERISK-25619 #close
+
+	  Change-Id: Iaaeff5c4c6a46535366b4d16ea0ed0ee75ab2ee7
+
+2015-12-07 13:07 +0000 [ddf4dddf4f]  Corey Farrell <git at cfware.com>
+
+	* app_meetme: Set default value for audio_buffers.
+
+	  The default value was never set for audio_buffers, causing bad
+	  audio quality.  This ensures the default is always set.
+
+	  ASTERISK-25569 #close
+
+	  Change-Id: I2d2ee3e644120b0f9f6ea6ab9286d7d590942a44
+2015-12-08 01:57 +0000 [142d4fefb8]  Filip Jenicek <phill at janevim.cz>
+
+	* chan_sip: Check sip_pvt pointer in ast_channel_get_t38_state(c)
+
+	  Asterisk may crash when calling ast_channel_get_t38_state(c)
+	  on a locked channel which is being hung up.
+
+	  ASTERISK-25609 #close
+
+	  Change-Id: Ifaa707c04b865a290ffab719bd2e5c48ff667c7b
+
+2015-12-08 17:49 +0000 [21962dad93]  gtjoseph <george.joseph at fairview5.com>
+
+	* res_pjsip:  Add existence and readablity checks for tls related files
+
+	  Both transport and endpoint now check for the existence and readability
+	  of tls certificate and key files before passing them on to pjproject.
+	  This will cause the object to not load rather than waiting for pjproject
+	  to discover that there's a problem when a session is attempted.
+
+	  NOTE: chan_sip also uses ast_rtp_dtls_cfg_parse but it's located
+	  in build_peer which is gigantic and I didn't want to disturb it.
+	  Error messages will emit but it won't interrupt chan_sip loading.
+
+	  ASTERISK-25618 #close
+
+	  Change-Id: Ie43f2c1d653ac1fda6a6f6faecb7c2ebadaf47c9
+	  Reported-by: George Joseph
+	  Tested-by: George Joseph
+
+2015-12-02 12:42 +0000 [28d9243079]  Eugene Voityuk <eugene at thirdlane.com>
+
+	* chan_sip.c: Start ICE negotiation when response is sent or received.
+
+	  The current logic for ICE negotiation starts it
+	  when receiving an SDP with ICE candidates. This is
+	  incorrect as ICE negotiation can only start when each 
+	  call party have at least one pair of local and remote 
+	  candidate. Starting ICE negotiation early would result 
+	  in negotiation failure and ultimately no audio.
+
+	  This change makes it so ICE negotiation is only started
+	  when a response with SDP is received or when a response
+	  with SDP is sent.
+
+	  ASTERISK-24146
+
+	  Change-Id: I55a632bde9e9827871b09141d82747e08379a8ca
+2015-12-08 11:03 +0000 [e03582a1c2]  gtjoseph <george.joseph at fairview5.com>
+
+	* res_pjsip/config_transport: Prevent async_operations > 1 when protocol = tls
+
+	  See ASTERISK-25615.
+	  If the transport protocol is tls and async_operations > 1, pjproject
+	  will segfault if more than one operation is attempted on the same socket.
+	  Until this is fixed upstream, a check has been added to throw an error
+	  if a tls transport config has async_operations set to > 1.
+
+	  ASTERISK-25615
+
+	  Change-Id: I76b9a5b2a5a0054fe71ca5851e635f2dca7685a6
+	  Reported-by: George Joseph
+	  Tested-by: George Joseph
+
+2015-12-08 08:39 +0000 [876600ce6e]  Alexander Traud <pabstraud at compuserve.com>
+
+	* codec_resample: Increase buffer for Opus Codec with FEC.
+
+	  ASTERISK-25599 #close
+
+	  Change-Id: Idbd187f711b2ec63dda949ca0f79aa0c1a0a0b6e
+
+2015-12-08 03:46 +0000 [69e3d40ad7]  Alexander Traud <pabstraud at compuserve.com>
+
+	* translate: Avoid a warning message when doing FEC within Opus Codec.
+
+	  ASTERISK-25616 #close
+
+	  Change-Id: Ibe729aaf2e6e25506cff247cec5149ec1e589319
+
+2015-12-04 15:36 +0000 [2b992014dc]  Richard Mudgett <rmudgett at digium.com>
+
+	* chan_sip: Fix crash involving the bogus peer during sip reload.
+
+	  A crash happens sometimes when performing a CLI "sip reload".  The bogus
+	  peer gets refreshed while it is in use by a new call which can cause the
+	  crash.
+
+	  * Protected the global bogus peer object with an ao2 global object
+	  container.
+
+	  ASTERISK-25610 #close
+
+	  Change-Id: I5b528c742195681abcf713c6e1011ea65354eeed
+
+2015-12-06 16:32 +0000 [529535f0c2]  Matt Jordan <mjordan at digium.com>
+
+	* Revert "bridges/bridge_t38: Add a bridging module for managing T.38 state"
+
+	  This reverts commit 6614babea27fbafbe11820ea03737dd5c4f9ecec.
+
+	  Unfortunately, using a bridge to manage T.38 state will cause severe deadlocks
+	  in core_unreal/chan_local. Local channels attempt to reach across both their
+	  peer and the peer's bridge to inspect T.38 state. Given the propensity of
+	  Local channel chains, managing the locking situation in such a scenario is
+	  practically infeasible.
+
+	  Change-Id: Ic687397ffea08dfb899345a443bd990ec3d0416a
+
+2015-12-04 16:23 +0000 [450579e908]  gtjoseph <george.joseph at fairview5.com>
+
+	* res_pjsip/contacts/statsd:  Make contact lifecycle events more consistent
+
+	  It will never be perfect or even pretty, mostly because of the differences
+	  between static and dynamic contacts.
+
+	  Created:
+
+	  Can't use the contact or contact_status alloc functions
+	  because the objects come and go regardless of the actual state.
+
+	  Can't use the contact_apply_handler, ast_sip_location_add_contact or
+	  a sorcery created handler because they only get called for dynamic
+	  contacts.  Similarly, permanent_uri_handler only gets called for
+	  static contacts.
+
+	  So, Matt had it right. :)  ast_res_pjsip_find_or_create_contact_status is
+	  the only place it can go and not have duplicated code.  Both
+	  permanent_uri_handler and contact_apply_handler call find_or_create.
+
+	  Removed:
+
+	  Can't use the destructors for the same reason as above.  The only
+	  place to put this is in persistent_endpoint_contact_deleted_observer
+	  which I believe is the "correct" place but even that will handle only
+	  dynamic contacts.  This doesn't called on shutdown however.  There is
+	  no hook to use for static contacts that may be removed because of a
+	  config change while asterisk is in operation.
+
+	  I moved the cleanup of contact_status from ast_sip_location_delete_contact
+	  to the handler as well.
+
+	  Status Change and RTT:
+
+	  Although they worked fine where they were (in update_contact_status) I
+	  moved them to persistent_endpoint_contact_status_observer to make it
+	  more consistent with removed.  There was logic there already to detect
+	  a state change.
+
+	  Finally, fixed a nit in permanent_uri_handler rmudgett reported
+	  eralier.
+
+	  ASTERISK-25608 #close
+
+	  Change-Id: I4b56e7dfc3be3baaaf6f1eac5b2068a0b79e357d
+	  Reported-by: George Joseph
+	  Tested-by: George Joseph
+
+2015-11-21 06:02 +0000 [5a18193dc0]  Alexander Traud <pabstraud at compuserve.com>
+
+	* res_format_attr_vp8: In SDP, forward max-fr and max-fs for video-codec VP8.
+
+	  ASTERISK-25584 #close
+
+	  Change-Id: Iae00071b4ff1ae76f24995aeac4d00284fd14f91
+
+2015-11-21 05:21 +0000 [3e2178c05e]  Alexander Traud <pabstraud at compuserve.com>
+
+	* res_format_attr_opus: Update to latest RFC 7587.
+
+	  Beside that, the format-attribute module sends only non-default values in the
+	  line fmtp, now. This avoids unnecessary overhead in SDP messages. Furthermore,
+	  previously the parameter stereo was not parsed when being the first parameter.
+
+	  ASTERISK-25583 #close
+
+	  Change-Id: Iae85ba3e5960bfd5d51cf65bcffad00dd4875a73
+2015-12-02 14:11 +0000 [072d94183c]  Jonathan Rose <jrose at digium.com>
+
+	* Fix crash in audiohook translate to slin
+
+	  This patch fixes a crash which would occur when an audiohook was
+	  applied to a channel using an audio codec that could not be translated
+	  to signed linear (such as when using pass-through codecs like OPUS or
+	  when the codec translator module for the format in use is not loaded).
+
+	  ASTERISK-25498 #close
+	  Reported by: Ben Langfeld
+
+	  Change-Id: Ib6ea7373fcc22e537cad373996136636201f4384
+
+2015-12-03 12:07 +0000 [9184fbeb34]  gtjoseph <george.joseph at fairview5.com>
+
+	* res_pjsip: Use a MD5 hash for static Contact IDs
+
+	  When 90d9a70789 was merged, it mostly tested dynamic contacts created as
+	  a result of registering a PJSIP endpoint. Contacts generated in this
+	  fashion typically have a long alphanumeric string as their object identifier,
+	  which maps reasonably well for StatsD. Unfortunately, this doesn't work in the
+	  general case. StatsD treats both '.' and ':' characters as special characters.
+	  In particular, having a ':' appear in the middle of a StatsD metric will
+	  result in the metric being rejected.
+
+	  This causes some obvious issues with SIP URIs.
+
+	  The StatsD API should not be responsible for escaping the metric name passed
+	  to it. The metric is treated as a single long string, and it would be
+	  challenging to know what to escape in the string passed to the function.
+	  Likewise, we don't want to escape the metric in PJSIP, as that involves
+	  overhead that is wasted when either res_statsd isn't loaded or enabled.
+
+	  This patch takes an alternative approach. The Contact ID has been changed
+	  to be "aor@@uri_hash" instead of "aor@@uri". This (a) won't contain any of the
+	  aforementioned special characters, (b) can be done on Contact creation,
+	  which has minimal impact on run-time performance, and (c) also conforms to an
+	  earlier commit that changed the ID for dynamic contacts.
+
+	  The downside of this is that StatsD users will have to map SHA1 hashes back to
+	  the Contacts that are emitting the statistics. To that end, the CLI commands
+	  have been updated to include the first 10 characters of the MD5 hash, which
+	  should be enough to match what is shown in Graphite (or some other StatsD
+	  backend).
+
+	  ASTERISK-25595 #close
+
+	  Change-Id: Ic674a3307280365b4a45864a3571c295b48a01e2
+	  Reported-by: Matt Jordan
+	  Tested-by: George Joseph
+
+2015-11-30 22:19 +0000 [ed9134282e]  gtjoseph <george.joseph at fairview5.com>
+
+	* res_pjsip:  Update logging to show contact->uri in messages
+
+	  An earlier commit changed the id of dynamic contacts to contain
+	  a hash instead of the uri.  This patch updates status change
+	  logging to show the aor/uri instead of the id.  This required
+	  adding the aor id to contact and contact_status and adding
+	  uri to contact_status.  The aor id gets added to contact and
+	  contact_status in their allocators and the uri gets added to
+	  contact_status in pjsip_options when the contact_status is
+	  created or updated.
+
+	  ASTERISK-25598 #close
+
+	  Reported-by: George Joseph
+	  Tested-by: George Joseph
+
+	  Change-Id: I56cbec1d2ddbe8461367dd8b6da8a6f47f6fe511
+
+2015-12-01 16:11 +0000 [eadad24b59]  Jonathan Rose <jrose at digium.com>
+
+	* Unset BRIDGEPEER when leaving a bridge
+
+	  Currently if a channel is transferred out of a bridge, the BRIDGEPEER
+	  variable (also BRIDGEPVTCALLID) remain set even once the channel is
+	  out of the bridge. This patch removes these variables when leaving
+	  the bridge.
+
+	  ASTERISK-25600 #close
+	  Reported by: Mark Michelson
+
+	  Change-Id: I753ead2fffbfc65427ed4e9244c7066610e546da
+
+2015-11-30 14:22 +0000 [bb0b60619d]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_sorcery_memory_cache.c: Fix off nominal ref leak.
+
+	  Change-Id: If83d63cf11cbc6df9b15251848b01feb570ade49
+
+2015-11-30 16:42 +0000 [e7c88e11aa]  Richard Mudgett <rmudgett at digium.com>
+
+	* sched.c: Make not return a sched id of 0.
+
+	  According to the API doxygen a sched ID of 0 is valid.  Unfortunately, 0
+	  was never returned historically and several users incorrectly coded usage
+	  of the returned sched ID assuming that 0 was invalid.
+
+	  ASTERISK-25476
+
+	  Change-Id: Ib19c7ebb44ec9fd393ef6646dea806d4f34e3a20
+
+2015-11-25 12:23 +0000 [4aed349a7b]  Richard Mudgett <rmudgett at digium.com>
+
+	* Audit improper usage of scheduler exposed by 5c713fdf18f. (v13 additions)
+
+	  chan_sip.c:
+	  * Initialize mwi subscription scheduler ids earlier because of ASTOBJ to
+	  ao2 conversion.
+
+	  * Initialize register scheduler ids earlier because of ASTOBJ to ao2
+	  conversion.
+
+	  chan_skinny.c:
+	  * Fix more scheduler usage for the valid 0 id value.
+
+	  ASTERISK-25476
+
+	  Change-Id: If9f0e5d99638b2f9d102d1ebc9c5a14b2d706e95
+
+2015-11-24 12:44 +0000 [6d9156d10f]  Richard Mudgett <rmudgett at digium.com>
+
+	* Audit improper usage of scheduler exposed by 5c713fdf18f.
+
+	  channels/chan_iax2.c:
+	  * Initialize struct chan_iax2_pvt scheduler ids earlier because of
+	  iax2_destroy_helper().
+
+	  channels/chan_sip.c:
+	  channels/sip/config_parser.c:
+	  * Fix initialization of scheduler id struct members.  Some off nominal
+	  paths had 0 as a scheduler id to be destroyed when it was never started.
+
+	  chan_skinny.c:
+	  * Fix some scheduler id comparisons that excluded the valid 0 id.
+
+	  channel.c:
+	  * Fix channel initialization of the video stream scheduler id.
+
+	  pbx_dundi.c:
+	  * Fix channel initialization of the packet retransmission scheduler id.
+
+	  ASTERISK-25476
+
+	  Change-Id: I07a3449f728f671d326a22fcbd071f150ba2e8c8
+
+2015-12-01 07:55 +0000 [b76c196e13]  Alexander Traud <pabstraud at compuserve.com>
+
+	* codec_resample: Increase buffer for Opus Codec.
+
+	  ASTERISK-25599 #close
+
+	  Change-Id: I1f88a88c59fb4e1e62bbdbb100c7152d48e73f10
+
+2015-11-28 08:46 +0000 [6614babea2]  Matt Jordan <mjordan at digium.com>
+
+	* bridges/bridge_t38: Add a bridging module for managing T.38 state
+
+	  When 4875e5ac32 was merged, it fixed several issues with a direct media bridge
+	  transitioning to handling a T.38 fax. However, it uncovered a race condition
+	  caused by the bridging core. When a channel involved in a T.38 fax leaves a
+	  bridge, the frame queued by the channel driver that should inform the far side
+	  that it is no longer in a T.38 fax may not make it across the bridge. The
+	  bridging framework is *extremely* aggressive in tearing down the bridge, and
+	  control frames that are currently in flight *may* get dropped.
+
+	  This patch adds a new module to the bridging framework, bridge_t38. This module
+	  maintains some notion of the T.38 state for the two channels in a bridge. When
+	  the bridge detects that it is being torn down or when one of the two channels
+	  leaves, it informs the respective channel(s) that they should stop faxing. This
+	  ensures that channels switch back to audio if they survive and are ejected out
+	  of a bridge while faxing.
+
+	  ASTERISK-25582
+
+	  Change-Id: If5b0bb478eb01c4607c9f4a7fc17c7957d260ea0
+
+2015-11-27 07:39 +0000 [3fcf160fae]  Niklas Larsson <niklas at tese.se>
+
+	* CHANGES: Fix a typo
+
+	  Change-Id: Iceb3d9bb78140c376174a7bee197dfcf8ef9cda7
+2015-11-25 15:26 +0000 [45efbf8503]  Kevin Harwell <kharwell at digium.com>
+
+	* fastagi: record file closed after sending result
+
+	  The fastagi record-file testsuite test sometimes fails reporting an empty
+	  recorded file. This was happening because Asterisk was sending the agi result
+	  notification prior to actually closing the file and the data, being buffered,
+	  had not been written to the file yet when the test attempts to check the file
+	  size.
+
+	  This patch makes it so the record file stream is closed prior to sending the
+	  agi result notification.
+
+	  ASTERISK-25593 #close
+
+	  Change-Id: I6b2b3be3ae37f7c7b18e672c419a89b3b8513cde
+
+2015-11-25 13:29 +0000 [b2787876d6]  Walter Doekes <walter+asterisk at wjd.nu>
+
+	* main: Slight refactor of main. Improve color situation.
+
+	  Several issues are addressed here:
+	  - main() is large, and half of it is only used if we're not rasterisk;
+	    fixed by spliting up the daemon part into a separate function.
+	  - Call ast_term_init from rasterisk as well.
+	  - Remove duplicate code reading/writing asterisk history file.
+	  - Attempt to tackle background color issues and color changes that
+	    occur. Tested by starting asterisk -c until the colors stopped
+	    changing at odd locations.
+
+	  ASTERISK-25585 #close
+
+	  Change-Id: Ib641a0964c59ef9fe6f59efa8ccb481a9580c52f
+
+2015-11-24 13:54 +0000 [59881fbb99]  David M. Lee <dlee at respoke.io>
+
+	* Fixed some typos
+
+	  Fixes some minor typos in the CHANGES file, plus an embarrasing typo in
+	  the StatsD API.
+
+	  Change-Id: I9ca4858c64a4a07d2643b81baa64baebb27a4eb7
+
+2015-11-24 13:07 +0000 [b75f587d15]  Corey Farrell <git at cfware.com>
+
+	* res_pjsip_notify: Fix CLI usage info
+
+	  The usage info for 'pjsip send notify' previously referenced the
+	  chan_sip configuration sip_notify.conf.  Fix this to reference
+	  the correct configuration pjsip_notify.conf.
+
+	  ASTERISK-25590 #close
+
+	  Change-Id: I3898271a8e8a8b1db201741e790ebe2c6bf5cdea
+
+2015-11-23 14:27 +0000 [fc45f4040d]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_sorcery_realtime.c: Fix crash from NULL sorcery object type.
+
+	  If the sorcery object type is not found a NULL is returned.
+	  Unfortunately, sorcery_realtime_filter_objectset() will crash after
+	  complaining about not finding the object type and saying to expect errors.
+
+	  * Use ao2_cleanup() instead of ao2_ref() to prevent the crash.
+
+	  ASTERISK-25165
+	  Reported by Corey Farrell
+
+	  Change-Id: Ic3b64453ea3058cb68d5c26d97d4fe7b8eea2e97
+
+2015-11-20 21:08 +0000 [4875e5ac32]  Matt Jordan <mjordan at digium.com>
+
+	* chan_pjsip: Handle T.38 faxes with direct media bridges
+
+	  When a channel is in a direct media bridge, a re-INVITE may arrive that forces
+	  Asterisk to re-negotiate the media to a T.38 fax. When this occurs, the bridge
+	  must change its technology to a simple bridge, and re-INVITE the media back
+	  to Asterisk.
+
+	  Generally, this logic mostly already exists in Asterisk. However, prior to this
+	  patch, there were a few bugs:
+	  (1) The T.38 framehook currently prevents a channel capable of T.38 faxes from
+	      ever entering into a direct media bridge. This applies even when the only
+	      media being passed over the channel is audio. This patch fixes this bug
+	      by having the framehook specify that it defers caring about any frame type.
+	      This allows the channels to enter into a direct media bridge, which will
+	      be broken when a re-INVITE is received.
+	  (2) When a re-INVITE is received, nothing instructed the bridging layer to
+	      re-inspect the allowed bridging technology. This now occurs when either
+	      a re-INVITE is received from a peer, or when a response is received from
+	      the far end (that is, when the T.38 state changes to either
+	      T38_PEER_REINVITE or T38_LOCAL_REINVITE).
+	  (3) chan_pjsip needs to do a small amount of work to prevent a direct media
+	      bridge from being chosen when a T.38 session is in progress. When a T.38
+	      session supplement has a t38 datastore - which is added when we detect
+	      we should start thinking about T.38 on a channel - we now refuse a native
+	      RTP bridge.
+	  (4) When a BYE request is received, we don't terminate the T.38 session. If
+	      the other side of a T.38 fax survives the hangup (due to the 'g' flag
+	      in Dial, for example), we don't currently re-INVITE the media on the
+	      other channel back to audio. This patch now has res_pjsip_t38 intercept
+	      BYE requests and inform the far side that the T.38 session is terminated.
+	      This naturally causes the correct re-INVITEs to be sent.
+
+	  ASTERISK-25582
+
+	  Change-Id: Iabd6aa578e633d16e6b9f342091264e4324a79eb
+
+2015-11-20 21:07 +0000 [2b94d9a10d]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_pjsip_t38: Add debug statements
+
+	  This patch adds some debug statements to res_pjsip_t38. These statements help
+	  to determine which SDP negotiation callbacks are being executed, and, when
+	  a particular callback exits, why a callback may not have applied its logic
+	  to the local or remote SDP.
+
+	  Change-Id: I61b3fb9183b7ebbb5da8e9f48b59a5d9d7042d77
+
+2015-10-22 09:44 +0000 [af288b2d96]  Matt Jordan <mjordan at digium.com>
+
+	* main/cli: Use proper string methods to check existence of context/exten/app
+
+	  Because the context, extension, and application are stored in stringfields,
+	  checking for them being NULL doesn't work so well. This patch uses the
+	  appropriate string library call, ast_strlen_zero, to see if there is a value
+	  in the context/exten/app values.
+
+	  Change-Id: Ie09623bfdf35f5a8d3b23dd596647fe3c97b9a23
+
+2015-11-18 09:43 +0000 [d27aac0a9d]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_endpoint_stats: Add module to emit endpoint StatsD statistics
+
+	  This patch adds a module that emits StatsD statistics about Asterisk
+	  endpoints. This includes:
+	   * A GUAGE statistic for endpoint states, tracking how many endpoints are in
+	     a particular state.
+	   * A GUAGE statistic for each endpoint, counting the number of channels
+	     currently associated with an endpoint.
+
+	  ASTERISK-25572
+
+	  Change-Id: If7e1333c5aeda8d136850b30c2101c0ee1c97305
+
+2015-11-18 10:07 +0000 [90d9a70789]  Matt Jordan <mjordan at digium.com>
+
+	* res_pjsip/pjsip_options: Add StatsD statistics for PJSIP contacts
+
+	  This patch adds the ability to send StatsD statistics related to the
+	  state of PJSIP contacts. This includes:
+	   * A GUAGE statistic measuring the count of contacts in a particular state.
+	     This measures how many contacts are reachable, unreachable, etc.
+	   * The RTT time for each contact, if those contacts are qualified. This
+	     provides StatsD engines useful time-based data about each contact.
+
+	  ASTERISK-25571
+
+	  Change-Id: Ib8378d73afedfc622be0643b87c542557e0b332c
+
+2015-11-13 10:34 +0000 [75097a0955]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_pjsip_outbound_registration: Add registration statistics for StatsD
+
+	  This patch adds outbound registration statistics for StatsD. This includes
+	  the following:
+	   * A GUAGE metric for the overall count of outbound registrations.
+	   * A GUAGE metric for each state an outbound registration can be in. As the
+	     outbound registrations change state, the overall count of how many
+	     outbound registrations are in the particular state is changed.
+
+	  These statistics are particularly useful for systems with a large number of
+	  SIP trunks, and where measuring the change in state of the trunks is useful
+	  for monitoring.
+
+	  ASTERISK-25571
+
+	  Change-Id: Iba6ff248f5d1c1e01acbb63e9f0da1901692eb37
+
+2015-11-19 09:40 +0000 [8f71263e72]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_pjsip_outbound_registration: Apply configuration on object type load
+
+	  When Asterisk is configured to use a dynamic sorcery backend (such as
+	  res_sorcery_astdb) with 'registration' objects, it will fail to create the
+	  internal state objects associated with the registration objects on module
+	  load. This is due to nothing actually querying for the specific objects
+	  and calling their sorcery apply handler during module load.
+
+	  This patch fixes that by calling get_registrations in the sorcery observer's
+	  object_type_loaded handler. Doing this causes the sorcery backends to be
+	  asked for the current state of all registration objects, which causes the
+	  apply handler to be called and the internal run-time state to be created.
+
+	  ASTERISK-25575 #close
+
+	  Change-Id: Ie9306e797098c6d4da7bcf4a5434a15891508b23
+
+2015-11-11 11:51 +0000 [0b508789ab]  Alexander Traud <pabstraud at compuserve.com>
+
+	* translate: Provide translation modules the result of SDP negotiation.
+
+	  Previously, a trancoding module did not have access to the joint but cached
+	  format. Therefore, the module did not have access to the attributes negotiated
+	  via SDP (line fmtp). Now, a translation module receives the joint format.
+
+	  ASTERISK-25545 #close
+
+	  Change-Id: Id6878a989b50573298dab115d3371ea369e1a718
+
+2015-11-19 01:14 +0000 [1aa552b2a2]  Alexander Traud <pabstraud at compuserve.com>
+
+	* res_format_attr_h264: Do not reset string buffer.
+
+	  When no parameter is present, Asterisk does not generate the line fmtp, as
+	  expected. However, because a buffer was reset, even rtpmap and fmtp of previous
+	  media codecs got removed. Now, Asterisk does not reset other codecs in case of
+	  no parameter for H.264.
+
+	  ASTERISK-25573 #close
+
+	  Change-Id: I93811331f4a28c45418a9e14ee46c0debd47a286
+
+2015-11-18 10:05 +0000 [3354b325c6]  Matt Jordan <mjordan at digium.com>
+
+	* res_statsd: Add functions that support variable arguments
+
+	  Often, the metric names of statistics we are generating for StatsD have some
+	  dynamic component to them. This can be the name of a particular resource, or
+	  some internal status label in Asterisk. With the current set of functions,
+	  callers of the statsd API must first build the metric name themselves, then
+	  pass this to the API functions. This results in a large amount of boilerplate
+	  code and usage of either fixed length static buffers or dynamic memory
+	  allocation, neither of which is desireable.
+
+	  This patch adds two new functions to the StatsD API that support a printf
+	  style format specifier for constructing the metric name. A dynamic string,
+	  allocated in threadstorage, is used to build the metric name. This eases
+	  the burden on users of the StatsD API.
+
+	  Change-Id: If533c72d1afa26d807508ea48b4d8c7b32f414ea
+
+2015-11-17 14:53 +0000 [d4a522d587]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_outbound_registration.c: Be tolerant of short registration timeouts.
+
+	  Change-Id: Ie16f5053ebde0dc6507845393709b4d6a3ea526d
+
+2015-11-17 14:53 +0000 [e44ab3816c]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_outbound_registration.c: Fix 423 response handling.
+
+	  Receiving a 423 Interval Too Brief response after authentication for an
+	  outbound registration attempt results in assuming that the registrar has
+	  rejected the registration permanently.  If there are no configured retries
+	  for fatal responses then the outbound registration is stopped for that
+	  endpoint.
+
+	  For registrations, PJSIP/PJPROJECT intercepts the handling of 423
+	  responses and does not include any authentication in the updated
+	  registration request.  When the updated request is challenged then the
+	  Asterisk code assumes that we were challenged again because the peer
+	  rejected the authentication we sent earlier.
+
+	  * Made registration challenges keep track of the CSeq number to determine
+	  if the received challenge response was for the request we thought we sent.
+	  If the response's CSeq number differs from the CSeq number we last sent
+	  with authentication then authenticate again because it is a challenge to a
+	  different request.
+
+	  Change-Id: I81b4bd36d1be095bab606e34b8b44e6302971b09
+
+2015-11-03 14:36 +0000 [1e0040b88f]  Tyler Cambron <tcambron at digium.com>
+
+	* StatsD: Add res_statsd compatibility
+
+	  Added a new api to res_statsd.c to allow it to receive a
+	  character pointer for the value argument. This allows for a
+	  '+' and a '-' to easily be sent with the value.
+
+	  ASTERISK-25419
+	  Reported By: Ashley Sanders
+
+	  Change-Id: Id6bb53600943d27347d2bcae26c0bd5643567611
+
+2015-11-16 13:56 +0000 [f62b642fe3]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_pjsip: Fix off nominal crash with requests that fail and have a timer
+
+	  When a request is sent using pjsip_endpt_send_request and fails, a condition
+	  exists where the request wrapper, which is an AO2 object, may be de-ref'd
+	  more times than it should. This occurs when the request's callback is called,
+	  and, in the callback, the timer on the PJSIP heap is cancelled. When that
+	  occurs, the request wrapper's lifetime is decremented. When
+	  pjsip_endpt_send_request fails, we unilaterally decrement the lifetime of
+	  the request wrapper again, even though we've already cancelled the reference
+	  associated with the timer.
+
+	  This patch checks the return result of pj_timer_heap_cancel_if_active before
+	  removing the reference associated with the timer. We now only decrement it
+	  in this case if a timer is cancelled as a result of the function call.
+
+	  Change-Id: I21332343a1a019c1117076f9bf2df27be2850102
+
+2015-11-13 14:03 +0000 [fdd2afcd16]  Mark Michelson <mmichelson at digium.com>
+
+	* Confbridge: Add a user timeout option
+
+	  This option adds the ability to specify a timeout, in seconds, for a
+	  participant in a ConfBridge. When the user's timeout has been reached,
+	  the user is ejected from the conference with the CONFBRIDGE_RESULT
+	  channel variable set to "TIMEOUT".
+
+	  The rationale for this change is that there have been times where we
+	  have seen channels get "stuck" in ConfBridge because a network issue
+	  results in a SIP BYE not being received by Asterisk. While these
+	  channels can be hung up manually via CLI/AMI/ARI, adding some sort of
+	  automatic cleanup of the channels is a nice feature to have.
+
+	  ASTERISK-25549 #close
+	  Reported by Mark Michelson
+
+	  Change-Id: I2996b6c5e16a3dda27595f8352abad0bda9c2d98
+
+2015-11-16 04:29 +0000 [7debb986a5]  Alec Davis <sivad.a at paradise.net.nz>
+
+	* app_queue: (try_calling): mutex 'qe->chan' freed more times than we've locked!
+
+	  commit aae45acbd (Mark Michelson 2015-04-15 10:38:02 -0500 6525)
+	  refer ASTERISK-24958
+
+	  above commit removed ast_channel_lock(qe->chan);
+	  but failed to remove corresponding ast_channel_unlock(qe->chan);
+
+	  ASTERISK-25561 #close
+	  Reported Alec Davis
+
+	  Change-Id: Ie05f4e2d08912606178bf1fded57cc022c7a2e1a
+
+2015-11-14 07:02 +0000 [afd9a89e5a]  Joshua Colp <jcolp at digium.com>
+
+	* hashtab: Add NULL check when destroying iterator.
+
+	  The hashtab API is pretty NULL tolerant which has resulted
+	  in remaining callers not doing much checks themselves.
+	  Unfortunately the function to destroy an iterator does not
+	  do a NULL check and will result in a crash if passed NULL.
+	  This change fixes that.
+
+	  ASTERISK-25552 #close
+
+	  Change-Id: Ic1bf8eec3639e5a440f1c941d3ae3893ac6ed619
+
+2015-11-13 14:32 +0000 [c0f2f8de45]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_rfc3326.c: Fix crash when channel goes away.
+
+	  If an authenticated incoming caller does not respond to our 200 OK INVITE
+	  response with an ACK then PJSIP will hangup the call.  Unfortunately,
+	  there is a chance that the session's channel will go away between one use
+	  of the channel pointer and another when building the BYE request because
+	  the BYE is being built by the monitor thread and not the call's serializer
+	  thread.
+
+	  * Added a check to ensure that the thread trying to add the Reason header
+	  is the call's serializer thread.  This ensures that the channel will not
+	  go away on us.
+
+	  Change-Id: I866388d2b97ea2032eaae3f3ab3f1ca6cbd2df89
+
+2015-11-13 14:19 +0000 [4f43b85c92]  Mark Michelson <mmichelson at digium.com>
+
+	* Taskprocessors: Increase high-water mark
+
+	  In practical tests, we have seen certain taskprocessors, specifically
+	  Stasis subscription taskprocessors, cross the recently-added high-water
+	  mark and emit a warning. This high-water mark warning is only intended
+	  to be emitted when things have tanked on the system and things are
+	  heading south quickly. In the practical tests, the Stasis taskprocessors
+	  sometimes had a max depth of 180 tasks in them, and Asterisk wasn't in
+	  any danger at all.
+
+	  As such, this ups the high-water mark to 500 tasks instead. It also
+	  redefines the SIP threadpool request denial number to be a multiple of
+	  the taskprocessor high-water mark.
+
+	  Change-Id: Ic8d3e9497452fecd768ac427bb6f58aa616eebce
+
+2015-11-11 11:46 +0000 [d8d3991390]  Alexander Traud <pabstraud at compuserve.com>
+
+	* format: Register format-attribute module with cached formats.
+
+	  In Asterisk 13, cached formats are created before their corresponding format-
+	  attribute module is registered. Cached formats are involved when a local
+	  extension is called. Therefore, ast_format_generate_sdp_fmtp did not work
+	  on local extensions. This change affects the Opus Codec, H.263 (Plus), H.264,
+	  and format-attribute modules provided externally.
+
+	  ASTERISK-25160 #close
+
+	  Change-Id: I1ea1f0483e5261e2a050112e4ebdfc22057d1354
+
+2015-11-12 11:17 +0000 [367972e42d]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip distributor: Don't send 503 response to responses.
+
+	  When the SIP threadpool is backed up with tasks, we send 503 responses
+	  to ensure that we don't try to overload ourselves. The problem is that
+	  we were not insuring that we were not trying to send a 503 to an
+	  incoming SIP response.
+
+	  This change makes it so that we only send the 503 on incoming requests.
+
+	  Change-Id: Ie2b418d89c0e453cc6c2b5c7d543651c981e1404
+
+2015-11-11 17:11 +0000 [2f9cb7d62b]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip: Deny requests when threadpool queue is backed up.
+
+	  We have observed situations where the SIP threadpool may become
+	  deadlocked. However, because incoming traffic is still arriving, the SIP
+	  threadpool's queue can continue to grow, eventually running the system
+	  out of memory.
+
+	  This change makes it so that incoming traffic gets rejected with a 503
+	  response if the queue is backed up too much.
+
+	  Change-Id: I4e736d48a2ba79fd1f8056c0dcd330e38e6a3816
+
+2015-11-12 06:24 +0000 [4e5bf12b33]  Joshua Colp <jcolp at digium.com>
+
+	* format_cap: Don't append the 'none' format when appending all.
+
+	  When appending all formats of a type all the codecs are iterated
+	  and added. This operation was incorrectly adding the ast_format_none
+	  format which is special in that it is supposed to be used when no
+	  format is present. It shouldn't be appended.
+
+	  ASTERISK-25535
+
+	  Change-Id: I7b00f3bdf4a5f3022e483d6ece602b1e8b12827c
+
+2015-11-11 04:16 +0000 [07583c2888]  Steve Davies <steve at one47.co.uk>
+
+	* Further fixes to improper usage of scheduler
+
+	  When ASTERISK-25449 was closed, a number of scheduler issues mentioned in
+	  the comments were missed. These have since beed raised in ASTERISK-25476
+	  and elsewhere.
+
+	  This patch attempts to collect all of the scheduler issues discovered so
+	  far and address them sensibly.
+
+	  ASTERISK-25476 #close
+
+	  Change-Id: I87a77d581e2e0d91d33b4b2fbff80f64a566d05b
+
+2015-11-11 11:04 +0000 [b818d70533]  Joshua Colp <jcolp at digium.com>
+
+	* threadpool: Handle worker thread transitioning to dead when going active.
+
+	  This change adds handling of dead worker threads when moving them
+	  to be active. When this happens the worker thread is removed from
+	  both the active and idle threads container. If no threads are able
+	  to be moved to active then the pool grows as configured.
+
+	  A unit test has also been added which thrashes the idle timeout
+	  and thread activation to exploit any race conditions between the
+	  two.
+
+	  ASTERISK-25546 #close
+
+	  Change-Id: I6c455f9a40de60d9e86458d447b548fb52ba1143
+
+2015-11-10 09:27 +0000 [4bf84459c7]  Alexander Traud <pabstraud at compuserve.com>
+
+	* rtp_engine: Init a format-attribute module to its RFC defaults.
+
+	  Previously, format-attribute modules relied on an existing fmtp line in SDP
+	  negotiation. However, fmtp is optional for several formats like the Opus Codec.
+	  Now, the format-attribute module is called with an empty fmtp, which allows the
+	  module to initialise itself to RFC defaults. Furthermore now, Asterisk is able
+	  to differentiate between internally and externally created formats.
+
+	  ASTERISK-25537 #close
+
+	  Change-Id: I28f680cef7fdf51c0969ff8da71548edad72ec52
+
+2015-11-09 03:04 +0000 [1bff400df7]  Alexander Traud <pabstraud at compuserve.com>
+
+	* ast_format_cap_get_names: To display all formats, the buffer was increased.
+
+	  ASTERISK-25533 #close
+
+	  Change-Id: Ie1a9d1a6511b3f1a56b93d04475fbf8a4e40010a
+
+2015-11-09 07:04 +0000 [f3ac4d8090]  Alexander Traud <pabstraud at compuserve.com>
+
+	* ast_format_cap: Avoid format creation on module load, use cache instead.
+
+	  Since Asterisk 13, formats are immutable and cached. However while loading a
+	  module like chan_sip, some formats were created instead using cached ones.
+
+	  ASTERISK-25535 #close
+
+	  Change-Id: I479cdc220d5617c840a98f3389b3bd91e91fbd9b
+
+2015-11-06 07:54 +0000 [6d1bdb9d3b]  Walter Doekes <walter+asterisk at wjd.nu>
+
+	* func_callerid: Document that CALLERID(pres) is available.
+
+	  CALLERPRES() says that it's deprecated in favor of CALLERID(num-pres)
+	  and CALLERID(name-pres).  But for channel driver that don't make a
+	  distinction between the two (e.g. SIP), it makes more sense to get/set
+	  both at once.  This change reveals the availability of CALLERID(pres),
+	  CONNECTEDLINE(pres), REDIRECTING(orig-pres), REDIRECTING(to-pres) and
+	  REDIRECTING(from-pres).
+
+	  ASTERISK-25373 #close
+
+	  Change-Id: I5614ae4ab7d3bbe9c791c1adf147e10de8698d7a
+2015-11-06 07:52 +0000 [8410336681]  Walter Doekes <walter+asterisk at wjd.nu>
 
-2015-10-07 13:41 +0000 [316d47755b]  Kevin Harwell <kharwell at lunkwill>
+	* docs: Fix a few typo's in app docs (more then, resourse).
 
-	* .version: Update for 13.6.0-rc3
+	  Change-Id: Iba57efadf6c0b822e762c7a001bc89611d98afd7
 
-2015-10-07 13:41 +0000 [74a86d0a72]  Kevin Harwell <kharwell at lunkwill>
+2015-11-06 07:36 +0000 [0d425f2eb4]  Walter Doekes <walter+asterisk at wjd.nu>
 
-	* .lastclean: Update for 13.6.0-rc3
+	* xmldoc: Improve xmldoc wrapping of 'core show ...' output.
 
-2015-10-07 13:41 +0000 [4c39bea6f0]  Kevin Harwell <kharwell at lunkwill>
+	  Previously, the wrapping did both lookahead and lookback, which,
+	  together with color escape sequences, caused some lines to be wrapped
+	  way earlier than other lines.  This led to inconsistent output.
 
-	* realtime: Add database scripts for 13.6.0-rc3
+	  This simplifies the wrapping code and makes it more sane: if maxcolumns
+	  is hit, we simply jump back to the last space and wrap there.
 
-2015-10-06 20:43 +0000 [10e790f81a]  Matt Jordan <mjordan at digium.com>
+	  ASTERISK-25527 #close
+
+	  Change-Id: I56d01c6f9a812642b1b05535c98d4db48d17c957
+
+2015-11-06 06:57 +0000 [33752e0837]  Sean Bright (license #5060)
+
+	* res_pjsip_sdp_rtp: Enable Opus to be negotiated via SIP/SDP.
+
+	  In SIP/SDP, Opus has two channels always (see RFC 7587 section 7). The actual
+	  amount of channels is negotiated in-band. Therefore now, the Opus codec and its
+	  attribute rtpmap are registered with two channels.
+
+	  ASTERISK-24779 #close
+	  Reported by: PowerPBX
+	  Tested by: Alexander Traud
+	  patches:
+	    asterisk-24779.patch submitted by Sean Bright (license #5060)
+
+	  Change-Id: Ic7ac13cafa1d3450b4fa4987350924b42cbb657b
+
+2015-11-03 16:19 +0000 [6ff48319d9]  Jonathan Rose <jrose at digium.com>
+
+	* taskprocessor: Add high water mark warnings
+
+	  If a taskprocessor's queue grows large, this can indicate that there
+	  may be a problem with tasks not leaving the processor or else that
+	  the number of available task processors for a given type of task is
+	  too low. This patch makes it so that if a taskprocessor's task queue
+	  grows above 100 queued tasks that it will emit a warning message.
+	  Warning messages are emitted only once per task processor.
+
+	  ASTERISK-25518 #close
+	  Reported by: Jonathan Rose
+
+	  Change-Id: Ib1607c35d18c1d6a0575b3f0e3ff5d932fd6600c
+
+2015-11-04 14:31 +0000 [506aea26e6]  Matt Jordan <mjordan at digium.com>
+
+	* main/dial: Protect access to the format_cap structure of the requesting channel
+
+	  When a dial attempt is made that involves a requesting channel, we previously
+	  were not:
+	  a) Protecting access to the native format capabilities structure on the
+	     requesting channel. That is inherently unsafe.
+	  b) Reference bumping the lifetime of the format capabilities structure.
+
+	  In both cases, something else could sneak in, blow away the format
+	  capabilities, and we'd be holding onto an invalid format_cap structure. When
+	  the newly created channel attempts to construct its format capabilities, things
+	  go poorly.
+
+	  This patch:
+	  a) Ensures that we get a reference to the native format capabilities while
+	     the requesting channel is locked
+	  b) Holds a reference to the native format capabilities during the creation
+	     of the new channel.
+
+	  ASTERISK-25522 #close
+
+	  Change-Id: I0bfb7ba8b9711f4158cbeaae96edf9626e88a54f
+
+2015-10-30 22:57 +0000 [d098d00424]  Corey Farrell <git at cfware.com>
+
+	* Fix cli display of build options.
+
+	  A previous commit reduced the AST_BUILDOPTS compiler define to
+	  only include options that affected ABI.  This included some options
+	  that were previously displayed by cli "core show settings".  This
+	  change corrects the CLI display while still restricting buildopts.h
+	  to ABI effecting options only.
+
+	  ASTERISK-25434 #close
+	  Reported by: Rusty Newton
+
+	  Change-Id: Id07af6bedd1d7d325878023e403fbd9d3607e325
+
+2015-11-03 11:15 +0000 [afec1b1b64]  Matt Jordan <mjordan at digium.com>
+
+	* res_pjsip/location: Destroy contact_status objects on contact deletion
+
+	  The contact_status Sorcery objects are currently not destroyed when a contact
+	  is deleted. This causes the contact's last known RTT/status to be 'sticky'
+	  when the contact itself may no longer exist. This patch causes the
+	  contact_status objects associated with both dynamic and static contacts to
+	  be destroyed if the AoR holding those contacts is also destroyed (or via
+	  other paths where a contact may be deleted.)
+
+	  Change-Id: I7feec8b9278cac3c5263a4c0483f4a0f3b62426e
+
+2015-11-03 10:58 +0000 [715f770c9f]  Matt Jordan <mjordan at digium.com>
+
+	* pjsip_configuration: On delete, remove the persistent version of an endpoint
+
+	  When an endpoint is deleted (such as through an API), the persistent endpoint
+	  currently continues to lurk around. While this isn't harmful from a memory
+	  consumption perspective - as all persistent endpoints are reclaimed on
+	  shutdown - it does cause Stasis endpoint related operations to continue
+	  to believe that the endpoint may or may not exist.
+
+	  This patch causes the persistent endpoint related to a PJSIP endpoint to be
+	  destroyed if the PJSIP endpoint is deleted.
+
+	  Change-Id: I85ac707b4d5e6aad882ac275b0c2e2154affa5bb
+2015-11-03 08:15 +0000 [f0f190af08]  Matt Jordan <mjordan at digium.com>
+
+	* main/stasis_endpoints: Fix ContactStatusChange JSON for roundtrip_usec field
+
+	  The JSON packing for the ContactStatusChange event forgot to include the
+	  roundtrip_usec field. As a result, the field never showed up in any event,
+	  even when the data was available. This patch corrects that error by properly
+	  packing the JSON blob with the data.
+
+	  Change-Id: I8df80da659a44010afbd48f645967518ff5daa17
+
+2015-11-02 20:24 +0000 [0393bd6bed]  Corey Farrell <git at cfware.com>
+
+	* chan_sip: Allow websockets to be disabled.
+
+	  This patch adds a new setting "websockets_enabled" to sip.conf.
+	  Setting this to false allows chan_sip to be used without causing
+	  conflicts with res_pjsip_transport_websocket.
+
+	  ASTERISK-24106 #close
+	  Reported by: Andrew Nagy
+
+	  Change-Id: I04fe8c4f2d57b2d7375e0e25826c91a72e93bea7
+
+2015-11-02 17:19 +0000 [6fbffe42e1]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip: Set threadpool max size default to 50.
+
+	  During a stress test of subscriptions, a huge blast of
+	  subscription-related traffic resulted in the threadpool expanding to a
+	  ridiculous number of threads. The balooning of threads resulted in an
+	  increase of memory, which led to a crash due to being out of memory.
+
+	  An easy fix for the particular test was to limit the size of the
+	  threadpool, thus reining in the amount of memory that would be used. It
+	  was decided that there really is no downside to having a non-infinite
+	  default value for the maximum size of the threadpool, so this change
+	  introduces 50 threads as the maximum threadpool size for the SIP
+	  threadpool.
+
+	  ASTERISK-25513 #close
+	  Reported by John Bigelow
+
+	  Change-Id: If0b9514f1d9b172540ce1a6e2f2ffa1f2b6119be
+
+2015-11-02 06:57 +0000 [11e54b1932]  Matt Jordan <mjordan at digium.com>
+
+	* pjsip_options: Schedule/unschedule qualifies on AoR creation/destruction
+
+	  When an AoR is created or destroyed dynamically, the scheduled OPTIONS
+	  requests that qualify the contacts on the AoR are not necessarily started
+	  or destroyed, particularly for persistent contacts created for that AoR.
+	  This patch adds create/update/delete sorcery observers for an AoR, which
+	  schedule/unschedule the qualifies as expected.
+
+	  Change-Id: Ic287ed2e2952a7808ee068776fe966f9554bdf7d
+
+2015-10-30 13:22 +0000 [118d628e08]  Matt Jordan <mjordan at digium.com>
+
+	* Makefile: Add a rule 'basic-pbx' that installs the Basic PBX configs
+
+	  This patch adds a rule for installing the Super Awesome Company based 'Basic
+	  PBX' configuration files. As part of adding this rule, a bit of the content
+	  that makes up installing the configuration files under the 'samples' target
+	  was refactored into a make subroutine for usage by additional later config
+	  make targets.
+
+	  Change-Id: I6c2e27906f73e2919a2b691da0be20ae70302404
+2015-10-29 08:28 +0000 [9a021a42ad]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip_pubsub: Fix assertion when UAS dialog creation fails.
+
+	  When compiled with assertions enabled one will occur when destroying
+	  the subscription tree when UAS dialog creation fails. This is because
+	  the code assumes that a dialog will always exist on a subscription
+	  tree when in reality during this specific scenario it won't.
+
+	  This change makes it so a dialog is not removed from the subscription
+	  tree if it is not present.
+
+	  ASTERISK-25505 #close
+
+	  Change-Id: Id5c182b055aacc5e66c80546c64804ce19218dee
+
+2015-10-26 11:42 +0000 [1256aedf66]  Alexander Traud <pabstraud at compuserve.com>
+
+	* chan_sip: Do not send all codecs on INVITE.
+
+	  Since version 13, Asterisk sent all allowed codecs as callee, even when the
+	  caller did not request/support them. In case of dynamic RTP payloads, this led
+	  to the same ID for different codecs, which is not allowed by SIP/SDP. Now, the
+	  intersection between the requested and the supported codecs is send again.
+
+	  ASTERISK-24543 #close
+
+	  Change-Id: Ie90cb8bf893b0895f8d505e77343de3ba152a287
+
+2015-10-24 13:08 +0000 [5f593e7c38]  gtjoseph <george.joseph at fairview5.com>
+
+	* build: GCC 5.1.x catches some new const, array bounds and missing paren issues
+
+	  Fixed 1 issue in each of the affected files.
+
+	  ASTERISK-25494 #close
+	  Reported-by: George Joseph
+	  Tested-by: George Joseph
+
+	  Change-Id: I818f149cd66a93b062df421e1c73c7942f5a4a77
+
+2015-10-20 16:02 +0000 [162acd45f7]  gtjoseph <george.joseph at fairview5.com>
+
+	* res_pjsip:  Add "like" processing to pjsip list and show commands
+
+	  Add the ability to filter output from pjsip list and show commands
+	  using the "like" predicate like chan_sip.
+
+	  For endpoints, aors, auths, registrations, identifyies and transports,
+	  the modification was a simple change of an ast_sorcery_retrieve_by_fields
+	  call to ast_sorcery_retrieve_by_regex.  For channels and contacts a
+	  little more work had to be done because neither of those objects are
+	  true sorcery objects.  That was just removing the non-matching object
+	  from the final container.  Of course, a little extra plumbing in the
+	  common pjsip_cli code was needed to parse the "like" and pass the regex
+	  to the get_container callbacks.
+
+	  Some of the get_container code in res_pjsip_endpoint_identifier was also
+	  refactored for simplicity.
+
+	  ASTERISK-25477 #close
+	  Reported by: Bryant Zimmerman
+	  Tested by: George Joseph
+
+	  Change-Id: I646d9326b778aac26bb3e2bcd7fa1346d24434f1
+
+2015-10-21 11:51 +0000 [c58091737d]  Kevin Harwell <kharwell at digium.com>
+
+	* res_pjsip_outbound_registration: registration stops due to fatal 4xx response
+
+	  During outbound registration it is possible to receive a fatal (any permanent/
+	  non-temporary 4xx, 5xx, 6xx) response from the registrar that is simply due
+	  to a problem with the registrar itself. Upon receiving the failure response
+	  Asterisk terminates outbound registration for the given endpoint.
+
+	  This patch adds an option, 'fatal_retry_interval', that when set continues
+	  outbound registration at the given interval up to 'max_retries' upon receiving
+	  a fatal response.
+
+	  ASTERISK-25485 #close
+
+	  Change-Id: Ibc2c7b47164ac89cc803433c0bbe7063bfa143a2
+
+2015-10-22 17:07 +0000 [ebe69dee0d]  Mark Michelson <mmichelson at digium.com>
+
+	* format_cap: Detect vector allocation failures.
+
+	  A crash was seen on a system that ran out of memory due to Asterisk not
+	  checking for vector allocation failures in format_cap.c. With this
+	  change, if either of the AST_VECTOR_INIT calls fail, we will return a
+	  value indicating failure.
+
+	  Change-Id: Ieb9c59f39dfde6d11797a92b45e0cf8ac5722bc8
+
+2015-10-02 15:32 +0000 [3b19efefef]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_pubsub: Prevent sending NOTIFY on destroyed dialog.
+
+	  A certain situation can result in our attempting to send a NOTIFY on a
+	  destroyed dialog. Say we attempt to send a NOTIFY to a subscriber, but
+	  that subscriber has dropped off the network. We end up retransmitting
+	  that NOTIFY until the appropriate SIP timer says to destroy the NOTIFY
+	  transaction. When the pjsip evsub code is told that the transaction has
+	  been terminated, it responds in kind by alerting us that the
+	  subscription has been terminated, destroying the subscription, and then
+	  removing its reference to the dialog, thus destroying the dialog.
+
+	  The problem is that when we get told that the subscription is being
+	  terminated, we detect that we have not sent a terminating NOTIFY
+	  request, so we queue up such a NOTIFY to be sent out. By the time that
+	  queued NOTIFY gets sent, the dialog has been destroyed, so attempting to
+	  send that NOTIFY can result in a crash.
+
+	  The fix being introduced here is actually a reintroduction of something
+	  the pubsub code used to employ. We hold a reference to the dialog and
+	  wait to decrement our reference to the dialog until our subscription
+	  tree object is destroyed. This way, we can send messages on the dialog
+	  even if the PJSIP evsub code wants to terminate earlier than we would
+	  like.
+
+	  In doing this, some NULL checks for subscription tree dialogs have been
+	  removed since NULL dialogs are no longer actually possible.
+
+	  Change-Id: I013f43cddd9408bb2a31b77f5db87a7972bfe1e5
+
+2015-09-29 14:53 +0000 [0a346f095f]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_pubsub: Ensure dialog lock balance.
+
+	  When sending a NOTIFY, we lock the dialog and then unlock the dialog
+	  when finished. A recent change made it so that the subscription tree's
+	  dialog pointer will be set NULL when sending the final NOTIFY request
+	  out. This means that when we attempt to unlock the dialog, we pass a
+	  NULL pointer to pjsip_dlg_dec_lock(). The result is that the dialog
+	  remains locked after we think we have unlocked it. When a response to
+	  the NOTIFY arrives, the monitor thread attempts to lock the dialog, but
+	  it cannot because we never released the dialog lock. This results in
+	  Asterisk being unable to process incoming SIP traffic any longer.
+
+	  The fix in this patch is to use a local pointer to save off the pointer
+	  value of the subscription tree's dialog when locking and unlocking the
+	  dialog. This way, if the subscription tree's dialog pointer is NULLed
+	  out, the local pointer will still have point to the proper place and the
+	  dialog lock will be unlocked as we expect.
+
+	  Change-Id: I7ddb3eaed7276cceb9a65daca701c3d5e728e63a
+
+2015-09-28 16:36 +0000 [ad39508095]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_pubsub: Prevent crashes on final NOTIFY.
+
+	  The SIP dialog is removed from the subscription tree when the final
+	  NOTIFY is sent. However, after the final NOTIFY is sent, the persistence
+	  update function still attempts to access the cseq from the dialog,
+	  resulting in a crash.
+
+	  This fix removes the subscription persistence at the same time that the
+	  dialog is removed from the subscription tree. This way, there is no
+	  attempt to update persistence when the subscription is being destroyed.
+
+	  Change-Id: Ibb46977a6cef9c51dc95f40f43446e3d11eed5bb
+
+2015-09-17 17:28 +0000 [067f408760]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_pubsub: Remove serializer when sending final NOTIFY.
+
+	  There have been crashes seen where a taskprocessor's listener is NULL
+	  unexpectedly.
+
+	  Looking at backtraces, the problem was specifically seen in PJSIP
+	  serializers.
+
+	  Subscriptions make the mistake of removing a serializer from a dialog
+	  during subscription tree destruction. Since subscription trees are
+	  reference-counted, guaranteeing the circumstances behind the destruction
+	  are not possible. This makes it so that the dialog serializer can be
+	  removed while not holding the dialog lock. This makes it possible for
+	  the distributor to get a pointer to the dialog serializer and have that
+	  serializer get freed out from under it.
+
+	  The fix for this is to remove the serializer from a subscription dialog
+	  when sending the final NOTIFY. This guarantees that the serializer is
+	  removed with the dialog lock held. By doing this, we guarantee that if
+	  the distributor gains access to the dialog's serializer, it will not be
+	  possible for the serializer to get freed by another thread.
+
+	  Change-Id: I21f5dac33529f65cec45679bdace60670800ff66
+
+2015-09-02 09:14 +0000 [1bcc592765]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_pubsub: Fix crash on destruction of empty subscription tree.
+
+	  If an old persistent subscription is recreated but then immediately
+	  destroyed because it is out of date, the subscription tree will have no
+	  leaf subscriptions on it. This was resulting in a crash when attempting
+	  to destroy the subscription tree.
+
+	  A simple NULL check fixes this problem.
+
+	  Change-Id: I85570b9e2bcc7260a3fe0ad85904b2a9bf36d2ac
+
+2015-09-01 15:47 +0000 [b3cc2bd7df]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_pubsub: Solidify lifetime and ownership of objects.
+
+	  There have been crashes and general instability seen in the pubsub code,
+	  so this patch introduces three changes to increase the stability.
+
+	  First, the ownership model for subscriptions has been modified. Due to
+	  RLS, subscriptions are stored in memory as a tree structure. Prior to my
+	  patch, the PJSIP subscription was the owner of the subscription tree.
+	  When the PJSIP subscription told us that it was terminating, we started
+	  destroying the subscription tree along with all of the individual leaf
+	  subscriptions that belong to the tree. The problem with this model is
+	  that the two actors in play here, the PJSIP subscription and the
+	  individual leaf subscriptions, need to have joint ownership of the
+	  subscription tree. So now, the PJSIP subscription and the individual
+	  leaf subscriptions each have a reference to the subscription tree. This
+	  way, we will not actually free memory until no players are left that
+	  care. The PJSIP subscription is a bigger stakeholder, in that if the
+	  PJSIP subscription's reference to the subscription tree is removed, the
+	  subscription tree instructs the leaf subscriptions to shut down and drop
+	  their references to the subscription tree when possible. The individual
+	  leaf subscriptions, upon being told to shut down, can drop their stasis
+	  subscriptions or whatever they use to learn of new state, and then drop
+	  their reference to the subscription tree once they are ready to die.
+
+	  Second, the lifetime of a PJSIP subscription's reference to our
+	  subscription tree has been altered. As I learned from doing a deep dive,
+	  the PJSIP evsub code can tell Asterisk multiple times that the
+	  subscription has been terminated, and not all of these times
+	  are especially helpful. I have altered the message flow that we use for
+	  SIP subscriptions such that we will always drop the PJSIP subscription's
+	  reference to the subscription tree when we send the NOTIFY that
+	  terminates a SIP subscription. This also means that we will now queue
+	  NOTIFY requests to be sent after responding to incoming SUBSCRIBEs so
+	  that we can have predictable state changes from the PJSIP evsub code.
+
+	  Third, the synchronization of operations has been improved. PJSIP can
+	  call into our code from a serializer thread (e.g. upon receiving an
+	  incoming request) or from the monitor thread (e.g. when a subscription
+	  times out). Because of this, there is the possibility of competing
+	  threads stepping on each other. PJSIP attempts to do some
+	  synchronization on its own by always keeping the dialog lock held when
+	  it calls into us. However, since we end up pushing tasks into the
+	  serializer, the result was that serialized operations were not grabbing
+	  the dialog lock and could, as a result, step on something that was being
+	  attempted by a different thread. Now we ensure that serialized
+	  operations grab the dialog lock, then check for extenuating
+	  circumstances, then proceed with their operation if they can.
+
+	  Change-Id: Iff2990c40178dad9cc5f6a5c7f76932ec644b2e5
+
+2015-10-19 15:28 +0000 [c8c65dfa41]  Richard Mudgett <rmudgett at digium.com>
+
+	* strings.c: Fix __ast_str_helper() to always return a terminated string.
+
+	  Users of functions which call __ast_str_helper() such as the ones listed
+	  below are likely to not check the return value for failure so ensuring
+	  that the string is always nil terminated is a good safety measure.
+
+	  ast_str_set_va()
+	  ast_str_append_va()
+	  ast_str_set()
+	  ast_str_append()
+
+	  Change-Id: I36ab2d14bb6015868b49329dda8639d70fbcae07
+
+2015-10-19 15:27 +0000 [b271d4a28a]  Richard Mudgett <rmudgett at digium.com>
+
+	* Add missing failure checks to ast_str_set_va() callers.
+
+	  Change-Id: I0c2cdcd53727bdc6634095c61294807255bd278f
+
+2015-10-21 11:44 +0000 [f2725c8b77]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip: Move URI validation to use time.
+
+	  In a realtime based system with a limited number of threadpool threads
+	  it is possible for a deadlock to occur. This happens when permanent
+	  endpoint state is updated, which will cause database queries to be done.
+	  These queries may result in URI validation being done which is done
+	  synchronously using a PJSIP thread. If all PJSIP threads are in use
+	  processing traffic they themselves may be blocked waiting to get the
+	  permanent endpoint container lock when identifying an endpoint.
+
+	  This change moves URI validation to occur at use time instead of
+	  configuration time. While this comes at a cost of not seeing a problem
+	  until you use it it does solve the underlying deadlock problem.
+
+	  ASTERISK-25486 #close
+
+	  Change-Id: I2d7d167af987d23b3e8199e4a68f3359eba4c76a
+
+2015-10-21 08:08 +0000 [84ff075d41]  Alexander Traud <pabstraud at compuserve.com>
+
+	* format: Update the maximum packetization time for iLBC 30.
+
+	  In September 2006, the maximum packetization time (ptime) were set to such a
+	  low value, packetization was disabled for many codecs actually. This was fixed
+	  for many codecs but not for iLBC 30. This enables packetization for iLBC which
+	  can be enabled for example via allow=ilbc:60,gsm,alaw,ulaw in the file sip.conf.
+
+	  ASTERISK-7803
+
+	  Change-Id: I2ef90023d35efb7cb8fe96ed74f53f6846ffad12
+2015-10-21 09:51 +0000 [869ef2a8ee]  Alexander Traud <pabstraud at compuserve.com>
+
+	* chan_sip: Fix autoframing=yes.
+
+	  With Asterisk 13, the structures ast_format and ast_codec changed. Because of
+	  that, the paketization timing (framing) of the RTP channel moved away from the
+	  formats/codecs. In the course of that change, the ptime of the callee was not
+	  honored anymore, when the optional autoframing was enabled.
+
+	  ASTERISK-25484 #close
+
+	  Change-Id: Ic600ccaa125e705922f89c72212c698215d239b4
+
+2015-10-20 22:24 +0000 [9fd2adc204]  Matt Jordan <mjordan at digium.com>
+
+	* rest-api-templates: Wikify error code response reasons
+
+	  Error response code descriptions may contain wiki markup that need to be
+	  escaped. Without this patch, Confluence will reject the document being sent
+	  and the responsible script will raise an exception.
+
+	  Change-Id: I21fcb66fee7f6332381f2b99b1b0195dff215ee5
+
+2015-10-20 12:06 +0000 [72cbb6df55]  Matt Jordan <mjordan at digium.com>
+
+	* funcs/func_holdintercept: Actually add the HOLD_INTERCEPT function
+
+	  When ab803ec342 was committed, it accidentally forgot to actually *add* the
+	  HOLD_INTERCEPT function. This highlights two interesting points:
+	  * Gerrit forces you to put the patch as it is going to into the repo up for
+	    review, which Review Board did not. Yay Gerrit.
+	  * No one apparently bothered to use this feature, or else they don't know about
+	    it. I'm going to go with the latter explanation.
+
+	  ASTERISK-24922
+
+	  Change-Id: Ida38278f259dd07c334a36f9b7d5475b5db72396
+
+2015-10-19 19:59 +0000 [9fc9777fa3]  Matt Jordan <mjordan at digium.com>
+
+	* contrib/scripts/autosupport: Update for Asterisk 13
+
+	  This patch adds some minor tweaks for autosupport to update it for Asterisk 13.
+	  This includes:
+	  * Finally removing most references to Zaptel
+	  * Adding support for some additional 'core' commands, and fixing nomenclature
+	    that generally hasn't been used for some time
+	  * Adding some PJSIP/SIP commands to gather endpoints/peers and active channels
+
+	  Change-Id: Ic997b418cbd9313588b6608e50f47b0ce6f4f1f1
+
+2015-10-14 14:15 +0000 [dc6ec661b3]  mdu113 <mulitskiy at acedsl.com>
+
+	* res_config_pgsql.c: Fix deadlock loading realtime configuration.
+
+	  On v13, loading several thousand PJSIP endpoints on Asterisk start causes
+	  a deadlock most of the time.
+
+	  Thanks to mdu113 for discovering that there was a call to pgsql_exec() not
+	  protected by the pgsql_lock reentrancy lock.
+
+	  {quote}
+	  I believe a code path exists that attempts to use pgsql connection without
+	  locking pgsql_lock.  I believe what happens during that deadlock that I
+	  see is two concurrent threads are both attempting to send query to pgsql,
+	  one of the thread is using a code path without locking pgsql_lock.  If
+	  they managed to send queries at the same time, it seems postgres ignores
+	  one of the queries and replies only to the one of them.  If it happens so
+	  that the thread holding the lock didn't receive the reply it will wait for
+	  it (and hold the lock) forever (or at least for very long time), thus
+	  completely blocking all access to db.
+	  {quote}
+
+	  * Added missing reentrancy locking around pgsql_exec() in find_table().
+
+	  * Moved unlock of pgsql_lock in unload_module() to avoid locking inversion
+	  between the psql_tables list lock and the pgsql_lock.
+
+	  ASTERISK-25455 #close
+	  Reported by:  mdu113
+	  Patches:
+	        res_config_pgsql.c-connlock2.diff (license #5543) patch uploaded by mdu113
+
+	  Change-Id: Id9e7cdf8a3b65ff19964b0cf942ace567938c4e2
+
+2015-10-13 14:13 +0000 [f8707ae9a5]  Olle Johansson (License 5267)
+
+	* channels/chan_sip: Set cause code to 44 on RTP timeout
+
+	  To quote Olle:
+
+	  "When issuing a hangup due to RTP timeouts the cause code is not set. I have
+	  selected 44 based on Cisco's implementation..."
+
+	  ASTERISK-25135 #close
+	  Reported by: Olle Johansson
+	  patches:
+	    rtp-timeout-cause-1.8.diff uploaded by Olle Johansson (License 5267)
+
+	  Change-Id: Ia62100c55077d77901caee0bcae299f8dc7375fc
+
+2015-10-10 15:20 +0000 [486b172b50]  Ivan Poddubny <ivan.poddubny at gmail.com>
+
+	* Build: Add menuselect options for using compiler sanitizers
+
+	  This patch adds menuselect options for building Asterisk with
+	  various sanitizers provided by gcc and clang.
+
+	  When one of *SANITIZER flags is set in menuselect, the appropriate
+	  option is added to CFLAGS ad LDFLAGS for the build.
+
+	  Information on sanitizers in the project wiki:
+	  https://github.com/google/sanitizers/wiki
+
+	  GCC Manual:
+	  https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html
+
+	  Clang Compiler User's Manual:
+	  http://clang.llvm.org/docs/UsersManual.html#controlling-code-generation
+
+	  ASTERISK-24718 #close
+	  Reported by: Badalian Vyacheslav
+
+	  Change-Id: Iafa51b792b7bcb20e848b99d16cf362d08590fa0
+
+2015-10-12 11:21 +0000 [e14023ca35]  Richard Mudgett <rmudgett at digium.com>
+
+	* config.c: Fix off-nominal memory leak.
+
+	  Change-Id: I06e346e9a5c63cc5071e7eda537310c4b43bffe0
+
+2015-10-12 11:20 +0000 [a99e821520]  Richard Mudgett <rmudgett at digium.com>
+
+	* config.c: Fix potential memory corruption after [section](+).
+
+	  The memory corruption could happen if the [section](+) is the last section
+	  in the file with trailing comments.  In this case process_text_line() has
+	  left *last_cat is set to newcat and newcat is destroyed.
+
+	  Change-Id: I0d1d999f553986f591becd000e7cc6ddfb978d93
+
+2015-10-12 11:21 +0000 [8d31d2526b]  Richard Mudgett <rmudgett at digium.com>
+
+	* config.c: Fix #include after [section](+).
+
+	  An #include right after a [section](+) would associate any variable
+	  assignments before a new section in the #include with the wrong section.
+
+	  * Fix section association by setting the current section to the appended
+	  section.
+
+	  * Fix '+' and '!' section flag interaction corner case depending upon
+	  which flag came first.  If the '!' came first then it would be ignored.
+	  If the '!' came after then it would affect the appended section.  The '!'
+	  will now no longer be ignored.
+
+	  ASTERISK-25461 #close
+	  Reported by: Sean Pimental
+
+	  Change-Id: Ic9d3191c8758048e2cbce6432f854b32531731c3
+
+2015-10-06 18:01 +0000 [3329c714f7]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip: Fix deadlock when sending out-of-dialog requests.
+
+	  The struct send_request_wrapper has a pjsip lock associated with it that
+	  is created non-recursive.  There is a code path for the struct
+	  send_request_wrapper lock that will attempt to lock it recursively.  The
+	  reporter's deadlock showed that the thread calling endpt_send_request()
+	  deadlocked itself right after the wrapper object got created.
+
+	  Out-of-dialog requests such as MESSAGE, qualify OPTIONS, and unsolicited
+	  MWI NOTIFY messages can hit this deadlock.
+
+	  * Replaced the struct send_request_wrapper pjsip lock with the mutex lock
+	  that can come with an ao2 object since all of Asterisk's mutexes are
+	  recursive.  Benefits include removal of code maintaining the pjsip
+	  non-recursive lock since ao2 objects already know how to maintain their
+	  own lock and the lock will show up in the CLI "core show locks" output.
+
+	  ASTERISK-25435 #close
+	  Reported by: Dmitriy Serov
+
+	  Change-Id: I458e131dd1b9816f9e963f796c54136e9e84322d
+
+2015-10-06 11:05 +0000 [a1435aa3fa]  Stefan Engström <stefanen at kth.se>
+
+	* res/res_rtp_asterisk.c: Fix incorrect assignment of frame->subclass.frame_ending
+
+	  In ast_rtp_read, the value of the variable 'mark' which we try to assign to a
+	  frame->subclass.frame_ending may be 0, 1 or (1<<23), but we should translate
+	  it to 0 or 1.
+
+	  ASTERISK-25451 #close
+	  Change-Id: I53bdf5c026041730184a6a809009c028549ce626
+
+2015-10-07 01:24 +0000 [3357678b94]  Ivan Poddubny <ivan.poddubny at gmail.com>
+
+	* func_presencestate: Return "not_set" when no data is set in AstDB
+
+	  Return AST_PRESENCE_NOT_SET when CustomPresence AstDB key does not
+	  exist, i.e. when a new CustomPresence is added in the dialplan.
+
+	  ASTERISK-25400 #close
+	  Reported by: Andrew Nagy
+
+	  Change-Id: I6fb17b16591b5a55fbffe96f3994ec26b1b1723a
+
+2015-10-06 20:43 +0000 [b714b2152d]  Matt Jordan <mjordan at digium.com>
 
 	* res/res_rtp_asterisk: Fix assignment after ao2 decrement
 
@@ -74,31 +1927,45 @@
 
 	  Change-Id: Ie6d3cb4adc7b1a6c078b1c38c19fc84cf787cda7
 
-2015-10-06 18:40 +0000  Asterisk Development Team <asteriskteam at digium.com>
+2015-10-06 12:40 +0000 [f939e2bd48]  Florian Sauerteig <ffs at ccn.net>
 
-	* asterisk 13.6.0-rc2 Released.
+	* chan_sip: Fix port parsing for IPv6 addresses in SIP Via headers.
 
-2015-10-06 13:40 +0000 [a44f6aa046]  Kevin Harwell <kharwell at lunkwill>
+	  If a Via header containes an IPv6 address and a port number is ommitted,
+	  as it is the standard port, we now leave the port empty and to not set it
+	  to the value after the first colon of the IPv6 address.
 
-	* Release summaries: Add summaries for 13.6.0-rc2
+	  ASTERISK-25443 #close
 
-2015-10-06 13:40 +0000 [dd74af7e46]  Kevin Harwell <kharwell at lunkwill.digium.internal>
+	  Change-Id: Ie3c2f05471cd006bf04ed15598589c09577b1e70
 
-	* Release summaries: Remove previous versions
+2015-10-05 16:53 +0000 [426263a64d]  Richard Mudgett <rmudgett at digium.com>
 
-2015-10-06 13:40 +0000 [a11a78ca34]  Kevin Harwell <kharwell at lunkwill>
+	* chan_pjsip: Fix crash on reINVITE before initial INVITE completes.
 
-	* .version: Update for 13.6.0-rc2
+	  Apparently some endpoints attempt to send a reINVITE before completing the
+	  initial INVITE transaction.  In this case PJSIP responds appropriately to
+	  the reINVITE with a 491 INVITE request pending.  Unfortunately chan_pjsip
+	  is using the initial INVITE transaction state to determine if an INVITE is
+	  the initial INVITE or a reINVITE.  Since the initial INVITE transaction
+	  has not been confirmed yet chan_pjsip thinks the reINVITE is an initial
+	  INVITE and starts another PBX thread on the channel.  The extra PBX thread
+	  ensures that hilarity ensues.
 
-2015-10-06 13:40 +0000 [570329ec8a]  Kevin Harwell <kharwell at lunkwill>
+	  * Fix checks for a reINVITE on incoming requests to look for the presence
+	  of a to-tag instead of the initial INVITE transaction state.
 
-	* .lastclean: Update for 13.6.0-rc2
+	  * Made caller_id_incoming_request() determine what to do if there is a
+	  channel on the session or not.  After a channel is created it is too late
+	  to just store the new party id on the session because the session's party
+	  id has already been copied to the channel's caller id.
 
-2015-10-06 13:40 +0000 [51c9ff47f6]  Kevin Harwell <kharwell at lunkwill>
+	  ASTERISK-25404 #close
+	  Reported by: Chet Stevens
 
-	* realtime: Add database scripts for 13.6.0-rc2
+	  Change-Id: Ie78201c304a2b13226f3a4ce59908beecc2c68be
 
-2015-10-05 21:34 +0000 [754daeca0a]  Matt Jordan <mjordan at digium.com>
+2015-10-05 21:34 +0000 [50fa9ff997]  Matt Jordan <mjordan at digium.com>
 
 	* Fix improper usage of scheduler exposed by 5c713fdf18f
 
@@ -117,7 +1984,92 @@
 	  ASTERISK-25449 #close
 
 	  Change-Id: I019a9aa8b6997584f66876331675981ac9e07e39
-2015-09-30 13:42 +0000 [9913d47697]  Joshua Colp <jcolp at digium.com>
+2015-08-26 16:58 +0000 [8f777ab584]  Debian Amtelco <dan at amtelco.com>
+
+	* chan_pjsip: Add Referred-By header to the PJSIP REFER packet.
+
+	  Some systems require the REFER packet to include a Referred-By header.
+	  If the channel variable SIPREFERREDBYHDR is set, it passes that value as the
+	  Referred-By header value.  Otherwise, it adds the current dialog’s local info.
+
+	  Reported by: Dan Cropp
+	  Tested by: Dan Cropp
+
+	  Change-Id: I3d17912ce548667edf53cb549e88a25475eda245
+
+2015-10-03 06:27 +0000 [74635b5638]  Ivan Poddubny <ivan.poddubny at gmail.com>
+
+	* manager: Fix GetConfigJSON returning invalid JSON
+
+	  When GetConfigJSON was introduced back in 1.6, it returned each
+	  section as an array of strings: ["key=value", "key2=value2"].
+	  Afterwards, it was changed a few times and became
+	  ["key": "value", "key2": "value2"], which is not a correct JSON.
+	  This patch fixes that by constructing a JSON object {} instead of
+	  an array [].
+
+	  Also, the keys "istemplate" and "tempates" that are used to
+	  indicate templates and their inherited categories are now wrapped in
+	  quotes.
+
+	  ASTERISK-25391 #close
+	  Reported by: Bojan Nemčić
+
+	  Change-Id: Ibbe93c6a227dff14d4a54b0d152341857bcf6ad8
+
+2015-09-30 17:28 +0000 [40c69e78f5]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_sorcery_memory_cache.c: Fix deadlock with scheduler.
+
+	  A deadlock can happen when a sorcery object is being expired from the
+	  memory cache when at the same time another object is being placed into the
+	  memory cache.  There are a couple other variations on this theme that
+	  could cause the deadlock.  Basically if an object is being expired from
+	  the sorcery memory cache at the same time as another thread tries to
+	  update the next object expiration timer the deadlock can happen.
+
+	  * Add a deadlock avoidance loop in expire_objects_from_cache() to check if
+	  someone is trying to remove the scheduler callback from the scheduler.
+
+	  ASTERISK-25441 #close
+
+	  Change-Id: Iec7b0bdb81a72b39477727b1535b2539ad0cf4dc
+
+2015-10-01 14:30 +0000 [dfeb513e85]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_sorcery_memory_cache.c: Replace inline code with function.
+
+	  Make sorcery_memory_cache_close() call remove_all_from_cache() instead of
+	  partially inlining it.
+
+	  ASTERISK-25441
+
+	  Change-Id: I1aa6cb425b1a4307096f3f914d17af8ec179a74c
+
+2015-10-01 14:27 +0000 [ced0a2d71b]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_sorcery_memory_cache.c: Shutdown in a less crash potential order.
+
+	  Basically you should shutdown in the opposite order of how you setup since
+	  later setup pieces likely depend on earlier setup pieces.  e.g.,
+	  Registering your external API with the rest of the system should be the
+	  last thing setup and the first thing unregistered during shutdown.
+
+	  Change-Id: I5715765b723100c8d3c2642e9e72cc7ad5ad115e
+
+2015-09-30 17:27 +0000 [cc279eea11]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_sorcery_memory_cache.c: Misc tweaks.
+
+	  Change-Id: I8cd32dffbb4f33bb0c39518d6e4c991e73573160
+
+2015-09-30 17:27 +0000 [9af3b613f6]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_sorcery_memory_cache.c: Made use OBJ_SEARCH_MASK.
+
+	  Change-Id: Ibca6574dc3c213b29cc93486e01ccd51f5caa46c
+
+2015-09-30 13:42 +0000 [56ed7b9dd5]  Joshua Colp <jcolp at digium.com>
 
 	* res_rtp_asterisk: Move "Set role" warning to be debug.
 
@@ -128,7 +2080,12 @@
 	  ASTERISK-25438 #close
 
 	  Change-Id: I0e17e4316f0f0d7f095c78c3d4fd73a913b6ba69
-	  (cherry picked from commit 56ed7b9dd560e468be31684e56a8070b88ae0205)
+
+2015-09-28 15:31 +0000 [ddebb217f0]  Richard Mudgett <rmudgett at digium.com>
+
+	* sched.c: Add warning about negative time interval request.
+
+	  Change-Id: Ib91435fb45b7f5f7c0fc83d0eec20b88098707bc
 
 2015-09-29 21:15 +0000  Asterisk Development Team <asteriskteam at digium.com>
 
diff --git a/Makefile b/Makefile
index 46b7193..d4ae9fd 100644
--- a/Makefile
+++ b/Makefile
@@ -124,7 +124,7 @@ empty:=
 space:=$(empty) $(empty)
 ASTTOPDIR:=$(subst $(space),\$(space),$(CURDIR))
 
-# Overwite config files on "make samples"
+# Overwite config files on "make samples" or other config installation targets
 OVERWRITE=y
 
 # Include debug and macro symbols in the executables (-g) and profiling info (-pg)
@@ -400,7 +400,7 @@ defaults.h: makeopts .lastclean build_tools/make_defaults_h
 	@cmp -s $@.tmp $@ || mv $@.tmp $@
 	@rm -f $@.tmp
 
-main/version.c: FORCE .lastclean
+main/version.c: FORCE menuselect.makeopts .lastclean
 	@build_tools/make_version_c > $@.tmp
 	@cmp -s $@.tmp $@ || mv $@.tmp $@
 	@rm -f $@.tmp
@@ -652,7 +652,12 @@ install: badshell bininstall datafiles
 	@echo " + configuration files (overwriting any      +"
 	@echo " + existing config files), run:              +"
 	@echo " +                                           +"
-	@echo " +               $(mK) samples               +"
+	@echo " + For generic reference documentation:      +"
+	@echo " +   $(mK) samples                           +"
+	@echo " +                                           +"
+	@echo " + For a sample basic PBX:                   +"
+	@echo " +   $(mK) basic-pbx                         +"
+	@echo " +                                           +"
 	@echo " +                                           +"
 	@echo " +-----------------  or ---------------------+"
 	@echo " +                                           +"
@@ -670,24 +675,14 @@ isntall: install
 
 upgrade: bininstall
 
-# XXX why *.adsi is installed first ?
-adsi:
-	@echo Installing adsi config files...
-	$(INSTALL) -d "$(DESTDIR)$(ASTETCDIR)"
-	@for x in configs/samples/*.adsi; do \
-		dst="$(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x`" ; \
-		if [ -f "$${dst}" ] ; then \
-			echo "Overwriting $$x" ; \
-		else \
-			echo "Installing $$x" ; \
-		fi ; \
-		$(INSTALL) -m 644 "$$x" "$(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x`" ; \
-	done
 
-samples: adsi
-	@echo Installing other config files...
-	@for x in configs/samples/*.sample; do \
-		dst="$(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x .sample`" ;	\
+# Install configuration files from the specified directory
+# Parameters:
+#  (1) the configuration directory to install from
+#  (2) the extension to strip off
+define INSTALL_CONFIGS
+	@for x in configs/$(1)/*$(2); do \
+		dst="$(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x $(2)`"; \
 		if [ -f "$${dst}" ]; then \
 			if [ "$(OVERWRITE)" = "y" ]; then \
 				if cmp -s "$${dst}" "$$x" ; then \
@@ -702,7 +697,7 @@ samples: adsi
 		fi ; \
 		echo "Installing file $$x"; \
 		$(INSTALL) -m 644 "$$x" "$${dst}" ;\
-	done
+	done ; \
 	if [ "$(OVERWRITE)" = "y" ]; then \
 		echo "Updating asterisk.conf" ; \
 		sed -e 's|^astetcdir.*$$|astetcdir => $(ASTETCDIR)|' \
@@ -719,10 +714,28 @@ samples: adsi
 			"$(DESTDIR)$(ASTCONFPATH)" > "$(DESTDIR)$(ASTCONFPATH).tmp" ; \
 		$(INSTALL) -m 644 "$(DESTDIR)$(ASTCONFPATH).tmp" "$(DESTDIR)$(ASTCONFPATH)" ; \
 		rm -f "$(DESTDIR)$(ASTCONFPATH).tmp" ; \
-	fi ; \
+	fi
+endef
+
+# XXX why *.adsi is installed first ?
+adsi:
+	@echo Installing adsi config files...
+	$(INSTALL) -d "$(DESTDIR)$(ASTETCDIR)"
+	@for x in configs/samples/*.adsi; do \
+		dst="$(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x`" ; \
+		if [ -f "$${dst}" ] ; then \
+			echo "Overwriting $$x" ; \
+		else \
+			echo "Installing $$x" ; \
+		fi ; \
+		$(INSTALL) -m 644 "$$x" "$(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x`" ; \
+	done
+
+samples: adsi
+	@echo Installing other config files...
+	$(call INSTALL_CONFIGS,samples,.sample)
 	$(INSTALL) -d "$(DESTDIR)$(ASTSPOOLDIR)/voicemail/default/1234/INBOX"
 	build_tools/make_sample_voicemail "$(DESTDIR)/$(ASTDATADIR)" "$(DESTDIR)/$(ASTSPOOLDIR)"
-
 	@for x in phoneprov/*; do \
 		dst="$(DESTDIR)$(ASTDATADIR)/$$x" ;	\
 		if [ -f "$${dst}" ]; then \
@@ -741,6 +754,10 @@ samples: adsi
 		$(INSTALL) -m 644 "$$x" "$${dst}" ;\
 	done
 
+basic-pbx:
+	@echo Installing basic-pbx config files...
+	$(call INSTALL_CONFIGS,basic-pbx)
+
 webvmail:
 	@[ -d "$(DESTDIR)$(HTTP_DOCSDIR)/" ] || ( printf "http docs directory not found.\nUpdate assignment of variable HTTP_DOCSDIR in Makefile!\n" && exit 1 )
 	@[ -d "$(DESTDIR)$(HTTP_CGIDIR)" ] || ( printf "cgi-bin directory not found.\nUpdate assignment of variable HTTP_CGIDIR in Makefile!\n" && exit 1 )
@@ -1013,6 +1030,7 @@ endif
 .PHONY: validate-docs
 .PHONY: _clean
 .PHONY: ari-stubs
+.PHONY: basic-pbx
 .PHONY: $(SUBDIRS_INSTALL)
 .PHONY: $(SUBDIRS_DIST_CLEAN)
 .PHONY: $(SUBDIRS_CLEAN)
diff --git a/Makefile.rules b/Makefile.rules
index a274c95..1031f2d 100644
--- a/Makefile.rules
+++ b/Makefile.rules
@@ -27,6 +27,26 @@
 # extra cflags to build dependencies. Recursively expanded.
 MAKE_DEPS=-MD -MT $@ -MF .$(subst /,_,$@).d -MP
 
+ifeq ($(findstring ADDRESS_SANITIZER,$(MENUSELECT_CFLAGS)),ADDRESS_SANITIZER)
+    _ASTLDFLAGS+=-fsanitize=address
+    _ASTCFLAGS+=-fno-omit-frame-pointer -fsanitize=address
+endif
+
+ifeq ($(findstring THREAD_SANITIZER,$(MENUSELECT_CFLAGS)),THREAD_SANITIZER)
+    _ASTLDFLAGS+=-fsanitize=thread -pie -fPIE
+    _ASTCFLAGS+=-fno-omit-frame-pointer -pie -fPIE -fsanitize=thread
+endif
+
+ifeq ($(findstring LEAK_SANITIZER,$(MENUSELECT_CFLAGS)),LEAK_SANITIZER)
+    _ASTLDFLAGS+=-fsanitize=leak
+    _ASTCFLAGS+=-fno-omit-frame-pointer -fsanitize=leak
+endif
+
+ifeq ($(findstring UNDEFINED_SANITIZER,$(MENUSELECT_CFLAGS)),UNDEFINED_SANITIZER)
+    _ASTLDFLAGS+=-fsanitize=undefined
+    _ASTCFLAGS+=-fno-omit-frame-pointer -fsanitize=undefined
+endif
+
 ifeq ($(NOISY_BUILD),)
     ECHO_PREFIX=@
     CMD_PREFIX=@
diff --git a/addons/chan_mobile.c b/addons/chan_mobile.c
index 3304702..f748f79 100644
--- a/addons/chan_mobile.c
+++ b/addons/chan_mobile.c
@@ -902,7 +902,7 @@ static struct ast_channel *mbl_request(const char *type, struct ast_format_cap *
 	}
 
 	if (ast_format_cap_iscompatible_format(cap, DEVICE_FRAME_FORMAT) == AST_FORMAT_CMP_NOT_EQUAL) {
-		struct ast_str *codec_buf = ast_str_alloca(64);
+		struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", ast_format_cap_get_names(cap, &codec_buf));
 		*cause = AST_CAUSE_FACILITY_NOT_IMPLEMENTED;
 		return NULL;
diff --git a/addons/chan_ooh323.c b/addons/chan_ooh323.c
index ef67b78..b530697 100644
--- a/addons/chan_ooh323.c
+++ b/addons/chan_ooh323.c
@@ -576,7 +576,7 @@ static struct ast_channel *ooh323_request(const char *type, struct ast_format_ca
 		const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause)
 
 {
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	struct ast_channel *chan = NULL;
 	struct ooh323_pvt *p = NULL;
 	struct ooh323_peer *peer = NULL;
@@ -1192,7 +1192,7 @@ static int ooh323_write(struct ast_channel *ast, struct ast_frame *f)
 
 			if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
 				if (ast_format_cap_count(ast_channel_nativeformats(ast))) {
-					struct ast_str *codec_buf = ast_str_alloca(64);
+					struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 					ast_log(LOG_WARNING,
 							"Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
 							ast_format_get_name(f->subclass.format),
@@ -1545,7 +1545,7 @@ void ooh323_set_write_format(ooCallData *call, struct ast_format *fmt, int txfra
 			return;
 		}
 		if (gH323Debug) {
-			struct ast_str *codec_buf = ast_str_alloca(64);
+			struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 			ast_verb(0, "Writeformat before update %s/%s\n", 
 			  ast_format_get_name(ast_channel_writeformat(p->owner)),
 			  ast_format_cap_get_names(ast_channel_nativeformats(p->owner), &codec_buf));
@@ -2114,7 +2114,7 @@ int onNewCallCreated(ooCallData *call)
 		}
 
 		if (gH323Debug) {
-			struct ast_str *codec_buf = ast_str_alloca(64);
+			struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_verb(0, " Outgoing call %s(%s) - Codec prefs - %s\n", 
 				p->username?p->username:"NULL", call->callToken,
@@ -3231,7 +3231,7 @@ static char *handle_cli_ooh323_show_peer(struct ast_cli_entry *e, int cmd, struc
 static char *handle_cli_ooh323_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	struct ooh323_peer *prev = NULL, *peer = NULL;
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
    char ip_port[30];
 #define FORMAT  "%-15.15s  %-15.15s  %-23.23s  %-s\n"
 
@@ -3370,7 +3370,7 @@ static char *handle_cli_ooh323_show_user(struct ast_cli_entry *e, int cmd, struc
 static char *handle_cli_ooh323_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	struct ooh323_user *prev = NULL, *user = NULL;
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 #define FORMAT1  "%-15.15s  %-15.15s  %-15.15s  %-s\n"
 
 	switch (cmd) {
@@ -3504,7 +3504,7 @@ static char *handle_cli_ooh323_show_gk(struct ast_cli_entry *e, int cmd, struct
 static char *handle_cli_ooh323_show_config(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	char value[FORMAT_STRING_SIZE];
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	ooAliases *pAlias = NULL, *pAliasNext = NULL;;
 
 	switch (cmd) {
diff --git a/apps/app_chanisavail.c b/apps/app_chanisavail.c
index 47b631a..84200db 100644
--- a/apps/app_chanisavail.c
+++ b/apps/app_chanisavail.c
@@ -56,10 +56,10 @@ static const char app[] = "ChanIsAvail";
 			<parameter name="Technology/Resource" required="true" argsep="&">
 				<argument name="Technology2/Resource2" multiple="true">
 					<para>Optional extra devices to check</para>
-					<para>If you need more then one enter them as
-					Technology2/Resource2&Technology3/Resourse3&.....</para>
+					<para>If you need more than one enter them as
+					Technology2/Resource2&Technology3/Resource3&.....</para>
 				</argument>
-				<para>Specification of the device(s) to check.  These must be in the format of 
+				<para>Specification of the device(s) to check.  These must be in the format of
 				<literal>Technology/Resource</literal>, where <replaceable>Technology</replaceable>
 				represents a particular channel driver, and <replaceable>Resource</replaceable>
 				represents a resource available to that particular channel driver.</para>
diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c
index 8045680..5976d39 100644
--- a/apps/app_confbridge.c
+++ b/apps/app_confbridge.c
@@ -118,6 +118,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 					<value name="KICKED">The channel was kicked from the conference.</value>
 					<value name="ENDMARKED">The channel left the conference as a result of the last marked user leaving.</value>
 					<value name="DTMF">The channel pressed a DTMF sequence to exit the conference.</value>
+					<value name="TIMEOUT">The channel reached its configured timeout.</value>
 				</variable>
 			</variablelist>
 		</description>
@@ -1542,6 +1543,13 @@ static int conf_get_pin(struct ast_channel *chan, struct confbridge_user *user)
 	return -1;
 }
 
+static int user_timeout(struct ast_bridge_channel *bridge_channel, void *ignore)
+{
+	ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
+	pbx_builtin_setvar_helper(bridge_channel->chan, "CONFBRIDGE_RESULT", "TIMEOUT");
+	return -1;
+}
+
 static int conf_rec_name(struct confbridge_user *user, const char *conf_name)
 {
 	char destdir[PATH_MAX];
@@ -1776,6 +1784,16 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
 		ast_autoservice_stop(chan);
 	}
 
+	if (user.u_profile.timeout) {
+		ast_bridge_interval_hook(&user.features,
+			0,
+			user.u_profile.timeout * 1000,
+			user_timeout,
+			NULL,
+			NULL,
+			AST_BRIDGE_HOOK_REMOVE_ON_PULL);
+	}
+
 	/* See if we need to automatically set this user as a video source or not */
 	handle_video_on_join(conference, user.chan, ast_test_flag(&user.u_profile, USER_OPT_MARKEDUSER));
 
diff --git a/apps/app_dial.c b/apps/app_dial.c
index 6c8501d..d65dcae 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -84,12 +84,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 				</argument>
 				<argument name="Technology2/Resource2" required="false" multiple="true">
 					<para>Optional extra devices to dial in parallel</para>
-					<para>If you need more then one enter them as
-					Technology2/Resource2&Technology3/Resourse3&.....</para>
+					<para>If you need more than one enter them as
+					Technology2/Resource2&Technology3/Resource3&.....</para>
 				</argument>
 			</parameter>
 			<parameter name="timeout" required="false">
-				<para>Specifies the number of seconds we attempt to dial the specified devices</para>
+				<para>Specifies the number of seconds we attempt to dial the specified devices.</para>
 				<para>If not specified, this defaults to 136 years.</para>
 			</parameter>
 			<parameter name="options" required="false">
@@ -491,7 +491,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 			<para>Unless there is a timeout specified, the Dial application will wait
 			indefinitely until one of the called channels answers, the user hangs up, or
-			if all of the called channels are busy or unavailable. Dialplan executing will
+			if all of the called channels are busy or unavailable. Dialplan execution will
 			continue if no requested channels can be called, or if the timeout expires.
 			This application will report normal termination if the originating channel
 			hangs up, or if the call is bridged and either of the parties in the bridge
diff --git a/apps/app_dumpchan.c b/apps/app_dumpchan.c
index 1794c25..1a5a446 100644
--- a/apps/app_dumpchan.c
+++ b/apps/app_dumpchan.c
@@ -72,7 +72,7 @@ static int serialize_showchan(struct ast_channel *c, char *buf, size_t size)
 {
 	long elapsed_seconds = 0;
 	int hour = 0, min = 0, sec = 0;
-	struct ast_str *format_buf = ast_str_alloca(64);
+	struct ast_str *format_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	char cgrp[256];
 	char pgrp[256];
 	struct ast_str *write_transpath = ast_str_alloca(256);
diff --git a/apps/app_meetme.c b/apps/app_meetme.c
index 341014c..b2131ff 100644
--- a/apps/app_meetme.c
+++ b/apps/app_meetme.c
@@ -5774,6 +5774,9 @@ static void meetme_set_defaults(void)
 
 	/*  Logging of participants defaults to ON for compatibility reasons */
 	rt_log_members = 1;
+
+	/* Set default number of buffers to be allocated. */
+	audio_buffers = DEFAULT_AUDIO_BUFFERS;
 }
 
 static void load_config_meetme(int reload)
diff --git a/apps/app_page.c b/apps/app_page.c
index cd87e06..590b15b 100644
--- a/apps/app_page.c
+++ b/apps/app_page.c
@@ -60,7 +60,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 				<argument name="Technology2/Resource2" multiple="true">
 					<para>Optional extra devices to dial in parallel</para>
 					<para>If you need more than one, enter them as Technology2/Resource2&
-					Technology3/Resourse3&.....</para>
+					Technology3/Resource3&.....</para>
 				</argument>
 			</parameter>
 			<parameter name="options">
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 6dfb143..5a8dcd2 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -6500,7 +6500,6 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
 		ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT),
 		forwardsallowed, ringing);
 
-	ast_channel_unlock(qe->chan);
 	ao2_lock(qe->parent);
 	if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) {
 		store_next_rr(qe, outgoing);
diff --git a/apps/confbridge/conf_config_parser.c b/apps/confbridge/conf_config_parser.c
index 6d6f7ab..b8b1e2a 100644
--- a/apps/confbridge/conf_config_parser.c
+++ b/apps/confbridge/conf_config_parser.c
@@ -227,6 +227,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 				<configOption name="template">
 					<synopsis>When using the CONFBRIDGE dialplan function, use a user profile as a template for creating a new temporary profile</synopsis>
 				</configOption>
+				<configOption name="timeout">
+					<synopsis>Kick the user out of the conference after this many seconds. 0 means there is no timeout for the user.</synopsis>
+				</configOption>
 			</configObject>
 			<configObject name="bridge_profile">
 				<synopsis>A named profile to apply to specific bridges.</synopsis>
@@ -2109,6 +2112,7 @@ int conf_load_config(void)
 	aco_option_register(&cfg_info, "dsp_silence_threshold", ACO_EXACT, user_types, __stringify(DEFAULT_SILENCE_THRESHOLD), OPT_UINT_T, 0, FLDSET(struct user_profile, silence_threshold));
 	aco_option_register(&cfg_info, "dsp_talking_threshold", ACO_EXACT, user_types, __stringify(DEFAULT_TALKING_THRESHOLD), OPT_UINT_T, 0, FLDSET(struct user_profile, talking_threshold));
 	aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_JITTERBUFFER);
+	aco_option_register(&cfg_info, "timeout", ACO_EXACT, user_types, "0", OPT_UINT_T, 0, FLDSET(struct user_profile, timeout));
 	/* This option should only be used with the CONFBRIDGE dialplan function */
 	aco_option_register_custom(&cfg_info, "template", ACO_EXACT, user_types, NULL, user_template_handler, 0);
 
diff --git a/apps/confbridge/include/confbridge.h b/apps/confbridge/include/confbridge.h
index 6f8439a..8d2dffb 100644
--- a/apps/confbridge/include/confbridge.h
+++ b/apps/confbridge/include/confbridge.h
@@ -140,6 +140,8 @@ struct user_profile {
 	unsigned int talking_threshold;
 	/*! The time in ms of silence before a user is considered to be silent by the dsp. */
 	unsigned int silence_threshold;
+	/*! The time in ms the user may stay in the confbridge */
+	unsigned int timeout;
 };
 
 enum conf_sounds {
diff --git a/asterisk-13.6.0-summary.html b/asterisk-13.6.0-summary.html
deleted file mode 100644
index 5213c19..0000000
--- a/asterisk-13.6.0-summary.html
+++ /dev/null
@@ -1,322 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><title>Release Summary - asterisk-13.6.0</title><h1 align="center"><a name="top">Release Summary</a></h1><h3 align="center">asterisk-13.6.0</h3><h3 align="center">Date: 2015-10-09</h3><h3 align="center"><asteriskteam at digium.com></h3><hr><h2 align="center">Table of Contents</h2><ol>
-<li><a href="#summary">Summary</a></li>
-<li><a href="#contributors">Contributors</a></li>
-<li><a href="#closed_issues">Closed Issues</a></li>
-<li><a href="#open_issues">Open Issues</a></li>
-<li><a href="#commits">Other Changes</a></li>
-<li><a href="#diffstat">Diffstat</a></li>
-</ol><hr><a name="summary"><h2 align="center">Summary</h2></a><center><a href="#top">[Back to Top]</a></center><p>This release is a point release of an existing major version. The changes included were made to address problems that have been identified in this release series, or are minor, backwards compatible new features or improvements. Users should be able to safely upgrade to this version if this release series is already in use. Users considering upgrading from a previous version a [...]
-<tr><th width="33%">Coders</th><th width="33%">Testers</th><th width="33%">Reporters</th></tr>
-<tr valign="top"><td width="33%">40 Richard Mudgett <rmudgett at digium.com><br/>18 Kevin Harwell <kharwell at lunkwill><br/>16 Joshua Colp <jcolp at digium.com><br/>15 Mark Michelson <mmichelson at digium.com><br/>13 Matt Jordan <mjordan at digium.com><br/>9 Scott Griepentrog <scott at griepentrog.com><br/>8 Kevin Harwell <kharwell at lunkwill.digium.internal><br/>3 Scott Emidy <jemidy at digium.com><br/>3 David M. Lee <dlee at respoke.io><br/>2 Alexander Traud <pabstraud at compuserve.com><br/>2 Alexander Anikin <m [...]
-</table><hr><a name="closed_issues"><h2 align="center">Closed Issues</h2></a><center><a href="#top">[Back to Top]</a></center><p>This is a list of all issues from the issue tracker that were closed by changes that went into this release.</p><h3>New Feature</h3><h4>Category: Resources/res_ari</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25252">ASTERISK-25252</a>: ARI: Add the ability to manipulate log channels<br/>Reported by: Matt Jordan<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=df9ce3636695781be6ab2479f90766a56747dbd7">[df9ce36366]</a> Scott Emidy -- ARI: Retrieve existing log channels</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=e9f1bc08cbda7759707c30b8883b266555d0fefc">[e9f1bc08cb]</a> Scott Emidy -- ARI: Creating log channels</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=78364132ce94d9ded24ae6e6ab44b97d256b506d">[78364132ce]</a> Scott Emidy -- ARI: Deleting log channels</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=1ae762634c317fbcbd98a8c34d2474f7d4b654ed">[1ae762634c]</a> Benjamin Ford -- ARI: Rotate log channels.</li>
-</ul><br><h4>Category: Resources/res_pjsip</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25377">ASTERISK-25377</a>: res_pjsip: Change default "From user" from UUID to something more palatable<br/>Reported by: Mark Michelson<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=ac62928d6b7333a0d502be2eba99c238549ae1a3">[ac62928d6b]</a> Mark Michelson -- res_pjsip: Change default from user value.</li>
-</ul><br><h3>Bug</h3><h4>Category: Addons/chan_ooh323</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25227">ASTERISK-25227</a>: No audio at in-band announcements in ooh323 channel<br/>Reported by: Alexandr Dranchuk<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=71408df2b82c932329d250a6077475e4f51a2b0d">[71408df2b8]</a> Alexander Anikin -- chan_ooh323: Add ProgressIndicator IE with inband info available</li>
-</ul><br><h4>Category: Applications/app_dial</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25423">ASTERISK-25423</a>: Caller gets no Connected line update during call pickup.<br/>Reported by: Richard Mudgett<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=6b1e7583c150cc9bafcb567c789b6c23c60e2c71">[6b1e7583c1]</a> Richard Mudgett -- app_queue.c: Force COLP update if outgoing channel name changed.</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=6bf304bf251e31ab7c7a4d89508445b84fb5d551">[6bf304bf25]</a> Richard Mudgett -- app_queue.c: Factor out a connected line update routine.</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=e36b5f1e8e1fd1e805184fca015bb0808b5e7fb8">[e36b5f1e8e]</a> Richard Mudgett -- app_dial.c: Make 'A' option pass COLP updates.</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=747bfac895c4d51f55ce687322aa6a95c52be4e2">[747bfac895]</a> Richard Mudgett -- app_dial.c: Force COLP update if outgoing channel name changed.</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=14481d9aa0365682ace22c97d5c115166be5429d">[14481d9aa0]</a> Richard Mudgett -- app_dial.c: Factor out a connected line update routine.</li>
-</ul><br><h4>Category: Applications/app_mixmonitor</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25322">ASTERISK-25322</a>: Crash occurs when using MixMonitor with t() or r() options.<br/>Reported by: Richard Mudgett<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=b3a56bee83c01dbb538620a11947d420b17cf458">[b3a56bee83]</a> Richard Mudgett -- audiohook.c: Fix MixMonitor crash when using the r() or t() options.</li>
-</ul><br><h4>Category: Applications/app_page</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25384">ASTERISK-25384</a>: Regular Asterisk crashes when using Page application. "user_data is NULL"<br/>Reported by: Chet Stevens<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=5f15cd93f0b2cb622d54061515b815e3ebbe76b1">[5f15cd93f0]</a> Richard Mudgett -- app_page.c: Fix crash when forwarding with a predial handler.</li>
-</ul><br><h4>Category: Applications/app_queue</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25423">ASTERISK-25423</a>: Caller gets no Connected line update during call pickup.<br/>Reported by: Richard Mudgett<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=6b1e7583c150cc9bafcb567c789b6c23c60e2c71">[6b1e7583c1]</a> Richard Mudgett -- app_queue.c: Force COLP update if outgoing channel name changed.</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=6bf304bf251e31ab7c7a4d89508445b84fb5d551">[6bf304bf25]</a> Richard Mudgett -- app_queue.c: Factor out a connected line update routine.</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=e36b5f1e8e1fd1e805184fca015bb0808b5e7fb8">[e36b5f1e8e]</a> Richard Mudgett -- app_dial.c: Make 'A' option pass COLP updates.</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=747bfac895c4d51f55ce687322aa6a95c52be4e2">[747bfac895]</a> Richard Mudgett -- app_dial.c: Force COLP update if outgoing channel name changed.</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=14481d9aa0365682ace22c97d5c115166be5429d">[14481d9aa0]</a> Richard Mudgett -- app_dial.c: Factor out a connected line update routine.</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25399">ASTERISK-25399</a>: app_queue: AgentComplete event has wrong reason<br/>Reported by: Kevin Harwell<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=4fb95bbc4e4928dd3403a20d401c285a568f0d09">[4fb95bbc4e]</a> Kevin Harwell -- app_queue: AgentComplete event has wrong reason</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25185">ASTERISK-25185</a>: Segfault in app_queue on transfer scenarios<br/>Reported by: Etienne Lessard<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=6409e7b11a2310196a9978b30a6b79e2760be592">[6409e7b11a]</a> Kevin Harwell -- app_queue: Crash when transferring</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25215">ASTERISK-25215</a>: Differences in queue.log between Set QUEUE_MEMBER and using PauseQueueMember<br/>Reported by: Lorne Gaetz<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=e5f5b9f384eef0389a7e35d40c91d3586869a125">[e5f5b9f384]</a> Richard Mudgett -- app_queue.c: Fix setting QUEUE_MEMBER 'paused' and 'ringinuse'.</li>
-</ul><br><h4>Category: Applications/app_record</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25410">ASTERISK-25410</a>: app_record: RECORDED_FILE variable not being populated<br/>Reported by: Kevin Harwell<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=aeddee39fb492ea5ec873bdf02ea3858c5282601">[aeddee39fb]</a> Kevin Harwell -- app_record: RECORDED_FILE variable not being populated</li>
-</ul><br><h4>Category: Bridges/bridge_holding</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25271">ASTERISK-25271</a>: Parking & blind transfer: Transferer channel not hung up if no MOH<br/>Reported by: Kevin Harwell<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=8458b8d441c2f4143ff135163ff3da4f88fe14c8">[8458b8d441]</a> Jonathan Rose -- holding_bridge: ensure moh participants get frames</li>
-</ul><br><h4>Category: Channels/chan_dahdi</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25315">ASTERISK-25315</a>: DAHDI channels send shortened duration DTMF tones.<br/>Reported by: Richard Mudgett<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=256bc52b6684141b100c5018dbf0ad3ce6111585">[256bc52b66]</a> Richard Mudgett -- chan_dahdi.c: Flush the DAHDI write buffer after starting DTMF.</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=800e0ea48de88c016e1f477edf7db7b9aadc4b54">[800e0ea48d]</a> Richard Mudgett -- chan_dahdi.c: Lock private struct for ast_write().</li>
-</ul><br><h4>Category: Channels/chan_sip/CodecHandling</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25309">ASTERISK-25309</a>: [patch] iLBC 20 advertised<br/>Reported by: Alexander Traud<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=f68c995bc97c9b6cb4887043b344087d82aeef10">[f68c995bc9]</a> Alexander Traud -- chan_sip: Fix negotiation of iLBC 30.</li>
-</ul><br><h4>Category: Channels/chan_sip/General</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25346">ASTERISK-25346</a>: chan_sip: Overwriting answered elsewhere hangup cause on call pickup<br/>Reported by: Joshua Colp<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=c01111223f9dbd383a4dd1cf786b63eff214f238">[c01111223f]</a> Joshua Colp -- chan_sip: Allow call pickup to set the hangup cause.</li>
-</ul><br><h4>Category: Channels/chan_sip/Interoperability</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25396">ASTERISK-25396</a>: chan_sip: Extremely long callerid name causes invalid SIP<br/>Reported by: Walter Doekes<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=b59c4d82b58a7a10e1791f9ed5759af8ac637df2">[b59c4d82b5]</a> Walter Doekes -- chan_sip: Fix From header truncation for extremely long CALLERID(name).</li>
-</ul><br><h4>Category: Channels/chan_sip/Security Framework</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25320">ASTERISK-25320</a>: chan_sip.c: sip_report_security_event searches for wrong or non existent peer on invite<br/>Reported by: Kevin Harwell<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=25af2d71c863062868b8bda6cf83515a1935d27e">[25af2d71c8]</a> Kevin Harwell -- chan_sip.c: wrong peer searched in sip_report_security_event</li>
-</ul><br><h4>Category: Channels/chan_skinny</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25296">ASTERISK-25296</a>: RTP performance issue with several channel drivers.<br/>Reported by: Richard Mudgett<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=aeeb170fc4fe4e681a96b87f8e81ade717aa2426">[aeeb170fc4]</a> Richard Mudgett -- rtp_engine.c: Fix performance issue with several channel drivers that use RTP.</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=84262749d2a1f59f669e801d5f796b016b223960">[84262749d2]</a> Richard Mudgett -- res_rtp_asterisk.c: Fix off-nominal crash potential.</li>
-</ul><br><h4>Category: Channels/chan_unistim</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25296">ASTERISK-25296</a>: RTP performance issue with several channel drivers.<br/>Reported by: Richard Mudgett<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=aeeb170fc4fe4e681a96b87f8e81ade717aa2426">[aeeb170fc4]</a> Richard Mudgett -- rtp_engine.c: Fix performance issue with several channel drivers that use RTP.</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=84262749d2a1f59f669e801d5f796b016b223960">[84262749d2]</a> Richard Mudgett -- res_rtp_asterisk.c: Fix off-nominal crash potential.</li>
-</ul><br><h4>Category: Codecs/General</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25353">ASTERISK-25353</a>: [patch] Transcoding while different in Frame size = Frames lost<br/>Reported by: Alexander Traud<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=b88c54fa4bd537bde46519abb95e30a5f96673ac">[b88c54fa4b]</a> Alexander Traud -- translate: Fix transcoding while different in frame size.</li>
-</ul><br><h4>Category: Core/Bridging</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25341">ASTERISK-25341</a>: bridge: Hangups may get lost when executing actions<br/>Reported by: Joshua Colp<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=6c2dab1e888e59cb429ed61219815bd00eee66c0">[6c2dab1e88]</a> Joshua Colp -- bridge: Kick channel from bridge if hung up during action.</li>
-</ul><br><h4>Category: Core/BuildSystem</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25383">ASTERISK-25383</a>: Core dumps on startup and shutdown with MALLOC_DEBUG enabled<br/>Reported by: yaron nahum<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=028033e5a8dcf2b6d9c454786736825fe0288141">[028033e5a8]</a> Richard Mudgett -- res/ari/config.c: Fix conf_alloc() object init.</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25265">ASTERISK-25265</a>: [patch]DTLS Failure when calling WebRTC-peer on Firefox 39 - add ECDH support and fallback to prime256v1<br/>Reported by: Stefan Engström<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=9a12804e592b97d74ff7b909e0d0022f1ca72386">[9a12804e59]</a> Joshua Colp -- res_rtp_asterisk: Don't leak temporary key when enabling PFS.</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=aed068844c1c9748da9c67b74ea4d90622be8f46">[aed068844c]</a> Mark Duncan -- res/res_rtp_asterisk: Add ECDH support</li>
-</ul><br><h4>Category: Core/General</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25449">ASTERISK-25449</a>: main/sched: Regression introduced by 5c713fdf18f causes erroneous duplicate RTCP messages; other potential scheduling issues in chan_sip/chan_skinny<br/>Reported by: Matt Jordan<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=10e790f81ad3be322193cf948e0334f9af09b00f">[10e790f81a]</a> Matt Jordan -- res/res_rtp_asterisk: Fix assignment after ao2 decrement</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=754daeca0a48e5c9365cc7fa4c5a3da6c61ae7f6">[754daeca0a]</a> Matt Jordan -- Fix improper usage of scheduler exposed by 5c713fdf18f</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25383">ASTERISK-25383</a>: Core dumps on startup and shutdown with MALLOC_DEBUG enabled<br/>Reported by: yaron nahum<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=028033e5a8dcf2b6d9c454786736825fe0288141">[028033e5a8]</a> Richard Mudgett -- res/ari/config.c: Fix conf_alloc() object init.</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25418">ASTERISK-25418</a>: On-hold channels redirected out of a bridge appear to still be on hold<br/>Reported by: Mark Michelson<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=629458d34930e5aca56f749bc05562baf95d13f7">[629458d349]</a> Mark Michelson -- Do not swallow frames on channels leaving bridges.</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25355">ASTERISK-25355</a>: sched: ast_sched_del may return prematurely due to spurious wakeup<br/>Reported by: Joshua Colp<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=85e1cb51b21c2d194647e16b81b5a1344d2ff911">[85e1cb51b2]</a> Joshua Colp -- sched: ast_sched_del may return prematurely due to spurious wakeup</li>
-</ul><br><h4>Category: Core/Logging</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25305">ASTERISK-25305</a>: Dynamic logger channels can be added multiple times<br/>Reported by: Mark Michelson<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=f050fa76eb8535a2b8a3b047527a42ea369d8792">[f050fa76eb]</a> Mark Michelson -- logger: Prevent duplicate dynamic channels from being added.</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25407">ASTERISK-25407</a>: Asterisk fails to log to multiple syslog destinations<br/>Reported by: Elazar Broad<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=ec514ad64dbc0014525008977c8c74c2856c9d3a">[ec514ad64d]</a> Elazar Broad -- core/logging: Fix logging to more than one syslog channel</li>
-</ul><br><h4>Category: Core/PBX</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25394">ASTERISK-25394</a>: pbx: Incorrect device and presence state when changing hint details<br/>Reported by: Joshua Colp<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=2bd27d12223fe33b58c453965ed5c6ed3af7c4f5">[2bd27d1222]</a> Joshua Colp -- pbx: Update device and presence state when changing a hint extension.</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25367">ASTERISK-25367</a>: pbx: Long pattern match hints may cause "core show hints" to crash<br/>Reported by: Joshua Colp<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=cc1363209e7a5b6c67bf96c593e3beb0884c1fb0">[cc1363209e]</a> Joshua Colp -- pbx: Fix crash when issuing "core show hints" with long pattern match.</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25362">ASTERISK-25362</a>: Deadlock due to presence state callback<br/>Reported by: Mark Michelson<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=03fe79f29eae42be24589b323a5ef3fa9259158d">[03fe79f29e]</a> Mark Michelson -- Fix deadlock on presence state changes.</li>
-</ul><br><h4>Category: Core/RTP</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25296">ASTERISK-25296</a>: RTP performance issue with several channel drivers.<br/>Reported by: Richard Mudgett<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=aeeb170fc4fe4e681a96b87f8e81ade717aa2426">[aeeb170fc4]</a> Richard Mudgett -- rtp_engine.c: Fix performance issue with several channel drivers that use RTP.</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=84262749d2a1f59f669e801d5f796b016b223960">[84262749d2]</a> Richard Mudgett -- res_rtp_asterisk.c: Fix off-nominal crash potential.</li>
-</ul><br><h4>Category: Resources/res_ari</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25325">ASTERISK-25325</a>: ARI PUT reload chan_sip HTTP response 404<br/>Reported by: Rodrigo Ramirez Norambuena<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=865377fc38134234f17def6634c47a989cf0e77a">[865377fc38]</a> Rodrigo Ramírez Norambuena -- chan_sip.c: Validation on module reload</li>
-</ul><br><h4>Category: Resources/res_http_websocket</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25312">ASTERISK-25312</a>: res_http_websocket: Terminate connection on fatal cases<br/>Reported by: Joshua Colp<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=b4e9416138339274176cb87b26db905723e553ba">[b4e9416138]</a> Joshua Colp -- res_http_websocket: Forcefully terminate on write errors.</li>
-</ul><br><h4>Category: Resources/res_parking</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25369">ASTERISK-25369</a>: res_parking: ParkAndAnnounce - Inheritable variables aren't applied to the announcer channel<br/>Reported by: Jonathan Rose<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=fbf720db91ae8942e9c2ba092179ab2352d44b06">[fbf720db91]</a> Jonathan Rose -- ParkAndAnnounce: Add variable inheritance</li>
-</ul><br><h4>Category: Resources/res_pjsip</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25295">ASTERISK-25295</a>: res_pjsip crash - pjsip_uri_get_uri at /usr/include/pjsip/sip_uri.h<br/>Reported by: Dmitriy Serov<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=5469caa9ddf002d2e75b5fe5dec0c4dbebea1d1e">[5469caa9dd]</a> Joshua Colp -- res_pjsip: Use hash for contact object identity instead of Contact URI.</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=a676ba2aad5525926ae31b8317b95ae52cbbabbb">[a676ba2aad]</a> Joshua Colp -- taskprocessor: Fix race condition between unreferencing and finding.</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25381">ASTERISK-25381</a>: res_pjsip: AoRs deleted via ARI (or other mechanism) do not destroy their related contacts<br/>Reported by: Matt Jordan<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=c3e6debdb95a5895894ed2b58b600fcdf17927b9">[c3e6debdb9]</a> Matt Jordan -- res/res_pjsip: Purge contacts when an AoR is deleted</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25339">ASTERISK-25339</a>: res_pjsip: Empty "auth" sections from non-config backgrounds are interpreted as valid<br/>Reported by: Matt Jordan<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=bc6fe07f5c114bdeaef4a3b83a11faaa9d1046eb">[bc6fe07f5c]</a> Matt Jordan -- res_pjsip/pjsip_configuration: Disregard empty auth values</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25304">ASTERISK-25304</a>: res_pjsip: XML sanitization may write past buffer<br/>Reported by: Joshua Colp<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=8521a86367ac6090210a89878c8fee6d19c43642">[8521a86367]</a> Joshua Colp -- res_pjsip: Ensure sanitized XML is NULL terminated.</li>
-</ul><br><h4>Category: Resources/res_pjsip_nat</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25387">ASTERISK-25387</a>: res_pjsip_nat: Malformed REGISTER request causes NAT'd Contact header to not be rewritten<br/>Reported by: Matt Jordan<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=1dd0e220bf98ca93b825d7b5af4160f7718eab38">[1dd0e220bf]</a> Matt Jordan -- res/res_pjsip_nat: Ignore REGISTER requests when looking for a Record-Route</li>
-</ul><br><h4>Category: Resources/res_pjsip_pubsub</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25306">ASTERISK-25306</a>: Persistent subscriptions can save multiple SIP messages at once, leading to potential crashes.<br/>Reported by: Mark Michelson<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=c126afe18f9073f3ee74e45f574da421131b9fa2">[c126afe18f]</a> Richard Mudgett -- res_pjsip.c: Fix crash from corrupt saved SUBSCRIBE message.</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=e25569ef95c6de6e9267df4673bd1d774b82a000">[e25569ef95]</a> Mark Michelson -- res_pjsip_pubsub: More accurately persist packet.</li>
-</ul><br><h4>Category: Resources/res_pjsip_sdp_rtp</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25356">ASTERISK-25356</a>: res_pjsip_sdp_rtp: Multiple keepalive scheduled items may exist<br/>Reported by: Joshua Colp<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=1b1561f4c854c37691bd24227b8f722d1dac4291">[1b1561f4c8]</a> Joshua Colp -- res_pjsip_sdp_rtp: Fix multiple keepalive scheduled items.</li>
-</ul><br><h4>Category: Resources/res_pjsip_session</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25297">ASTERISK-25297</a>: Crashes running channels/pjsip/resolver/srv/failover/in_dialog testsuite tests<br/>Reported by: Richard Mudgett<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=13eb491e35ae6a99164dec6a62d7f05784c75c11">[13eb491e35]</a> Richard Mudgett -- res_pjsip_session.c: Fix crashes seen when call cancelled.</li>
-</ul><br><h4>Category: Resources/res_rtp_asterisk</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25438">ASTERISK-25438</a>: res_rtp_asterisk: ICE role message even when ICE is not enabled<br/>Reported by: Joshua Colp<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=9913d47697716040917b49c0c60ae2d98493d516">[9913d47697]</a> Joshua Colp -- res_rtp_asterisk: Move "Set role" warning to be debug.</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25265">ASTERISK-25265</a>: [patch]DTLS Failure when calling WebRTC-peer on Firefox 39 - add ECDH support and fallback to prime256v1<br/>Reported by: Stefan Engström<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=9a12804e592b97d74ff7b909e0d0022f1ca72386">[9a12804e59]</a> Joshua Colp -- res_rtp_asterisk: Don't leak temporary key when enabling PFS.</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=aed068844c1c9748da9c67b74ea4d90622be8f46">[aed068844c]</a> Mark Duncan -- res/res_rtp_asterisk: Add ECDH support</li>
-</ul><br><h4>Category: Tests/testsuite</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25318">ASTERISK-25318</a>: tests/rest_api/applications/subscribe-endpoint/nominal/resource: Sporadically failing<br/>Reported by: Joshua Colp<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=c2c73190825bf4c9cedb1031327199767a4a3ca8">[c2c7319082]</a> Joshua Colp -- res_pjsip_session: Don't invoke session supplements twice for BYE requests.</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25292">ASTERISK-25292</a>: Testuite: tests/apps/bridge/bridge_wait/bridge_wait_e_options fails<br/>Reported by: Kevin Harwell<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=10ba72a9279c591e800ad2656d367b881f73203d">[10ba72a927]</a> Mark Michelson -- Add a test event for inband ringing.</li>
-</ul><br><h3>Improvement</h3><h4>Category: Core/General</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25310">ASTERISK-25310</a>: [patch]on FreeBSD also pthread_attr_init() defaults to PTHREAD_EXPLICIT_SCHED<br/>Reported by: Guido Falsi<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=4ed9c9a280c08a17fce602c15d2b01de199ca736">[4ed9c9a280]</a> Guido Falsi -- Core/General: Add #ifdef needed on FreeBSD.</li>
-</ul><br><h4>Category: Resources/res_ari_applications</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-24870">ASTERISK-24870</a>: ARI: Subscriptions to bridges generally not super useful<br/>Reported by: Matt Jordan<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=90165e306d958293bae47dd901e2c672dca95006">[90165e306d]</a> Matt Jordan -- res/res_stasis: Fix accidental subscription to 'all' bridge topic</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=b50e372394bf0950ebbc96793d9594de97282749">[b50e372394]</a> Matt Jordan -- ARI: Add events for Contact and Peer Status changes</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=3502c0431db52d00eb16dc1cc2462be7a509ba5e">[3502c0431d]</a> Matt Jordan -- res/res_stasis_device_state: Allow for subscribing to 'all' device state</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=4c9f613309d66ae6a8e5454cd53276459bcd2674">[4c9f613309]</a> Matt Jordan -- ARI: Add the ability to subscribe to all events</li>
-</ul><br><h4>Category: Resources/res_ari_bridges</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-24870">ASTERISK-24870</a>: ARI: Subscriptions to bridges generally not super useful<br/>Reported by: Matt Jordan<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=90165e306d958293bae47dd901e2c672dca95006">[90165e306d]</a> Matt Jordan -- res/res_stasis: Fix accidental subscription to 'all' bridge topic</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=b50e372394bf0950ebbc96793d9594de97282749">[b50e372394]</a> Matt Jordan -- ARI: Add events for Contact and Peer Status changes</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=3502c0431db52d00eb16dc1cc2462be7a509ba5e">[3502c0431d]</a> Matt Jordan -- res/res_stasis_device_state: Allow for subscribing to 'all' device state</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=4c9f613309d66ae6a8e5454cd53276459bcd2674">[4c9f613309]</a> Matt Jordan -- ARI: Add the ability to subscribe to all events</li>
-</ul><br><hr><a name="open_issues"><h2 align="center">Open Issues</h2></a><center><a href="#top">[Back to Top]</a></center><p>This is a list of all open issues from the issue tracker that were referenced by changes that went into this release.</p><h3>Bug</h3><h4>Category: Addons/chan_ooh323</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25299">ASTERISK-25299</a>: RTP port leaks with incoming OOH323 calls<br/>Reported by: Alexandr Dranchuk<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=480c443e2691272a7227e0949244e80e53bc31b2">[480c443e26]</a> Alexander Anikin -- chan_ooh323: call ast_rtp_instance_stop on ooh323_destroy</li>
-</ul><br><h4>Category: General</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25323">ASTERISK-25323</a>: Asterisk: ongoing segfaults uncovered by CHAOS_DEBUG<br/>Reported by: Scott Griepentrog<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=c94f46080f60435fffd197d14441ccf9d963521b">[c94f46080f]</a> Scott Griepentrog -- CHAOS: avoid crash if string create fails</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=4cc59533b903b3d55e8b388f28385287e712ae62">[4cc59533b9]</a> Richard Mudgett -- CHAOS: res_pjsip_diversion avoid crash if allocation fails</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=fb6b5c684b8772ba008339a417725a208f72409e">[fb6b5c684b]</a> Scott Griepentrog -- PJSIP: avoid crash when getting rtp peer</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=f72f9ceefca52002c45f5910219dbcb0f9437a79">[f72f9ceefc]</a> Scott Griepentrog -- pjsip: avoid possible crash req_caps allocation failure</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=6862c2a167f4ed2cb8511bb1ae94a13582afa25b">[6862c2a167]</a> Scott Griepentrog -- Chaos: handle failed allocation in get_media_encryption_type</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=f1cd6366588c66dce5be66541ceb7f828fde3773">[f1cd636658]</a> Scott Griepentrog -- Chaos: make hangup NULL tolerant</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=ab373f2ceffcad3a497663027199f4f4a81f644b">[ab373f2cef]</a> Scott Griepentrog -- CHAOS: prevent sorcery object with null id</li>
-</ul><br><h4>Category: Resources/res_hep_rtcp</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25352">ASTERISK-25352</a>: res_hep_rtcp correlation_id is different then res_hep<br/>Reported by: Kevin Scott Adams<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=78d0b9d97ecf034468e252440217dd4bc371ef71">[78d0b9d97e]</a> Matt Jordan -- channels/pjsip/dialplan_functions: Add an option for extracting the SIP call-id</li>
-</ul><br><h4>Category: pjproject/pjsip</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-24602">ASTERISK-24602</a>: Unable to call WebRTC client via wss on chan_pjsip<br/>Reported by: Oleg Kozlov<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=d32e516c7cd48979db092a82b97a7ac4a743f526">[d32e516c7c]</a> Martin Tomec -- res/pjsip: Mark WSS transport as secure</li>
-</ul><br><h3>Improvement</h3><hr><a name="commits"><h2 align="center">Commits Not Associated with an Issue</h2></a><center><a href="#top">[Back to Top]</a></center><p>This is a list of all changes that went into this release that did not reference a JIRA issue.</p><table width="100%" border="1">
-<tr><th>Revision</th><th>Author</th><th>Summary</th></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=cc0eff5651e792d9f97a433fbbac99992c20cb46">cc0eff5651</a></td><td>Kevin Harwell</td><td>Release summaries: Remove previous versions</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=8cd191b885c5410faa11e28b52d37fea9d5c5197">8cd191b885</a></td><td>Kevin Harwell</td><td>.version: Update for 13.6.0</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=a3777c24fd93fe757ce8c68daf9c2749938eea8f">a3777c24fd</a></td><td>Kevin Harwell</td><td>.lastclean: Update for 13.6.0</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=68121cef21b3753a28248b493d211a6d3176e67a">68121cef21</a></td><td>Kevin Harwell</td><td>realtime: Add database scripts for 13.6.0</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=d72dab4f402c06b304e339f4e8fd801b502cb003">d72dab4f40</a></td><td>Kevin Harwell</td><td>ChangeLog: Updated for 13.6.0-rc3</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=9da83dbd1524aa1c74907066ec6480e9d56651f9">9da83dbd15</a></td><td>Kevin Harwell</td><td>Release summaries: Add summaries for 13.6.0-rc3</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=8c60f9326ce609061316f4bc3b1522caa7282482">8c60f9326c</a></td><td>Kevin Harwell</td><td>Release summaries: Remove previous versions</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=316d47755b0d6239cb5bbd674d7596046ad4cddd">316d47755b</a></td><td>Kevin Harwell</td><td>.version: Update for 13.6.0-rc3</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=74a86d0a72c9858dc965e8e2b59ae4a1e0294033">74a86d0a72</a></td><td>Kevin Harwell</td><td>.lastclean: Update for 13.6.0-rc3</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=4c39bea6f00560dd6bc52d9d793e1ae7ebf130b7">4c39bea6f0</a></td><td>Kevin Harwell</td><td>realtime: Add database scripts for 13.6.0-rc3</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=c3521e9469f95b962e52edbbb6e0cdef6d3ceab4">c3521e9469</a></td><td>Kevin Harwell</td><td>ChangeLog: Updated for 13.6.0-rc2</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=a44f6aa046c988f3c1ce9defba5038f83255a4c4">a44f6aa046</a></td><td>Kevin Harwell</td><td>Release summaries: Add summaries for 13.6.0-rc2</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=dd74af7e460083df8ca774db2793791ff6413c6e">dd74af7e46</a></td><td>Kevin Harwell</td><td>Release summaries: Remove previous versions</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=a11a78ca34fae7ef2069d0a9ae6a5cf1fd2856d7">a11a78ca34</a></td><td>Kevin Harwell</td><td>.version: Update for 13.6.0-rc2</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=570329ec8a05c27675721734e2da3c42c707f0f7">570329ec8a</a></td><td>Kevin Harwell</td><td>.lastclean: Update for 13.6.0-rc2</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=51c9ff47f6bab1aed30f95a8601e0ddf253b657e">51c9ff47f6</a></td><td>Kevin Harwell</td><td>realtime: Add database scripts for 13.6.0-rc2</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=a0fb436eda914dca26e96d304d3c9daca2be54de">a0fb436eda</a></td><td>Kevin Harwell</td><td>ChangeLog: Updated for 13.6.0-rc1</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=bba1c4066be4d752f106266a7c084ba2340c2777">bba1c4066b</a></td><td>Kevin Harwell</td><td>Release summaries: Add summaries for 13.6.0-rc1</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=82c4aecdbb4466843a41e0591ed0e7c16c1902a6">82c4aecdbb</a></td><td>Kevin Harwell</td><td>.version: Update for 13.6.0-rc1</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=bc18db73884513e5636fc4af8d1b8bf2ba355c3a">bc18db7388</a></td><td>Kevin Harwell</td><td>.lastclean: Update for 13.6.0-rc1</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=b9c53f95e38e7e9e47c9843bcbb0e8836cafed1b">b9c53f95e3</a></td><td>Kevin Harwell</td><td>realtime: Add database scripts for 13.6.0-rc1</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=d30939b6e84f18eb1fa3eb9819951fe8a1c764f4">d30939b6e8</a></td><td>Kevin Harwell</td><td>ARI: Changed version from 1.8.0 to 1.9.0</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=5f19c9baded56a5adb419a7bcb1ac00fbe09f404">5f19c9bade</a></td><td>Richard Mudgett</td><td>res/ari/config.c: Fix user sort compare function.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=3a8576403968bd1f49c8bef67735c04d05fb6983">3a85764039</a></td><td>Richard Mudgett</td><td>res/ari/config.c: Optimize conf_alloc() object init.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=bbeda190c3f05e95a82c7d9609c66dcf3ce35bd3">bbeda190c3</a></td><td>Richard Mudgett</td><td>app_dial.c: Remove some no-op code.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=fe5077b1f8caed2df419e1bd7b872657b7def726">fe5077b1f8</a></td><td>Mark Michelson</td><td>res_pjsip_pubsub: Eliminate race during initial NOTIFY.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=5c713fdf18ffa934e0cac8ddb29e4ad95a68200b">5c713fdf18</a></td><td>Mark Michelson</td><td>scheduler: Use queue for allocating sched IDs.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=e75aff53e6ee68833595db101e43329adf9a4459">e75aff53e6</a></td><td>Richard Mudgett</td><td>res_pjsip_pubsub.c: Mark ast_sip_create_subscription() as not used.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=4d91d01df180a712485aa60d14fda2aa9e5063d2">4d91d01df1</a></td><td>Richard Mudgett</td><td>res_pjsip_pubsub.c: Add some notification comments.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=f36a9d122171781dd8a99d32792a0c19103b1f15">f36a9d1221</a></td><td>Richard Mudgett</td><td>res_pjsip_pubsub.c: Set dlg_status code instead of sending SIP response.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=94582f8fabb926d197ce0f3a01208b385975ec09">94582f8fab</a></td><td>Richard Mudgett</td><td>res_pjsip_pubsub.c: Fix off-nominal memory leak.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=8b3ed52239b24546b1ee12156dadccb70db7403e">8b3ed52239</a></td><td>Richard Mudgett</td><td>res_pjsip_pubsub.c: Fix one byte buffer overrun error.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=4329bd1e4c059e714122465901ea2c46dd924b71">4329bd1e4c</a></td><td>Richard Mudgett</td><td>res_pjsip_pubsub.c: Use ast_alloca() instead of alloca().</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=a456a20ecf835bcf70ebc2a279e230df402bec08">a456a20ecf</a></td><td>Richard Mudgett</td><td>res_pjsip_pubsub.c: Add missing error return in load_module().</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=f58f4c6e2762e5ad7eccf7065e63b345f4cda7f6">f58f4c6e27</a></td><td>Richard Mudgett</td><td>res_pjsip/location.c: Use the builtin ao2_callback() match function instead.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=4eedd9ef9d7c000cd8d67cbeb1789ac6d71860aa">4eedd9ef9d</a></td><td>Matt Jordan</td><td>main/config_options: Check for existance of internal object before derefing</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=695f26cbb759f8ecc18c6b1d6cf84b3105b2f007">695f26cbb7</a></td><td>David M. Lee</td><td>res_rtp_asterisk: Add more ICE debugging</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=61c6c6aa6c60636207567a49c6320946c1840e99">61c6c6aa6c</a></td><td>David M. Lee</td><td>Fix when remote candidates exceed PJ_ICE_MAX_CAND</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=ad9cb6c2ce6dbe9c985c6891daf53cc4160e3a13">ad9cb6c2ce</a></td><td>Mark Michelson</td><td>res_pjsip: Fix contact refleak on stateful responses.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=7c4d0c3506374b89502bd6c1bda89c3f241b6708">7c4d0c3506</a></td><td>Joshua Colp</td><td>res_pjsip_pubsub: On recreated notify fail deleted sub_tree is referenced</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=0582776f7fe55e72acda586fb32185ad7879aeab">0582776f7f</a></td><td>Richard Mudgett</td><td>ari/ari_websockets.c: Fix ast_debug parameter type mismatch.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=77518d54344945d7d3bfc1ebfe61d97704fa5dfa">77518d5434</a></td><td>Richard Mudgett</td><td>res_http_websocket.c: Fix some off nominal path cleanup.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=c61547fee6fdc1e132d359311da48e87d98d25b1">c61547fee6</a></td><td>Richard Mudgett</td><td>res_ari.c: Add missing off nominal unlock and remove a RAII_VAR().</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=bd867cd0787b124984caf2604478212651ea4c03">bd867cd078</a></td><td>Richard Mudgett</td><td>app_queue.c: Extract some functions for simpler code.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=ded51e3d77a13eed059a85a083d0ab0324a77db7">ded51e3d77</a></td><td>Richard Mudgett</td><td>app_queue.c: Fix error checking in QUEUE_MEMBER() read.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=b719f56c72c9cc66879eeef11de2ef4498cba648">b719f56c72</a></td><td>Mark Michelson</td><td>res_pjsip_sdp_rtp: Restore removed NULL check.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=cea5dc7b8afd0e8cbde4c5d253bac3219125b168">cea5dc7b8a</a></td><td>Richard Mudgett</td><td>audiohook.c: Simplify variable usage in audiohook_read_frame_both().</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=e18c300550453df06507e32c4bc78ef91d369f27">e18c300550</a></td><td>Joshua Colp</td><td>res_http_websocket: When shutting down a session don't close closed socket</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=8e194047acb3ba846356041c1a6222caefc65a2e">8e194047ac</a></td><td>Matt Jordan</td><td>res/res_format_attr_silk: Expose format attributes to other modules</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=a0f451c35ed56b08353c4c3150bf847867f74fe7">a0f451c35e</a></td><td>Matt Jordan</td><td>main/format: Add an API call for retrieving format attributes</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=26f0559a94519b735396ed4ce90e23c1d9d5b332">26f0559a94</a></td><td>David M. Lee</td><td>Replace htobe64 with htonll</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=27dc2094e98b59b8a50b059ddd6048285a42e6b9">27dc2094e9</a></td><td>Mark Michelson</td><td>res_http_websocket: Debug write lengths.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=39cc28f6ea2140ad6d561fd4c9e9a66f065cecee">39cc28f6ea</a></td><td>Mark Michelson</td><td>res_http_websocket: Avoid passing strlen() to ast_websocket_write().</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=1519eb44a796911c7c438bbe4e31bb89be244387">1519eb44a7</a></td><td>Richard Mudgett</td><td>rtp_engine.c: Must protect mime_types_len with mime_types_lock.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=a93b7a927c5975c0a8889dc66868f81e4eef8aa3">a93b7a927c</a></td><td>Richard Mudgett</td><td>res_pjsip_sdp_rtp.c: Fix processing wrong SDP media list.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=741fa0d26d38f7a9ed98c595ee1bb6b6ce8a9923">741fa0d26d</a></td><td>Richard Mudgett</td><td>res_pjsip_sdp_rtp.c: Fixup some whitespace.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=89b21fd9a38bcd89402249440c1670ce48781f30">89b21fd9a3</a></td><td>Richard Mudgett</td><td>rtp_engine.h: No sense allowing payload types larger than RFC allows.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=7427c7f13b1d0c9095e83a3ea38394f521d3a75e">7427c7f13b</a></td><td>Richard Mudgett</td><td>rtp_engine.c: Minor tweaks.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=e20f435b6093c2c72e678b9fad1ed037c3191b88">e20f435b60</a></td><td>Richard Mudgett</td><td>rtp_engine.h: Misc comment fixes.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=bc5d7f9c37fe8a4ff5744ab8620898ccae6a7d2a">bc5d7f9c37</a></td><td>Richard Mudgett</td><td>chan_sip.c: Tweak glue->update_peer() parameter nil value.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=48698a5e21d7307f61b5fb2bd39fd593bc1423ca">48698a5e21</a></td><td>Mark Michelson</td><td>res_http_websocket: Properly encode 64 bit payload</td></tr>
-</table><hr><a name="diffstat"><h2 align="center">Diffstat Results</h2></a><center><a href="#top">[Back to Top]</a></center><p>This is a summary of the changes to the source code that went into this release that was generated using the diffstat utility.</p><pre>asterisk-13.5.0-summary.html                                                  |  441 --
-asterisk-13.5.0-summary.txt                                                   | 1141 ------
-b/.version                                                                    |    2
-b/CHANGES                                                                     |   35
-b/ChangeLog                                                                   | 1884 +++++++++-
-b/Makefile                                                                    |    1
-b/UPGRADE.txt                                                                 |    6
-b/addons/chan_ooh323.c                                                        |    1
-b/addons/ooh323c/src/ooq931.c                                                 |    6
-b/apps/app_dial.c                                                             |  156
-b/apps/app_page.c                                                             |   28
-b/apps/app_queue.c                                                            |  412 +-
-b/apps/app_record.c                                                           |    3
-b/bridges/bridge_holding.c                                                    |    6
-b/channels/chan_dahdi.c                                                       |   55
-b/channels/chan_pjsip.c                                                       |   18
-b/channels/chan_sip.c                                                         |   78
-b/channels/chan_skinny.c                                                      |   26
-b/channels/pjsip/dialplan_functions.c                                         |    5
-b/channels/sip/include/security_events.h                                      |    3
-b/channels/sip/security_events.c                                              |    5
-b/codecs/codec_gsm.c                                                          |   29
-b/codecs/codec_ilbc.c                                                         |   28
-b/codecs/codec_lpc10.c                                                        |   41
-b/codecs/codec_speex.c                                                        |   60
-b/configure                                                                   |   63
-b/configure.ac                                                                |    6
-b/contrib/ast-db-manage/config/versions/154177371065_add_default_from_user.py |   22
-b/contrib/realtime/mssql/mssql_config.sql                                     |    8
-b/contrib/realtime/mysql/mysql_config.sql                                     |    6
-b/contrib/realtime/oracle/oracle_config.sql                                   |    8
-b/contrib/realtime/postgresql/postgresql_config.sql                           |    6
-b/contrib/scripts/astversion                                                  |  536 ++
-b/contrib/scripts/install_prereq                                              |    2
-b/include/asterisk/autoconfig.h.in                                            |    3
-b/include/asterisk/format.h                                                   |   23
-b/include/asterisk/logger.h                                                   |   44
-b/include/asterisk/res_pjsip.h                                                |   40
-b/include/asterisk/res_pjsip_session.h                                        |    6
-b/include/asterisk/rtp_engine.h                                               |   18
-b/include/asterisk/stasis_app.h                                               |   15
-b/main/audiohook.c                                                            |   43
-b/main/bridge_channel.c                                                       |    7
-b/main/channel.c                                                              |    9
-b/main/config_options.c                                                       |    4
-b/main/dial.c                                                                 |   25
-b/main/endpoints.c                                                            |    3
-b/main/format.c                                                               |   11
-b/main/logger.c                                                               |  272 +
-b/main/pbx.c                                                                  |  146
-b/main/rtp_engine.c                                                           |  153
-b/main/sched.c                                                                |  143
-b/main/sorcery.c                                                              |    4
-b/main/stasis_endpoints.c                                                     |   78
-b/main/taskprocessor.c                                                        |   12
-b/main/translate.c                                                            |   55
-b/main/utils.c                                                                |    4
-b/res/ari/ari_model_validators.c                                              |  445 ++
-b/res/ari/ari_model_validators.h                                              |  118
-b/res/ari/ari_websockets.c                                                    |    9
-b/res/ari/config.c                                                            |   72
-b/res/ari/resource_asterisk.c                                                 |  127
-b/res/ari/resource_asterisk.h                                                 |   63
-b/res/ari/resource_events.c                                                   |   24
-b/res/ari/resource_events.h                                                   |    2
-b/res/parking/parking_applications.c                                          |   65
-b/res/res_ari.c                                                               |   19
-b/res/res_ari_asterisk.c                                                      |  300 +
-b/res/res_ari_events.c                                                        |    6
-b/res/res_config_sqlite.c                                                     |    8
-b/res/res_format_attr_silk.c                                                  |   24
-b/res/res_http_websocket.c                                                    |   80
-b/res/res_pjsip.c                                                             |  131
-b/res/res_pjsip/config_global.c                                               |   18
-b/res/res_pjsip/location.c                                                    |   51
-b/res/res_pjsip/pjsip_configuration.c                                         |    4
-b/res/res_pjsip/presence_xml.c                                                |   31
-b/res/res_pjsip_diversion.c                                                   |    4
-b/res/res_pjsip_multihomed.c                                                  |   23
-b/res/res_pjsip_nat.c                                                         |    2
-b/res/res_pjsip_pidf_digium_body_supplement.c                                 |    2
-b/res/res_pjsip_pubsub.c                                                      |   80
-b/res/res_pjsip_sdp_rtp.c                                                     |   70
-b/res/res_pjsip_session.c                                                     |   37
-b/res/res_pjsip_t38.c                                                         |   15
-b/res/res_pjsip_transport_websocket.c                                         |    5
-b/res/res_rtp_asterisk.c                                                      |   79
-b/res/res_stasis.c                                                            |   45
-b/res/res_stasis_device_state.c                                               |   54
-b/res/stasis/app.c                                                            |  335 +
-b/res/stasis/app.h                                                            |   15
-b/res/stasis/messaging.c                                                      |   44
-b/rest-api-templates/ari_model_validators.c.mustache                          |    2
-b/rest-api/api-docs/applications.json                                         |    2
-b/rest-api/api-docs/asterisk.json                                             |  130
-b/rest-api/api-docs/bridges.json                                              |    2
-b/rest-api/api-docs/channels.json                                             |    2
-b/rest-api/api-docs/deviceStates.json                                         |    2
-b/rest-api/api-docs/endpoints.json                                            |    2
-b/rest-api/api-docs/events.json                                               |  109
-b/rest-api/api-docs/mailboxes.json                                            |    2
-b/rest-api/api-docs/playbacks.json                                            |    2
-b/rest-api/api-docs/recordings.json                                           |    2
-b/rest-api/api-docs/sounds.json                                               |    2
-b/rest-api/resources.json                                                     |    2
-b/tests/test_core_format.c                                                    |   57
-106 files changed, 6435 insertions(+), 2535 deletions(-)</pre><br></html>
\ No newline at end of file
diff --git a/asterisk-13.6.0-summary.txt b/asterisk-13.6.0-summary.txt
deleted file mode 100644
index c9c5590..0000000
--- a/asterisk-13.6.0-summary.txt
+++ /dev/null
@@ -1,839 +0,0 @@
-                                Release Summary
-
-                                asterisk-13.6.0
-
-                                Date: 2015-10-09
-
-                           <asteriskteam at digium.com>
-
-     ----------------------------------------------------------------------
-
-                               Table of Contents
-
-    1. Summary
-    2. Contributors
-    3. Closed Issues
-    4. Open Issues
-    5. Other Changes
-    6. Diffstat
-
-     ----------------------------------------------------------------------
-
-                                    Summary
-
-                                 [Back to Top]
-
-   This release is a point release of an existing major version. The changes
-   included were made to address problems that have been identified in this
-   release series, or are minor, backwards compatible new features or
-   improvements. Users should be able to safely upgrade to this version if
-   this release series is already in use. Users considering upgrading from a
-   previous version are strongly encouraged to review the UPGRADE.txt
-   document as well as the CHANGES document for information about upgrading
-   to this release series.
-
-   The data in this summary reflects changes that have been made since the
-   previous release, asterisk-13.5.0.
-
-     ----------------------------------------------------------------------
-
-                                  Contributors
-
-                                 [Back to Top]
-
-   This table lists the people who have submitted code, those that have
-   tested patches, as well as those that reported issues on the issue tracker
-   that were resolved in this release. For coders, the number is how many of
-   their patches (of any size) were committed into this release. For testers,
-   the number is the number of times their name was listed as assisting with
-   testing a patch. Finally, for reporters, the number is the number of
-   issues that they reported that were affected by commits that went into
-   this release.
-
-   Coders                       Testers          Reporters                    
-   40 Richard Mudgett           1 Elazar Broad   13 Matt Jordan               
-   18 Kevin Harwell                              11 Joshua Colp               
-   16 Joshua Colp                                11 Richard Mudgett           
-   15 Mark Michelson                             10 Scott Griepentrog         
-   13 Matt Jordan                                8 Mark Michelson             
-   9 Scott Griepentrog                           8 Mark Michelson             
-   8 Kevin Harwell                               7 John Hardin                
-   3 Scott Emidy                                 5 Kevin Harwell              
-   3 David M. Lee                                4 Scott Griepentrog          
-   2 Alexander Traud                             3 Richard Mudgett            
-   2 Alexander Anikin                            2 Kevin Harwell              
-   2 Jonathan Rose                               2 Alexander Traud            
-   1 Martin Tomec                                2 Alexandr Dranchuk          
-   1 Elazar Broad                                2 Dmitriy Serov              
-   1 Walter Doekes                               2 Stefan EngstrAP:m          
-   1 Rodrigo RamArez Norambuena                  1 Oleg Kozlov                
-   1 Mark Duncan                                 1 Walter Doekes              
-   1 Benjamin Ford                               1 Etienne Lessard            
-   1 Guido Falsi                                 1 Lorne Gaetz                
-                                                 1 Chet Stevens               
-                                                 1 Rodrigo Ramirez Norambuena 
-                                                 1 Ashley Sanders             
-                                                 1 Guido Falsi                
-                                                 1 Lorne Gaetz                
-                                                 1 Chet Stevens               
-                                                 1 Kevin Scott Adams          
-                                                 1 Elazar Broad               
-                                                 1 Sean Pimental              
-                                                 1 Jonathan Rose              
-                                                 1 Etienne Lessard            
-                                                 1 Alexandr Dranchuk          
-                                                 1 yaron nahum                
-                                                 1 Elazar Broad               
-                                                 1 yaron nahum                
-                                                 1 Guido Falsi                
-
-     ----------------------------------------------------------------------
-
-                                 Closed Issues
-
-                                 [Back to Top]
-
-   This is a list of all issues from the issue tracker that were closed by
-   changes that went into this release.
-
-  New Feature
-
-    Category: Resources/res_ari
-
-   ASTERISK-25252: ARI: Add the ability to manipulate log channels
-   Reported by: Matt Jordan
-     * [df9ce36366] Scott Emidy -- ARI: Retrieve existing log channels
-     * [e9f1bc08cb] Scott Emidy -- ARI: Creating log channels
-     * [78364132ce] Scott Emidy -- ARI: Deleting log channels
-     * [1ae762634c] Benjamin Ford -- ARI: Rotate log channels.
-
-    Category: Resources/res_pjsip
-
-   ASTERISK-25377: res_pjsip: Change default "From user" from UUID to
-   something more palatable
-   Reported by: Mark Michelson
-     * [ac62928d6b] Mark Michelson -- res_pjsip: Change default from user
-       value.
-
-  Bug
-
-    Category: Addons/chan_ooh323
-
-   ASTERISK-25227: No audio at in-band announcements in ooh323 channel
-   Reported by: Alexandr Dranchuk
-     * [71408df2b8] Alexander Anikin -- chan_ooh323: Add ProgressIndicator IE
-       with inband info available
-
-    Category: Applications/app_dial
-
-   ASTERISK-25423: Caller gets no Connected line update during call pickup.
-   Reported by: Richard Mudgett
-     * [6b1e7583c1] Richard Mudgett -- app_queue.c: Force COLP update if
-       outgoing channel name changed.
-     * [6bf304bf25] Richard Mudgett -- app_queue.c: Factor out a connected
-       line update routine.
-     * [e36b5f1e8e] Richard Mudgett -- app_dial.c: Make 'A' option pass COLP
-       updates.
-     * [747bfac895] Richard Mudgett -- app_dial.c: Force COLP update if
-       outgoing channel name changed.
-     * [14481d9aa0] Richard Mudgett -- app_dial.c: Factor out a connected
-       line update routine.
-
-    Category: Applications/app_mixmonitor
-
-   ASTERISK-25322: Crash occurs when using MixMonitor with t() or r()
-   options.
-   Reported by: Richard Mudgett
-     * [b3a56bee83] Richard Mudgett -- audiohook.c: Fix MixMonitor crash when
-       using the r() or t() options.
-
-    Category: Applications/app_page
-
-   ASTERISK-25384: Regular Asterisk crashes when using Page application.
-   "user_data is NULL"
-   Reported by: Chet Stevens
-     * [5f15cd93f0] Richard Mudgett -- app_page.c: Fix crash when forwarding
-       with a predial handler.
-
-    Category: Applications/app_queue
-
-   ASTERISK-25423: Caller gets no Connected line update during call pickup.
-   Reported by: Richard Mudgett
-     * [6b1e7583c1] Richard Mudgett -- app_queue.c: Force COLP update if
-       outgoing channel name changed.
-     * [6bf304bf25] Richard Mudgett -- app_queue.c: Factor out a connected
-       line update routine.
-     * [e36b5f1e8e] Richard Mudgett -- app_dial.c: Make 'A' option pass COLP
-       updates.
-     * [747bfac895] Richard Mudgett -- app_dial.c: Force COLP update if
-       outgoing channel name changed.
-     * [14481d9aa0] Richard Mudgett -- app_dial.c: Factor out a connected
-       line update routine.
-   ASTERISK-25399: app_queue: AgentComplete event has wrong reason
-   Reported by: Kevin Harwell
-     * [4fb95bbc4e] Kevin Harwell -- app_queue: AgentComplete event has wrong
-       reason
-   ASTERISK-25185: Segfault in app_queue on transfer scenarios
-   Reported by: Etienne Lessard
-     * [6409e7b11a] Kevin Harwell -- app_queue: Crash when transferring
-   ASTERISK-25215: Differences in queue.log between Set QUEUE_MEMBER and
-   using PauseQueueMember
-   Reported by: Lorne Gaetz
-     * [e5f5b9f384] Richard Mudgett -- app_queue.c: Fix setting QUEUE_MEMBER
-       'paused' and 'ringinuse'.
-
-    Category: Applications/app_record
-
-   ASTERISK-25410: app_record: RECORDED_FILE variable not being populated
-   Reported by: Kevin Harwell
-     * [aeddee39fb] Kevin Harwell -- app_record: RECORDED_FILE variable not
-       being populated
-
-    Category: Bridges/bridge_holding
-
-   ASTERISK-25271: Parking & blind transfer: Transferer channel not hung up
-   if no MOH
-   Reported by: Kevin Harwell
-     * [8458b8d441] Jonathan Rose -- holding_bridge: ensure moh participants
-       get frames
-
-    Category: Channels/chan_dahdi
-
-   ASTERISK-25315: DAHDI channels send shortened duration DTMF tones.
-   Reported by: Richard Mudgett
-     * [256bc52b66] Richard Mudgett -- chan_dahdi.c: Flush the DAHDI write
-       buffer after starting DTMF.
-     * [800e0ea48d] Richard Mudgett -- chan_dahdi.c: Lock private struct for
-       ast_write().
-
-    Category: Channels/chan_sip/CodecHandling
-
-   ASTERISK-25309: [patch] iLBC 20 advertised
-   Reported by: Alexander Traud
-     * [f68c995bc9] Alexander Traud -- chan_sip: Fix negotiation of iLBC 30.
-
-    Category: Channels/chan_sip/General
-
-   ASTERISK-25346: chan_sip: Overwriting answered elsewhere hangup cause on
-   call pickup
-   Reported by: Joshua Colp
-     * [c01111223f] Joshua Colp -- chan_sip: Allow call pickup to set the
-       hangup cause.
-
-    Category: Channels/chan_sip/Interoperability
-
-   ASTERISK-25396: chan_sip: Extremely long callerid name causes invalid SIP
-   Reported by: Walter Doekes
-     * [b59c4d82b5] Walter Doekes -- chan_sip: Fix From header truncation for
-       extremely long CALLERID(name).
-
-    Category: Channels/chan_sip/Security Framework
-
-   ASTERISK-25320: chan_sip.c: sip_report_security_event searches for wrong
-   or non existent peer on invite
-   Reported by: Kevin Harwell
-     * [25af2d71c8] Kevin Harwell -- chan_sip.c: wrong peer searched in
-       sip_report_security_event
-
-    Category: Channels/chan_skinny
-
-   ASTERISK-25296: RTP performance issue with several channel drivers.
-   Reported by: Richard Mudgett
-     * [aeeb170fc4] Richard Mudgett -- rtp_engine.c: Fix performance issue
-       with several channel drivers that use RTP.
-     * [84262749d2] Richard Mudgett -- res_rtp_asterisk.c: Fix off-nominal
-       crash potential.
-
-    Category: Channels/chan_unistim
-
-   ASTERISK-25296: RTP performance issue with several channel drivers.
-   Reported by: Richard Mudgett
-     * [aeeb170fc4] Richard Mudgett -- rtp_engine.c: Fix performance issue
-       with several channel drivers that use RTP.
-     * [84262749d2] Richard Mudgett -- res_rtp_asterisk.c: Fix off-nominal
-       crash potential.
-
-    Category: Codecs/General
-
-   ASTERISK-25353: [patch] Transcoding while different in Frame size = Frames
-   lost
-   Reported by: Alexander Traud
-     * [b88c54fa4b] Alexander Traud -- translate: Fix transcoding while
-       different in frame size.
-
-    Category: Core/Bridging
-
-   ASTERISK-25341: bridge: Hangups may get lost when executing actions
-   Reported by: Joshua Colp
-     * [6c2dab1e88] Joshua Colp -- bridge: Kick channel from bridge if hung
-       up during action.
-
-    Category: Core/BuildSystem
-
-   ASTERISK-25383: Core dumps on startup and shutdown with MALLOC_DEBUG
-   enabled
-   Reported by: yaron nahum
-     * [028033e5a8] Richard Mudgett -- res/ari/config.c: Fix conf_alloc()
-       object init.
-   ASTERISK-25265: [patch]DTLS Failure when calling WebRTC-peer on Firefox 39
-   - add ECDH support and fallback to prime256v1
-   Reported by: Stefan EngstrAP:m
-     * [9a12804e59] Joshua Colp -- res_rtp_asterisk: Don't leak temporary key
-       when enabling PFS.
-     * [aed068844c] Mark Duncan -- res/res_rtp_asterisk: Add ECDH support
-
-    Category: Core/General
-
-   ASTERISK-25449: main/sched: Regression introduced by 5c713fdf18f causes
-   erroneous duplicate RTCP messages; other potential scheduling issues in
-   chan_sip/chan_skinny
-   Reported by: Matt Jordan
-     * [10e790f81a] Matt Jordan -- res/res_rtp_asterisk: Fix assignment after
-       ao2 decrement
-     * [754daeca0a] Matt Jordan -- Fix improper usage of scheduler exposed by
-       5c713fdf18f
-   ASTERISK-25383: Core dumps on startup and shutdown with MALLOC_DEBUG
-   enabled
-   Reported by: yaron nahum
-     * [028033e5a8] Richard Mudgett -- res/ari/config.c: Fix conf_alloc()
-       object init.
-   ASTERISK-25418: On-hold channels redirected out of a bridge appear to
-   still be on hold
-   Reported by: Mark Michelson
-     * [629458d349] Mark Michelson -- Do not swallow frames on channels
-       leaving bridges.
-   ASTERISK-25355: sched: ast_sched_del may return prematurely due to
-   spurious wakeup
-   Reported by: Joshua Colp
-     * [85e1cb51b2] Joshua Colp -- sched: ast_sched_del may return
-       prematurely due to spurious wakeup
-
-    Category: Core/Logging
-
-   ASTERISK-25305: Dynamic logger channels can be added multiple times
-   Reported by: Mark Michelson
-     * [f050fa76eb] Mark Michelson -- logger: Prevent duplicate dynamic
-       channels from being added.
-   ASTERISK-25407: Asterisk fails to log to multiple syslog destinations
-   Reported by: Elazar Broad
-     * [ec514ad64d] Elazar Broad -- core/logging: Fix logging to more than
-       one syslog channel
-
-    Category: Core/PBX
-
-   ASTERISK-25394: pbx: Incorrect device and presence state when changing
-   hint details
-   Reported by: Joshua Colp
-     * [2bd27d1222] Joshua Colp -- pbx: Update device and presence state when
-       changing a hint extension.
-   ASTERISK-25367: pbx: Long pattern match hints may cause "core show hints"
-   to crash
-   Reported by: Joshua Colp
-     * [cc1363209e] Joshua Colp -- pbx: Fix crash when issuing "core show
-       hints" with long pattern match.
-   ASTERISK-25362: Deadlock due to presence state callback
-   Reported by: Mark Michelson
-     * [03fe79f29e] Mark Michelson -- Fix deadlock on presence state changes.
-
-    Category: Core/RTP
-
-   ASTERISK-25296: RTP performance issue with several channel drivers.
-   Reported by: Richard Mudgett
-     * [aeeb170fc4] Richard Mudgett -- rtp_engine.c: Fix performance issue
-       with several channel drivers that use RTP.
-     * [84262749d2] Richard Mudgett -- res_rtp_asterisk.c: Fix off-nominal
-       crash potential.
-
-    Category: Resources/res_ari
-
-   ASTERISK-25325: ARI PUT reload chan_sip HTTP response 404
-   Reported by: Rodrigo Ramirez Norambuena
-     * [865377fc38] Rodrigo RamArez Norambuena -- chan_sip.c: Validation on
-       module reload
-
-    Category: Resources/res_http_websocket
-
-   ASTERISK-25312: res_http_websocket: Terminate connection on fatal cases
-   Reported by: Joshua Colp
-     * [b4e9416138] Joshua Colp -- res_http_websocket: Forcefully terminate
-       on write errors.
-
-    Category: Resources/res_parking
-
-   ASTERISK-25369: res_parking: ParkAndAnnounce - Inheritable variables
-   aren't applied to the announcer channel
-   Reported by: Jonathan Rose
-     * [fbf720db91] Jonathan Rose -- ParkAndAnnounce: Add variable
-       inheritance
-
-    Category: Resources/res_pjsip
-
-   ASTERISK-25295: res_pjsip crash - pjsip_uri_get_uri at
-   /usr/include/pjsip/sip_uri.h
-   Reported by: Dmitriy Serov
-     * [5469caa9dd] Joshua Colp -- res_pjsip: Use hash for contact object
-       identity instead of Contact URI.
-     * [a676ba2aad] Joshua Colp -- taskprocessor: Fix race condition between
-       unreferencing and finding.
-   ASTERISK-25381: res_pjsip: AoRs deleted via ARI (or other mechanism) do
-   not destroy their related contacts
-   Reported by: Matt Jordan
-     * [c3e6debdb9] Matt Jordan -- res/res_pjsip: Purge contacts when an AoR
-       is deleted
-   ASTERISK-25339: res_pjsip: Empty "auth" sections from non-config
-   backgrounds are interpreted as valid
-   Reported by: Matt Jordan
-     * [bc6fe07f5c] Matt Jordan -- res_pjsip/pjsip_configuration: Disregard
-       empty auth values
-   ASTERISK-25304: res_pjsip: XML sanitization may write past buffer
-   Reported by: Joshua Colp
-     * [8521a86367] Joshua Colp -- res_pjsip: Ensure sanitized XML is NULL
-       terminated.
-
-    Category: Resources/res_pjsip_nat
-
-   ASTERISK-25387: res_pjsip_nat: Malformed REGISTER request causes NAT'd
-   Contact header to not be rewritten
-   Reported by: Matt Jordan
-     * [1dd0e220bf] Matt Jordan -- res/res_pjsip_nat: Ignore REGISTER
-       requests when looking for a Record-Route
-
-    Category: Resources/res_pjsip_pubsub
-
-   ASTERISK-25306: Persistent subscriptions can save multiple SIP messages at
-   once, leading to potential crashes.
-   Reported by: Mark Michelson
-     * [c126afe18f] Richard Mudgett -- res_pjsip.c: Fix crash from corrupt
-       saved SUBSCRIBE message.
-     * [e25569ef95] Mark Michelson -- res_pjsip_pubsub: More accurately
-       persist packet.
-
-    Category: Resources/res_pjsip_sdp_rtp
-
-   ASTERISK-25356: res_pjsip_sdp_rtp: Multiple keepalive scheduled items may
-   exist
-   Reported by: Joshua Colp
-     * [1b1561f4c8] Joshua Colp -- res_pjsip_sdp_rtp: Fix multiple keepalive
-       scheduled items.
-
-    Category: Resources/res_pjsip_session
-
-   ASTERISK-25297: Crashes running
-   channels/pjsip/resolver/srv/failover/in_dialog testsuite tests
-   Reported by: Richard Mudgett
-     * [13eb491e35] Richard Mudgett -- res_pjsip_session.c: Fix crashes seen
-       when call cancelled.
-
-    Category: Resources/res_rtp_asterisk
-
-   ASTERISK-25438: res_rtp_asterisk: ICE role message even when ICE is not
-   enabled
-   Reported by: Joshua Colp
-     * [9913d47697] Joshua Colp -- res_rtp_asterisk: Move "Set role" warning
-       to be debug.
-   ASTERISK-25265: [patch]DTLS Failure when calling WebRTC-peer on Firefox 39
-   - add ECDH support and fallback to prime256v1
-   Reported by: Stefan EngstrAP:m
-     * [9a12804e59] Joshua Colp -- res_rtp_asterisk: Don't leak temporary key
-       when enabling PFS.
-     * [aed068844c] Mark Duncan -- res/res_rtp_asterisk: Add ECDH support
-
-    Category: Tests/testsuite
-
-   ASTERISK-25318:
-   tests/rest_api/applications/subscribe-endpoint/nominal/resource:
-   Sporadically failing
-   Reported by: Joshua Colp
-     * [c2c7319082] Joshua Colp -- res_pjsip_session: Don't invoke session
-       supplements twice for BYE requests.
-   ASTERISK-25292: Testuite:
-   tests/apps/bridge/bridge_wait/bridge_wait_e_options fails
-   Reported by: Kevin Harwell
-     * [10ba72a927] Mark Michelson -- Add a test event for inband ringing.
-
-  Improvement
-
-    Category: Core/General
-
-   ASTERISK-25310: [patch]on FreeBSD also pthread_attr_init() defaults to
-   PTHREAD_EXPLICIT_SCHED
-   Reported by: Guido Falsi
-     * [4ed9c9a280] Guido Falsi -- Core/General: Add #ifdef needed on
-       FreeBSD.
-
-    Category: Resources/res_ari_applications
-
-   ASTERISK-24870: ARI: Subscriptions to bridges generally not super useful
-   Reported by: Matt Jordan
-     * [90165e306d] Matt Jordan -- res/res_stasis: Fix accidental
-       subscription to 'all' bridge topic
-     * [b50e372394] Matt Jordan -- ARI: Add events for Contact and Peer
-       Status changes
-     * [3502c0431d] Matt Jordan -- res/res_stasis_device_state: Allow for
-       subscribing to 'all' device state
-     * [4c9f613309] Matt Jordan -- ARI: Add the ability to subscribe to all
-       events
-
-    Category: Resources/res_ari_bridges
-
-   ASTERISK-24870: ARI: Subscriptions to bridges generally not super useful
-   Reported by: Matt Jordan
-     * [90165e306d] Matt Jordan -- res/res_stasis: Fix accidental
-       subscription to 'all' bridge topic
-     * [b50e372394] Matt Jordan -- ARI: Add events for Contact and Peer
-       Status changes
-     * [3502c0431d] Matt Jordan -- res/res_stasis_device_state: Allow for
-       subscribing to 'all' device state
-     * [4c9f613309] Matt Jordan -- ARI: Add the ability to subscribe to all
-       events
-
-     ----------------------------------------------------------------------
-
-                                  Open Issues
-
-                                 [Back to Top]
-
-   This is a list of all open issues from the issue tracker that were
-   referenced by changes that went into this release.
-
-  Bug
-
-    Category: Addons/chan_ooh323
-
-   ASTERISK-25299: RTP port leaks with incoming OOH323 calls
-   Reported by: Alexandr Dranchuk
-     * [480c443e26] Alexander Anikin -- chan_ooh323: call
-       ast_rtp_instance_stop on ooh323_destroy
-
-    Category: General
-
-   ASTERISK-25323: Asterisk: ongoing segfaults uncovered by CHAOS_DEBUG
-   Reported by: Scott Griepentrog
-     * [c94f46080f] Scott Griepentrog -- CHAOS: avoid crash if string create
-       fails
-     * [4cc59533b9] Richard Mudgett -- CHAOS: res_pjsip_diversion avoid crash
-       if allocation fails
-     * [fb6b5c684b] Scott Griepentrog -- PJSIP: avoid crash when getting rtp
-       peer
-     * [f72f9ceefc] Scott Griepentrog -- pjsip: avoid possible crash req_caps
-       allocation failure
-     * [6862c2a167] Scott Griepentrog -- Chaos: handle failed allocation in
-       get_media_encryption_type
-     * [f1cd636658] Scott Griepentrog -- Chaos: make hangup NULL tolerant
-     * [ab373f2cef] Scott Griepentrog -- CHAOS: prevent sorcery object with
-       null id
-
-    Category: Resources/res_hep_rtcp
-
-   ASTERISK-25352: res_hep_rtcp correlation_id is different then res_hep
-   Reported by: Kevin Scott Adams
-     * [78d0b9d97e] Matt Jordan -- channels/pjsip/dialplan_functions: Add an
-       option for extracting the SIP call-id
-
-    Category: pjproject/pjsip
-
-   ASTERISK-24602: Unable to call WebRTC client via wss on chan_pjsip
-   Reported by: Oleg Kozlov
-     * [d32e516c7c] Martin Tomec -- res/pjsip: Mark WSS transport as secure
-
-  Improvement
-
-     ----------------------------------------------------------------------
-
-                      Commits Not Associated with an Issue
-
-                                 [Back to Top]
-
-   This is a list of all changes that went into this release that did not
-   reference a JIRA issue.
-
-   +------------------------------------------------------------------------+
-   | Revision   | Author          | Summary                                 |
-   |------------+-----------------+-----------------------------------------|
-   | cc0eff5651 | Kevin Harwell   | Release summaries: Remove previous      |
-   |            |                 | versions                                |
-   |------------+-----------------+-----------------------------------------|
-   | 8cd191b885 | Kevin Harwell   | .version: Update for 13.6.0             |
-   |------------+-----------------+-----------------------------------------|
-   | a3777c24fd | Kevin Harwell   | .lastclean: Update for 13.6.0           |
-   |------------+-----------------+-----------------------------------------|
-   | 68121cef21 | Kevin Harwell   | realtime: Add database scripts for      |
-   |            |                 | 13.6.0                                  |
-   |------------+-----------------+-----------------------------------------|
-   | d72dab4f40 | Kevin Harwell   | ChangeLog: Updated for 13.6.0-rc3       |
-   |------------+-----------------+-----------------------------------------|
-   | 9da83dbd15 | Kevin Harwell   | Release summaries: Add summaries for    |
-   |            |                 | 13.6.0-rc3                              |
-   |------------+-----------------+-----------------------------------------|
-   | 8c60f9326c | Kevin Harwell   | Release summaries: Remove previous      |
-   |            |                 | versions                                |
-   |------------+-----------------+-----------------------------------------|
-   | 316d47755b | Kevin Harwell   | .version: Update for 13.6.0-rc3         |
-   |------------+-----------------+-----------------------------------------|
-   | 74a86d0a72 | Kevin Harwell   | .lastclean: Update for 13.6.0-rc3       |
-   |------------+-----------------+-----------------------------------------|
-   | 4c39bea6f0 | Kevin Harwell   | realtime: Add database scripts for      |
-   |            |                 | 13.6.0-rc3                              |
-   |------------+-----------------+-----------------------------------------|
-   | c3521e9469 | Kevin Harwell   | ChangeLog: Updated for 13.6.0-rc2       |
-   |------------+-----------------+-----------------------------------------|
-   | a44f6aa046 | Kevin Harwell   | Release summaries: Add summaries for    |
-   |            |                 | 13.6.0-rc2                              |
-   |------------+-----------------+-----------------------------------------|
-   | dd74af7e46 | Kevin Harwell   | Release summaries: Remove previous      |
-   |            |                 | versions                                |
-   |------------+-----------------+-----------------------------------------|
-   | a11a78ca34 | Kevin Harwell   | .version: Update for 13.6.0-rc2         |
-   |------------+-----------------+-----------------------------------------|
-   | 570329ec8a | Kevin Harwell   | .lastclean: Update for 13.6.0-rc2       |
-   |------------+-----------------+-----------------------------------------|
-   | 51c9ff47f6 | Kevin Harwell   | realtime: Add database scripts for      |
-   |            |                 | 13.6.0-rc2                              |
-   |------------+-----------------+-----------------------------------------|
-   | a0fb436eda | Kevin Harwell   | ChangeLog: Updated for 13.6.0-rc1       |
-   |------------+-----------------+-----------------------------------------|
-   | bba1c4066b | Kevin Harwell   | Release summaries: Add summaries for    |
-   |            |                 | 13.6.0-rc1                              |
-   |------------+-----------------+-----------------------------------------|
-   | 82c4aecdbb | Kevin Harwell   | .version: Update for 13.6.0-rc1         |
-   |------------+-----------------+-----------------------------------------|
-   | bc18db7388 | Kevin Harwell   | .lastclean: Update for 13.6.0-rc1       |
-   |------------+-----------------+-----------------------------------------|
-   | b9c53f95e3 | Kevin Harwell   | realtime: Add database scripts for      |
-   |            |                 | 13.6.0-rc1                              |
-   |------------+-----------------+-----------------------------------------|
-   | d30939b6e8 | Kevin Harwell   | ARI: Changed version from 1.8.0 to      |
-   |            |                 | 1.9.0                                   |
-   |------------+-----------------+-----------------------------------------|
-   | 5f19c9bade | Richard Mudgett | res/ari/config.c: Fix user sort compare |
-   |            |                 | function.                               |
-   |------------+-----------------+-----------------------------------------|
-   | 3a85764039 | Richard Mudgett | res/ari/config.c: Optimize conf_alloc() |
-   |            |                 | object init.                            |
-   |------------+-----------------+-----------------------------------------|
-   | bbeda190c3 | Richard Mudgett | app_dial.c: Remove some no-op code.     |
-   |------------+-----------------+-----------------------------------------|
-   | fe5077b1f8 | Mark Michelson  | res_pjsip_pubsub: Eliminate race during |
-   |            |                 | initial NOTIFY.                         |
-   |------------+-----------------+-----------------------------------------|
-   | 5c713fdf18 | Mark Michelson  | scheduler: Use queue for allocating     |
-   |            |                 | sched IDs.                              |
-   |------------+-----------------+-----------------------------------------|
-   |            |                 | res_pjsip_pubsub.c: Mark                |
-   | e75aff53e6 | Richard Mudgett | ast_sip_create_subscription() as not    |
-   |            |                 | used.                                   |
-   |------------+-----------------+-----------------------------------------|
-   | 4d91d01df1 | Richard Mudgett | res_pjsip_pubsub.c: Add some            |
-   |            |                 | notification comments.                  |
-   |------------+-----------------+-----------------------------------------|
-   | f36a9d1221 | Richard Mudgett | res_pjsip_pubsub.c: Set dlg_status code |
-   |            |                 | instead of sending SIP response.        |
-   |------------+-----------------+-----------------------------------------|
-   | 94582f8fab | Richard Mudgett | res_pjsip_pubsub.c: Fix off-nominal     |
-   |            |                 | memory leak.                            |
-   |------------+-----------------+-----------------------------------------|
-   | 8b3ed52239 | Richard Mudgett | res_pjsip_pubsub.c: Fix one byte buffer |
-   |            |                 | overrun error.                          |
-   |------------+-----------------+-----------------------------------------|
-   | 4329bd1e4c | Richard Mudgett | res_pjsip_pubsub.c: Use ast_alloca()    |
-   |            |                 | instead of alloca().                    |
-   |------------+-----------------+-----------------------------------------|
-   | a456a20ecf | Richard Mudgett | res_pjsip_pubsub.c: Add missing error   |
-   |            |                 | return in load_module().                |
-   |------------+-----------------+-----------------------------------------|
-   | f58f4c6e27 | Richard Mudgett | res_pjsip/location.c: Use the builtin   |
-   |            |                 | ao2_callback() match function instead.  |
-   |------------+-----------------+-----------------------------------------|
-   |            |                 | main/config_options: Check for          |
-   | 4eedd9ef9d | Matt Jordan     | existance of internal object before     |
-   |            |                 | derefing                                |
-   |------------+-----------------+-----------------------------------------|
-   | 695f26cbb7 | David M. Lee    | res_rtp_asterisk: Add more ICE          |
-   |            |                 | debugging                               |
-   |------------+-----------------+-----------------------------------------|
-   | 61c6c6aa6c | David M. Lee    | Fix when remote candidates exceed       |
-   |            |                 | PJ_ICE_MAX_CAND                         |
-   |------------+-----------------+-----------------------------------------|
-   | ad9cb6c2ce | Mark Michelson  | res_pjsip: Fix contact refleak on       |
-   |            |                 | stateful responses.                     |
-   |------------+-----------------+-----------------------------------------|
-   | 7c4d0c3506 | Joshua Colp     | res_pjsip_pubsub: On recreated notify   |
-   |            |                 | fail deleted sub_tree is referenced     |
-   |------------+-----------------+-----------------------------------------|
-   | 0582776f7f | Richard Mudgett | ari/ari_websockets.c: Fix ast_debug     |
-   |            |                 | parameter type mismatch.                |
-   |------------+-----------------+-----------------------------------------|
-   | 77518d5434 | Richard Mudgett | res_http_websocket.c: Fix some off      |
-   |            |                 | nominal path cleanup.                   |
-   |------------+-----------------+-----------------------------------------|
-   | c61547fee6 | Richard Mudgett | res_ari.c: Add missing off nominal      |
-   |            |                 | unlock and remove a RAII_VAR().         |
-   |------------+-----------------+-----------------------------------------|
-   | bd867cd078 | Richard Mudgett | app_queue.c: Extract some functions for |
-   |            |                 | simpler code.                           |
-   |------------+-----------------+-----------------------------------------|
-   | ded51e3d77 | Richard Mudgett | app_queue.c: Fix error checking in      |
-   |            |                 | QUEUE_MEMBER() read.                    |
-   |------------+-----------------+-----------------------------------------|
-   | b719f56c72 | Mark Michelson  | res_pjsip_sdp_rtp: Restore removed NULL |
-   |            |                 | check.                                  |
-   |------------+-----------------+-----------------------------------------|
-   | cea5dc7b8a | Richard Mudgett | audiohook.c: Simplify variable usage in |
-   |            |                 | audiohook_read_frame_both().            |
-   |------------+-----------------+-----------------------------------------|
-   | e18c300550 | Joshua Colp     | res_http_websocket: When shutting down  |
-   |            |                 | a session don't close closed socket     |
-   |------------+-----------------+-----------------------------------------|
-   | 8e194047ac | Matt Jordan     | res/res_format_attr_silk: Expose format |
-   |            |                 | attributes to other modules             |
-   |------------+-----------------+-----------------------------------------|
-   | a0f451c35e | Matt Jordan     | main/format: Add an API call for        |
-   |            |                 | retrieving format attributes            |
-   |------------+-----------------+-----------------------------------------|
-   | 26f0559a94 | David M. Lee    | Replace htobe64 with htonll             |
-   |------------+-----------------+-----------------------------------------|
-   | 27dc2094e9 | Mark Michelson  | res_http_websocket: Debug write         |
-   |            |                 | lengths.                                |
-   |------------+-----------------+-----------------------------------------|
-   | 39cc28f6ea | Mark Michelson  | res_http_websocket: Avoid passing       |
-   |            |                 | strlen() to ast_websocket_write().      |
-   |------------+-----------------+-----------------------------------------|
-   | 1519eb44a7 | Richard Mudgett | rtp_engine.c: Must protect              |
-   |            |                 | mime_types_len with mime_types_lock.    |
-   |------------+-----------------+-----------------------------------------|
-   | a93b7a927c | Richard Mudgett | res_pjsip_sdp_rtp.c: Fix processing     |
-   |            |                 | wrong SDP media list.                   |
-   |------------+-----------------+-----------------------------------------|
-   | 741fa0d26d | Richard Mudgett | res_pjsip_sdp_rtp.c: Fixup some         |
-   |            |                 | whitespace.                             |
-   |------------+-----------------+-----------------------------------------|
-   | 89b21fd9a3 | Richard Mudgett | rtp_engine.h: No sense allowing payload |
-   |            |                 | types larger than RFC allows.           |
-   |------------+-----------------+-----------------------------------------|
-   | 7427c7f13b | Richard Mudgett | rtp_engine.c: Minor tweaks.             |
-   |------------+-----------------+-----------------------------------------|
-   | e20f435b60 | Richard Mudgett | rtp_engine.h: Misc comment fixes.       |
-   |------------+-----------------+-----------------------------------------|
-   | bc5d7f9c37 | Richard Mudgett | chan_sip.c: Tweak glue->update_peer()   |
-   |            |                 | parameter nil value.                    |
-   |------------+-----------------+-----------------------------------------|
-   | 48698a5e21 | Mark Michelson  | res_http_websocket: Properly encode 64  |
-   |            |                 | bit payload                             |
-   +------------------------------------------------------------------------+
-
-     ----------------------------------------------------------------------
-
-                                Diffstat Results
-
-                                 [Back to Top]
-
-   This is a summary of the changes to the source code that went into this
-   release that was generated using the diffstat utility.
-
- asterisk-13.5.0-summary.html                                                  |  441 --
- asterisk-13.5.0-summary.txt                                                   | 1141 ------
- b/.version                                                                    |    2
- b/CHANGES                                                                     |   35
- b/ChangeLog                                                                   | 1884 +++++++++-
- b/Makefile                                                                    |    1
- b/UPGRADE.txt                                                                 |    6
- b/addons/chan_ooh323.c                                                        |    1
- b/addons/ooh323c/src/ooq931.c                                                 |    6
- b/apps/app_dial.c                                                             |  156
- b/apps/app_page.c                                                             |   28
- b/apps/app_queue.c                                                            |  412 +-
- b/apps/app_record.c                                                           |    3
- b/bridges/bridge_holding.c                                                    |    6
- b/channels/chan_dahdi.c                                                       |   55
- b/channels/chan_pjsip.c                                                       |   18
- b/channels/chan_sip.c                                                         |   78
- b/channels/chan_skinny.c                                                      |   26
- b/channels/pjsip/dialplan_functions.c                                         |    5
- b/channels/sip/include/security_events.h                                      |    3
- b/channels/sip/security_events.c                                              |    5
- b/codecs/codec_gsm.c                                                          |   29
- b/codecs/codec_ilbc.c                                                         |   28
- b/codecs/codec_lpc10.c                                                        |   41
- b/codecs/codec_speex.c                                                        |   60
- b/configure                                                                   |   63
- b/configure.ac                                                                |    6
- b/contrib/ast-db-manage/config/versions/154177371065_add_default_from_user.py |   22
- b/contrib/realtime/mssql/mssql_config.sql                                     |    8
- b/contrib/realtime/mysql/mysql_config.sql                                     |    6
- b/contrib/realtime/oracle/oracle_config.sql                                   |    8
- b/contrib/realtime/postgresql/postgresql_config.sql                           |    6
- b/contrib/scripts/astversion                                                  |  536 ++
- b/contrib/scripts/install_prereq                                              |    2
- b/include/asterisk/autoconfig.h.in                                            |    3
- b/include/asterisk/format.h                                                   |   23
- b/include/asterisk/logger.h                                                   |   44
- b/include/asterisk/res_pjsip.h                                                |   40
- b/include/asterisk/res_pjsip_session.h                                        |    6
- b/include/asterisk/rtp_engine.h                                               |   18
- b/include/asterisk/stasis_app.h                                               |   15
- b/main/audiohook.c                                                            |   43
- b/main/bridge_channel.c                                                       |    7
- b/main/channel.c                                                              |    9
- b/main/config_options.c                                                       |    4
- b/main/dial.c                                                                 |   25
- b/main/endpoints.c                                                            |    3
- b/main/format.c                                                               |   11
- b/main/logger.c                                                               |  272 +
- b/main/pbx.c                                                                  |  146
- b/main/rtp_engine.c                                                           |  153
- b/main/sched.c                                                                |  143
- b/main/sorcery.c                                                              |    4
- b/main/stasis_endpoints.c                                                     |   78
- b/main/taskprocessor.c                                                        |   12
- b/main/translate.c                                                            |   55
- b/main/utils.c                                                                |    4
- b/res/ari/ari_model_validators.c                                              |  445 ++
- b/res/ari/ari_model_validators.h                                              |  118
- b/res/ari/ari_websockets.c                                                    |    9
- b/res/ari/config.c                                                            |   72
- b/res/ari/resource_asterisk.c                                                 |  127
- b/res/ari/resource_asterisk.h                                                 |   63
- b/res/ari/resource_events.c                                                   |   24
- b/res/ari/resource_events.h                                                   |    2
- b/res/parking/parking_applications.c                                          |   65
- b/res/res_ari.c                                                               |   19
- b/res/res_ari_asterisk.c                                                      |  300 +
- b/res/res_ari_events.c                                                        |    6
- b/res/res_config_sqlite.c                                                     |    8
- b/res/res_format_attr_silk.c                                                  |   24
- b/res/res_http_websocket.c                                                    |   80
- b/res/res_pjsip.c                                                             |  131
- b/res/res_pjsip/config_global.c                                               |   18
- b/res/res_pjsip/location.c                                                    |   51
- b/res/res_pjsip/pjsip_configuration.c                                         |    4
- b/res/res_pjsip/presence_xml.c                                                |   31
- b/res/res_pjsip_diversion.c                                                   |    4
- b/res/res_pjsip_multihomed.c                                                  |   23
- b/res/res_pjsip_nat.c                                                         |    2
- b/res/res_pjsip_pidf_digium_body_supplement.c                                 |    2
- b/res/res_pjsip_pubsub.c                                                      |   80
- b/res/res_pjsip_sdp_rtp.c                                                     |   70
- b/res/res_pjsip_session.c                                                     |   37
- b/res/res_pjsip_t38.c                                                         |   15
- b/res/res_pjsip_transport_websocket.c                                         |    5
- b/res/res_rtp_asterisk.c                                                      |   79
- b/res/res_stasis.c                                                            |   45
- b/res/res_stasis_device_state.c                                               |   54
- b/res/stasis/app.c                                                            |  335 +
- b/res/stasis/app.h                                                            |   15
- b/res/stasis/messaging.c                                                      |   44
- b/rest-api-templates/ari_model_validators.c.mustache                          |    2
- b/rest-api/api-docs/applications.json                                         |    2
- b/rest-api/api-docs/asterisk.json                                             |  130
- b/rest-api/api-docs/bridges.json                                              |    2
- b/rest-api/api-docs/channels.json                                             |    2
- b/rest-api/api-docs/deviceStates.json                                         |    2
- b/rest-api/api-docs/endpoints.json                                            |    2
- b/rest-api/api-docs/events.json                                               |  109
- b/rest-api/api-docs/mailboxes.json                                            |    2
- b/rest-api/api-docs/playbacks.json                                            |    2
- b/rest-api/api-docs/recordings.json                                           |    2
- b/rest-api/api-docs/sounds.json                                               |    2
- b/rest-api/resources.json                                                     |    2
- b/tests/test_core_format.c                                                    |   57
- 106 files changed, 6435 insertions(+), 2535 deletions(-)
diff --git a/asterisk-13.7.0-rc2-summary.html b/asterisk-13.7.0-rc2-summary.html
new file mode 100644
index 0000000..1863eba
--- /dev/null
+++ b/asterisk-13.7.0-rc2-summary.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><title>Release Summary - asterisk-13.7.0-rc2</title><h1 align="center"><a name="top">Release Summary</a></h1><h3 align="center">asterisk-13.7.0-rc2</h3><h3 align="center">Date: 2015-12-18</h3><h3 align="center"><asteriskteam at digium.com></h3><hr><h2 align="center">Table of Contents</h2><ol>
+<li><a href="#summary">Summary</a></li>
+<li><a href="#contributors">Contributors</a></li>
+<li><a href="#closed_issues">Closed Issues</a></li>
+<li><a href="#commits">Other Changes</a></li>
+<li><a href="#diffstat">Diffstat</a></li>
+</ol><hr><a name="summary"><h2 align="center">Summary</h2></a><center><a href="#top">[Back to Top]</a></center><p>This release is a point release of an existing major version. The changes included were made to address problems that have been identified in this release series, or are minor, backwards compatible new features or improvements. Users should be able to safely upgrade to this version if this release series is already in use. Users considering upgrading from a previous version a [...]
+<tr><th width="33%">Coders</th><th width="33%">Testers</th><th width="33%">Reporters</th></tr>
+<tr valign="top"><td width="33%">4 Joshua Colp <jcolp at digium.com><br/>3 Kevin Harwell <kharwell at lunkwill><br/>2 Mark Michelson <mmichelson at digium.com><br/>1 Kevin Harwell <kharwell at lunkwill.digium.internal><br/></td><td width="33%"><td width="33%">2 Joshua Colp <jcolp at digium.com><br/></td></tr>
+</table><hr><a name="closed_issues"><h2 align="center">Closed Issues</h2></a><center><a href="#top">[Back to Top]</a></center><p>This is a list of all issues from the issue tracker that were closed by changes that went into this release.</p><h3>Bug</h3><h4>Category: Core/General</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25601">ASTERISK-25601</a>: json: Audit reference usage and thread safety<br/>Reported by: Joshua Colp<ul>
+<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=acd19d5f1f84b5c10acc2828b5e73cecfa1ed6ba">[acd19d5f1f]</a> Joshua Colp -- json: Audit ast_json_* usage for thread safety.</li>
+</ul><br><h4>Category: Core/Sorcery</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25625">ASTERISK-25625</a>: res_sorcery_memory_cache: Add full backend caching<br/>Reported by: Joshua Colp<ul>
+<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=11ff4e6b3fb6ef9b1470d348e1f43090fbe17a17">[11ff4e6b3f]</a> Joshua Colp -- res_sorcery_memory_cache: Add support for a full backend cache.</li>
+</ul><br><hr><a name="commits"><h2 align="center">Commits Not Associated with an Issue</h2></a><center><a href="#top">[Back to Top]</a></center><p>This is a list of all changes that went into this release that did not reference a JIRA issue.</p><table width="100%" border="1">
+<tr><th>Revision</th><th>Author</th><th>Summary</th></tr>
+<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=8e201b742a804ff003d86e7c8566856210d02026">8e201b742a</a></td><td>Kevin Harwell</td><td>Release summaries: Remove previous versions</td></tr>
+<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=5a164c70f2dadcdcf325ccf326efc6edc2315d1b">5a164c70f2</a></td><td>Kevin Harwell</td><td>.version: Update for 13.7.0-rc2</td></tr>
+<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=e039eca0a7ee4589b64cc8dbd0d18700eed7a31f">e039eca0a7</a></td><td>Kevin Harwell</td><td>.lastclean: Update for 13.7.0-rc2</td></tr>
+<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=bfe2eb875168864d243dfbed2d4b184e72001110">bfe2eb8751</a></td><td>Kevin Harwell</td><td>realtime: Add database scripts for 13.7.0-rc2</td></tr>
+<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=805297783dc96976c5806d371f1618a5a3901ff4">805297783d</a></td><td>Mark Michelson</td><td>Alembic: Add PJSIP global keep_alive_interval.</td></tr>
+<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=63df9bb56001a29765a7e85702019144380581cf">63df9bb560</a></td><td>Mark Michelson</td><td>Alembic: Increase column size of PJSIP AOR "contact".</td></tr>
+<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=9bc1e49325c0319418c1bbd8ee8e04f31946ef8c">9bc1e49325</a></td><td>Joshua Colp</td><td>rtp_engine: Ignore empty filenames in DTLS configuration.</td></tr>
+<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=c78eb1e82bc6b92ced080df5766b3fbd7dedeb72">c78eb1e82b</a></td><td>Joshua Colp</td><td>chan_sip: Enable WebSocket support by default.</td></tr>
+</table><hr><a name="diffstat"><h2 align="center">Diffstat Results</h2></a><center><a href="#top">[Back to Top]</a></center><p>This is a summary of the changes to the source code that went into this release that was generated using the diffstat utility.</p><pre>asterisk-13.7.0-rc1-summary.html                                                     |  373 ---
+asterisk-13.7.0-rc1-summary.txt                                                      |  967 ---------
+b/.version                                                                           |    2
+b/CHANGES                                                                            |    8
+b/channels/chan_sip.c                                                                |    1
+b/contrib/ast-db-manage/config/versions/189a235b3fd7_add_keep_alive_interval.py      |   22
+b/contrib/ast-db-manage/config/versions/2d078ec071b7_increaes_contact_column_size.py |   22
+b/contrib/realtime/mssql/mssql_config.sql                                            |   14
+b/contrib/realtime/mysql/mysql_config.sql                                            |   12
+b/contrib/realtime/oracle/oracle_config.sql                                          |   14
+b/contrib/realtime/postgresql/postgresql_config.sql                                  |   10
+b/main/aoc.c                                                                         |   20
+b/main/loader.c                                                                      |    4
+b/main/rtp_engine.c                                                                  |   22
+b/main/sorcery.c                                                                     |    6
+b/main/stasis.c                                                                      |    4
+b/main/stasis_channels.c                                                             |   24
+b/res/res_fax.c                                                                      |    4
+b/res/res_pjsip_endpoint_identifier_ip.c                                             |    2
+b/res/res_sorcery_memory_cache.c                                                     | 1040 +++++++++-
+b/res/res_stasis.c                                                                   |    6
+b/res/res_stasis_playback.c                                                          |    2
+22 files changed, 1136 insertions(+), 1443 deletions(-)</pre><br></html>
\ No newline at end of file
diff --git a/asterisk-13.7.0-rc2-summary.txt b/asterisk-13.7.0-rc2-summary.txt
new file mode 100644
index 0000000..d24ff2c
--- /dev/null
+++ b/asterisk-13.7.0-rc2-summary.txt
@@ -0,0 +1,149 @@
+                                Release Summary
+
+                              asterisk-13.7.0-rc2
+
+                                Date: 2015-12-18
+
+                           <asteriskteam at digium.com>
+
+     ----------------------------------------------------------------------
+
+                               Table of Contents
+
+    1. Summary
+    2. Contributors
+    3. Closed Issues
+    4. Other Changes
+    5. Diffstat
+
+     ----------------------------------------------------------------------
+
+                                    Summary
+
+                                 [Back to Top]
+
+   This release is a point release of an existing major version. The changes
+   included were made to address problems that have been identified in this
+   release series, or are minor, backwards compatible new features or
+   improvements. Users should be able to safely upgrade to this version if
+   this release series is already in use. Users considering upgrading from a
+   previous version are strongly encouraged to review the UPGRADE.txt
+   document as well as the CHANGES document for information about upgrading
+   to this release series.
+
+   The data in this summary reflects changes that have been made since the
+   previous release, asterisk-13.7.0-rc1.
+
+     ----------------------------------------------------------------------
+
+                                  Contributors
+
+                                 [Back to Top]
+
+   This table lists the people who have submitted code, those that have
+   tested patches, as well as those that reported issues on the issue tracker
+   that were resolved in this release. For coders, the number is how many of
+   their patches (of any size) were committed into this release. For testers,
+   the number is the number of times their name was listed as assisting with
+   testing a patch. Finally, for reporters, the number is the number of
+   issues that they reported that were affected by commits that went into
+   this release.
+
+   Coders                   Testers                  Reporters                
+   4 Joshua Colp                                     2 Joshua Colp            
+   3 Kevin Harwell          
+   2 Mark Michelson         
+   1 Kevin Harwell          
+
+     ----------------------------------------------------------------------
+
+                                 Closed Issues
+
+                                 [Back to Top]
+
+   This is a list of all issues from the issue tracker that were closed by
+   changes that went into this release.
+
+  Bug
+
+    Category: Core/General
+
+   ASTERISK-25601: json: Audit reference usage and thread safety
+   Reported by: Joshua Colp
+     * [acd19d5f1f] Joshua Colp -- json: Audit ast_json_* usage for thread
+       safety.
+
+    Category: Core/Sorcery
+
+   ASTERISK-25625: res_sorcery_memory_cache: Add full backend caching
+   Reported by: Joshua Colp
+     * [11ff4e6b3f] Joshua Colp -- res_sorcery_memory_cache: Add support for
+       a full backend cache.
+
+     ----------------------------------------------------------------------
+
+                      Commits Not Associated with an Issue
+
+                                 [Back to Top]
+
+   This is a list of all changes that went into this release that did not
+   reference a JIRA issue.
+
+   +------------------------------------------------------------------------+
+   | Revision   | Author         | Summary                                  |
+   |------------+----------------+------------------------------------------|
+   | 8e201b742a | Kevin Harwell  | Release summaries: Remove previous       |
+   |            |                | versions                                 |
+   |------------+----------------+------------------------------------------|
+   | 5a164c70f2 | Kevin Harwell  | .version: Update for 13.7.0-rc2          |
+   |------------+----------------+------------------------------------------|
+   | e039eca0a7 | Kevin Harwell  | .lastclean: Update for 13.7.0-rc2        |
+   |------------+----------------+------------------------------------------|
+   | bfe2eb8751 | Kevin Harwell  | realtime: Add database scripts for       |
+   |            |                | 13.7.0-rc2                               |
+   |------------+----------------+------------------------------------------|
+   | 805297783d | Mark Michelson | Alembic: Add PJSIP global                |
+   |            |                | keep_alive_interval.                     |
+   |------------+----------------+------------------------------------------|
+   | 63df9bb560 | Mark Michelson | Alembic: Increase column size of PJSIP   |
+   |            |                | AOR "contact".                           |
+   |------------+----------------+------------------------------------------|
+   | 9bc1e49325 | Joshua Colp    | rtp_engine: Ignore empty filenames in    |
+   |            |                | DTLS configuration.                      |
+   |------------+----------------+------------------------------------------|
+   | c78eb1e82b | Joshua Colp    | chan_sip: Enable WebSocket support by    |
+   |            |                | default.                                 |
+   +------------------------------------------------------------------------+
+
+     ----------------------------------------------------------------------
+
+                                Diffstat Results
+
+                                 [Back to Top]
+
+   This is a summary of the changes to the source code that went into this
+   release that was generated using the diffstat utility.
+
+ asterisk-13.7.0-rc1-summary.html                                                     |  373 ---
+ asterisk-13.7.0-rc1-summary.txt                                                      |  967 ---------
+ b/.version                                                                           |    2
+ b/CHANGES                                                                            |    8
+ b/channels/chan_sip.c                                                                |    1
+ b/contrib/ast-db-manage/config/versions/189a235b3fd7_add_keep_alive_interval.py      |   22
+ b/contrib/ast-db-manage/config/versions/2d078ec071b7_increaes_contact_column_size.py |   22
+ b/contrib/realtime/mssql/mssql_config.sql                                            |   14
+ b/contrib/realtime/mysql/mysql_config.sql                                            |   12
+ b/contrib/realtime/oracle/oracle_config.sql                                          |   14
+ b/contrib/realtime/postgresql/postgresql_config.sql                                  |   10
+ b/main/aoc.c                                                                         |   20
+ b/main/loader.c                                                                      |    4
+ b/main/rtp_engine.c                                                                  |   22
+ b/main/sorcery.c                                                                     |    6
+ b/main/stasis.c                                                                      |    4
+ b/main/stasis_channels.c                                                             |   24
+ b/res/res_fax.c                                                                      |    4
+ b/res/res_pjsip_endpoint_identifier_ip.c                                             |    2
+ b/res/res_sorcery_memory_cache.c                                                     | 1040 +++++++++-
+ b/res/res_stasis.c                                                                   |    6
+ b/res/res_stasis_playback.c                                                          |    2
+ 22 files changed, 1136 insertions(+), 1443 deletions(-)
diff --git a/bridges/bridge_native_rtp.c b/bridges/bridge_native_rtp.c
index c226dbd..7eacc9d 100644
--- a/bridges/bridge_native_rtp.c
+++ b/bridges/bridge_native_rtp.c
@@ -387,8 +387,8 @@ static int native_rtp_bridge_compatible(struct ast_bridge *bridge)
 		glue1->get_codec(bc1->chan, cap1);
 	}
 	if (ast_format_cap_count(cap0) != 0 && ast_format_cap_count(cap1) != 0 && !ast_format_cap_iscompatible(cap0, cap1)) {
-		struct ast_str *codec_buf0 = ast_str_alloca(64);
-		struct ast_str *codec_buf1 = ast_str_alloca(64);
+		struct ast_str *codec_buf0 = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *codec_buf1 = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_debug(1, "Channel codec0 = %s is not codec1 = %s, cannot native bridge in RTP.\n",
 			ast_format_cap_get_names(cap0, &codec_buf0), ast_format_cap_get_names(cap1, &codec_buf1));
 		return 0;
diff --git a/build_tools/cflags.xml b/build_tools/cflags.xml
index 1a2e78f..d11fb22 100644
--- a/build_tools/cflags.xml
+++ b/build_tools/cflags.xml
@@ -84,6 +84,34 @@
 			<conflict>MALLOC_DEBUG</conflict>
 			<support_level>core</support_level>
 		</member>
+		<member name="ADDRESS_SANITIZER" displayname="Address Sanitizer">
+			<support_level>extended</support_level>
+			<conflict>THREAD_SANITIZER</conflict>
+			<conflict>LEAK_SANITIZER</conflict>
+			<conflict>UNDEFINED_SANITIZER</conflict>
+			<conflict>MALLOC_DEBUG</conflict>
+			<conflict>DEBUG_CHAOS</conflict>
+		</member>
+		<member name="THREAD_SANITIZER" displayname="Thread Sanitizer">
+			<support_level>extended</support_level>
+			<conflict>ADDRESS_SANITIZER</conflict>
+			<conflict>LEAK_SANITIZER</conflict>
+			<conflict>UNDEFINED_SANITIZER</conflict>
+		</member>
+		<member name="LEAK_SANITIZER" displayname="Leak Sanitizer">
+			<support_level>extended</support_level>
+			<conflict>ADDRESS_SANITIZER</conflict>
+			<conflict>THREAD_SANITIZER</conflict>
+			<conflict>UNDEFINED_SANITIZER</conflict>
+			<conflict>MALLOC_DEBUG</conflict>
+			<conflict>DEBUG_CHAOS</conflict>
+		</member>
+		<member name="UNDEFINED_SANITIZER" displayname="Undefined Behavior Sanitizer">
+			<support_level>extended</support_level>
+			<conflict>ADDRESS_SANITIZER</conflict>
+			<conflict>THREAD_SANITIZER</conflict>
+			<conflict>LEAK_SANITIZER</conflict>
+		</member>
 		<member name="BUSYDETECT_TONEONLY" displayname="Enable additional comparision of only the tone duration not the silence part">
 			<conflict>BUSYDETECT_COMPARE_TONE_AND_SILENCE</conflict>
 			<defaultenabled>no</defaultenabled>
diff --git a/build_tools/make_version_c b/build_tools/make_version_c
index 3fea6ce..fcbd94e 100755
--- a/build_tools/make_version_c
+++ b/build_tools/make_version_c
@@ -1,4 +1,7 @@
 #!/bin/sh
+
+GREP=${GREP:-grep}
+
 if test ! -f .flavor ; then
     EXTRA=""
 elif test ! -f .version ; then
@@ -9,6 +12,21 @@ else
     aadkflavor=`cat .flavor`
     EXTRA=" (${aadkflavor} ${aadkver})"
 fi
+
+if ${GREP} "AST_DEVMODE" makeopts | ${GREP} -q "yes"
+then
+	BUILDOPTS="AST_DEVMODE"
+fi
+
+TMP=`${GREP} -e "^MENUSELECT_CFLAGS" menuselect.makeopts | sed 's/MENUSELECT_CFLAGS\=//g' | sed 's/-D//g'`
+for x in ${TMP}; do
+	if test "x${BUILDOPTS}" != "x" ; then
+		BUILDOPTS="${BUILDOPTS}, ${x}"
+	else
+		BUILDOPTS="${x}"
+	fi
+done
+
 cat << END
 /*
  * version.c
@@ -23,6 +41,8 @@ static const char asterisk_version[] = "${ASTERISKVERSION}${EXTRA}";
 
 static const char asterisk_version_num[] = "${ASTERISKVERSIONNUM}";
 
+static const char asterisk_build_opts[] = "${BUILDOPTS}";
+
 const char *ast_get_version(void)
 {
 	return asterisk_version;
@@ -33,4 +53,9 @@ const char *ast_get_version_num(void)
 	return asterisk_version_num;
 }
 
+const char *ast_get_build_opts(void)
+{
+	return asterisk_build_opts;
+}
+
 END
diff --git a/channels/chan_alsa.c b/channels/chan_alsa.c
index 74a6139..6508a1e 100644
--- a/channels/chan_alsa.c
+++ b/channels/chan_alsa.c
@@ -620,7 +620,7 @@ static struct ast_channel *alsa_request(const char *type, struct ast_format_cap
 	struct ast_channel *tmp = NULL;
 
 	if (ast_format_cap_iscompatible_format(cap, ast_format_slin) == AST_FORMAT_CMP_NOT_EQUAL) {
-		struct ast_str *codec_buf = ast_str_alloca(64);
+		struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_log(LOG_NOTICE, "Asked to get a channel of format '%s'\n", ast_format_cap_get_names(cap, &codec_buf));
 		return NULL;
 	}
diff --git a/channels/chan_console.c b/channels/chan_console.c
index 74f2922..02d6887 100644
--- a/channels/chan_console.c
+++ b/channels/chan_console.c
@@ -479,7 +479,7 @@ static struct ast_channel *console_request(const char *type, struct ast_format_c
 	}
 
 	if (!(ast_format_cap_iscompatible(cap, console_tech.capabilities))) {
-		struct ast_str *cap_buf = ast_str_alloca(64);
+		struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_log(LOG_NOTICE, "Channel requested with unsupported format(s): '%s'\n",
 			ast_format_cap_get_names(cap, &cap_buf));
 		goto return_unref;
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index 248a7fd..ef0ad65 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -18833,6 +18833,11 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 					cadence_is_ok = 0;
 				}
 
+				/* This check is only needed to satisfy the compiler that element_count can't cause an out of bounds */
+				if (element_count >= ARRAY_LEN(c)) {
+					element_count = ARRAY_LEN(c) - 1;
+				}
+
 				/* Ring cadences cannot be negative */
 				for (i = 0; i < element_count; i++) {
 					if (c[i] == 0) {
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index efe1bd3..8d5018d 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -2249,6 +2249,14 @@ static struct chan_iax2_pvt *new_iax(struct ast_sockaddr *addr, const char *host
 		return NULL;
 	}
 
+	tmp->pingid = -1;
+	tmp->lagid = -1;
+	tmp->autoid = -1;
+	tmp->authid = -1;
+	tmp->initid = -1;
+	tmp->keyrotateid = -1;
+	tmp->jbid = -1;
+
 	if (ast_string_field_init(tmp, 32)) {
 		ao2_ref(tmp, -1);
 		tmp = NULL;
@@ -2256,18 +2264,11 @@ static struct chan_iax2_pvt *new_iax(struct ast_sockaddr *addr, const char *host
 	}
 
 	tmp->prefs = prefs_global;
-	tmp->pingid = -1;
-	tmp->lagid = -1;
-	tmp->autoid = -1;
-	tmp->authid = -1;
-	tmp->initid = -1;
-	tmp->keyrotateid = -1;
 
 	ast_string_field_set(tmp,exten, "s");
 	ast_string_field_set(tmp,host, host);
 
 	tmp->jb = jb_new();
-	tmp->jbid = -1;
 	jbconf.max_jitterbuf = maxjitterbuffer;
 	jbconf.resync_threshold = resyncthreshold;
 	jbconf.max_contig_interp = maxjitterinterps;
@@ -3816,7 +3817,7 @@ static char *handle_cli_iax2_show_peer(struct ast_cli_entry *e, int cmd, struct
 	char status[30];
 	char cbuf[256];
 	struct iax2_peer *peer;
-	struct ast_str *codec_buf = ast_str_alloca(256);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	struct ast_str *encmethods = ast_str_alloca(256);
 	int load_realtime = 0;
 
@@ -5589,8 +5590,8 @@ static enum ast_bridge_result iax2_bridge(struct ast_channel *c0, struct ast_cha
 			return AST_BRIDGE_FAILED_NOWARN;
 		}
 		if (!(ast_format_cap_identical(ast_channel_nativeformats(c0), ast_channel_nativeformats(c1)))) {
-			struct ast_str *c0_buf = ast_str_alloca(64);
-			struct ast_str *c1_buf = ast_str_alloca(64);
+			struct ast_str *c0_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+			struct ast_str *c1_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_verb(3, "Operating with different codecs [%s] [%s] , can't native bridge...\n",
 				ast_format_cap_get_names(ast_channel_nativeformats(c0), &c0_buf),
@@ -7114,7 +7115,7 @@ static char *handle_cli_iax2_unregister(struct ast_cli_entry *e, int cmd, struct
 
 	p = find_peer(a->argv[2], 1);
 	if (p) {
-		if (p->expire > 0) {
+		if (p->expire > -1) {
 			struct iax2_peer *peer;
 
 			peer = ao2_find(peers, a->argv[2], OBJ_KEY);
@@ -7146,8 +7147,8 @@ static char *complete_iax2_unregister(const char *line, const char *word, int po
 	if (pos == 2) {
 		struct ao2_iterator i = ao2_iterator_init(peers, 0);
 		while ((p = ao2_iterator_next(&i))) {
-			if (!strncasecmp(p->name, word, wordlen) &&
-				++which > state && p->expire > 0) {
+			if (!strncasecmp(p->name, word, wordlen) && 
+				++which > state && p->expire > -1) {
 				res = ast_strdup(p->name);
 				peer_unref(p);
 				break;
@@ -10815,9 +10816,9 @@ static int socket_process_helper(struct iax2_thread *thread)
 									break;
 								}
 								if (authdebug) {
-									struct ast_str *peer_buf = ast_str_alloca(64);
-									struct ast_str *cap_buf = ast_str_alloca(64);
-									struct ast_str *peer_form_buf = ast_str_alloca(64);
+									struct ast_str *peer_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+									struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+									struct ast_str *peer_form_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 									if (ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP)) {
 										ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested '%s' incompatible with our capability '%s'.\n",
@@ -10862,9 +10863,9 @@ static int socket_process_helper(struct iax2_thread *thread)
 								}
 
 								if (!format) {
-									struct ast_str *peer_buf = ast_str_alloca(64);
-									struct ast_str *cap_buf = ast_str_alloca(64);
-									struct ast_str *peer_form_buf = ast_str_alloca(64);
+									struct ast_str *peer_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+									struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+									struct ast_str *peer_form_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 									memset(&ied0, 0, sizeof(ied0));
 									iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
@@ -11042,8 +11043,8 @@ static int socket_process_helper(struct iax2_thread *thread)
 						break;
 					}
 					if (authdebug) {
-						struct ast_str *peer_buf = ast_str_alloca(64);
-						struct ast_str *cap_buf = ast_str_alloca(64);
+						struct ast_str *peer_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+						struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 						ast_log(LOG_NOTICE, "Rejected call to %s, format %s incompatible with our capability %s.\n",
 							ast_sockaddr_stringify(&addr),
@@ -11056,7 +11057,7 @@ static int socket_process_helper(struct iax2_thread *thread)
 					ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
 					iax2_lock_owner(fr->callno);
 					if (iaxs[fr->callno] && iaxs[fr->callno]->owner && native) {
-						struct ast_str *cap_buf = ast_str_alloca(64);
+						struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 						/* Switch us to use a compatible format */
 						iax2_codec_pref_best_bitfield2cap(
@@ -11260,9 +11261,9 @@ static int socket_process_helper(struct iax2_thread *thread)
 						iax2_codec_pref_string(&iaxs[fr->callno]->prefs, host_pref_buf, sizeof(host_pref_buf) - 1);
 					}
 					if (!format) {
-						struct ast_str *cap_buf = ast_str_alloca(64);
-						struct ast_str *peer_buf = ast_str_alloca(64);
-						struct ast_str *peer_form_buf = ast_str_alloca(64);
+						struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+						struct ast_str *peer_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+						struct ast_str *peer_form_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 						if(!ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP)) {
 							ast_debug(1, "We don't do requested format %s, falling back to peer capability '%s'\n",
@@ -11323,9 +11324,9 @@ static int socket_process_helper(struct iax2_thread *thread)
 								}
 							}
 							if (!format) {
-								struct ast_str *cap_buf = ast_str_alloca(64);
-								struct ast_str *peer_buf = ast_str_alloca(64);
-								struct ast_str *peer_form_buf = ast_str_alloca(64);
+								struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+								struct ast_str *peer_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+								struct ast_str *peer_form_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 								ast_log(LOG_ERROR, "No best format in %s???\n",
 									iax2_getformatname_multiple(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability, &cap_buf));
@@ -11449,7 +11450,7 @@ immediatedial:
 							break;
 						}
 					} else {
-						struct ast_str *cap_buf = ast_str_alloca(64);
+						struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 						ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
 						ast_verb(3, "Accepting DIAL from %s, formats = %s\n",
 								ast_sockaddr_stringify(&addr),
@@ -12530,8 +12531,8 @@ static struct ast_channel *iax2_request(const char *type, struct ast_format_cap
 
 			res = ast_translator_best_choice(cap, ast_channel_nativeformats(c), &best_fmt_cap, &best_fmt_native);
 			if (res < 0) {
-				struct ast_str *native_cap_buf = ast_str_alloca(256);
-				struct ast_str *cap_buf = ast_str_alloca(256);
+				struct ast_str *native_cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+				struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 				ast_log(LOG_WARNING, "Unable to create translator path for %s to %s on %s\n",
 					ast_format_cap_get_names(ast_channel_nativeformats(c), &native_cap_buf),
@@ -14384,7 +14385,7 @@ static int function_iaxpeer(struct ast_channel *chan, const char *cmd, char *dat
 	} else  if (!strcasecmp(colname, "callerid_num")) {
 		ast_copy_string(buf, peer->cid_num, len);
 	} else  if (!strcasecmp(colname, "codecs")) {
-		struct ast_str *codec_buf = ast_str_alloca(256);
+		struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 		iax2_getformatname_multiple(peer->capability, &codec_buf);
 		ast_copy_string(buf, ast_str_buffer(codec_buf), len);
diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c
index 030df89..16ba7ec 100644
--- a/channels/chan_mgcp.c
+++ b/channels/chan_mgcp.c
@@ -1253,7 +1253,7 @@ static int mgcp_write(struct ast_channel *ast, struct ast_frame *frame)
 		}
 	} else {
 		if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
-			struct ast_str *cap_buf = ast_str_alloca(64);
+			struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_log(LOG_WARNING, "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
 				ast_format_get_name(frame->subclass.format),
@@ -1986,9 +1986,9 @@ static int process_sdp(struct mgcp_subchannel *sub, struct mgcp_request *req)
 	int codec, codec_count=0;
 	int iterator;
 	struct mgcp_endpoint *p = sub->parent;
-	struct ast_str *global_buf = ast_str_alloca(64);
-	struct ast_str *peer_buf = ast_str_alloca(64);
-	struct ast_str *pvt_buf = ast_str_alloca(64);
+	struct ast_str *global_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+	struct ast_str *peer_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+	struct ast_str *pvt_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 	/* Get codec and RTP info from SDP */
 	m = get_sdp(req, "m");
@@ -3972,7 +3972,7 @@ static struct ast_channel *mgcp_request(const char *type, struct ast_format_cap
 	char tmp[256];
 
 	if (!(ast_format_cap_iscompatible(cap, global_capability))) {
-		struct ast_str *cap_buf = ast_str_alloca(64);
+		struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%s'\n",
 			ast_format_cap_get_names(cap, &cap_buf));
 		/*return NULL;*/
diff --git a/channels/chan_motif.c b/channels/chan_motif.c
index 48f1b1f..24575fa 100644
--- a/channels/chan_motif.c
+++ b/channels/chan_motif.c
@@ -1706,7 +1706,7 @@ static int jingle_write(struct ast_channel *ast, struct ast_frame *frame)
 	switch (frame->frametype) {
 	case AST_FRAME_VOICE:
 		if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
-			struct ast_str *codec_buf = ast_str_alloca(64);
+			struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_log(LOG_WARNING,
 				"Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
diff --git a/channels/chan_nbs.c b/channels/chan_nbs.c
index 36c3435..2abde61 100644
--- a/channels/chan_nbs.c
+++ b/channels/chan_nbs.c
@@ -232,7 +232,7 @@ static struct ast_channel *nbs_request(const char *type, struct ast_format_cap *
 	struct ast_channel *tmp = NULL;
 
 	if (ast_format_cap_iscompatible_format(cap, ast_format_slin) == AST_FORMAT_CMP_NOT_EQUAL) {
-		struct ast_str *cap_buf = ast_str_alloca(64);
+		struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 		ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%s'\n",
 			ast_format_cap_get_names(cap, &cap_buf));
diff --git a/channels/chan_oss.c b/channels/chan_oss.c
index 51fce44..912a5eb 100644
--- a/channels/chan_oss.c
+++ b/channels/chan_oss.c
@@ -863,7 +863,7 @@ static struct ast_channel *oss_request(const char *type, struct ast_format_cap *
 		return NULL;
 	}
 	if (ast_format_cap_iscompatible_format(cap, ast_format_slin) == AST_FORMAT_CMP_NOT_EQUAL) {
-		struct ast_str *codec_buf = ast_str_alloca(64);
+		struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_log(LOG_NOTICE, "Format %s unsupported\n", ast_format_cap_get_names(cap, &codec_buf));
 		return NULL;
 	}
diff --git a/channels/chan_phone.c b/channels/chan_phone.c
index 2bf7bdd..451d9f2 100644
--- a/channels/chan_phone.c
+++ b/channels/chan_phone.c
@@ -1282,7 +1282,7 @@ static struct ast_channel *phone_request(const char *type, struct ast_format_cap
 	restart_monitor();
 	if (tmp == NULL) {
 		if (!(ast_format_cap_iscompatible(cap, phone_tech.capabilities))) {
-			struct ast_str *codec_buf = ast_str_alloca(64);
+			struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 			ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%s'\n",
 				ast_format_cap_get_names(cap, &codec_buf));
 			return NULL;
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index d7e291d..0a8d1bc 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -162,11 +162,18 @@ static enum ast_rtp_glue_result chan_pjsip_get_rtp_peer(struct ast_channel *chan
 	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(chan);
 	struct chan_pjsip_pvt *pvt;
 	struct ast_sip_endpoint *endpoint;
+	struct ast_datastore *datastore;
 
 	if (!channel || !channel->session || !(pvt = channel->pvt) || !pvt->media[SIP_MEDIA_AUDIO]->rtp) {
 		return AST_RTP_GLUE_RESULT_FORBID;
 	}
 
+	datastore = ast_sip_session_get_datastore(channel->session, "t38");
+	if (datastore) {
+		ao2_ref(datastore, -1);
+		return AST_RTP_GLUE_RESULT_FORBID;
+	}
+
 	endpoint = channel->session->endpoint;
 
 	*instance = pvt->media[SIP_MEDIA_AUDIO]->rtp;
@@ -673,7 +680,7 @@ static int chan_pjsip_write(struct ast_channel *ast, struct ast_frame *frame)
 			return 0;
 		}
 		if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
-			struct ast_str *cap_buf = ast_str_alloca(128);
+			struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 			struct ast_str *write_transpath = ast_str_alloca(256);
 			struct ast_str *read_transpath = ast_str_alloca(256);
 
@@ -1386,6 +1393,8 @@ static void transfer_refer(struct ast_sip_session *session, const char *target)
 	enum ast_control_transfer message = AST_TRANSFER_SUCCESS;
 	pj_str_t tmp;
 	pjsip_tx_data *packet;
+	const char *ref_by_val;
+	char local_info[pj_strlen(&session->inv_session->dlg->local.info_str) + 1];
 
 	if (pjsip_xfer_create_uac(session->inv_session->dlg, NULL, &sub) != PJ_SUCCESS) {
 		message = AST_TRANSFER_FAILED;
@@ -1402,6 +1411,14 @@ static void transfer_refer(struct ast_sip_session *session, const char *target)
 		return;
 	}
 
+	ref_by_val = pbx_builtin_getvar_helper(session->channel, "SIPREFERREDBYHDR");
+	if (!ast_strlen_zero(ref_by_val)) {
+		ast_sip_add_header(packet, "Referred-By", ref_by_val);
+	} else {
+		ast_copy_pj_str(local_info, &session->inv_session->dlg->local.info_str, sizeof(local_info));
+		ast_sip_add_header(packet, "Referred-By", local_info);
+	}
+
 	pjsip_xfer_send_request(sub, packet);
 	ast_queue_control_data(session->channel, AST_CONTROL_TRANSFER, &message, sizeof(message));
 }
@@ -2075,7 +2092,8 @@ static int chan_pjsip_incoming_request(struct ast_sip_session *session, struct p
 		return 0;
 	}
 
-	if (session->inv_session->state >= PJSIP_INV_STATE_CONFIRMED) {
+	/* Check for a to-tag to determine if this is a reinvite */
+	if (rdata->msg_info.to->tag.slen) {
 		/* Weird case. We've received a reinvite but we don't have a channel. The most
 		 * typical case for this happening is that a blind transfer fails, and so the
 		 * transferer attempts to reinvite himself back into the call. We already got
@@ -2122,8 +2140,9 @@ static int call_pickup_incoming_request(struct ast_sip_session *session, pjsip_r
 	struct ast_features_pickup_config *pickup_cfg;
 	struct ast_channel *chan;
 
-	/* We don't care about reinvites */
-	if (session->inv_session->state >= PJSIP_INV_STATE_CONFIRMED) {
+	/* Check for a to-tag to determine if this is a reinvite */
+	if (rdata->msg_info.to->tag.slen) {
+		/* We don't care about reinvites */
 		return 0;
 	}
 
@@ -2170,8 +2189,9 @@ static int pbx_start_incoming_request(struct ast_sip_session *session, pjsip_rx_
 {
 	int res;
 
-	/* We don't care about reinvites */
-	if (session->inv_session->state >= PJSIP_INV_STATE_CONFIRMED) {
+	/* Check for a to-tag to determine if this is a reinvite */
+	if (rdata->msg_info.to->tag.slen) {
+		/* We don't care about reinvites */
 		return 0;
 	}
 
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 384e843..500aeb7 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -997,9 +997,9 @@ static struct ao2_container *threadt;
 static struct ao2_container *peers;
 static struct ao2_container *peers_by_ip;
 
-/*! \brief  A bogus peer, to be used when authentication should fail */
-static struct sip_peer *bogus_peer;
-/*! \brief  We can recognise the bogus peer by this invalid MD5 hash */
+/*! \brief A bogus peer, to be used when authentication should fail */
+static AO2_GLOBAL_OBJ_STATIC(g_bogus_peer);
+/*! \brief We can recognize the bogus peer by this invalid MD5 hash */
 #define BOGUS_PEER_MD5SECRET "intentionally_invalid_md5_string"
 
 /*! \brief  The register list: Other SIP proxies we register with and receive calls from */
@@ -1708,6 +1708,7 @@ static int publish_expire(const void *data)
 	ast_assert(esc != NULL);
 
 	ao2_unlink(esc->compositor, esc_entry);
+	esc_entry->sched_id = -1;
 	ao2_ref(esc_entry, -1);
 	return 0;
 }
@@ -1740,6 +1741,11 @@ static struct sip_esc_entry *create_esc_entry(struct event_state_compositor *esc
 	/* Bump refcount for scheduler */
 	ao2_ref(esc_entry, +1);
 	esc_entry->sched_id = ast_sched_add(sched, expires_ms, publish_expire, esc_entry);
+	if (esc_entry->sched_id == -1) {
+		ao2_ref(esc_entry, -1);
+		ao2_ref(esc_entry, -1);
+		return NULL;
+	}
 
 	/* Note: This links the esc_entry into the ESC properly */
 	create_new_sip_etag(esc_entry, 0);
@@ -4746,6 +4752,11 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int
 	struct sip_pvt *p = (struct sip_pvt *) ast_channel_tech_pvt(chan);
 	char *cp;
 
+	if (!p) {
+		ast_debug(1, "Attempt to Ref a null pointer. Sip private structure is gone!\n");
+		return -1;
+	}
+
 	sip_pvt_lock(p);
 
 	switch (option) {
@@ -7209,7 +7220,7 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
 	switch (frame->frametype) {
 	case AST_FRAME_VOICE:
 		if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
-			struct ast_str *codec_buf = ast_str_alloca(64);
+			struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 			ast_log(LOG_WARNING, "Asked to transmit frame type %s, while native formats is %s read/write = %s/%s\n",
 				ast_format_get_name(frame->subclass.format),
 				ast_format_cap_get_names(ast_channel_nativeformats(ast), &codec_buf),
@@ -7846,7 +7857,7 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit
 	struct ast_variable *v = NULL;
 	struct ast_format *fmt;
 	struct ast_format_cap *what = NULL; /* SHALLOW COPY DO NOT DESTROY! */
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	int needvideo = 0;
 	int needtext = 0;
 	char *exten;
@@ -9368,6 +9379,9 @@ static int sip_register(const char *value, int lineno)
 		return -1;
 	}
 
+	reg->expire = -1;
+	reg->timeout = -1;
+
 	if (ast_string_field_init(reg, 256)) {
 		ao2_t_ref(reg, -1, "failed to string_field_init, drop reg");
 		return -1;
@@ -9441,6 +9455,8 @@ static int sip_subscribe_mwi(const char *value, int lineno)
 		return -1;
 	}
 
+	mwi->resub = -1;
+
 	if (ast_string_field_init(mwi, 256)) {
 		ao2_t_ref(mwi, -1, "failed to string_field_init, drop mwi");
 		return -1;
@@ -9455,7 +9471,6 @@ static int sip_subscribe_mwi(const char *value, int lineno)
 	}
 	ast_string_field_set(mwi, hostname, hostname);
 	ast_string_field_set(mwi, mailbox, mailbox);
-	mwi->resub = -1;
 	mwi->portno = portnum;
 	mwi->transport = transport;
 
@@ -9921,7 +9936,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 	int debug = sip_debug_test_pvt(p);
 
 	/* START UNKNOWN */
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	struct ast_format *tmp_fmt;
 	/* END UNKNOWN */
 
@@ -10570,11 +10585,11 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 
 	if (debug) {
 		/* shame on whoever coded this.... */
-		struct ast_str *cap_buf = ast_str_alloca(64);
-		struct ast_str *peer_buf = ast_str_alloca(64);
-		struct ast_str *vpeer_buf = ast_str_alloca(64);
-		struct ast_str *tpeer_buf = ast_str_alloca(64);
-		struct ast_str *joint_buf = ast_str_alloca(64);
+		struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *peer_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *vpeer_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *tpeer_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *joint_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 		ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s/text=%s, combined - %s\n",
 			    ast_format_cap_get_names(p->caps, &cap_buf),
@@ -10618,7 +10633,11 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 	/* Setup audio address and port */
 	if (p->rtp) {
 		if (sa && portno > 0) {
-			start_ice(p->rtp, (req->method != SIP_RESPONSE) ? 0 : 1);
+			/* Start ICE negotiation here, only when it is response, and setting that we are conrolling agent,
+			   as we are offerer */
+			if (req->method == SIP_RESPONSE) {
+				start_ice(p->rtp, 1);
+			}
 			ast_sockaddr_set_port(sa, portno);
 			ast_rtp_instance_set_remote_address(p->rtp, sa);
 			if (debug) {
@@ -10781,8 +10800,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 		unsigned int framing;
 
 		if (debug) {
-			struct ast_str *cap_buf = ast_str_alloca(64);
-			struct ast_str *joint_buf = ast_str_alloca(64);
+			struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+			struct ast_str *joint_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_debug(1, "Setting native formats after processing SDP. peer joint formats %s, old nativeformats %s\n",
 				ast_format_cap_get_names(p->jointcaps, &joint_buf),
@@ -11095,7 +11114,7 @@ static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_
 
 		if (framing && p->autoframing) {
 			ast_debug(1, "Setting framing to %ld\n", framing);
-			ast_rtp_codecs_set_framing(ast_rtp_instance_get_codecs(p->rtp), framing);
+			ast_format_cap_set_framing(p->caps, framing);
 		}
 		found = TRUE;
 	} else if (sscanf(a, "rtpmap: %30u %127[^/]/%30u", &codec, mimeSubtype, &sample_rate) == 3) {
@@ -13116,7 +13135,7 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
 	int min_video_packet_size = 0;
 	int min_text_packet_size = 0;
 
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 	/* Set the SDP session name */
 	snprintf(subject, sizeof(subject), "s=%s\r\n", ast_strlen_zero(global_sdpsession) ? "-" : global_sdpsession);
@@ -13332,7 +13351,7 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
 		}
 
 		/* Finally our remaining audio/video codecs */
-		for (x = 0; x < ast_format_cap_count(p->caps); x++) {
+		for (x = 0; ast_test_flag(&p->flags[0], SIP_OUTGOING) && x < ast_format_cap_count(p->caps); x++) {
 			tmp_fmt = ast_format_cap_get_format(p->caps, x);
 
 			if (ast_format_cap_iscompatible_format(alreadysent, tmp_fmt) != AST_FORMAT_CMP_NOT_EQUAL) {
@@ -13384,9 +13403,19 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
 			ast_str_append(&a_audio, 0, "a=maxptime:%d\r\n", max_audio_packet_size);
 		}
 
+		if (!ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
+			ast_debug(1, "Setting framing on incoming call: %u\n", min_audio_packet_size);
+			ast_rtp_codecs_set_framing(ast_rtp_instance_get_codecs(p->rtp), min_audio_packet_size);
+		}
+
 		if (!doing_directmedia) {
 			if (ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
 				add_ice_to_sdp(p->rtp, &a_audio);
+				/* Start ICE negotiation, and setting that we are controlled agent,
+				   as this is response to offer */
+				if (resp->method == SIP_RESPONSE) {
+					start_ice(p->rtp, 0);
+				}
 			}
 
 			add_dtls_to_sdp(p->rtp, &a_audio);
@@ -13676,10 +13705,6 @@ static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const
 		add_cc_call_info_to_response(p, &resp);
 	}
 	if (p->rtp) {
-		if (!p->autoframing && !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
-			ast_debug(1, "Setting framing from config on incoming call\n");
-			ast_rtp_codecs_set_framing(ast_rtp_instance_get_codecs(p->rtp), ast_format_cap_get_framing(p->caps));
-		}
 		ast_rtp_instance_activate(p->rtp);
 		try_suggested_sip_codec(p);
 		if (p->t38.state == T38_ENABLED) {
@@ -17173,8 +17198,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
 	/* If we don't want username disclosure, use the bogus_peer when a user
 	 * is not found. */
 	if (!peer && sip_cfg.alwaysauthreject && sip_cfg.autocreatepeer == AUTOPEERS_DISABLED) {
-		peer = bogus_peer;
-		sip_ref_peer(peer, "register_verify: ref the bogus_peer");
+		peer = ao2_t_global_obj_ref(g_bogus_peer, "register_verify: Get the bogus peer.");
 	}
 
 	if (!(peer && ast_apply_acl(peer->acl, addr, "SIP Peer ACL: "))) {
@@ -18427,6 +18451,7 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
 	enum check_auth_result res;
 	int debug = sip_debug_test_addr(addr);
 	struct sip_peer *peer;
+	struct sip_peer *bogus_peer;
 
 	if (sipmethod == SIP_SUBSCRIBE) {
 		/* For subscribes, match on device name only; for other methods,
@@ -18466,8 +18491,13 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
 		/* If you do mind, we use a peer that will never authenticate.
 		 * This ensures that we follow the same code path as regular
 		 * auth: less chance for username disclosure. */
-		peer = bogus_peer;
-		sip_ref_peer(peer, "sip_ref_peer: check_peer_ok: must ref bogus_peer so unreffing it does not fail");
+		peer = ao2_t_global_obj_ref(g_bogus_peer, "check_peer_ok: Get the bogus peer.");
+		if (!peer) {
+			return AUTH_DONT_KNOW;
+		}
+		bogus_peer = peer;
+	} else {
+		bogus_peer = NULL;
 	}
 
 	/*  build_peer, called through sip_find_peer, is not able to check the
@@ -20297,7 +20327,7 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
 	char status[30] = "";
 	char cbuf[256];
 	struct sip_peer *peer;
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	struct ast_variable *v;
 	int x = 0, load_realtime;
 	int realtimepeers;
@@ -20828,7 +20858,7 @@ static char *sip_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
 		return CLI_SHOWUSAGE;
 
 	if ((peer = sip_find_peer(a->argv[2], NULL, load_realtime, FINDPEERS, TRUE, 0))) {
-		if (peer->expire > 0) {
+		if (peer->expire > -1) {
 			AST_SCHED_DEL_UNREF(sched, peer->expire,
 				sip_unref_peer(peer, "remove register expire ref"));
 			expire_register(sip_ref_peer(peer, "ref for expire_register"));
@@ -20943,7 +20973,7 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
 {
 	int realtimepeers;
 	int realtimeregs;
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	const char *msg;	/* temporary msg pointer */
 	struct sip_auth_container *credentials;
 
@@ -21260,7 +21290,7 @@ static int show_channels_cb(void *__cur, void *__arg, int flags)
 	if (cur->subscribed == NONE && !arg->subscriptions) {
 		/* set if SIP transfer in progress */
 		const char *referstatus = cur->refer ? referstatus2str(cur->refer->status) : "";
-		struct ast_str *codec_buf = ast_str_alloca(64);
+		struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 		ast_cli(arg->fd, FORMAT, ast_sockaddr_stringify_addr(dst),
 				S_OR(cur->username, S_OR(cur->cid_num, "(None)")),
@@ -21407,7 +21437,7 @@ static char *complete_sip_registered_peer(const char *word, int state, int flags
        while ((peer = ao2_t_iterator_next(&i, "iterate thru peers table"))) {
 	       if (!strncasecmp(word, peer->name, wordlen) &&
 		   (!flags2 || ast_test_flag(&peer->flags[1], flags2)) &&
-		   ++which > state && peer->expire > 0)
+		   ++which > state && peer->expire > -1)
 		       result = ast_strdup(peer->name);
 	       if (result) {
 		       sip_unref_peer(peer, "toss iterator peer ptr before break");
@@ -21506,7 +21536,7 @@ static char *sip_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_a
 
 		if (!strncasecmp(cur->callid, a->argv[3], len)) {
 			struct ast_str *strbuf;
-			struct ast_str *codec_buf = ast_str_alloca(64);
+			struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_cli(a->fd, "\n");
 			if (cur->subscribed != NONE) {
@@ -22465,7 +22495,7 @@ static int function_sippeer(struct ast_channel *chan, const char *cmd, char *dat
 	} else  if (!strcasecmp(colname, "callerid_num")) {
 		ast_copy_string(buf, peer->cid_num, len);
 	} else  if (!strcasecmp(colname, "codecs")) {
-		struct ast_str *codec_buf = ast_str_alloca(64);
+		struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_format_cap_get_names(peer->caps, &codec_buf);
 		ast_copy_string(buf, ast_str_buffer(codec_buf), len);
 	} else if (!strcasecmp(colname, "encryption")) {
@@ -28806,7 +28836,8 @@ static int check_rtp_timeout(struct sip_pvt *dialog, time_t t)
 					ast_channel_name(dialog->owner), (long) (t - dialog->lastrtprx));
 				send_session_timeout(dialog->owner, "RTPTimeout");
 
-				/* Issue a softhangup */
+				/* Issue a softhangup - cause 44 (as used by Cisco for RTP timeouts) */
+				ast_channel_hangupcause_set(dialog->owner, AST_CAUSE_REQUESTED_CHAN_UNAVAIL);
 				ast_softhangup_nolock(dialog->owner, AST_SOFTHANGUP_DEV);
 				ast_channel_unlock(dialog->owner);
 				/* forget the timeouts for this call, since a hangup
@@ -29607,8 +29638,8 @@ static struct ast_channel *sip_request_call(const char *type, struct ast_format_
 	struct ast_channel *tmpc = NULL;
 	char *ext = NULL, *host;
 	char tmp[256];
-	struct ast_str *codec_buf = ast_str_alloca(64);
-	struct ast_str *cap_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+	struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	char *dnid;
 	char *secret = NULL;
 	char *md5secret = NULL;
@@ -30262,13 +30293,11 @@ static struct ast_variable *add_var(const char *buf, struct ast_variable *list)
 /*! \brief Set peer defaults before configuring specific configurations */
 static void set_peer_defaults(struct sip_peer *peer)
 {
-	if (peer->expire == 0) {
+	if (peer->expire < 0) {
 		/* Don't reset expire or port time during reload
 		   if we have an active registration
 		*/
-		peer->expire = -1;
-		peer->pokeexpire = -1;
-		peer->keepalivesend = -1;
+		peer_sched_cleanup(peer);
 		set_socket_transport(&peer->socket, AST_TRANSPORT_UDP);
 	}
 	peer->type = SIP_TYPE_PEER;
@@ -30353,6 +30382,10 @@ static struct sip_peer *temp_peer(const char *name)
 	}
 
 	ast_atomic_fetchadd_int(&apeerobjs, 1);
+	peer->expire = -1;
+	peer->pokeexpire = -1;
+	peer->keepalivesend = -1;
+
 	set_peer_defaults(peer);
 
 	ast_copy_string(peer->name, name, sizeof(peer->name));
@@ -30473,6 +30506,10 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
 			ast_debug(3, "-REALTIME- peer built. Name: %s. Peer objects: %d\n", name, rpeerobjs);
 		} else
 			ast_atomic_fetchadd_int(&speerobjs, 1);
+
+		peer->expire = -1;
+		peer->pokeexpire = -1;
+		peer->keepalivesend = -1;
 	}
 
 	/* Note that our peer HAS had its reference count increased */
@@ -31259,6 +31296,7 @@ static int reload_config(enum channelreloadreason reason)
 	int bindport = 0;
 	int acl_change_subscription_needed = 0;
 	int min_subexpiry_set = 0, max_subexpiry_set = 0;
+	int websocket_was_enabled = sip_cfg.websocket_enabled;
 
 	run_start = time(0);
 	ast_unload_realtime("sipregs");
@@ -31426,6 +31464,7 @@ static int reload_config(enum channelreloadreason reason)
 	sip_cfg.peer_rtupdate = TRUE;
 	global_dynamic_exclude_static = 0;	/* Exclude static peers */
 	sip_cfg.tcp_enabled = FALSE;
+	sip_cfg.websocket_enabled = TRUE;
 
 	/* Session-Timers */
 	global_st_mode = SESSION_TIMER_MODE_ACCEPT;
@@ -32045,6 +32084,8 @@ static int reload_config(enum channelreloadreason reason)
 				ast_log(LOG_WARNING, "'%s' is not a valid websocket_write_timeout value at line %d. Using default '%d'.\n", v->value, v->lineno, AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT);
 				sip_cfg.websocket_write_timeout = AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT;
 			}
+		} else if (!strcasecmp(v->name, "websocket_enabled")) {
+			sip_cfg.websocket_enabled = ast_true(v->value);
 		}
 	}
 
@@ -32195,6 +32236,12 @@ static int reload_config(enum channelreloadreason reason)
 		ast_log(LOG_ERROR, "SIP TCP Server start failed. Not listening on TCP socket.\n");
 	} else {
 		ast_debug(2, "SIP TCP server started\n");
+		if (sip_tcp_desc.accept_fd >= 0) {
+			int flags = 1;
+			if (setsockopt(sip_tcp_desc.accept_fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags))) {
+				ast_log(LOG_ERROR, "Error enabling TCP keep-alive on sip socket: %s\n", strerror(errno));
+			}
+		}
 	}
 
 	/* Start TLS server if needed */
@@ -32215,6 +32262,13 @@ static int reload_config(enum channelreloadreason reason)
 			ast_log(LOG_ERROR, "TLS Server start failed. Not listening on TLS socket.\n");
 			sip_tls_desc.tls_cfg = NULL;
 		}
+		if (sip_tls_desc.accept_fd >= 0) {
+			int flags = 1;
+			if (setsockopt(sip_tls_desc.accept_fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags))) {
+				ast_log(LOG_ERROR, "Error enabling TCP keep-alive on sip socket: %s\n", strerror(errno));
+				sip_tls_desc.tls_cfg = NULL;
+			}
+		}
 	} else if (sip_tls_desc.tls_cfg->enabled) {
 		sip_tls_desc.tls_cfg = NULL;
 		ast_log(LOG_WARNING, "SIP TLS server did not load because of errors.\n");
@@ -32390,6 +32444,15 @@ static int reload_config(enum channelreloadreason reason)
 		notify_types = NULL;
 	}
 
+	/* If the module is loading it's not time to enable websockets yet. */
+	if (reason != CHANNEL_MODULE_LOAD && websocket_was_enabled != sip_cfg.websocket_enabled) {
+		if (sip_cfg.websocket_enabled) {
+			ast_websocket_add_protocol("sip", sip_websocket_callback);
+		} else {
+			ast_websocket_remove_protocol("sip", sip_websocket_callback);
+		}
+	}
+
 	run_end = time(0);
 	ast_debug(4, "SIP reload_config done...Runtime= %d sec\n", (int)(run_end-run_start));
 
@@ -33110,7 +33173,7 @@ static int sip_do_reload(enum channelreloadreason reason)
 /*! \brief Force reload of module from cli */
 static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-	static struct sip_peer *tmp_peer, *new_peer;
+	static struct sip_peer *new_peer;
 
 	switch (cmd) {
 	case CLI_INIT:
@@ -33133,13 +33196,13 @@ static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a
 	ast_mutex_unlock(&sip_reload_lock);
 	restart_monitor();
 
-	tmp_peer = bogus_peer;
 	/* Create new bogus peer possibly with new global settings. */
 	if ((new_peer = temp_peer("(bogus_peer)"))) {
 		ast_string_field_set(new_peer, md5secret, BOGUS_PEER_MD5SECRET);
 		ast_clear_flag(&new_peer->flags[0], SIP_INSECURE);
-		bogus_peer = new_peer;
-		ao2_t_ref(tmp_peer, -1, "unref the old bogus_peer during reload");
+		ao2_t_global_obj_replace_unref(g_bogus_peer, new_peer,
+			"Replacing the old bogus peer during reload.");
+		ao2_t_ref(new_peer, -1, "done with new_peer");
 	} else {
 		ast_log(LOG_ERROR, "Could not update the fake authentication peer.\n");
 		/* You probably have bigger (memory?) issues to worry about though.. */
@@ -34390,6 +34453,8 @@ static int unload_module(void);
  */
 static int load_module(void)
 {
+	struct sip_peer *bogus_peer;
+
 	ast_verbose("SIP channel loading...\n");
 
 	if (STASIS_MESSAGE_TYPE_INIT(session_timeout_type)) {
@@ -34461,6 +34526,8 @@ static int load_module(void)
 	/* Make sure the auth will always fail. */
 	ast_string_field_set(bogus_peer, md5secret, BOGUS_PEER_MD5SECRET);
 	ast_clear_flag(&bogus_peer->flags[0], SIP_INSECURE);
+	ao2_t_global_obj_replace_unref(g_bogus_peer, bogus_peer, "Set the initial bogus peer.");
+	ao2_t_ref(bogus_peer, -1, "Module load is done with the bogus peer.");
 
 	/* Prepare the version that does not require DTMF BEGIN frames.
 	 * We need to use tricks such as memcpy and casts because the variable
@@ -34571,7 +34638,9 @@ static int load_module(void)
 	sip_register_tests();
 	network_change_stasis_subscribe();
 
-	ast_websocket_add_protocol("sip", sip_websocket_callback);
+	if (sip_cfg.websocket_enabled) {
+		ast_websocket_add_protocol("sip", sip_websocket_callback);
+	}
 
 	return AST_MODULE_LOAD_SUCCESS;
 }
@@ -34586,7 +34655,9 @@ static int unload_module(void)
 
 	ast_sip_api_provider_unregister();
 
-	ast_websocket_remove_protocol("sip", sip_websocket_callback);
+	if (sip_cfg.websocket_enabled) {
+		ast_websocket_remove_protocol("sip", sip_websocket_callback);
+	}
 
 	network_change_stasis_unsubscribe();
 	acl_change_event_stasis_unsubscribe();
@@ -34739,7 +34810,7 @@ static int unload_module(void)
 		ast_debug(2, "TCP/TLS thread container did not become empty :(\n");
 	}
 
-	ao2_t_cleanup(bogus_peer, "unref the bogus_peer");
+	ao2_t_global_obj_release(g_bogus_peer, "Release the bogus peer.");
 
 	ao2_t_cleanup(peers, "unref the peers table");
 	ao2_t_cleanup(peers_by_ip, "unref the peers_by_ip table");
diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c
index 23a4835..5cdfe1b 100644
--- a/channels/chan_skinny.c
+++ b/channels/chan_skinny.c
@@ -1364,6 +1364,12 @@ static int gendigittimeout = 8000;
 /* How long to wait for an extra digit, if there is an ambiguous match */
 static int matchdigittimeout = 3000;
 
+/*!
+ * To apease the stupid compiler option on ast_sched_del()
+ * since we don't care about the return value.
+ */
+static int not_used;
+
 #define SUBSTATE_UNSET 0
 #define SUBSTATE_OFFHOOK 1
 #define SUBSTATE_ONHOOK 2
@@ -2262,10 +2268,10 @@ static int skinny_register(struct skinny_req *req, struct skinnysession *s)
 	int instance;
 	int res = -1;
 
-	if (s->auth_timeout_sched && ast_sched_del(sched, s->auth_timeout_sched)) {
-		return 0;
+	if (-1 < s->auth_timeout_sched) {
+		not_used = ast_sched_del(sched, s->auth_timeout_sched);
+		s->auth_timeout_sched = -1;
 	}
-	s->auth_timeout_sched = 0;
 
 	AST_LIST_LOCK(&devices);
 	AST_LIST_TRAVERSE(&devices, d, list){
@@ -4176,7 +4182,7 @@ static char *_skinny_show_device(int type, int fd, struct mansession *s, const s
 	struct skinny_speeddial *sd;
 	struct skinny_addon *sa;
 	struct skinny_serviceurl *surl;
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 	if (argc < 4) {
 		return CLI_SHOWUSAGE;
@@ -4425,7 +4431,7 @@ static char *_skinny_show_line(int type, int fd, struct mansession *s, const str
 	struct skinny_device *d;
 	struct skinny_line *l;
 	struct skinny_subline *subline;
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	char group_buf[256];
 	char cbuf[256];
 
@@ -5075,7 +5081,7 @@ static int skinny_write(struct ast_channel *ast, struct ast_frame *frame)
 		}
 	} else {
 		if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
-			struct ast_str *codec_buf = ast_str_alloca(64);
+			struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 			ast_log(LOG_WARNING, "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
 				ast_format_get_name(frame->subclass.format),
 				ast_format_cap_get_names(ast_channel_nativeformats(ast), &codec_buf),
@@ -5351,7 +5357,7 @@ static struct ast_channel *skinny_new(struct skinny_line *l, struct skinny_subli
 	struct ast_format *tmpfmt;
 	struct ast_format_cap *caps;
 #ifdef AST_DEVMODE
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 #endif
 
 	if (!l->device || !l->device->session) {
@@ -5548,12 +5554,12 @@ static void setsubstate(struct skinny_subchannel *sub, int state)
 
 	skinny_locksub(sub);
 
-	if (sub->dialer_sched) {
+	if (-1 < sub->dialer_sched) {
 		skinny_sched_del(sub->dialer_sched, sub);
 		sub->dialer_sched = -1;
 	}
 
-	if (state != SUBSTATE_RINGIN && sub->aa_sched) {
+	if (state != SUBSTATE_RINGIN && -1 < sub->aa_sched) {
 		skinny_sched_del(sub->aa_sched, sub);
 		sub->aa_sched = -1;
 		sub->aa_beep = 0;
@@ -5571,6 +5577,7 @@ static void setsubstate(struct skinny_subchannel *sub, int state)
 			sub->cfwd_sched = -1;
 		} else if (state == SUBSTATE_ONHOOK) {
 			skinny_sched_del(sub->cfwd_sched, sub);
+			sub->cfwd_sched = -1;
 		}
 	}
 
@@ -6170,9 +6177,7 @@ static int handle_ip_port_message(struct skinny_req *req, struct skinnysession *
 
 static void handle_keepalive_message(struct skinny_req *req, struct skinnysession *s)
 {
-	if (ast_sched_del(sched, s->keepalive_timeout_sched)) {
-		return;
-	}
+	not_used = ast_sched_del(sched, s->keepalive_timeout_sched);
 
 #ifdef AST_DEVMODE
 	{
@@ -6238,7 +6243,7 @@ static int handle_keypad_button_message(struct skinny_req *req, struct skinnyses
 	}
 
 	if ((sub->owner && ast_channel_state(sub->owner) <  AST_STATE_UP)) {
-		if (sub->dialer_sched && !skinny_sched_del(sub->dialer_sched, sub)) {
+		if (-1 < sub->dialer_sched && !skinny_sched_del(sub->dialer_sched, sub)) {
 			SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %u - Got a digit and not timed out, so try dialing\n", sub->callid);
 			sub->dialer_sched = -1;
 			len = strlen(sub->exten);
@@ -6638,7 +6643,7 @@ static int handle_capabilities_res_message(struct skinny_req *req, struct skinny
 	struct ast_format_cap *codecs = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
 	int i;
 #ifdef AST_DEVMODE
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 #endif
 	
 
@@ -7075,7 +7080,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
 	case SOFTKEY_BKSPC:
 		SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_BKSPC from %s, inst %d, callref %d\n",
 			d->name, instance, callreference);
-		if (sub->dialer_sched && !skinny_sched_del(sub->dialer_sched, sub)) {
+		if (-1 < sub->dialer_sched && !skinny_sched_del(sub->dialer_sched, sub)) {
 			size_t len;
 			sub->dialer_sched = -1;
 			len = strlen(sub->exten);
@@ -7415,7 +7420,7 @@ static int skinny_noauth_cb(const void *data)
 {
 	struct skinnysession *s = (struct skinnysession *)data;
 	ast_log(LOG_WARNING, "Skinny Client failed to authenticate in %d seconds (SCHED %d)\n", auth_timeout, s->auth_timeout_sched);
-	s->auth_timeout_sched = 0;
+	s->auth_timeout_sched = -1;
 	end_session(s);
 	return 0;
 }
@@ -7424,7 +7429,7 @@ static int skinny_nokeepalive_cb(const void *data)
 {
 	struct skinnysession *s = (struct skinnysession *)data;
 	ast_log(LOG_WARNING, "Skinny Client failed to send keepalive in last %d seconds (SCHED %d)\n", keep_alive*3, s->keepalive_timeout_sched);
-	s->keepalive_timeout_sched = 0;
+	s->keepalive_timeout_sched = -1;
 	end_session(s);
 	return 0;
 }
@@ -7442,11 +7447,13 @@ static void skinny_session_cleanup(void *data)
 		ast_mutex_unlock(&s->lock);
 	}
 
-	if (s->auth_timeout_sched && !ast_sched_del(sched, s->auth_timeout_sched)) {
-		s->auth_timeout_sched = 0;
+	if (-1 < s->auth_timeout_sched) {
+		not_used = ast_sched_del(sched, s->auth_timeout_sched);
+		s->auth_timeout_sched = -1;
 	}
-	if (s->keepalive_timeout_sched && !ast_sched_del(sched, s->keepalive_timeout_sched)) {
-		s->keepalive_timeout_sched = 0;
+	if (-1 < s->keepalive_timeout_sched) {
+		not_used = ast_sched_del(sched, s->keepalive_timeout_sched);
+		s->keepalive_timeout_sched = -1;
 	}
 
 	if (d) {
@@ -7651,6 +7658,8 @@ static void *accept_thread(void *ignore)
 		ast_mutex_init(&s->lock);
 		memcpy(&s->sin, &sin, sizeof(sin));
 		s->fd = as;
+		s->auth_timeout_sched = -1;
+		s->keepalive_timeout_sched = -1;
 
 		if (ast_pthread_create(&s->t, NULL, skinny_session, s)) {
 			destroy_session(s);
@@ -7681,7 +7690,7 @@ static struct ast_channel *skinny_request(const char *type, struct ast_format_ca
 	char tmp[256];
 
 	if (!(ast_format_cap_has_type(cap, AST_MEDIA_TYPE_AUDIO))) {
-		struct ast_str *codec_buf = ast_str_alloca(64);
+		struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%s'\n", ast_format_cap_get_names(cap, &codec_buf));
 		return NULL;
 	}
diff --git a/channels/chan_unistim.c b/channels/chan_unistim.c
index f429617..db4720d 100644
--- a/channels/chan_unistim.c
+++ b/channels/chan_unistim.c
@@ -2889,7 +2889,7 @@ static void start_rtp(struct unistim_subchannel *sub)
 	ast_rtp_instance_set_remote_address(sub->rtp, &sin_tmp);
 	if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(sub->owner), ast_channel_readformat(sub->owner)) == AST_FORMAT_CMP_NOT_EQUAL) {
 		struct ast_format *tmpfmt;
-		struct ast_str *cap_buf = ast_str_alloca(64);
+		struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 		tmpfmt = ast_format_cap_get_format(ast_channel_nativeformats(sub->owner), 0);
 		ast_log(LOG_WARNING,
@@ -5142,7 +5142,7 @@ static struct ast_frame *unistim_rtp_read(const struct ast_channel *ast,
 		/* We already hold the channel lock */
 		if (f->frametype == AST_FRAME_VOICE) {
 			if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(sub->owner), f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
-				struct ast_str *cap_buf = ast_str_alloca(64);
+				struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 				struct ast_format_cap *caps;
 
 				ast_debug(1,
@@ -5192,7 +5192,7 @@ static int unistim_write(struct ast_channel *ast, struct ast_frame *frame)
 		}
 	} else {
 		if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
-			struct ast_str *cap_buf = ast_str_alloca(64);
+			struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_log(LOG_WARNING,
 					"Asked to transmit frame type %s, while native formats is %s (read/write = (%s/%s)\n",
@@ -5723,9 +5723,9 @@ static struct ast_channel *unistim_new(struct unistim_subchannel *sub, int state
 	tmpfmt = ast_format_cap_get_format(ast_channel_nativeformats(tmp), 0);
 
 	if (unistimdebug) {
-		struct ast_str *native_buf = ast_str_alloca(64);
-		struct ast_str *cap_buf = ast_str_alloca(64);
-		struct ast_str *global_buf = ast_str_alloca(64);
+		struct ast_str *native_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *global_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 		ast_verb(0, "Best codec = %s from nativeformats %s (line cap=%s global=%s)\n",
 			ast_format_get_name(tmpfmt),
@@ -5938,8 +5938,8 @@ static struct ast_channel *unistim_request(const char *type, struct ast_format_c
 	char tmp[256];
 
 	if (!(ast_format_cap_iscompatible(cap, global_cap))) {
-		struct ast_str *cap_buf = ast_str_alloca(64);
-		struct ast_str *global_buf = ast_str_alloca(64);
+		struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *global_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_log(LOG_NOTICE,
 				"Asked to get a channel of unsupported format %s while capability is %s\n",
 				ast_format_cap_get_names(cap, &cap_buf),
@@ -6014,7 +6014,7 @@ static char *unistim_show_info(struct ast_cli_entry *e, int cmd, struct ast_cli_
 	struct unistim_line *line;
 	struct unistim_subchannel *sub;
 	struct unistimsession *s;
-	struct ast_str *cap_buf = ast_str_alloca(64);
+	struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 	switch (cmd) {
 	case CLI_INIT:
diff --git a/channels/chan_vpb.cc b/channels/chan_vpb.cc
index f1ed392..0f050a8 100644
--- a/channels/chan_vpb.cc
+++ b/channels/chan_vpb.cc
@@ -2508,7 +2508,7 @@ static struct ast_channel *vpb_request(const char *type, struct ast_format_cap *
 	if (!(ast_format_cap_iscompatible_format(cap, ast_format_slin))) {
 		struct ast_str *buf;
 
-		buf = ast_str_create(256);
+		buf = ast_str_create(AST_FORMAT_CAP_NAMES_LEN);
 		if (!buf) {
 			return NULL;
 		}
diff --git a/channels/sig_pri.c b/channels/sig_pri.c
index 4b91391..d6b8507 100644
--- a/channels/sig_pri.c
+++ b/channels/sig_pri.c
@@ -4682,7 +4682,7 @@ static void sig_pri_handle_subcmds(struct sig_pri_span *pri, int chanpos, int ev
 					f.frametype = AST_FRAME_TEXT;
 					f.subclass.integer = 0;
 					f.offset = 0;
-					f.data.ptr = &subcmd->u.display.text;
+					f.data.ptr = (void *)&subcmd->u.display.text;
 					f.datalen = subcmd->u.display.length + 1;
 					ast_queue_frame(owner, &f);
 					ast_channel_unlock(owner);
diff --git a/channels/sip/config_parser.c b/channels/sip/config_parser.c
index d48ff08..5049542 100644
--- a/channels/sip/config_parser.c
+++ b/channels/sip/config_parser.c
@@ -79,13 +79,17 @@ int sip_parse_register_line(struct sip_registry *reg, int default_expiry, const
 		AST_APP_ARG(port);
 	);
 
-	if (!value) {
+	if (!reg) {
 		return -1;
 	}
 
-	if (!reg) {
+	reg->expire = -1;
+	reg->timeout = -1;
+
+	if (!value) {
 		return -1;
 	}
+
 	ast_copy_string(buf, value, sizeof(buf));
 
 	/*! register => [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry]
@@ -261,7 +265,6 @@ int sip_parse_register_line(struct sip_registry *reg, int default_expiry, const
 	ast_string_field_set(reg, regdomain, ast_strip_quoted(S_OR(user2.domain, ""), "\"", "\""));
 
 	reg->transport = transport;
-	reg->timeout = reg->expire = -1;
 	reg->portno = portnum;
 	reg->regdomainport = domainport;
 	reg->callid_valid = FALSE;
diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h
index 3ed3e8a..82f208c 100644
--- a/channels/sip/include/sip.h
+++ b/channels/sip/include/sip.h
@@ -774,6 +774,7 @@ struct sip_settings {
 	int tcp_enabled;
 	int default_max_forwards;    /*!< Default max forwards (SIP Anti-loop) */
 	int websocket_write_timeout; /*!< Socket write timeout for websocket transports, in ms */
+	int websocket_enabled;       /*!< Are websockets enabled? */
 };
 
 struct ast_websocket;
diff --git a/channels/sip/reqresp_parser.c b/channels/sip/reqresp_parser.c
index e5c1ff2..31832a3 100644
--- a/channels/sip/reqresp_parser.c
+++ b/channels/sip/reqresp_parser.c
@@ -2420,7 +2420,7 @@ struct sip_via *parse_via(const char *header)
 
 	/* store the port, we have to handle ipv6 addresses containing ':'
 	 * characters gracefully */
-	if (((parm = strchr(v->sent_by, ']')) && *(++parm) == ':') || (parm = strchr(v->sent_by, ':'))) {
+	if (((parm = strchr(v->sent_by, ']')) && *(++parm) == ':') || (!(parm = strchr(v->sent_by, ']')) && (parm = strchr(v->sent_by, ':')))) {
 		char *endptr;
 
 		v->port = strtol(++parm, &endptr, 10);
diff --git a/codecs/codec_resample.c b/codecs/codec_resample.c
index 26b1f0e..9eda242 100644
--- a/codecs/codec_resample.c
+++ b/codecs/codec_resample.c
@@ -38,7 +38,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/translate.h"
 #include "asterisk/slin.h"
 
-#define OUTBUF_SIZE   8096
+#define OUTBUF_SAMPLES   11520
 
 static struct ast_translator *translators;
 static int trans_size;
@@ -114,7 +114,7 @@ static void resamp_destroy(struct ast_trans_pvt *pvt)
 static int resamp_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
 {
 	SpeexResamplerState *resamp_pvt = pvt->pvt;
-	unsigned int out_samples = (OUTBUF_SIZE / sizeof(int16_t)) - pvt->samples;
+	unsigned int out_samples = OUTBUF_SAMPLES - pvt->samples;
 	unsigned int in_samples;
 
 	if (!f->datalen) {
@@ -167,8 +167,8 @@ static int load_module(void)
 			translators[idx].destroy = resamp_destroy;
 			translators[idx].framein = resamp_framein;
 			translators[idx].desc_size = 0;
-			translators[idx].buffer_samples = (OUTBUF_SIZE / sizeof(int16_t));
-			translators[idx].buf_size = OUTBUF_SIZE;
+			translators[idx].buffer_samples = OUTBUF_SAMPLES;
+			translators[idx].buf_size = (OUTBUF_SAMPLES * sizeof(int16_t));
 			memcpy(&translators[idx].src_codec, &codec_list[x], sizeof(struct ast_codec));
 			memcpy(&translators[idx].dst_codec, &codec_list[y], sizeof(struct ast_codec));
 			snprintf(translators[idx].name, sizeof(translators[idx].name), "slin %ukhz -> %ukhz",
diff --git a/configs/samples/confbridge.conf.sample b/configs/samples/confbridge.conf.sample
index 860f1cb..0419001 100644
--- a/configs/samples/confbridge.conf.sample
+++ b/configs/samples/confbridge.conf.sample
@@ -137,6 +137,12 @@ type=user
                        ; This option is off by default.
 ;announcement=</path/to/file> ; Play a sound file to the user when they join the conference.
 
+;timeout=3600 ; When set non-zero, this specifies the number of seconds that the participant
+              ; may stay in the conference before being automatically ejected. When the user
+              ; is ejected from the conference, the user's channel will have the CONFBRIDGE_RESULT
+              ; variable set to "TIMEOUT". A value of 0 indicates that there is no timeout.
+              ; Default: 0
+
 ; --- ConfBridge Bridge Profile Options ---
 [default_bridge]
 type=bridge
diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index fc70947..859635c 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -923,6 +923,14 @@
                         ; registration is unsuccessful (default: "60")
 ;forbidden_retry_interval=0     ; Interval used when receiving a 403 Forbidden
                                 ; response (default: "0")
+;fatal_retry_interval=0 ; Interval used when receiving a fatal response.
+                        ; (default: "0") A fatal response is any permanent
+                        ; failure (non-temporary 4xx, 5xx, 6xx) response
+                        ; received from the registrar. NOTE - if also set
+                        ; the 'forbidden_retry_interval' takes precedence
+                        ; over this one when a 403 is received. Also, if
+                        ; 'auth_rejection_permanent' equals 'yes' a 401 and
+                        ; 407 become subject to this retry interval.
 ;server_uri=    ; SIP URI of the server to register against (default: "")
 ;transport=     ; Transport used for outbound authentication (default: "")
 ;type=  ; Must be of type registration (default: "")
diff --git a/configs/samples/sip.conf.sample b/configs/samples/sip.conf.sample
index 44d2d43..a24ab30 100644
--- a/configs/samples/sip.conf.sample
+++ b/configs/samples/sip.conf.sample
@@ -229,6 +229,10 @@ tcpbindaddr=0.0.0.0             ; IP address for TCP server to bind to (0.0.0.0
 				; unauthenticated sessions that will be allowed
                                 ; to connect at any given time. (default: 100)
 
+;websocket_enabled = true       ; Set to false to prevent chan_sip from listening to websockets.  This
+                                ; is neeeded when using chan_sip and res_pjsip_transport_websockets on
+                                ; the same system.
+
 ;websocket_write_timeout = 100  ; Default write timeout to set on websocket transports.
                                 ; This value may need to be adjusted for connections where
                                 ; Asterisk must write a substantial amount of data and the
diff --git a/contrib/ast-db-manage/config/versions/189a235b3fd7_add_keep_alive_interval.py b/contrib/ast-db-manage/config/versions/189a235b3fd7_add_keep_alive_interval.py
new file mode 100644
index 0000000..aa52171
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/189a235b3fd7_add_keep_alive_interval.py
@@ -0,0 +1,22 @@
+"""add_keep_alive_interval
+
+Revision ID: 189a235b3fd7
+Revises: 28ce1e718f05
+Create Date: 2015-12-16 11:23:16.994523
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '189a235b3fd7'
+down_revision = '28ce1e718f05'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.add_column('ps_globals', sa.Column('keep_alive_interval', sa.Integer))
+
+
+def downgrade():
+    op.drop_column('ps_globals', 'keep_alive_interval')
diff --git a/contrib/ast-db-manage/config/versions/28ce1e718f05_add_fatal_response_interval.py b/contrib/ast-db-manage/config/versions/28ce1e718f05_add_fatal_response_interval.py
new file mode 100644
index 0000000..8c499ae
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/28ce1e718f05_add_fatal_response_interval.py
@@ -0,0 +1,22 @@
+"""add fatal_response_interval
+
+Revision ID: 28ce1e718f05
+Revises: 154177371065
+Create Date: 2015-10-20 17:57:45.560585
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '28ce1e718f05'
+down_revision = '154177371065'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.add_column('ps_registrations', sa.Column('fatal_retry_interval', sa.Integer))
+
+
+def downgrade():
+    op.drop_column('ps_registrations', 'fatal_retry_interval')
diff --git a/contrib/ast-db-manage/config/versions/2d078ec071b7_increaes_contact_column_size.py b/contrib/ast-db-manage/config/versions/2d078ec071b7_increaes_contact_column_size.py
new file mode 100644
index 0000000..2ade86f
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/2d078ec071b7_increaes_contact_column_size.py
@@ -0,0 +1,22 @@
+"""increaes_contact_column_size
+
+Revision ID: 2d078ec071b7
+Revises: 189a235b3fd7
+Create Date: 2015-12-16 11:26:54.218985
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '2d078ec071b7'
+down_revision = '189a235b3fd7'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.alter_column('ps_aors', 'contact', type_=sa.String(255))
+
+
+def downgrade():
+    op.alter_column('ps_aors', 'contact', type_=sa.String(40))
diff --git a/contrib/realtime/mssql/mssql_config.sql b/contrib/realtime/mssql/mssql_config.sql
index cd632c1..29ca157 100644
--- a/contrib/realtime/mssql/mssql_config.sql
+++ b/contrib/realtime/mssql/mssql_config.sql
@@ -1070,7 +1070,25 @@ ALTER TABLE ps_globals ADD default_from_user VARCHAR(80) NULL;
 
 GO
 
-INSERT INTO alembic_version (version_num) VALUES ('154177371065');
+-- Running upgrade 154177371065 -> 28ce1e718f05
+
+ALTER TABLE ps_registrations ADD fatal_retry_interval INTEGER NULL;
+
+GO
+
+-- Running upgrade 28ce1e718f05 -> 189a235b3fd7
+
+ALTER TABLE ps_globals ADD keep_alive_interval INTEGER NULL;
+
+GO
+
+-- Running upgrade 189a235b3fd7 -> 2d078ec071b7
+
+ALTER TABLE ps_aors ALTER COLUMN contact VARCHAR(255);
+
+GO
+
+INSERT INTO alembic_version (version_num) VALUES ('2d078ec071b7');
 
 GO
 
diff --git a/contrib/realtime/mysql/mysql_config.sql b/contrib/realtime/mysql/mysql_config.sql
index 95b0fb4..7204f06 100644
--- a/contrib/realtime/mysql/mysql_config.sql
+++ b/contrib/realtime/mysql/mysql_config.sql
@@ -767,3 +767,21 @@ ALTER TABLE ps_globals ADD COLUMN default_from_user VARCHAR(80);
 
 UPDATE alembic_version SET version_num='154177371065';
 
+-- Running upgrade 154177371065 -> 28ce1e718f05
+
+ALTER TABLE ps_registrations ADD COLUMN fatal_retry_interval INTEGER;
+
+UPDATE alembic_version SET version_num='28ce1e718f05';
+
+-- Running upgrade 28ce1e718f05 -> 189a235b3fd7
+
+ALTER TABLE ps_globals ADD COLUMN keep_alive_interval INTEGER;
+
+UPDATE alembic_version SET version_num='189a235b3fd7';
+
+-- Running upgrade 189a235b3fd7 -> 2d078ec071b7
+
+ALTER TABLE ps_aors CHANGE contact contact VARCHAR(255) NULL;
+
+UPDATE alembic_version SET version_num='2d078ec071b7';
+
diff --git a/contrib/realtime/oracle/oracle_config.sql b/contrib/realtime/oracle/oracle_config.sql
index be195d8..f179ca6 100644
--- a/contrib/realtime/oracle/oracle_config.sql
+++ b/contrib/realtime/oracle/oracle_config.sql
@@ -1072,7 +1072,25 @@ ALTER TABLE ps_globals ADD default_from_user VARCHAR2(80 CHAR)
 
 /
 
-INSERT INTO alembic_version (version_num) VALUES ('154177371065')
+-- Running upgrade 154177371065 -> 28ce1e718f05
+
+ALTER TABLE ps_registrations ADD fatal_retry_interval INTEGER
+
+/
+
+-- Running upgrade 28ce1e718f05 -> 189a235b3fd7
+
+ALTER TABLE ps_globals ADD keep_alive_interval INTEGER
+
+/
+
+-- Running upgrade 189a235b3fd7 -> 2d078ec071b7
+
+ALTER TABLE ps_aors MODIFY contact VARCHAR2(255 CHAR)
+
+/
+
+INSERT INTO alembic_version (version_num) VALUES ('2d078ec071b7')
 
 /
 
diff --git a/contrib/realtime/postgresql/postgresql_config.sql b/contrib/realtime/postgresql/postgresql_config.sql
index 11eed62..344b2a3 100644
--- a/contrib/realtime/postgresql/postgresql_config.sql
+++ b/contrib/realtime/postgresql/postgresql_config.sql
@@ -781,7 +781,19 @@ ALTER TABLE ps_endpoints ADD COLUMN rtp_timeout_hold INTEGER;
 
 ALTER TABLE ps_globals ADD COLUMN default_from_user VARCHAR(80);
 
-INSERT INTO alembic_version (version_num) VALUES ('154177371065');
+-- Running upgrade 154177371065 -> 28ce1e718f05
+
+ALTER TABLE ps_registrations ADD COLUMN fatal_retry_interval INTEGER;
+
+-- Running upgrade 28ce1e718f05 -> 189a235b3fd7
+
+ALTER TABLE ps_globals ADD COLUMN keep_alive_interval INTEGER;
+
+-- Running upgrade 189a235b3fd7 -> 2d078ec071b7
+
+ALTER TABLE ps_aors ALTER COLUMN contact TYPE VARCHAR(255);
+
+INSERT INTO alembic_version (version_num) VALUES ('2d078ec071b7');
 
 COMMIT;
 
diff --git a/contrib/scripts/autosupport b/contrib/scripts/autosupport
index 4d5ab59..e41215f 100644
--- a/contrib/scripts/autosupport
+++ b/contrib/scripts/autosupport
@@ -74,7 +74,7 @@ if [ $MYUID -ne  0 ]; then
 fi
 
 SUPPORTED_MODULES="wcb4xxp wct4xxp wctc4xxp wctdm wctdm24xxp wcte11xp wcte12xp"
-RELATED_MODULES="$SUPPORTED_MODULES dahdi_dummy dahdi_transcode dahdi_vpmadt032_loader zaptel ztdummy zttranscode";
+RELATED_MODULES="$SUPPORTED_MODULES dahdi_dummy dahdi_transcode dahdi_vpmadt032_loader";
 
 OUTPUT_FILE="${FILE_PREFIX}${OUTPUT_FILE}"
 TARBALL_OUTPUT_FILE="${FILE_PREFIX}${TARBALL_OUTPUT_FILE}"
@@ -152,18 +152,16 @@ echo >> $OUTPUT;
 echo >> $OUTPUT;
 echo -n "."
 
-# Check for loaded Zaptel/DAHDI modules
-for module in dahdi zaptel; do
-  if [ -d /sys/module/$module ]; then
-    echo "------------------" >> $OUTPUT
-    echo "$module version:" >> $OUTPUT
-    echo "------------------" >> $OUTPUT
-    echo "/sys/module/$module/version: " >> $OUTPUT
-    cat /sys/module/$module/version 2> /dev/null >> $OUTPUT
-    echo "" >> $OUTPUT;
-    echo -n "."
-  fi
-done
+
+if [ -d /sys/module/dahdi ]; then
+  echo "------------------" >> $OUTPUT
+  echo "dahdi version:" >> $OUTPUT
+  echo "------------------" >> $OUTPUT
+  echo "/sys/module/dahdi/version: " >> $OUTPUT
+  cat /sys/module/dahdi/version 2> /dev/null >> $OUTPUT
+  echo "" >> $OUTPUT;
+  echo -n "."
+fi
 
 echo "------------------" >> $OUTPUT;
 echo "DAHDI TOOLS : dahdi_cfg --help" >> $OUTPUT;
@@ -188,13 +186,16 @@ asterisk -V >> $OUTPUT;
 echo >> $OUTPUT;
 # Add check to see if asterisk is running.
 if [ -e /var/run/asterisk.ctl ] || [ -e /var/run/asterisk/asterisk.ctl ]; then
-  for command in "show version" "core show version" "pri show version" "dahdi show version" "transcoder show" \
-      "core show uptime" "pri show spans" "misdn show stacks" "zap show channels" "dahdi show status" "dahdi show channels" \
-      "dahdi show channel 1" "core show channels" "skype show version" "skype show licenses" "skype show users" \
-      "skype show hostid" "show g729" "g729 show version" "g729 show licenses" "g729 show hostid" "fax show version" \
-      "fax show licenses" "fax show hostid" "fax show stats" "digium_phones show version" "digium_phones show alerts" \
-			"digium_phones show applications" "digium_phones show firmwares" "digium_phones show lines" "digium_phones show networks" \
-			"digium_phones show phones" "digium_phones show sessions" "digium_phones show settings" "digium_phones show translations" ;
+  for command in "core show version" "pri show version" "dahdi show version" "core show translation" \
+      "core show uptime" "core show settings" "core show sysinfo" "core show channels" \
+      "pri show spans" "dahdi show status" "dahdi show channels" "dahdi show channel 1" \
+      "pjsip show endpoints" "pjsip show registrations" "pjsip list channels" \
+      "sip show peers" "sip show registry" "sip show channels" "sip show subscriptions" "sip show settings" \
+      "show g729" "g729 show version" "g729 show licenses" "g729 show hostid" \
+      "digium_phones show version" "digium_phones show alerts" "digium_phones show applications" \
+      "digium_phones show firmwares" "digium_phones show lines" "digium_phones show networks" \
+			"digium_phones show phones" "digium_phones show sessions" "digium_phones show settings" \
+      "digium_phones show translations" ;
 	do
     echo "asterisk -rx \"$command\"" >> $OUTPUT;
     asterisk -rx "$command" >> $OUTPUT;
@@ -317,19 +318,17 @@ echo >> $OUTPUT;
 echo -n "."
 
 echo "----------------------------" >> $OUTPUT;
-echo "CAT OF DAHDI/ZAPTEL CHANNELS : cat /proc/dahdi/" >> $OUTPUT;
+echo "CAT OF DAHDI CHANNELS : cat /proc/dahdi/" >> $OUTPUT;
 echo "----------------------------" >> $OUTPUT;
-for tech in dahdi zaptel zap; do
-  if [ -d /proc/$tech/ ]; then
-    for file in $(ls /proc/$tech/ 2> /dev/null); do
-      echo "----------------------------" >> $OUTPUT;
-      echo "/proc/$tech/$file:" >> $OUTPUT;
-      cat /proc/$tech/$file >> $OUTPUT;
-      echo >> $OUTPUT;
-      echo -n "."
-    done
-  fi
-done
+if [ -d /proc/dahdi/ ]; then
+  for file in $(ls /proc/dahdi/ 2> /dev/null); do
+    echo "----------------------------" >> $OUTPUT;
+    echo "/proc/dahdi/$file:" >> $OUTPUT;
+    cat /proc/dahdi/$file >> $OUTPUT;
+    echo >> $OUTPUT;
+    echo -n "."
+  done
+fi
 echo >> $OUTPUT;
 
 echo "------------------" >> $OUTPUT;
@@ -371,7 +370,7 @@ for mod in $SUPPORTED_MODULES; do
 done
 
 echo "------------------------" >> $OUTPUT;
-echo "DAHDI/ZAPTEL MODULE INFO : modinfo" >> $OUTPUT;
+echo "DAHDI MODULE INFO : modinfo" >> $OUTPUT;
 echo "------------------------" >> $OUTPUT;
 for file in $(ls /lib/modules/$(uname -r)/dahdi/*.ko \
   /lib/modules/$(uname -r)/dahdi/*/*.ko\
@@ -518,26 +517,9 @@ collect_config_backup()
     TAR_FILES="$TAR_FILES /etc/dahdi*"
   fi
 
-  # Check for Zaptel Module configuration
-  if [ -f /etc/sysconfig/zaptel ]; then
-    TAR_FILES="$TAR_FILES /etc/sysconfig/zaptel*"
-  fi
-
-  # Check for Zaptel Module configuration (alternate location)
-  if [ -f /etc/default/zaptel ]; then
-    TAR_FILES="$TAR_FILES /etc/default/zaptel*"
-  fi
-
-  # Grab the dahdi/zaptel init scripts, in case they have been modified
-  for driver in dahdi zaptel; do
-    if [ -f /etc/init.d/$driver ]; then
-      TAR_FILES="$TAR_FILES /etc/init.d/$driver"
-    fi
-  done
-
-  # Check for zaptel.conf
-  if [ -f /etc/zaptel.conf ]; then
-    TAR_FILES="$TAR_FILES /etc/zaptel*"
+  # Grab the dahdi init scripts, in case they have been modified
+  if [ -f /etc/init.d/dahdi ]; then
+    TAR_FILES="$TAR_FILES /etc/init.d/dahdi"
   fi
 
   # Check for fxotune.conf
@@ -567,6 +549,9 @@ collect_config_backup()
   if [ -f /var/log/messages ]; then
     TAR_FILES="$TAR_FILES /var/log/messages"
   fi
+  if [ -f /var/log/syslog ]; then
+    TAR_FILES="$TAR_FILES /var/log/syslog"
+  fi
 
   [ -f $TARBALL_OUTPUT ] && rm -rf $TARBALL_OUTPUT
   [ -f $TARBALL_OUTPUT.gz ] && rm -rf $TARBALL_OUTPUT.gz
@@ -616,8 +601,8 @@ else
   echo "pci listing, dmesg, running processes, and kernel version"
   echo
   echo "2. A backup of elements of your configuration such as:"
-  echo "asterisk config files, license files, loaded dahdi/zaptel module"
-  echo "parameters, and other asterisk/dahdi/zaptel related files."
+  echo "asterisk config files, license files, loaded dahdi module"
+  echo "parameters, and other asterisk/dahdi related files."
   echo
   echo "Collect this information [y/n] ? "
   read files;
diff --git a/funcs/func_callerid.c b/funcs/func_callerid.c
index 4f6a677..621a6f2 100644
--- a/funcs/func_callerid.c
+++ b/funcs/func_callerid.c
@@ -40,10 +40,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/callerid.h"
 
 /*
- * Do not document the CALLERID(pres) datatype.
- * The name and number now have their own presentation value.  The pres
- * option will simply live on as a historical relic with as best
- * as can be managed backward compatible meaning.
+ * The CALLERID(pres) datatype is shorthand for getting/setting the
+ * combined value of name-pres and num-pres.  Some channel drivers
+ * don't make a distinction, so it makes sense to only use one property
+ * to get/set it.  The same applies to CONNECTEDLINE(pres),
+ * REDIRECTING(orig-pres), REDIRECTING(from-pres) and REDIRECTING(to-pres).
  *
  * Do not document the CALLERID(ton) datatype.
  * It is an alias for num-plan.
@@ -55,11 +56,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  * It has turned out to not be needed.  The source value is really
  * only useful as a possible tracing aid.
  *
- * Do not document the CONNECTEDLINE(pres) datatype.
- * The name and number now have their own presentation value.  The pres
- * option will simply live on as a historical relic with as best
- * as can be managed backward compatible meaning.
- *
  * Do not document the CONNECTEDLINE(ton) datatype.
  * It is an alias for num-plan.
  *
@@ -69,12 +65,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  * they are active at the same time.  The plain pres option will simply
  * live on as a historical relic.
  *
- * Do not document the REDIRECTING(orig-pres), REDIRECTING(from-pres),
- * or REDIRECTING(to-pres) datatypes.
- * The name and number now have their own presentation value.  The orig-pres,
- * from-pres, and to-pres options will simply live on as a historical relic
- * with as best as can be managed backward compatible meaning.
- *
  * Do not document the REDIRECTING(orig-ton), REDIRECTING(from-ton),
  * or REDIRECTING(to-ton) datatypes.
  * They are aliases for orig-num-plan, from-num-plan, and to-num-plan
@@ -98,6 +88,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 					<enum name = "num-valid" />
 					<enum name = "num-plan" />
 					<enum name = "num-pres" />
+					<enum name = "pres" />
 					<enum name = "subaddr" />
 					<enum name = "subaddr-valid" />
 					<enum name = "subaddr-type" />
@@ -144,6 +135,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 		<description>
 			<para>Gets or sets Caller*ID data on the channel. Uses channel callerid by
 			default or optional callerid, if specified.</para>
+			<para>The <replaceable>pres</replaceable> field gets/sets a combined value
+			for <replaceable>name-pres</replaceable> and
+			<replaceable>num-pres</replaceable>.</para>
 			<para>The allowable values for the <replaceable>name-charset</replaceable>
 			field are the following:</para>
 			<enumlist>
@@ -168,7 +162,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 		<description>
 			<para>Gets or sets Caller*ID presentation on the channel.
 			This function is deprecated in favor of CALLERID(num-pres)
-			and CALLERID(name-pres).
+			and CALLERID(name-pres) or CALLERID(pres) to get/set both
+			at once.
 			The following values are valid:</para>
 			<enumlist>
 				<enum name="allowed_not_screened">
@@ -218,6 +213,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 					<enum name = "num-valid" />
 					<enum name = "num-plan" />
 					<enum name = "num-pres" />
+					<enum name = "pres" />
 					<enum name = "subaddr" />
 					<enum name = "subaddr-valid" />
 					<enum name = "subaddr-type" />
@@ -246,6 +242,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 		</syntax>
 		<description>
 			<para>Gets or sets Connected Line data on the channel.</para>
+			<para>The <replaceable>pres</replaceable> field gets/sets a combined value
+			for <replaceable>name-pres</replaceable> and
+			<replaceable>num-pres</replaceable>.</para>
 			<para>The allowable values for the <replaceable>name-charset</replaceable>
 			field are the following:</para>
 			<enumlist>
@@ -279,6 +278,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 					<enum name = "orig-num-valid" />
 					<enum name = "orig-num-plan" />
 					<enum name = "orig-num-pres" />
+					<enum name = "orig-pres" />
 					<enum name = "orig-subaddr" />
 					<enum name = "orig-subaddr-valid" />
 					<enum name = "orig-subaddr-type" />
@@ -294,6 +294,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 					<enum name = "from-num-valid" />
 					<enum name = "from-num-plan" />
 					<enum name = "from-num-pres" />
+					<enum name = "from-pres" />
 					<enum name = "from-subaddr" />
 					<enum name = "from-subaddr-valid" />
 					<enum name = "from-subaddr-type" />
@@ -308,6 +309,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 					<enum name = "to-num-valid" />
 					<enum name = "to-num-plan" />
 					<enum name = "to-num-pres" />
+					<enum name = "to-pres" />
 					<enum name = "to-subaddr" />
 					<enum name = "to-subaddr-valid" />
 					<enum name = "to-subaddr-type" />
@@ -366,6 +368,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 		</syntax>
 		<description>
 			<para>Gets or sets Redirecting data on the channel.</para>
+			<para>The <replaceable>orig-pres</replaceable>,
+			<replaceable>from-pres</replaceable> and <replaceable>to-pres</replaceable>
+			fields get/set a combined value for the corresponding
+			<replaceable>...-name-pres</replaceable> and <replaceable>...-num-pres</replaceable>
+			fields.</para>
 			<para>The allowable values for the <replaceable>reason</replaceable>
 			and <replaceable>orig-reason</replaceable> fields are the following:</para>
 			<enumlist>
diff --git a/funcs/func_channel.c b/funcs/func_channel.c
index 7f87f45..0f59bb5 100644
--- a/funcs/func_channel.c
+++ b/funcs/func_channel.c
@@ -443,7 +443,7 @@ static int func_channel_read(struct ast_channel *chan, const char *function,
 	if (!strcasecmp(data, "audionativeformat")) {
 		tmpcap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
 		if (tmpcap) {
-			struct ast_str *codec_buf = ast_str_alloca(128);
+			struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_channel_lock(chan);
 			ast_format_cap_append_from_cap(tmpcap, ast_channel_nativeformats(chan), AST_MEDIA_TYPE_AUDIO);
@@ -454,7 +454,7 @@ static int func_channel_read(struct ast_channel *chan, const char *function,
 	} else if (!strcasecmp(data, "videonativeformat")) {
 		tmpcap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
 		if (tmpcap) {
-			struct ast_str *codec_buf = ast_str_alloca(128);
+			struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_channel_lock(chan);
 			ast_format_cap_append_from_cap(tmpcap, ast_channel_nativeformats(chan), AST_MEDIA_TYPE_VIDEO);
diff --git a/funcs/func_holdintercept.c b/funcs/func_holdintercept.c
new file mode 100644
index 0000000..56d9a9e
--- /dev/null
+++ b/funcs/func_holdintercept.c
@@ -0,0 +1,236 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Digium, Inc.
+ *
+ * Matt Jordan <mjordan at 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.
+ */
+
+/*! \file
+ *
+ * \brief Function that intercepts HOLD frames from channels and raises events
+ *
+ * \author Matt Jordan <mjordan at digium.com>
+ *
+ * \ingroup functions
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/app.h"
+#include "asterisk/frame.h"
+#include "asterisk/stasis.h"
+#include "asterisk/stasis_channels.h"
+
+/*** DOCUMENTATION
+	<function name="HOLD_INTERCEPT" language="en_US">
+		<synopsis>
+			Intercepts hold frames on a channel and raises an event instead of passing the frame on
+		</synopsis>
+		<syntax>
+			<parameter name="action" required="true">
+				<optionlist>
+					<option name="remove">
+						<para>W/O. Removes the hold interception function.</para>
+					</option>
+					<option name="set">
+						<para>W/O. Enable hold interception on the channel. When
+						enabled, the channel will intercept any hold action that
+						is signalled from the device, and instead simply raise an
+						event (AMI/ARI) indicating that the channel wanted to put other
+						parties on hold.</para>
+					</option>
+				</optionlist>
+			</parameter>
+		</syntax>
+	</function>
+***/
+
+/*! \brief Private data structure used with the function's datastore */
+struct hold_intercept_data {
+	int framehook_id;
+};
+
+/*! \brief The channel datastore the function uses to store state */
+static const struct ast_datastore_info hold_intercept_datastore = {
+	.type = "hold_intercept",
+};
+
+/*! \internal \brief Disable hold interception on the channel */
+static int remove_hold_intercept(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore = NULL;
+	struct hold_intercept_data *data;
+	SCOPED_CHANNELLOCK(chan_lock, chan);
+
+	datastore = ast_channel_datastore_find(chan, &hold_intercept_datastore, NULL);
+	if (!datastore) {
+		ast_log(AST_LOG_WARNING, "Cannot remove HOLD_INTERCEPT from %s: HOLD_INTERCEPT not currently enabled\n",
+		        ast_channel_name(chan));
+		return -1;
+	}
+	data = datastore->data;
+
+	if (ast_framehook_detach(chan, data->framehook_id)) {
+		ast_log(AST_LOG_WARNING, "Failed to remove HOLD_INTERCEPT framehook from channel %s\n",
+		        ast_channel_name(chan));
+		return -1;
+	}
+
+	if (ast_channel_datastore_remove(chan, datastore)) {
+		ast_log(AST_LOG_WARNING, "Failed to remove HOLD_INTERCEPT datastore from channel %s\n",
+		        ast_channel_name(chan));
+		return -1;
+	}
+	ast_datastore_free(datastore);
+
+	return 0;
+}
+
+/*! \brief Frame hook that is called to intercept hold/unhold */
+static struct ast_frame *hold_intercept_framehook(struct ast_channel *chan,
+	struct ast_frame *f, enum ast_framehook_event event, void *data)
+{
+	int frame_type;
+
+	if (!f || (event != AST_FRAMEHOOK_EVENT_WRITE)) {
+		return f;
+	}
+
+	if (f->frametype != AST_FRAME_CONTROL) {
+		return f;
+	}
+
+	frame_type = f->subclass.integer;
+	if (frame_type != AST_CONTROL_HOLD && frame_type != AST_CONTROL_UNHOLD) {
+		return f;
+	}
+
+	/* Munch munch */
+	ast_frfree(f);
+	f = &ast_null_frame;
+
+	ast_channel_publish_cached_blob(chan,
+		frame_type == AST_CONTROL_HOLD ? ast_channel_hold_type() : ast_channel_unhold_type(),
+		NULL);
+
+	return f;
+}
+
+/*! \brief Callback function which informs upstream if we are consuming a frame of a specific type */
+static int hold_intercept_framehook_consume(void *data, enum ast_frame_type type)
+{
+	return (type == AST_FRAME_CONTROL ? 1 : 0);
+}
+
+/*! \internal \brief Enable hold interception on the channel */
+static int set_hold_intercept(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore;
+	struct hold_intercept_data *data;
+	static struct ast_framehook_interface hold_framehook_interface = {
+		.version = AST_FRAMEHOOK_INTERFACE_VERSION,
+		.event_cb = hold_intercept_framehook,
+		.consume_cb = hold_intercept_framehook_consume,
+		.disable_inheritance = 1,
+	};
+	SCOPED_CHANNELLOCK(chan_lock, chan);
+
+	datastore = ast_channel_datastore_find(chan, &hold_intercept_datastore, NULL);
+	if (datastore) {
+		ast_log(AST_LOG_WARNING, "HOLD_INTERCEPT already set on '%s'\n",
+		        ast_channel_name(chan));
+		return 0;
+	}
+
+	datastore = ast_datastore_alloc(&hold_intercept_datastore, NULL);
+	if (!datastore) {
+		return -1;
+	}
+
+	data = ast_calloc(1, sizeof(*data));
+	if (!data) {
+		ast_datastore_free(datastore);
+		return -1;
+	}
+
+	data->framehook_id = ast_framehook_attach(chan, &hold_framehook_interface);
+	if (data->framehook_id < 0) {
+		ast_log(AST_LOG_WARNING, "Failed to attach HOLD_INTERCEPT framehook to '%s'\n",
+		        ast_channel_name(chan));
+		ast_datastore_free(datastore);
+		ast_free(data);
+		return -1;
+	}
+	datastore->data = data;
+
+	ast_channel_datastore_add(chan, datastore);
+
+	return 0;
+}
+
+/*! \internal \brief HOLD_INTERCEPT write function callback */
+static int hold_intercept_fn_write(struct ast_channel *chan, const char *function,
+	char *data, const char *value)
+{
+	int res;
+
+	if (!chan) {
+		return -1;
+	}
+
+	if (ast_strlen_zero(data)) {
+		ast_log(AST_LOG_WARNING, "HOLD_INTERCEPT requires an argument\n");
+		return -1;
+	}
+
+	if (!strcasecmp(data, "set")) {
+		res = set_hold_intercept(chan);
+	} else if (!strcasecmp(data, "remove")) {
+		res = remove_hold_intercept(chan);
+	} else {
+		ast_log(AST_LOG_WARNING, "HOLD_INTERCEPT: unknown option %s\n", data);
+		res = -1;
+	}
+
+	return res;
+}
+
+/*! \brief Definition of the HOLD_INTERCEPT function */
+static struct ast_custom_function hold_intercept_function = {
+	.name = "HOLD_INTERCEPT",
+	.write = hold_intercept_fn_write,
+};
+
+/*! \internal \brief Unload the module */
+static int unload_module(void)
+{
+	return ast_custom_function_unregister(&hold_intercept_function);
+}
+
+/*! \internal \brief Load the module */
+static int load_module(void)
+{
+	return ast_custom_function_register(&hold_intercept_function) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Hold interception dialplan function");
diff --git a/funcs/func_presencestate.c b/funcs/func_presencestate.c
index f1af8b4..ad452df 100644
--- a/funcs/func_presencestate.c
+++ b/funcs/func_presencestate.c
@@ -255,7 +255,9 @@ static enum ast_presence_state custom_presence_callback(const char *data, char *
 	char *_message;
 	char *_subtype;
 
-	ast_db_get(astdb_family, data, buf, sizeof(buf));
+	if (ast_db_get(astdb_family, data, buf, sizeof(buf))) {
+		return AST_PRESENCE_NOT_SET;
+	}
 
 	if (parse_data(buf, &state, &_subtype, &_message, &_options)) {
 		return AST_PRESENCE_INVALID;
diff --git a/include/asterisk/ast_version.h b/include/asterisk/ast_version.h
index 51ff481..1ceac30 100644
--- a/include/asterisk/ast_version.h
+++ b/include/asterisk/ast_version.h
@@ -41,4 +41,7 @@ const char *ast_get_version(void);
  */
 const char *ast_get_version_num(void);
 
+/*! Retreive the Asterisk build options */
+const char *ast_get_build_opts(void);
+
 #endif /* __AST_VERSION_H */
diff --git a/include/asterisk/bridge.h b/include/asterisk/bridge.h
index fc37262..30ac095 100644
--- a/include/asterisk/bridge.h
+++ b/include/asterisk/bridge.h
@@ -746,6 +746,18 @@ int ast_bridge_suspend(struct ast_bridge *bridge, struct ast_channel *chan);
  */
 int ast_bridge_unsuspend(struct ast_bridge *bridge, struct ast_channel *chan);
 
+/*!
+ * \brief Sets BRIDGECHANNEL and BRIDGEPVTCALLID for a channel
+ *
+ * \pre chan must be locked before calling
+ *
+ * \param name channel name of the bridged peer
+ * \param pvtid Private CallID of the bridged peer
+ *
+ * \return nothing
+ */
+void ast_bridge_vars_set(struct ast_channel *chan, const char *name, const char *pvtid);
+
 struct ast_unreal_pvt;
 
 /*!
diff --git a/include/asterisk/format_cap.h b/include/asterisk/format_cap.h
index 94e81f8..a86fd21 100644
--- a/include/asterisk/format_cap.h
+++ b/include/asterisk/format_cap.h
@@ -321,6 +321,11 @@ int ast_format_cap_has_type(const struct ast_format_cap *cap, enum ast_media_typ
  */
 const char *ast_format_cap_get_names(struct ast_format_cap *cap, struct ast_str **buf);
 
+#ifndef AST_FORMAT_CAP_NAMES_LEN
+/*! Buffer size for callers of ast_format_cap_get_names to allocate. */
+#define AST_FORMAT_CAP_NAMES_LEN 384
+#endif
+
 /*!
  * \brief Determine if a format cap has no formats in it.
  *
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index a36935d..6ca56bd 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -170,6 +170,8 @@ struct ast_sip_contact {
 	double qualify_timeout;
 	/*! Endpoint that added the contact, only available in observers */
 	struct ast_sip_endpoint *endpoint;
+	/*! The name of the aor this contact belongs to */
+	char *aor;
 };
 
 #define CONTACT_STATUS "contact_status"
@@ -201,6 +203,10 @@ struct ast_sip_contact_status {
 	int64_t rtt;
 	/*! Last status for a contact (default - unavailable) */
 	enum ast_sip_contact_status_type last_status;
+	/*! The name of the aor this contact_status belongs to */
+	char *aor;
+	/*! The original contact's URI */
+	char *uri;
 };
 
 /*!
@@ -1190,6 +1196,11 @@ int ast_sip_push_task(struct ast_taskprocessor *serializer, int (*sip_task)(void
  * cause a deadlock. If you are in a SIP servant thread, just call your function
  * in-line.
  *
+ * \warning \b Never hold locks that may be acquired by a SIP servant thread when
+ * calling this function. Doing so may cause a deadlock if all SIP servant threads
+ * are blocked waiting to acquire the lock while the thread holding the lock is
+ * waiting for a free SIP servant thread.
+ *
  * \param serializer The SIP serializer to which the task belongs. May be NULL.
  * \param sip_task The task to execute
  * \param task_data The parameter to pass to the task when it executes
@@ -2111,4 +2122,10 @@ int ast_sip_get_host_ip(int af, pj_sockaddr *addr);
  */
 const char *ast_sip_get_host_ip_string(int af);
 
+/*!
+ * \brief Return the size of the SIP threadpool's task queue
+ * \since 13.7.0
+ */
+long ast_sip_threadpool_queue_size(void);
+
 #endif /* _RES_PJSIP_H */
diff --git a/include/asterisk/res_pjsip_cli.h b/include/asterisk/res_pjsip_cli.h
index 44979b7..c253521 100644
--- a/include/asterisk/res_pjsip_cli.h
+++ b/include/asterisk/res_pjsip_cli.h
@@ -61,7 +61,7 @@ struct ast_sip_cli_formatter_entry {
 	/*! The callback used to print the details of the object. */
 	ao2_callback_fn *print_body;
 	/*! The function used to retrieve a container of all objects of this type. */
-	struct ao2_container *(* get_container)(void);
+	struct ao2_container *(* get_container)(const char *regex);
 	/*! The function used to iterate over a container of objects. */
 	int (* iterate)(void *container, ao2_callback_fn callback, void *args);
 	/*! The function used to retrieve a specific object from it's container. */
diff --git a/include/asterisk/res_pjsip_pubsub.h b/include/asterisk/res_pjsip_pubsub.h
index afa0d69..c9b66dc 100644
--- a/include/asterisk/res_pjsip_pubsub.h
+++ b/include/asterisk/res_pjsip_pubsub.h
@@ -684,6 +684,15 @@ const char *ast_sip_subscription_get_body_type(struct ast_sip_subscription *sub)
  */
 const char *ast_sip_subscription_get_body_subtype(struct ast_sip_subscription *sub);
 
+/*!
+ * \since 13.6.0
+ * \brief Alert the pubsub core that the subscription is ready for destruction
+ *
+ * \param sub The subscription that is complete
+ * \return Nothing
+ */
+void ast_sip_subscription_destroy(struct ast_sip_subscription *sub);
+
 /*! \brief Determines whether the res_pjsip_pubsub module is loaded */
 #define CHECK_PJSIP_PUBSUB_MODULE_LOADED()			\
 	do {							\
diff --git a/include/asterisk/statsd.h b/include/asterisk/statsd.h
index 8e5e2f9..4cbd213 100644
--- a/include/asterisk/statsd.h
+++ b/include/asterisk/statsd.h
@@ -29,7 +29,12 @@
 #include "asterisk/optional_api.h"
 
 /*! An instantaneous measurement of a value. */
-#define AST_STATSD_GUAGE "g"
+#define AST_STATSD_GAUGE "g"
+/*!
+ * Embarrassingly, gauge was misspelled for quite some time.
+ * \deprecated You should spell gauge correctly.
+ */
+#define AST_STATSD_GUAGE AST_STATSD_GAUGE
 /*! A change in a value. */
 #define AST_STATSD_COUNTER "c"
 /*! Measure of milliseconds. */
@@ -42,8 +47,46 @@
 /*!
  * \brief Send a stat to the configured statsd server.
  *
- * The is the most flexible function for sending a message to the statsd server,
- * but also the least easy to use. See ast_statsd_log() or
+ * This function uses a character argument for value instead of
+ * an intmax_t argument. This is designed to be simpler to use for
+ * updating a current value rather than resetting it.
+ *
+ * \param metric_name String (UTF-8) name of the metric.
+ * \param type_str Type of metric to send.
+ * \param value Value to send.
+ * \param sample_rate Percentage of samples to send.
+ * \since 13
+ */
+AST_OPTIONAL_API(void, ast_statsd_log_string, (const char *metric_name,
+	const char *metric_type, const char *value, double sample_rate), {});
+
+/*!
+ * \brief Send a stat to the configured statsd server.
+ * \since 13.7.0
+ *
+ * This is the most flexible function for sending a message to the statsd
+ * server. In addition to allowing the string value and sample rate to be specified,
+ * the metric_name can be formed as a printf style string with variable
+ * arguments.
+ *
+ * \param metric_name Format string (UTF-8) specifying the name of the metric.
+ * \param metric_type Type of metric to send.
+ * \param value Value to send.
+ * \param sample_rate Percentage of samples to send.
+ *
+ * Example Usage:
+ * \code
+ *     ast_statsd_log_string_va(AST_STATSD_GAUGE, "+1", 1.0, "endpoints.states.%s", state_name);
+ * \endcode
+ */
+AST_OPTIONAL_API_ATTR(void, format(printf, 1, 5), ast_statsd_log_string_va,
+	(const char *metric_name, const char *metric_type, const char *value, double sample_rate, ...), {});
+
+/*!
+ * \brief Send a stat to the configured statsd server.
+ *
+ * The is nearly the most flexible function for sending a message to the statsd
+ * server, but also the least easy to use. See ast_statsd_log() or
  * ast_statsd_log_sample() for a slightly more convenient interface.
  *
  * \param metric_name String (UTF-8) name of the metric.
@@ -57,6 +100,28 @@ AST_OPTIONAL_API(void, ast_statsd_log_full, (const char *metric_name,
 
 /*!
  * \brief Send a stat to the configured statsd server.
+ * \since 13.7.0
+ *
+ * This is the most flexible function for sending a message to the statsd
+ * server. In addition to allowing the value and sample rate to be specified,
+ * the metric_name can be formed as a printf style string with variable
+ * arguments.
+ *
+ * \param metric_name Format string (UTF-8) specifying the name of the metric.
+ * \param metric_type Type of metric to send.
+ * \param value Value to send.
+ * \param sample_rate Percentage of samples to send.
+ *
+ * Example Usage:
+ * \code
+ *     ast_statsd_log_full_va(AST_STATSD_TIMER, rtt, 1.0, "endpoint.%s.rtt", endpoint_name);
+ * \endcode
+ */
+AST_OPTIONAL_API_ATTR(void, format(printf, 1, 5), ast_statsd_log_full_va,
+	(const char *metric_name, const char *metric_type, intmax_t value, double sample_rate, ...), {});
+
+/*!
+ * \brief Send a stat to the configured statsd server.
  * \param metric_name String (UTF-8) name of the metric.
  * \param metric_type Type of metric to send.
  * \param value Value to send.
diff --git a/include/asterisk/taskprocessor.h b/include/asterisk/taskprocessor.h
index f16f144..6ebf072 100644
--- a/include/asterisk/taskprocessor.h
+++ b/include/asterisk/taskprocessor.h
@@ -56,6 +56,8 @@
 
 struct ast_taskprocessor;
 
+#define AST_TASKPROCESSOR_HIGH_WATER_LEVEL 500
+
 /*!
  * \brief ast_tps_options for specification of taskprocessor options
  *
@@ -262,4 +264,10 @@ int ast_taskprocessor_is_task(struct ast_taskprocessor *tps);
  */
 const char *ast_taskprocessor_name(struct ast_taskprocessor *tps);
 
+/*!
+ * \brief Return the current size of the taskprocessor queue
+ * \since 13.7.0
+ */
+long ast_taskprocessor_size(struct ast_taskprocessor *tps);
+
 #endif /* __AST_TASKPROCESSOR_H__ */
diff --git a/include/asterisk/term.h b/include/asterisk/term.h
index 18d743b..fac36eb 100644
--- a/include/asterisk/term.h
+++ b/include/asterisk/term.h
@@ -67,8 +67,8 @@ extern "C" {
 #define COLORIZE_FMT	"%s%s%s"
 #define COLORIZE(fg, bg, str)	ast_term_color(fg,bg),str,ast_term_reset()
 /*! \brief Maximum number of characters needed for a color escape sequence,
- *         plus a null char */
-#define AST_TERM_MAX_ESCAPE_CHARS   12
+ *         and another one for a trailing reset, plus a null char */
+#define AST_TERM_MAX_ESCAPE_CHARS   23
 #define AST_TERM_MAX_ROTATING_BUFFERS	15
 
 /*! \brief Colorize a specified string by adding terminal color codes
diff --git a/include/asterisk/threadpool.h b/include/asterisk/threadpool.h
index 75ce0e4..0f360c7 100644
--- a/include/asterisk/threadpool.h
+++ b/include/asterisk/threadpool.h
@@ -292,4 +292,10 @@ struct ast_taskprocessor *ast_threadpool_serializer(const char *name, struct ast
 struct ast_taskprocessor *ast_threadpool_serializer_group(const char *name,
 	struct ast_threadpool *pool, struct ast_serializer_shutdown_group *shutdown_group);
 
+/*!
+ * \brief Return the size of the threadpool's task queue
+ * \since 13.7.0
+ */
+long ast_threadpool_queue_size(struct ast_threadpool *pool);
+
 #endif /* ASTERISK_THREADPOOL_H */
diff --git a/include/asterisk/translate.h b/include/asterisk/translate.h
index e8e4c02..e2a4f30 100644
--- a/include/asterisk/translate.h
+++ b/include/asterisk/translate.h
@@ -223,6 +223,14 @@ struct ast_trans_pvt {
 	struct ast_trans_pvt *next; /*!< next in translator chain */
 	struct timeval nextin;
 	struct timeval nextout;
+	/*! If a translation path using a format with attributes requires the output
+	 * to be a specific set of attributes, this variable will be set describing
+	 * those attributes to the translator. Otherwise, the translator must choose
+	 * a set of format attributes for the destination that preserves the quality
+	 * of the audio in the best way possible. For example with the Opus Codec,
+	 * explicit_dst contains an attribute which describes whether both parties
+	 * want to do forward-error correction (FEC). */
+	struct ast_format *explicit_dst;
 };
 
 /*! \brief generic frameout function */
diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h
index 664e347..c7a4737 100644
--- a/include/asterisk/utils.h
+++ b/include/asterisk/utils.h
@@ -1089,4 +1089,27 @@ char *ast_crypt_encrypt(const char *key);
  */
 int ast_crypt_validate(const char *key, const char *expected);
 
+/*
+ * \brief Test that a file exists and is readable by the effective user.
+ * \since 13.7.0
+ *
+ * \param filename File to test.
+ * \return True (non-zero) if the file exists and is readable.
+ * \return False (zero) if the file either doesn't exists or is not readable.
+ */
+int ast_file_is_readable(const char *filename);
+
+/*
+ * \brief Compare 2 major.minor.patch.extra version strings.
+ * \since 13.7.0
+ *
+ * \param version1.
+ * \param version2.
+ *
+ * \return <0 if version 1 < version 2.
+ * \return =0 if version 1 = version 2.
+ * \return >0 if version 1 > version 2.
+ */
+int ast_compare_versions(const char *version1, const char *version2);
+
 #endif /* _ASTERISK_UTILS_H */
diff --git a/main/aoc.c b/main/aoc.c
index 5bce066..29c5e87 100644
--- a/main/aoc.c
+++ b/main/aoc.c
@@ -1667,11 +1667,11 @@ static struct ast_json *charge_to_json(const struct ast_aoc_decoded *decoded)
 	}
 
 	return ast_json_pack(
-		"{s:s, s:s, s:s, s:O}",
+		"{s:s, s:s, s:s, s:o}",
 		"Type", aoc_charge_type_str(decoded->charge_type),
 		"BillingID", aoc_billingid_str(decoded->billing_id),
 		"TotalType", aoc_type_of_totaling_str(decoded->total_type),
-		obj_type, obj);
+		obj_type, ast_json_ref(obj));
 }
 
 static struct ast_json *association_to_json(const struct ast_aoc_decoded *decoded)
@@ -1738,10 +1738,10 @@ static struct ast_json *s_to_json(const struct ast_aoc_decoded *decoded)
 					"Scale", decoded->aoc_s_entries[i].rate.duration.granularity_time_scale);
 			}
 
-			type = ast_json_pack("{s:O, s:s, s:O, s:O}", "Currency", currency, "ChargingType",
+			type = ast_json_pack("{s:o, s:s, s:o, s:o}", "Currency", ast_json_ref(currency), "ChargingType",
 					     decoded->aoc_s_entries[i].rate.duration.charging_type ?
-					     "StepFunction" : "ContinuousCharging", "Time", time,
-					     "Granularity", granularity ? granularity : ast_json_null());
+					     "StepFunction" : "ContinuousCharging", "Time", ast_json_ref(time),
+					     "Granularity", granularity ? ast_json_ref(granularity) : ast_json_ref(ast_json_null()));
 
 			break;
 		}
@@ -1751,7 +1751,7 @@ static struct ast_json *s_to_json(const struct ast_aoc_decoded *decoded)
 				decoded->aoc_s_entries[i].rate.flat.amount,
 				decoded->aoc_s_entries[i].rate.flat.multiplier);
 
-			type = ast_json_pack("{s:O}", "Currency", currency);
+			type = ast_json_pack("{s:o}", "Currency", ast_json_ref(currency));
 			break;
 		case AST_AOC_RATE_TYPE_VOLUME:
 			currency = currency_to_json(
@@ -1760,9 +1760,9 @@ static struct ast_json *s_to_json(const struct ast_aoc_decoded *decoded)
 				decoded->aoc_s_entries[i].rate.volume.multiplier);
 
 			type = ast_json_pack(
-				"{s:s, s:O}", "Unit", aoc_volume_unit_str(
+				"{s:s, s:o}", "Unit", aoc_volume_unit_str(
 					decoded->aoc_s_entries[i].rate.volume.volume_unit),
-				"Currency", currency);
+				"Currency", ast_json_ref(currency));
 			break;
 		case AST_AOC_RATE_TYPE_SPECIAL_CODE:
 			type = ast_json_pack("{s:i}", "SpecialCode",
@@ -1772,8 +1772,8 @@ static struct ast_json *s_to_json(const struct ast_aoc_decoded *decoded)
 			break;
 		}
 
-		rate = ast_json_pack("{s:s, s:O}", "Chargeable", charge_item,
-				     aoc_rate_type_str(decoded->aoc_s_entries[i].rate_type), type);
+		rate = ast_json_pack("{s:s, s:o}", "Chargeable", charge_item,
+				     aoc_rate_type_str(decoded->aoc_s_entries[i].rate_type), ast_json_ref(type));
 		if (ast_json_array_append(rates, rate)) {
 			break;
 		}
diff --git a/main/asterisk.c b/main/asterisk.c
index a9c6d0f..3f16caf 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -372,9 +372,14 @@ struct console consoles[AST_MAX_CONNECTS];
 
 char ast_defaultlanguage[MAX_LANGUAGE] = DEFAULT_LANGUAGE;
 
-static int ast_el_add_history(char *);
-static int ast_el_read_history(char *);
-static int ast_el_write_history(char *);
+static int ast_el_add_history(const char *);
+static int ast_el_read_history(const char *);
+static int ast_el_write_history(const char *);
+
+static void ast_el_read_default_histfile(void);
+static void ast_el_write_default_histfile(void);
+
+static void asterisk_daemon(int isroot, const char *runuser, const char *rungroup);
 
 struct _cfg_paths {
 	char config_dir[PATH_MAX];
@@ -617,7 +622,7 @@ static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_c
 	ast_cli(a->fd, "\nPBX Core settings\n");
 	ast_cli(a->fd, "-----------------\n");
 	ast_cli(a->fd, "  Version:                     %s\n", ast_get_version());
-	ast_cli(a->fd, "  Build Options:               %s\n", S_OR(AST_BUILDOPTS, "(none)"));
+	ast_cli(a->fd, "  Build Options:               %s\n", S_OR(ast_get_build_opts(), "(none)"));
 	if (ast_option_maxcalls)
 		ast_cli(a->fd, "  Maximum calls:               %d (Current %d)\n", ast_option_maxcalls, ast_active_channels());
 	else
@@ -2064,13 +2069,7 @@ static void really_quit(int num, shutdown_nice_t niceness, int restart)
 	}
 
 	if (ast_opt_console || (ast_opt_remote && !ast_opt_exec)) {
-		char filename[80] = "";
-		if (getenv("HOME")) {
-			snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
-		}
-		if (!ast_strlen_zero(filename)) {
-			ast_el_write_history(filename);
-		}
+		ast_el_write_default_histfile();
 		if (consolethread == AST_PTHREADT_NULL || consolethread == pthread_self()) {
 			/* Only end if we are the consolethread, otherwise there's a race with that thread. */
 			if (el != NULL) {
@@ -2301,7 +2300,7 @@ static void console_verboser(const char *s)
 	}
 }
 
-static int ast_all_zeros(char *s)
+static int ast_all_zeros(const char *s)
 {
 	while (*s) {
 		if (*s > 32)
@@ -2312,7 +2311,7 @@ static int ast_all_zeros(char *s)
 }
 
 /* This is the main console CLI command handler.  Run by the main() thread. */
-static void consolehandler(char *s)
+static void consolehandler(const char *s)
 {
 	printf("%s", term_end());
 	fflush(stdout);
@@ -2330,7 +2329,7 @@ static void consolehandler(char *s)
 		ast_cli_command(STDOUT_FILENO, s);
 }
 
-static int remoteconsolehandler(char *s)
+static int remoteconsolehandler(const char *s)
 {
 	int ret = 0;
 
@@ -3247,7 +3246,7 @@ static int ast_el_initialize(void)
 
 #define MAX_HISTORY_COMMAND_LENGTH 256
 
-static int ast_el_add_history(char *buf)
+static int ast_el_add_history(const char *buf)
 {
 	HistEvent ev;
 	char *stripped_buf;
@@ -3269,7 +3268,7 @@ static int ast_el_add_history(char *buf)
 	return history(el_hist, &ev, H_ENTER, stripped_buf);
 }
 
-static int ast_el_write_history(char *filename)
+static int ast_el_write_history(const char *filename)
 {
 	HistEvent ev;
 
@@ -3279,7 +3278,7 @@ static int ast_el_write_history(char *filename)
 	return (history(el_hist, &ev, H_SAVE, filename));
 }
 
-static int ast_el_read_history(char *filename)
+static int ast_el_read_history(const char *filename)
 {
 	HistEvent ev;
 
@@ -3290,11 +3289,32 @@ static int ast_el_read_history(char *filename)
 	return history(el_hist, &ev, H_LOAD, filename);
 }
 
+static void ast_el_read_default_histfile(void)
+{
+	char histfile[80] = "";
+	const char *home = getenv("HOME");
+
+	if (!ast_strlen_zero(home)) {
+		snprintf(histfile, sizeof(histfile), "%s/.asterisk_history", home);
+		ast_el_read_history(histfile);
+	}
+}
+
+static void ast_el_write_default_histfile(void)
+{
+	char histfile[80] = "";
+	const char *home = getenv("HOME");
+
+	if (!ast_strlen_zero(home)) {
+		snprintf(histfile, sizeof(histfile), "%s/.asterisk_history", home);
+		ast_el_write_history(histfile);
+	}
+}
+
 static void ast_remotecontrol(char *data)
 {
 	char buf[256] = "";
 	int res;
-	char filename[80] = "";
 	char *hostname;
 	char *cpid;
 	char *version;
@@ -3304,6 +3324,10 @@ static void ast_remotecontrol(char *data)
 	char *ebuf;
 	int num = 0;
 
+	ast_term_init();
+	printf("%s", term_end());
+	fflush(stdout);
+
 	memset(&sig_flags, 0, sizeof(sig_flags));
 	signal(SIGINT, __remote_quit_handler);
 	signal(SIGTERM, __remote_quit_handler);
@@ -3402,16 +3426,12 @@ static void ast_remotecontrol(char *data)
 
 	ast_verbose("Connected to Asterisk %s currently running on %s (pid = %d)\n", version, hostname, pid);
 	remotehostname = hostname;
-	if (getenv("HOME"))
-		snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
 	if (el_hist == NULL || el == NULL)
 		ast_el_initialize();
+	ast_el_read_default_histfile();
 
 	el_set(el, EL_GETCFN, ast_el_read_char);
 
-	if (!ast_strlen_zero(filename))
-		ast_el_read_history(filename);
-
 	for (;;) {
 		ebuf = (char *)el_gets(el, &num);
 
@@ -3870,18 +3890,11 @@ static void main_atexit(void)
 int main(int argc, char *argv[])
 {
 	int c;
-	char filename[80] = "";
-	char hostname[MAXHOSTNAMELEN] = "";
 	char * xarg = NULL;
 	int x;
-	FILE *f;
-	sigset_t sigs;
-	int num;
 	int isroot = 1, rundir_exists = 0;
-	char *buf;
 	const char *runuser = NULL, *rungroup = NULL;
 	char *remotesock = NULL;
-	int moduleresult;         /*!< Result from the module load subsystem */
 	struct rlimit l;
 
 	/* Remember original args for restart */
@@ -3900,12 +3913,8 @@ int main(int argc, char *argv[])
 	if (argv[0] && (strstr(argv[0], "rasterisk")) != NULL) {
 		ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_REMOTE);
 	}
-	if (gethostname(hostname, sizeof(hostname)-1))
-		ast_copy_string(hostname, "<Unknown>", sizeof(hostname));
 	ast_mainpid = getpid();
 
-	if (getenv("HOME"))
-		snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
 	/*! \brief Check for options
 	 *
 	 * \todo Document these options
@@ -4249,6 +4258,10 @@ int main(int argc, char *argv[])
 				quit_handler(0, SHUTDOWN_FAST, 0);
 				exit(0);
 			}
+			ast_term_init();
+			printf("%s", term_end());
+			fflush(stdout);
+
 			print_intro_message(runuser, rungroup);
 			printf("%s", term_quit());
 			ast_remotecontrol(NULL);
@@ -4265,6 +4278,19 @@ int main(int argc, char *argv[])
 		exit(1);
 	}
 
+	/* Not a remote console? Start the daemon. */
+	asterisk_daemon(isroot, runuser, rungroup);
+	return 0;
+}
+
+static void asterisk_daemon(int isroot, const char *runuser, const char *rungroup)
+{
+	FILE *f;
+	sigset_t sigs;
+	int num;
+	char *buf;
+	int moduleresult;         /*!< Result from the module load subsystem */
+
 	/* This needs to remain as high up in the initial start up as possible.
 	 * daemon causes a fork to occur, which has all sorts of unintended
 	 * consequences for things that interact with threads.  This call *must*
@@ -4361,9 +4387,7 @@ int main(int argc, char *argv[])
 	if (ast_opt_console) {
 		if (el_hist == NULL || el == NULL)
 			ast_el_initialize();
-
-		if (!ast_strlen_zero(filename))
-			ast_el_read_history(filename);
+		ast_el_read_default_histfile();
 	}
 
 	ast_json_init();
@@ -4528,7 +4552,7 @@ int main(int argc, char *argv[])
 
 	/* initialize the data retrieval API */
 	if (ast_data_init()) {
-		printf ("Failed: ast_data_init\n%s", term_quit());
+		printf("Failed: ast_data_init\n%s", term_quit());
 		exit(1);
 	}
 
@@ -4695,6 +4719,11 @@ int main(int argc, char *argv[])
 		/* Console stuff now... */
 		/* Register our quit function */
 		char title[256];
+		char hostname[MAXHOSTNAMELEN] = "";
+
+		if (gethostname(hostname, sizeof(hostname) - 1)) {
+			ast_copy_string(hostname, "<Unknown>", sizeof(hostname));
+		}
 
 		ast_pthread_create_detached(&mon_sig_flags, NULL, monitor_sig_flags, NULL);
 
@@ -4712,30 +4741,17 @@ int main(int argc, char *argv[])
 			buf = (char *) el_gets(el, &num);
 
 			if (!buf && write(1, "", 1) < 0)
-				goto lostterm;
+				return; /* quit */
 
 			if (buf) {
 				if (buf[strlen(buf)-1] == '\n')
 					buf[strlen(buf)-1] = '\0';
 
-				consolehandler((char *)buf);
-			} else if (ast_opt_remote && (write(STDOUT_FILENO, "\nUse EXIT or QUIT to exit the asterisk console\n",
-				   strlen("\nUse EXIT or QUIT to exit the asterisk console\n")) < 0)) {
-				/* Whoa, stdout disappeared from under us... Make /dev/null's */
-				int fd;
-				fd = open("/dev/null", O_RDWR);
-				if (fd > -1) {
-					dup2(fd, STDOUT_FILENO);
-					dup2(fd, STDIN_FILENO);
-				} else
-					ast_log(LOG_WARNING, "Failed to open /dev/null to recover from dead console. Bad things will happen!\n");
-				break;
+				consolehandler(buf);
 			}
 		}
 	}
 
+	/* Stall until a quit signal is given */
 	monitor_sig_flags(NULL);
-
-lostterm:
-	return 0;
 }
diff --git a/main/audiohook.c b/main/audiohook.c
index 93d3521..5dfbb5d 100644
--- a/main/audiohook.c
+++ b/main/audiohook.c
@@ -824,13 +824,20 @@ static struct ast_frame *audiohook_list_translate_to_slin(struct ast_audiohook_l
 		return new_frame;
 	}
 
-	if (ast_format_cmp(frame->subclass.format, in_translate->format) == AST_FORMAT_CMP_NOT_EQUAL) {
+	if (!in_translate->format ||
+		ast_format_cmp(frame->subclass.format, in_translate->format) != AST_FORMAT_CMP_EQUAL) {
+		struct ast_trans_pvt *new_trans;
+
+		new_trans = ast_translator_build_path(slin, frame->subclass.format);
+		if (!new_trans) {
+			return NULL;
+		}
+
 		if (in_translate->trans_pvt) {
 			ast_translator_free_path(in_translate->trans_pvt);
 		}
-		if (!(in_translate->trans_pvt = ast_translator_build_path(slin, frame->subclass.format))) {
-			return NULL;
-		}
+		in_translate->trans_pvt = new_trans;
+
 		ao2_replace(in_translate->format, frame->subclass.format);
 	}
 
diff --git a/main/bridge.c b/main/bridge.c
index 7644884..b5c5951 100644
--- a/main/bridge.c
+++ b/main/bridge.c
@@ -1213,7 +1213,7 @@ static void check_bridge_play_sounds(struct ast_bridge *bridge)
 	}
 }
 
-static void update_bridge_vars_set(struct ast_channel *chan, const char *name, const char *pvtid)
+void ast_bridge_vars_set(struct ast_channel *chan, const char *name, const char *pvtid)
 {
 	ast_channel_stage_snapshot(chan);
 	pbx_builtin_setvar_helper(chan, "BRIDGEPEER", name);
@@ -1253,12 +1253,12 @@ static void set_bridge_peer_vars_2party(struct ast_channel *c0, struct ast_chann
 	ast_channel_unlock(c1);
 
 	ast_channel_lock(c0);
-	update_bridge_vars_set(c0, c1_name, c1_pvtid);
+	ast_bridge_vars_set(c0, c1_name, c1_pvtid);
 	UPDATE_BRIDGE_VARS_GET(c0, c0_name, c0_pvtid);
 	ast_channel_unlock(c0);
 
 	ast_channel_lock(c1);
-	update_bridge_vars_set(c1, c0_name, c0_pvtid);
+	ast_bridge_vars_set(c1, c0_name, c0_pvtid);
 	ast_channel_unlock(c1);
 }
 
@@ -1359,7 +1359,7 @@ static void set_bridge_peer_vars_multiparty(struct ast_bridge *bridge)
 		++idx;
 
 		ast_channel_lock(bridge_channel->chan);
-		update_bridge_vars_set(bridge_channel->chan, buf, NULL);
+		ast_bridge_vars_set(bridge_channel->chan, buf, NULL);
 		ast_channel_unlock(bridge_channel->chan);
 	}
 }
@@ -1381,7 +1381,7 @@ static void set_bridge_peer_vars_holding(struct ast_bridge *bridge)
 
 	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
 		ast_channel_lock(bridge_channel->chan);
-		update_bridge_vars_set(bridge_channel->chan, NULL, NULL);
+		ast_bridge_vars_set(bridge_channel->chan, NULL, NULL);
 		ast_channel_unlock(bridge_channel->chan);
 	}
 }
diff --git a/main/bridge_channel.c b/main/bridge_channel.c
index 8ce0220..3874e50 100644
--- a/main/bridge_channel.c
+++ b/main/bridge_channel.c
@@ -289,6 +289,10 @@ void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_ch
 
 	channel_set_cause(bridge_channel->chan, cause);
 
+	ast_channel_lock(bridge_channel->chan);
+	ast_bridge_vars_set(bridge_channel->chan, NULL, NULL);
+	ast_channel_unlock(bridge_channel->chan);
+
 	/* Change the state on the bridge channel */
 	bridge_channel->state = new_state;
 
diff --git a/main/channel.c b/main/channel.c
index 9aeae5f..6507ef9 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -339,7 +339,7 @@ static char *complete_channeltypes(struct ast_cli_args *a)
 static char *handle_cli_core_show_channeltype(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	struct chanlist *cl = NULL;
-	struct ast_str *codec_buf = ast_str_alloca(256);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 	switch (cmd) {
 	case CLI_INIT:
@@ -872,6 +872,7 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
 	ast_channel_hold_state_set(tmp, AST_CONTROL_UNHOLD);
 
 	ast_channel_streamid_set(tmp, -1);
+	ast_channel_vstreamid_set(tmp, -1);
 
 	ast_channel_fin_set(tmp, global_fin);
 	ast_channel_fout_set(tmp, global_fout);
@@ -5094,7 +5095,7 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
 			f = fr;
 		} else {
 			if (ast_format_cmp(ast_channel_writeformat(chan), fr->subclass.format) != AST_FORMAT_CMP_EQUAL) {
-				struct ast_str *codec_buf = ast_str_alloca(256);
+				struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 				/*
 				 * We are not setup to write this frame.  Things may have changed
@@ -5435,8 +5436,8 @@ static int set_format(struct ast_channel *chan, struct ast_format_cap *cap_set,
 		res = ast_translator_best_choice(cap_native, cap_set, &best_native_fmt, &best_set_fmt);
 	}
 	if (res < 0) {
-		struct ast_str *codec_native = ast_str_alloca(256);
-		struct ast_str *codec_set = ast_str_alloca(256);
+		struct ast_str *codec_native = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *codec_set = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 		ast_format_cap_get_names(cap_native, &codec_native);
 		ast_channel_unlock(chan);
@@ -5978,8 +5979,8 @@ struct ast_channel *ast_request(const char *type, struct ast_format_cap *request
 			res = ast_translator_best_choice(tmp_cap, chan->tech->capabilities, &tmp_fmt, &best_audio_fmt);
 			ao2_ref(tmp_cap, -1);
 			if (res < 0) {
-				struct ast_str *tech_codecs = ast_str_alloca(64);
-				struct ast_str *request_codecs = ast_str_alloca(64);
+				struct ast_str *tech_codecs = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+				struct ast_str *request_codecs = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 				ast_log(LOG_WARNING, "No translator path exists for channel type %s (native %s) to %s\n", type,
 					ast_format_cap_get_names(chan->tech->capabilities, &tech_codecs),
diff --git a/main/cli.c b/main/cli.c
index 47c48d6..7f86eab 100644
--- a/main/cli.c
+++ b/main/cli.c
@@ -1076,10 +1076,12 @@ static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
 				char locbuf[40] = "(None)";
 				char appdata[40] = "(None)";
 
-				if (!cs->context && !cs->exten)
+				if (!ast_strlen_zero(cs->context) && !ast_strlen_zero(cs->exten)) {
 					snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", cs->exten, cs->context, cs->priority);
-				if (cs->appl)
+				}
+				if (!ast_strlen_zero(cs->appl)) {
 					snprintf(appdata, sizeof(appdata), "%s(%s)", cs->appl, S_OR(cs->data, ""));
+				}
 				ast_cli(a->fd, FORMAT_STRING, cs->name, locbuf, ast_state2str(cs->state), appdata);
 			}
 		}
@@ -1528,7 +1530,7 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
 	struct ast_var_t *var;
 	struct ast_str *write_transpath = ast_str_alloca(256);
 	struct ast_str *read_transpath = ast_str_alloca(256);
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	struct ast_bridge *bridge;
 	struct ast_callid *callid;
 	char callid_buf[32];
diff --git a/main/codec_builtin.c b/main/codec_builtin.c
index 2e89e9a..346b47b 100644
--- a/main/codec_builtin.c
+++ b/main/codec_builtin.c
@@ -594,7 +594,7 @@ static struct ast_codec ilbc = {
 	.type = AST_MEDIA_TYPE_AUDIO,
 	.sample_rate = 8000,
 	.minimum_ms = 30,
-	.maximum_ms = 30,
+	.maximum_ms = 300,
 	.default_ms = 30,
 	.minimum_bytes = 50,
 	.samples_count = ilbc_samples,
diff --git a/main/config.c b/main/config.c
index 7484b66..eefc8ca 100644
--- a/main/config.c
+++ b/main/config.c
@@ -1647,7 +1647,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
 		 *		You can put a comma-separated list of categories and templates
 		 *		and '!' and '+' between parentheses, with obvious meaning.
 		 */
-		struct ast_category *newcat = NULL;
+		struct ast_category *newcat;
 		char *catname;
 
 		c = strchr(cur, ']');
@@ -1660,14 +1660,13 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
 		if (*c++ != '(')
 			c = NULL;
 		catname = cur;
-		if (!(*cat = newcat = ast_category_new(catname,
-				S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
-				lineno))) {
+		*cat = newcat = ast_category_new(catname,
+			S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
+			lineno);
+		if (!newcat) {
 			return -1;
 		}
 		(*cat)->lineno = lineno;
-		*last_var = 0;
-		*last_cat = newcat;
 
 		/* add comments */
 		if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
@@ -1680,6 +1679,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
 		/* If there are options or categories to inherit from, process them now */
 		if (c) {
 			if (!(cur = strchr(c, ')'))) {
+				ast_category_destroy(newcat);
 				ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
 				return -1;
 			}
@@ -1690,12 +1690,15 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
 				} else if (!strcasecmp(cur, "+")) {
 					*cat = ast_category_get(cfg, catname, NULL);
 					if (!(*cat)) {
-						if (newcat)
+						if (newcat) {
 							ast_category_destroy(newcat);
+						}
 						ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
 						return -1;
 					}
 					if (newcat) {
+						ast_config_set_current_category(cfg, *cat);
+						(*cat)->ignored |= newcat->ignored;
 						move_variables(newcat, *cat);
 						ast_category_destroy(newcat);
 						newcat = NULL;
@@ -1705,18 +1708,35 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
 
 					base = ast_category_get(cfg, cur, "TEMPLATES=include");
 					if (!base) {
+						if (newcat) {
+							ast_category_destroy(newcat);
+						}
 						ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
 						return -1;
 					}
 					if (ast_category_inherit(*cat, base)) {
+						if (newcat) {
+							ast_category_destroy(newcat);
+						}
 						ast_log(LOG_ERROR, "Inheritence requested, but allocation failed\n");
 						return -1;
 					}
 				}
 			}
 		}
-		if (newcat)
-			ast_category_append(cfg, *cat);
+
+		/*
+		 * We need to set *last_cat to newcat here regardless.  If the
+		 * category is being appended to we have no place for trailing
+		 * comments on the appended category.  The appended category
+		 * may be in another file or it already has trailing comments
+		 * that we would then leak.
+		 */
+		*last_var = NULL;
+		*last_cat = newcat;
+		if (newcat) {
+			ast_category_append(cfg, newcat);
+		}
 	} else if (cur[0] == '#') { /* A directive - #include or #exec */
 		char *cur2;
 		char real_inclusion_name[256];
@@ -1872,7 +1892,7 @@ set_new_variable:
 			} else if ((v = ast_variable_new(cur, ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
 				v->lineno = lineno;
 				v->object = object;
-				*last_cat = 0;
+				*last_cat = NULL;
 				*last_var = v;
 				/* Put and reset comments */
 				v->blanklines = 0;
@@ -1912,8 +1932,8 @@ static struct ast_config *config_text_file_load(const char *database, const char
 	struct stat statbuf;
 	struct cache_file_mtime *cfmtime = NULL;
 	struct cache_file_include *cfinclude;
-	struct ast_variable *last_var = 0;
-	struct ast_category *last_cat = 0;
+	struct ast_variable *last_var = NULL;
+	struct ast_category *last_cat = NULL;
 	/*! Growable string buffer */
 	struct ast_str *comment_buffer = NULL;	/*!< this will be a comment collector.*/
 	struct ast_str *lline_buffer = NULL;	/*!< A buffer for stuff behind the ; */
diff --git a/main/dial.c b/main/dial.c
index 97942b8..fe59203 100644
--- a/main/dial.c
+++ b/main/dial.c
@@ -295,6 +295,7 @@ static int begin_dial_prerun(struct ast_dial_channel *channel, struct ast_channe
 	char numsubst[AST_MAX_EXTENSION];
 	struct ast_format_cap *cap_all_audio = NULL;
 	struct ast_format_cap *cap_request;
+	struct ast_format_cap *requester_cap = NULL;
 	struct ast_assigned_ids assignedids = {
 		.uniqueid = channel->assignedid1,
 		.uniqueid2 = channel->assignedid2,
@@ -305,6 +306,7 @@ static int begin_dial_prerun(struct ast_dial_channel *channel, struct ast_channe
 
 		ast_channel_lock(chan);
 		max_forwards = ast_max_forwards_get(chan);
+		requester_cap = ao2_bump(ast_channel_nativeformats(chan));
 		ast_channel_unlock(chan);
 
 		if (max_forwards <= 0) {
@@ -318,8 +320,8 @@ static int begin_dial_prerun(struct ast_dial_channel *channel, struct ast_channe
 
 	if (cap && ast_format_cap_count(cap)) {
 		cap_request = cap;
-	} else if (chan) {
-		cap_request = ast_channel_nativeformats(chan);
+	} else if (requester_cap) {
+		cap_request = requester_cap;
 	} else {
 		cap_all_audio = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
 		ast_format_cap_append_by_type(cap_all_audio, AST_MEDIA_TYPE_AUDIO);
@@ -332,6 +334,7 @@ static int begin_dial_prerun(struct ast_dial_channel *channel, struct ast_channe
 		return -1;
 	}
 	cap_request = NULL;
+	ao2_cleanup(requester_cap);
 	ao2_cleanup(cap_all_audio);
 
 	if (chan) {
diff --git a/main/file.c b/main/file.c
index 64406bf..7ce0213 100644
--- a/main/file.c
+++ b/main/file.c
@@ -1096,7 +1096,7 @@ int ast_streamfile(struct ast_channel *chan, const char *filename, const char *p
 
 	fs = ast_openstream(chan, filename, preflang);
 	if (!fs) {
-		struct ast_str *codec_buf = ast_str_alloca(64);
+		struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_log(LOG_WARNING, "Unable to open %s (format %s): %s\n",
 			filename, ast_format_cap_get_names(ast_channel_nativeformats(chan), &codec_buf), strerror(errno));
 		return -1;
diff --git a/main/format.c b/main/format.c
index 8ac82f0..5bf38df 100644
--- a/main/format.c
+++ b/main/format.c
@@ -302,6 +302,14 @@ const void *ast_format_attribute_get(const struct ast_format *format, const char
 {
 	const struct ast_format_interface *interface = format->interface;
 
+	if (!interface) {
+		struct format_interface *format_interface = ao2_find(interfaces, format->codec->name, OBJ_SEARCH_KEY);
+		if (format_interface) {
+			interface = format_interface->interface;
+			ao2_ref(format_interface, -1);
+		}
+	}
+
 	if (!interface || !interface->format_attribute_get) {
 		return NULL;
 	}
@@ -330,11 +338,21 @@ struct ast_format *ast_format_parse_sdp_fmtp(const struct ast_format *format, co
 
 void ast_format_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
 {
-	if (!format->interface || !format->interface->format_generate_sdp_fmtp) {
+	const struct ast_format_interface *interface = format->interface;
+
+	if (!interface) {
+		struct format_interface *format_interface = ao2_find(interfaces, format->codec->name, OBJ_SEARCH_KEY);
+		if (format_interface) {
+			interface = format_interface->interface;
+			ao2_ref(format_interface, -1);
+		}
+	}
+
+	if (!interface || !interface->format_generate_sdp_fmtp) {
 		return;
 	}
 
-	format->interface->format_generate_sdp_fmtp(format, payload, str);
+	interface->format_generate_sdp_fmtp(format, payload, str);
 }
 
 struct ast_codec *ast_format_get_codec(const struct ast_format *format)
diff --git a/main/format_cap.c b/main/format_cap.c
index b344f84..8d6046a 100644
--- a/main/format_cap.c
+++ b/main/format_cap.c
@@ -93,14 +93,27 @@ static void format_cap_destroy(void *obj)
 	AST_VECTOR_FREE(&cap->preference_order);
 }
 
-static inline void format_cap_init(struct ast_format_cap *cap, enum ast_format_cap_flags flags)
+/*
+ * \brief Initialize values on an ast_format_cap
+ *
+ * \param cap ast_format_cap to initialize
+ * \param flags Unused.
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+static inline int format_cap_init(struct ast_format_cap *cap, enum ast_format_cap_flags flags)
 {
-	AST_VECTOR_INIT(&cap->formats, 0);
+	if (AST_VECTOR_INIT(&cap->formats, 0)) {
+		return -1;
+	}
 
 	/* TODO: Look at common usage of this and determine a good starting point */
-	AST_VECTOR_INIT(&cap->preference_order, 5);
+	if (AST_VECTOR_INIT(&cap->preference_order, 5)) {
+		return -1;
+	}
 
 	cap->framing = UINT_MAX;
+	return 0;
 }
 
 struct ast_format_cap *__ast_format_cap_alloc(enum ast_format_cap_flags flags)
@@ -112,7 +125,10 @@ struct ast_format_cap *__ast_format_cap_alloc(enum ast_format_cap_flags flags)
 		return NULL;
 	}
 
-	format_cap_init(cap, flags);
+	if (format_cap_init(cap, flags)) {
+		ao2_ref(cap, -1);
+		return NULL;
+	}
 
 	return cap;
 }
@@ -126,7 +142,10 @@ struct ast_format_cap *__ast_format_cap_alloc_debug(enum ast_format_cap_flags fl
 		return NULL;
 	}
 
-	format_cap_init(cap, flags);
+	if (format_cap_init(cap, flags)) {
+		ao2_ref(cap, -1);
+		return NULL;
+	}
 
 	return cap;
 }
@@ -245,7 +264,18 @@ int ast_format_cap_append_by_type(struct ast_format_cap *cap, enum ast_media_typ
 			continue;
 		}
 
-		format = ast_format_create(codec);
+		format = ast_format_cache_get(codec->name);
+
+		if (format == ast_format_none) {
+			ao2_ref(format, -1);
+			ao2_ref(codec, -1);
+			continue;
+		}
+
+		if (!format || (codec != ast_format_get_codec(format))) {
+			ao2_cleanup(format);
+			format = ast_format_create(codec);
+		}
 		ao2_ref(codec, -1);
 
 		if (!format) {
diff --git a/main/hashtab.c b/main/hashtab.c
index 4b76597..9b334d4 100644
--- a/main/hashtab.c
+++ b/main/hashtab.c
@@ -745,6 +745,8 @@ struct ast_hashtab_iter *ast_hashtab_start_write_traversal(struct ast_hashtab *t
 
 void ast_hashtab_end_traversal(struct ast_hashtab_iter *it)
 {
+	if (!it)
+		return;
 	if (it->tab->do_locking)
 		ast_rwlock_unlock(&it->tab->lock);
 	free(it);
diff --git a/main/loader.c b/main/loader.c
index 940b53e..2b3bd1f 100644
--- a/main/loader.c
+++ b/main/loader.c
@@ -852,10 +852,10 @@ static void publish_reload_message(const char *name, enum ast_module_reload_resu
 	event_object = ast_json_pack("{s: s, s: s}",
 			"Module", S_OR(name, "All"),
 			"Status", res_buffer);
-	json_object = ast_json_pack("{s: s, s: i, s: O}",
+	json_object = ast_json_pack("{s: s, s: i, s: o}",
 			"type", "Reload",
 			"class_type", EVENT_FLAG_SYSTEM,
-			"event", event_object);
+			"event", ast_json_ref(event_object));
 
 	if (!json_object) {
 		return;
diff --git a/main/manager.c b/main/manager.c
index 035115f..99c5502 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -2811,6 +2811,7 @@ AST_THREADSTORAGE(userevent_buf);
  */
 void astman_append(struct mansession *s, const char *fmt, ...)
 {
+	int res;
 	va_list ap;
 	struct ast_str *buf;
 
@@ -2819,8 +2820,11 @@ void astman_append(struct mansession *s, const char *fmt, ...)
 	}
 
 	va_start(ap, fmt);
-	ast_str_set_va(&buf, 0, fmt, ap);
+	res = ast_str_set_va(&buf, 0, fmt, ap);
 	va_end(ap);
+	if (res == AST_DYNSTR_BUILD_FAILED) {
+		return;
+	}
 
 	if (s->f != NULL || s->session->f != NULL) {
 		send_string(s, ast_str_buffer(buf));
@@ -2880,6 +2884,7 @@ void astman_send_error(struct mansession *s, const struct message *m, char *erro
 
 void astman_send_error_va(struct mansession *s, const struct message *m, const char *fmt, ...)
 {
+	int res;
 	va_list ap;
 	struct ast_str *buf;
 	char *msg;
@@ -2889,8 +2894,11 @@ void astman_send_error_va(struct mansession *s, const struct message *m, const c
 	}
 
 	va_start(ap, fmt);
-	ast_str_set_va(&buf, 0, fmt, ap);
+	res = ast_str_set_va(&buf, 0, fmt, ap);
 	va_end(ap);
+	if (res == AST_DYNSTR_BUILD_FAILED) {
+		return;
+	}
 
 	/* astman_append will use the same underlying buffer, so copy the message out
 	 * before sending the response */
@@ -3469,18 +3477,18 @@ static int action_getconfigjson(struct mansession *s, const struct message *m)
 		category_name = ast_category_get_name(cur_category);
 		astman_append(s, "%s\"", comma1 ? "," : "");
 		astman_append_json(s, category_name);
-		astman_append(s, "\":[");
+		astman_append(s, "\":{");
 		comma1 = 1;
 
 		if (ast_category_is_template(cur_category)) {
-			astman_append(s, "istemplate:1");
+			astman_append(s, "\"istemplate\":1");
 			comma2 = 1;
 		}
 
 		if ((templates = ast_category_get_templates(cur_category))
 			&& ast_str_strlen(templates) > 0) {
 			astman_append(s, "%s", comma2 ? "," : "");
-			astman_append(s, "templates:\"%s\"", ast_str_buffer(templates));
+			astman_append(s, "\"templates\":\"%s\"", ast_str_buffer(templates));
 			ast_free(templates);
 			comma2 = 1;
 		}
@@ -3494,7 +3502,7 @@ static int action_getconfigjson(struct mansession *s, const struct message *m)
 			comma2 = 1;
 		}
 
-		astman_append(s, "]");
+		astman_append(s, "}");
 	}
 	astman_append(s, "}\r\n\r\n");
 
@@ -4331,7 +4339,7 @@ static int action_status(struct mansession *s, const struct message *m)
 	struct ast_str *write_transpath = ast_str_alloca(256);
 	struct ast_str *read_transpath = ast_str_alloca(256);
 	struct ast_channel *chan;
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	int channels = 0;
 	int all = ast_strlen_zero(name); /* set if we want all channels */
 	char id_text[256];
diff --git a/main/rtp_engine.c b/main/rtp_engine.c
index 8ad3d9e..6ff8f52 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -727,9 +727,13 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs,
 		new_type->rtp_code = t->payload_type.rtp_code;
 		if ((ast_format_cmp(t->payload_type.format, ast_format_g726) == AST_FORMAT_CMP_EQUAL) &&
 				t->payload_type.asterisk_format && (options & AST_RTP_OPT_G726_NONSTANDARD)) {
-			new_type->format = ao2_bump(ast_format_g726_aal2);
+			new_type->format = ast_format_g726_aal2;
 		} else {
-			new_type->format = ao2_bump(t->payload_type.format);
+			new_type->format = t->payload_type.format;
+		}
+		if (new_type->format) {
+			/* SDP parsing automatically increases the reference count */
+			new_type->format = ast_format_parse_sdp_fmtp(new_type->format, "");
 		}
 		AST_VECTOR_REPLACE(&codecs->payloads, pt, new_type);
 
@@ -1630,18 +1634,34 @@ int ast_rtp_dtls_cfg_parse(struct ast_rtp_dtls_cfg *dtls_cfg, const char *name,
 		}
 	} else if (!strcasecmp(name, "dtlscertfile")) {
 		ast_free(dtls_cfg->certfile);
+		if (!ast_strlen_zero(value) && !ast_file_is_readable(value)) {
+			ast_log(LOG_ERROR, "%s file %s does not exist or is not readable\n", name, value);
+			return -1;
+		}
 		dtls_cfg->certfile = ast_strdup(value);
 	} else if (!strcasecmp(name, "dtlsprivatekey")) {
 		ast_free(dtls_cfg->pvtfile);
+		if (!ast_strlen_zero(value) && !ast_file_is_readable(value)) {
+			ast_log(LOG_ERROR, "%s file %s does not exist or is not readable\n", name, value);
+			return -1;
+		}
 		dtls_cfg->pvtfile = ast_strdup(value);
 	} else if (!strcasecmp(name, "dtlscipher")) {
 		ast_free(dtls_cfg->cipher);
 		dtls_cfg->cipher = ast_strdup(value);
 	} else if (!strcasecmp(name, "dtlscafile")) {
 		ast_free(dtls_cfg->cafile);
+		if (!ast_strlen_zero(value) && !ast_file_is_readable(value)) {
+			ast_log(LOG_ERROR, "%s file %s does not exist or is not readable\n", name, value);
+			return -1;
+		}
 		dtls_cfg->cafile = ast_strdup(value);
 	} else if (!strcasecmp(name, "dtlscapath") || !strcasecmp(name, "dtlscadir")) {
 		ast_free(dtls_cfg->capath);
+		if (!ast_strlen_zero(value) && !ast_file_is_readable(value)) {
+			ast_log(LOG_ERROR, "%s file %s does not exist or is not readable\n", name, value);
+			return -1;
+		}
 		dtls_cfg->capath = ast_strdup(value);
 	} else if (!strcasecmp(name, "dtlssetup")) {
 		if (!strcasecmp(value, "active")) {
@@ -1988,12 +2008,12 @@ static struct ast_json *rtcp_report_to_json(struct stasis_message *msg,
 		}
 	}
 
-	json_rtcp_report = ast_json_pack("{s: i, s: i, s: i, s: O, s: O}",
+	json_rtcp_report = ast_json_pack("{s: i, s: i, s: i, s: o, s: o}",
 			"ssrc", payload->report->ssrc,
 			"type", payload->report->type,
 			"report_count", payload->report->reception_report_count,
-			"sender_information", json_rtcp_sender_info ? json_rtcp_sender_info : ast_json_null(),
-			"report_blocks", json_rtcp_report_blocks);
+			"sender_information", json_rtcp_sender_info ? ast_json_ref(json_rtcp_sender_info) : ast_json_ref(ast_json_null()),
+			"report_blocks", ast_json_ref(json_rtcp_report_blocks));
 	if (!json_rtcp_report) {
 		return NULL;
 	}
@@ -2005,10 +2025,10 @@ static struct ast_json *rtcp_report_to_json(struct stasis_message *msg,
 		}
 	}
 
-	return ast_json_pack("{s: O, s: O, s: O}",
-		"channel", payload->snapshot ? json_channel : ast_json_null(),
-		"rtcp_report", json_rtcp_report,
-		"blob", payload->blob);
+	return ast_json_pack("{s: o, s: o, s: o}",
+		"channel", payload->snapshot ? ast_json_ref(json_channel) : ast_json_ref(ast_json_null()),
+		"rtcp_report", ast_json_ref(json_rtcp_report),
+		"blob", ast_json_deep_copy(payload->blob));
 }
 
 static void rtp_rtcp_report_dtor(void *obj)
diff --git a/main/sched.c b/main/sched.c
index 89ef9ae..f851670 100644
--- a/main/sched.c
+++ b/main/sched.c
@@ -315,9 +315,16 @@ static int add_ids(struct ast_sched_context *con)
 		if (!new_id) {
 			break;
 		}
-		new_id->id = i;
+
+		/*
+		 * According to the API doxygen a sched ID of 0 is valid.
+		 * Unfortunately, 0 was never returned historically and
+		 * several users incorrectly coded usage of the returned
+		 * sched ID assuming that 0 was invalid.
+		 */
+		new_id->id = ++con->id_queue_size;
+
 		AST_LIST_INSERT_TAIL(&con->id_queue, new_id, list);
-		++con->id_queue_size;
 	}
 
 	return con->id_queue_size - original_size;
@@ -450,6 +457,17 @@ static int sched_settime(struct timeval *t, int when)
 {
 	struct timeval now = ast_tvnow();
 
+	if (when < 0) {
+		/*
+		 * A negative when value is likely a bug as it
+		 * represents a VERY large timeout time.
+		 */
+		ast_log(LOG_WARNING,
+			"Bug likely: Negative time interval %d (interpreted as %u ms) requested!\n",
+			when, (unsigned int) when);
+		ast_assert(0);
+	}
+
 	/*ast_debug(1, "TV -> %lu,%lu\n", tv->tv_sec, tv->tv_usec);*/
 	if (ast_tvzero(*t))	/* not supplied, default to now */
 		*t = now;
diff --git a/main/sorcery.c b/main/sorcery.c
index 43a395a..e78fc5c 100644
--- a/main/sorcery.c
+++ b/main/sorcery.c
@@ -304,7 +304,7 @@ static int chararray_handler_fn(const void *obj, const intptr_t *args, char **bu
 
 static int codec_handler_fn(const void *obj, const intptr_t *args, char **buf)
 {
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	struct ast_format_cap **cap = (struct ast_format_cap **)(obj + args[0]);
 	return !(*buf = ast_strdup(ast_format_cap_get_names(*cap, &codec_buf)));
 }
@@ -1897,7 +1897,7 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch
 			}
 		}
 
-		if ((flags & AST_RETRIEVE_FLAG_MULTIPLE) || !object) {
+		if (((flags & AST_RETRIEVE_FLAG_MULTIPLE) && (!ao2_container_count(object) || !wizard->caching)) || !object) {
 			continue;
 		}
 
@@ -1935,6 +1935,10 @@ struct ao2_container *ast_sorcery_retrieve_by_regex(const struct ast_sorcery *so
 		}
 
 		wizard->wizard->callbacks.retrieve_regex(sorcery, wizard->data, object_type->name, objects, regex);
+
+		if (wizard->caching && ao2_container_count(objects)) {
+			break;
+		}
 	}
 	AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
diff --git a/main/stasis.c b/main/stasis.c
index 6adbfc3..962efc8 100644
--- a/main/stasis.c
+++ b/main/stasis.c
@@ -1274,8 +1274,8 @@ static struct ast_json *multi_user_event_to_json(
 
 	ast_json_object_set(out, "type", ast_json_string_create("ChannelUserevent"));
 	ast_json_object_set(out, "timestamp", ast_json_timeval(*tv, NULL));
-	ast_json_object_set(out, "eventname", ast_json_ref(ast_json_object_get(blob, "eventname")));
-	ast_json_object_set(out, "userevent", ast_json_ref(blob)); /* eventname gets duplicated, that's ok */
+	ast_json_object_set(out, "eventname", ast_json_string_create(ast_json_string_get((ast_json_object_get(blob, "eventname")))));
+	ast_json_object_set(out, "userevent", ast_json_deep_copy(blob));
 
 	for (type = 0; type < STASIS_UMOS_MAX; ++type) {
 		for (i = 0; i < AST_VECTOR_SIZE(&multi->snapshots[type]); ++i) {
diff --git a/main/stasis_channels.c b/main/stasis_channels.c
index b8efd40..d46a8dd 100644
--- a/main/stasis_channels.c
+++ b/main/stasis_channels.c
@@ -1016,6 +1016,10 @@ static struct ast_json *dtmf_end_to_json(
 	struct ast_channel_snapshot *snapshot = channel_blob->snapshot;
 	const char *direction =
 		ast_json_string_get(ast_json_object_get(blob, "direction"));
+	const char *digit =
+		ast_json_string_get(ast_json_object_get(blob, "digit"));
+	long duration_ms =
+		ast_json_integer_get(ast_json_object_get(blob, "duration_ms"));
 	const struct timeval *tv = stasis_message_timestamp(message);
 	struct ast_json *json_channel;
 
@@ -1029,11 +1033,11 @@ static struct ast_json *dtmf_end_to_json(
 		return NULL;
 	}
 
-	return ast_json_pack("{s: s, s: o, s: O, s: O, s: o}",
+	return ast_json_pack("{s: s, s: o, s: s, s: i, s: o}",
 		"type", "ChannelDtmfReceived",
 		"timestamp", ast_json_timeval(*tv, NULL),
-		"digit", ast_json_object_get(blob, "digit"),
-		"duration_ms", ast_json_object_get(blob, "duration_ms"),
+		"digit", digit,
+		"duration_ms", duration_ms,
 		"channel", json_channel);
 }
 
@@ -1057,6 +1061,12 @@ static struct ast_json *dial_to_json(
 {
 	struct ast_multi_channel_blob *payload = stasis_message_data(message);
 	struct ast_json *blob = ast_multi_channel_blob_get_json(payload);
+	const char *dialstatus =
+		ast_json_string_get(ast_json_object_get(blob, "dialstatus"));
+	const char *forward =
+		ast_json_string_get(ast_json_object_get(blob, "forward"));
+	const char *dialstring =
+		ast_json_string_get(ast_json_object_get(blob, "dialstring"));
 	struct ast_json *caller_json = ast_channel_snapshot_to_json(ast_multi_channel_blob_get_channel(payload, "caller"), sanitize);
 	struct ast_json *peer_json = ast_channel_snapshot_to_json(ast_multi_channel_blob_get_channel(payload, "peer"), sanitize);
 	struct ast_json *forwarded_json = ast_channel_snapshot_to_json(ast_multi_channel_blob_get_channel(payload, "forwarded"), sanitize);
@@ -1064,12 +1074,12 @@ static struct ast_json *dial_to_json(
 	const struct timeval *tv = stasis_message_timestamp(message);
 	int res = 0;
 
-	json = ast_json_pack("{s: s, s: o, s: O, s: O, s: O}",
+	json = ast_json_pack("{s: s, s: o, s: s, s: s, s: s}",
 		"type", "Dial",
 		"timestamp", ast_json_timeval(*tv, NULL),
-		"dialstatus", ast_json_object_get(blob, "dialstatus"),
-		"forward", ast_json_object_get(blob, "forward"),
-		"dialstring", ast_json_object_get(blob, "dialstring"));
+		"dialstatus", dialstatus,
+		"forward", forward,
+		"dialstring", dialstring);
 	if (!json) {
 		ast_json_unref(caller_json);
 		ast_json_unref(peer_json);
diff --git a/main/stasis_endpoints.c b/main/stasis_endpoints.c
index 157f98c..fbca2ad 100644
--- a/main/stasis_endpoints.c
+++ b/main/stasis_endpoints.c
@@ -247,7 +247,7 @@ static struct ast_json *contactstatus_to_json(struct stasis_message *msg, const
 		return NULL;
 	}
 
-	json_final = ast_json_pack("{s: s, s: o, s: o, s: { s: s, s: s, s: s } } ",
+	json_final = ast_json_pack("{s: s, s: o, s: o, s: { s: s, s: s, s: s, s: s } } ",
 		"type", "ContactStatusChange",
 		"timestamp", ast_json_timeval(*tv, NULL),
 		"endpoint", json_endpoint,
diff --git a/main/strings.c b/main/strings.c
index da63cdf..53d5095 100644
--- a/main/strings.c
+++ b/main/strings.c
@@ -60,55 +60,78 @@ int __ast_str_helper(struct ast_str **buf, ssize_t max_len,
 	int append, const char *fmt, va_list ap)
 #endif
 {
-	int res, need;
+	int res;
+	int added;
+	int need;
 	int offset = (append && (*buf)->__AST_STR_LEN) ? (*buf)->__AST_STR_USED : 0;
 	va_list aq;
 
+	if (max_len < 0) {
+		max_len = (*buf)->__AST_STR_LEN;	/* don't exceed the allocated space */
+	}
+
 	do {
-		if (max_len < 0) {
-			max_len = (*buf)->__AST_STR_LEN;	/* don't exceed the allocated space */
-		}
-		/*
-		 * Ask vsnprintf how much space we need. Remember that vsnprintf
-		 * does not count the final <code>'\\0'</code> so we must add 1.
-		 */
 		va_copy(aq, ap);
 		res = vsnprintf((*buf)->__AST_STR_STR + offset, (*buf)->__AST_STR_LEN - offset, fmt, aq);
+		va_end(aq);
+
+		if (res < 0) {
+			/*
+			 * vsnprintf write to string failed.
+			 * I don't think this is possible with a memory buffer.
+			 */
+			res = AST_DYNSTR_BUILD_FAILED;
+			added = 0;
+			break;
+		}
 
-		need = res + offset + 1;
 		/*
-		 * If there is not enough space and we are below the max length,
-		 * reallocate the buffer and return a message telling to retry.
+		 * vsnprintf returns how much space we used or would need.
+		 * Remember that vsnprintf does not count the nil terminator
+		 * so we must add 1.
 		 */
-		if (need > (*buf)->__AST_STR_LEN && (max_len == 0 || (*buf)->__AST_STR_LEN < max_len) ) {
-			int len = (int)(*buf)->__AST_STR_LEN;
-			if (max_len && max_len < need) {	/* truncate as needed */
-				need = max_len;
-			} else if (max_len == 0) {	/* if unbounded, give more room for next time */
-				need += 16 + need / 4;
-			}
-			if (
+		added = res;
+		need = offset + added + 1;
+		if (need <= (*buf)->__AST_STR_LEN
+			|| (max_len && max_len <= (*buf)->__AST_STR_LEN)) {
+			/*
+			 * There was enough room for the string or we are not
+			 * allowed to try growing the string buffer.
+			 */
+			break;
+		}
+
+		/* Reallocate the buffer and try again. */
+		if (max_len == 0) {
+			/* unbounded, give more room for next time */
+			need += 16 + need / 4;
+		} else if (max_len < need) {
+			/* truncate as needed */
+			need = max_len;
+		}
+
+		if (
 #if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
-					_ast_str_make_space(buf, need, file, lineno, function)
+			_ast_str_make_space(buf, need, file, lineno, function)
 #else
-					ast_str_make_space(buf, need)
+			ast_str_make_space(buf, need)
 #endif
-				) {
-				ast_log_safe(LOG_VERBOSE, "failed to extend from %d to %d\n", len, need);
-				va_end(aq);
-				return AST_DYNSTR_BUILD_FAILED;
-			}
-			(*buf)->__AST_STR_STR[offset] = '\0';	/* Truncate the partial write. */
+			) {
+			ast_log_safe(LOG_VERBOSE, "failed to extend from %d to %d\n",
+				(int) (*buf)->__AST_STR_LEN, need);
 
-			/* Restart va_copy before calling vsnprintf() again. */
-			va_end(aq);
-			continue;
+			res = AST_DYNSTR_BUILD_FAILED;
+			break;
 		}
-		va_end(aq);
-		break;
 	} while (1);
-	/* update space used, keep in mind the truncation */
-	(*buf)->__AST_STR_USED = (res + offset > (*buf)->__AST_STR_LEN) ? (*buf)->__AST_STR_LEN - 1: res + offset;
+
+	/* Update space used, keep in mind truncation may be necessary. */
+	(*buf)->__AST_STR_USED = ((*buf)->__AST_STR_LEN <= offset + added)
+		? (*buf)->__AST_STR_LEN - 1
+		: offset + added;
+
+	/* Ensure that the string is terminated. */
+	(*buf)->__AST_STR_STR[(*buf)->__AST_STR_USED] = '\0';
 
 	return res;
 }
diff --git a/main/taskprocessor.c b/main/taskprocessor.c
index e8dc8f5..7c50089 100644
--- a/main/taskprocessor.c
+++ b/main/taskprocessor.c
@@ -83,6 +83,8 @@ struct ast_taskprocessor {
 	pthread_t thread;
 	/*! Indicates if the taskprocessor is currently executing a task */
 	unsigned int executing:1;
+	/*! Indicates that a high water warning has been issued on this task processor */
+	unsigned int high_water_warned:1;
 };
 
 /*!
@@ -128,9 +130,6 @@ static int tps_ping_handler(void *datap);
 /*! \brief Remove the front task off the taskprocessor queue */
 static struct tps_task *tps_taskprocessor_pop(struct ast_taskprocessor *tps);
 
-/*! \brief Return the size of the taskprocessor queue */
-static int tps_taskprocessor_depth(struct ast_taskprocessor *tps);
-
 static char *cli_tps_ping(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
 static char *cli_tps_report(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
 
@@ -506,7 +505,7 @@ static struct tps_task *tps_taskprocessor_pop(struct ast_taskprocessor *tps)
 	return task;
 }
 
-static int tps_taskprocessor_depth(struct ast_taskprocessor *tps)
+long ast_taskprocessor_size(struct ast_taskprocessor *tps)
 {
 	return (tps) ? tps->tps_queue_size : -1;
 }
@@ -733,6 +732,13 @@ static int taskprocessor_push(struct ast_taskprocessor *tps, struct tps_task *t)
 	ao2_lock(tps);
 	AST_LIST_INSERT_TAIL(&tps->tps_queue, t, list);
 	previous_size = tps->tps_queue_size++;
+
+	if (previous_size >= AST_TASKPROCESSOR_HIGH_WATER_LEVEL && !tps->high_water_warned) {
+		ast_log(LOG_WARNING, "The '%s' task processor queue reached %d scheduled tasks.\n",
+			tps->name, previous_size);
+		tps->high_water_warned = 1;
+	}
+
 	/* The currently executing task counts as still in queue */
 	was_empty = tps->executing ? 0 : previous_size == 0;
 	ao2_unlock(tps);
@@ -754,7 +760,7 @@ int ast_taskprocessor_execute(struct ast_taskprocessor *tps)
 {
 	struct ast_taskprocessor_local local;
 	struct tps_task *t;
-	int size;
+	long size;
 
 	ao2_lock(tps);
 	t = tps_taskprocessor_pop(tps);
@@ -786,7 +792,7 @@ int ast_taskprocessor_execute(struct ast_taskprocessor *tps)
 	 * after we pop an empty stack.
 	 */
 	tps->executing = 0;
-	size = tps_taskprocessor_depth(tps);
+	size = ast_taskprocessor_size(tps);
 	/* If we executed a task, bump the stats */
 	if (tps->stats) {
 		tps->stats->_tasks_processed_count++;
diff --git a/main/term.c b/main/term.c
index 871a7cb..6a1a645 100644
--- a/main/term.c
+++ b/main/term.c
@@ -175,16 +175,14 @@ end:
 		if (ast_opt_light_background) {
 			snprintf(prepdata, sizeof(prepdata), "%c[%dm", ESC, COLOR_BROWN);
 			snprintf(enddata, sizeof(enddata), "%c[%dm", ESC, COLOR_BLACK);
-			snprintf(quitdata, sizeof(quitdata), "%c[0m", ESC);
 		} else if (ast_opt_force_black_background) {
 			snprintf(prepdata, sizeof(prepdata), "%c[%d;%d;%dm", ESC, ATTR_BRIGHT, COLOR_BROWN, COLOR_BLACK + 10);
 			snprintf(enddata, sizeof(enddata), "%c[%d;%d;%dm", ESC, ATTR_RESET, COLOR_WHITE, COLOR_BLACK + 10);
-			snprintf(quitdata, sizeof(quitdata), "%c[0m", ESC);
 		} else {
 			snprintf(prepdata, sizeof(prepdata), "%c[%d;%dm", ESC, ATTR_BRIGHT, COLOR_BROWN);
-			snprintf(enddata, sizeof(enddata), "%c[%d;%dm", ESC, ATTR_RESET, COLOR_WHITE);
-			snprintf(quitdata, sizeof(quitdata), "%c[0m", ESC);
+			snprintf(enddata, sizeof(enddata), "%c[%dm", ESC, ATTR_RESET);
 		}
+		snprintf(quitdata, sizeof(quitdata), "%c[%dm", ESC, ATTR_RESET);
 	}
 	return 0;
 }
@@ -216,9 +214,12 @@ char *term_color(char *outbuf, const char *inbuf, int fgcolor, int bgcolor, int
 	}
 
 	if (ast_opt_force_black_background) {
-		snprintf(outbuf, maxout, "%c[%d;%d;%dm%s%c[%d;%dm", ESC, attr, fgcolor, bgcolor + 10, inbuf, ESC, COLOR_WHITE, COLOR_BLACK + 10);
+		if (!bgcolor) {
+			bgcolor = COLOR_BLACK;
+		}
+		snprintf(outbuf, maxout, "%c[%d;%d;%dm%s%s", ESC, attr, fgcolor, bgcolor + 10, inbuf, term_end());
 	} else {
-		snprintf(outbuf, maxout, "%c[%d;%dm%s%c[0m", ESC, attr, fgcolor, inbuf, ESC);
+		snprintf(outbuf, maxout, "%c[%d;%dm%s%s", ESC, attr, fgcolor, inbuf, term_end());
 	}
 	return outbuf;
 }
@@ -242,16 +243,16 @@ static void check_bgcolor(int *bgcolor)
 	}
 }
 
-static int check_colors_allowed(int fgcolor)
+static int check_colors_allowed(void)
 {
-	return (!vt100compat || !fgcolor) ? 0 : 1;
+	return vt100compat;
 }
 
 int ast_term_color_code(struct ast_str **str, int fgcolor, int bgcolor)
 {
 	int attr = 0;
 
-	if (!check_colors_allowed(fgcolor)) {
+	if (!check_colors_allowed()) {
 		return -1;
 	}
 
@@ -273,7 +274,7 @@ char *term_color_code(char *outbuf, int fgcolor, int bgcolor, int maxout)
 {
 	int attr = 0;
 
-	if (!check_colors_allowed(fgcolor)) {
+	if (!check_colors_allowed()) {
 		*outbuf = '\0';
 		return outbuf;
 	}
@@ -310,11 +311,7 @@ const char *ast_term_color(int fgcolor, int bgcolor)
 
 const char *ast_term_reset(void)
 {
-	if (ast_opt_force_black_background) {
-		return enddata;
-	} else {
-		return quitdata;
-	}
+	return term_end();
 }
 
 char *term_strip(char *outbuf, const char *inbuf, int maxout)
diff --git a/main/threadpool.c b/main/threadpool.c
index d97a7ad..60e1e9a 100644
--- a/main/threadpool.c
+++ b/main/threadpool.c
@@ -168,7 +168,7 @@ static void *worker_start(void *arg);
 static struct worker_thread *worker_thread_alloc(struct ast_threadpool *pool);
 static int worker_thread_start(struct worker_thread *worker);
 static int worker_idle(struct worker_thread *worker);
-static void worker_set_state(struct worker_thread *worker, enum worker_state state);
+static int worker_set_state(struct worker_thread *worker, enum worker_state state);
 static void worker_shutdown(struct worker_thread *worker);
 
 /*!
@@ -482,7 +482,16 @@ static int activate_thread(void *obj, void *arg, int flags)
 				worker->id);
 		return 0;
 	}
-	worker_set_state(worker, ALIVE);
+
+	if (worker_set_state(worker, ALIVE)) {
+		ast_debug(1, "Failed to activate thread %d. It is dead\n",
+				worker->id);
+		/* The worker thread will no longer exist in the active threads or
+		 * idle threads container after this.
+		 */
+		ao2_unlink(pool->active_threads, worker);
+	}
+
 	return CMP_MATCH;
 }
 
@@ -538,20 +547,33 @@ static int queued_task_pushed(void *data)
 	struct task_pushed_data *tpd = data;
 	struct ast_threadpool *pool = tpd->pool;
 	int was_empty = tpd->was_empty;
+	unsigned int existing_active;
 
 	if (pool->listener && pool->listener->callbacks->task_pushed) {
 		pool->listener->callbacks->task_pushed(pool, pool->listener, was_empty);
 	}
-	if (ao2_container_count(pool->idle_threads) == 0) {
+
+	existing_active = ao2_container_count(pool->active_threads);
+
+	/* The first pass transitions any existing idle threads to be active, and
+	 * will also remove any worker threads that have recently entered the dead
+	 * state.
+	 */
+	ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA,
+			activate_thread, pool);
+
+	/* If no idle threads could be transitioned to active grow the pool as permitted. */
+	if (ao2_container_count(pool->active_threads) == existing_active) {
 		if (!pool->options.auto_increment) {
+			ao2_ref(tpd, -1);
 			return 0;
 		}
 		grow(pool, pool->options.auto_increment);
+		/* An optional second pass transitions any newly added threads. */
+		ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA,
+				activate_thread, pool);
 	}
 
-	ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA,
-			activate_thread, pool);
-
 	threadpool_send_state_changed(pool);
 	ao2_ref(tpd, -1);
 	return 0;
@@ -797,7 +819,7 @@ static int queued_set_size(void *data)
 
 	/* We don't count zombie threads as being "live" when potentially resizing */
 	unsigned int current_size = ao2_container_count(pool->active_threads) +
-		ao2_container_count(pool->idle_threads);
+			ao2_container_count(pool->idle_threads);
 
 	if (current_size == num_threads) {
 		ast_debug(3, "Not changing threadpool size since new size %u is the same as current %u\n",
@@ -806,6 +828,12 @@ static int queued_set_size(void *data)
 	}
 
 	if (current_size < num_threads) {
+		ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE,
+				activate_thread, pool);
+
+		/* As the above may have altered the number of current threads update it */
+		current_size = ao2_container_count(pool->active_threads) +
+				ao2_container_count(pool->idle_threads);
 		grow(pool, num_threads - current_size);
 		ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE,
 				activate_thread, pool);
@@ -1117,13 +1145,36 @@ static int worker_idle(struct worker_thread *worker)
  *
  * The threadpool calls into this function in order to let a worker know
  * how it should proceed.
+ *
+ * \retval -1 failure (state transition not permitted)
+ * \retval 0 success
  */
-static void worker_set_state(struct worker_thread *worker, enum worker_state state)
+static int worker_set_state(struct worker_thread *worker, enum worker_state state)
 {
 	SCOPED_MUTEX(lock, &worker->lock);
+
+	switch (state) {
+	case ALIVE:
+		/* This can occur due to a race condition between being told to go active
+		 * and an idle timeout happening.
+		 */
+		if (worker->state == DEAD) {
+			return -1;
+		}
+		ast_assert(worker->state != ZOMBIE);
+		break;
+	case DEAD:
+		break;
+	case ZOMBIE:
+		ast_assert(worker->state != DEAD);
+		break;
+	}
+
 	worker->state = state;
 	worker->wake_up = 1;
 	ast_cond_signal(&worker->cond);
+
+	return 0;
 }
 
 /*! Serializer group shutdown control object. */
@@ -1346,3 +1397,8 @@ struct ast_taskprocessor *ast_threadpool_serializer(const char *name, struct ast
 {
 	return ast_threadpool_serializer_group(name, pool, NULL);
 }
+
+long ast_threadpool_queue_size(struct ast_threadpool *pool)
+{
+	return ast_taskprocessor_size(pool->tps);
+}
diff --git a/main/translate.c b/main/translate.c
index 6d92777..6a39bac 100644
--- a/main/translate.c
+++ b/main/translate.c
@@ -298,6 +298,10 @@ static void destroy(struct ast_trans_pvt *pvt)
 		t->destroy(pvt);
 	}
 	ao2_cleanup(pvt->f.subclass.format);
+	if (pvt->explicit_dst) {
+		ao2_ref(pvt->explicit_dst, -1);
+		pvt->explicit_dst = NULL;
+	}
 	ast_free(pvt);
 	ast_module_unref(t->module);
 }
@@ -306,7 +310,7 @@ static void destroy(struct ast_trans_pvt *pvt)
  * \brief Allocate the descriptor, required outbuf space,
  * and possibly desc.
  */
-static struct ast_trans_pvt *newpvt(struct ast_translator *t)
+static struct ast_trans_pvt *newpvt(struct ast_translator *t, struct ast_format *explicit_dst)
 {
 	struct ast_trans_pvt *pvt;
 	int len;
@@ -332,6 +336,12 @@ static struct ast_trans_pvt *newpvt(struct ast_translator *t)
 	if (t->buf_size) {/* finally buffer and header */
 		pvt->outbuf.c = ofs + AST_FRIENDLY_OFFSET;
 	}
+	/*
+	 * If the format has an attribute module, explicit_dst includes the (joined)
+	 * result of the SDP negotiation. For example with the Opus Codec, the format
+	 * knows whether both parties want to do forward-error correction (FEC).
+	 */
+	pvt->explicit_dst = ao2_bump(explicit_dst);
 
 	ast_module_ref(t->module);
 
@@ -349,9 +359,16 @@ static struct ast_trans_pvt *newpvt(struct ast_translator *t)
 	pvt->f.src = pvt->t->name;
 	pvt->f.data.ptr = pvt->outbuf.c;
 
-	/* if the translator has not provided a format find one in the cache or create one */
+	/*
+	 * If the translator has not provided a format
+	 * A) use the joined one,
+	 * B) use the cached one, or
+	 * C) create one.
+	 */
 	if (!pvt->f.subclass.format) {
-		if (!ast_strlen_zero(pvt->t->format)) {
+		pvt->f.subclass.format = ao2_bump(pvt->explicit_dst);
+
+		if (!pvt->f.subclass.format && !ast_strlen_zero(pvt->t->format)) {
 			pvt->f.subclass.format = ast_format_cache_get(pvt->t->format);
 		}
 
@@ -380,9 +397,6 @@ static struct ast_trans_pvt *newpvt(struct ast_translator *t)
 /*! \brief framein wrapper, deals with bound checks.  */
 static int framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
 {
-	int ret;
-	int samples = pvt->samples;	/* initial value */
-
 	/* Copy the last in jb timing info to the pvt */
 	ast_copy_flags(&pvt->f, f, AST_FRFLAG_HAS_TIMING_INFO);
 	pvt->f.ts = f->ts;
@@ -406,12 +420,7 @@ static int framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
 	/* we require a framein routine, wouldn't know how to do
 	 * it otherwise.
 	 */
-	ret = pvt->t->framein(pvt, f);
-	/* diagnostic ... */
-	if (pvt->samples == samples)
-		ast_log(LOG_WARNING, "%s did not update samples %d\n",
-			pvt->t->name, pvt->samples);
-	return ret;
+	return pvt->t->framein(pvt, f);
 }
 
 /*! \brief generic frameout routine.
@@ -477,6 +486,7 @@ struct ast_trans_pvt *ast_translator_build_path(struct ast_format *dst, struct a
 
 	while (src_index != dst_index) {
 		struct ast_trans_pvt *cur;
+		struct ast_format *explicit_dst = NULL;
 		struct ast_translator *t = matrix_get(src_index, dst_index)->step;
 		if (!t) {
 			ast_log(LOG_WARNING, "No translator path from %s to %s\n",
@@ -484,7 +494,10 @@ struct ast_trans_pvt *ast_translator_build_path(struct ast_format *dst, struct a
 			AST_RWLIST_UNLOCK(&translators);
 			return NULL;
 		}
-		if (!(cur = newpvt(t))) {
+		if ((t->dst_codec.sample_rate == ast_format_get_sample_rate(dst)) && (t->dst_codec.type == ast_format_get_type(dst)) && (!strcmp(t->dst_codec.name, ast_format_get_name(dst)))) {
+			explicit_dst = dst;
+		}
+		if (!(cur = newpvt(t, explicit_dst))) {
 			ast_log(LOG_WARNING, "Failed to build translator step from %s to %s\n",
 				ast_format_get_name(src), ast_format_get_name(dst));
 			if (head) {
@@ -638,7 +651,7 @@ static void generate_computational_cost(struct ast_translator *t, int seconds)
 		return;
 	}
 
-	pvt = newpvt(t);
+	pvt = newpvt(t, NULL);
 	if (!pvt) {
 		ast_log(LOG_WARNING, "Translator '%s' appears to be broken and will probably fail.\n", t->name);
 		t->comp_cost = 999999;
diff --git a/main/utils.c b/main/utils.c
index ffef3ac..3a8d46a 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -1404,7 +1404,13 @@ int ast_carefulwrite(int fd, char *s, int len, int timeoutms)
 
 		if (res < 0 && errno != EAGAIN && errno != EINTR) {
 			/* fatal error from write() */
-			ast_log(LOG_ERROR, "write() returned error: %s\n", strerror(errno));
+			if (errno == EPIPE) {
+#ifndef STANDALONE
+				ast_debug(1, "write() failed due to reading end being closed: %s\n", strerror(errno));
+#endif
+			} else {
+				ast_log(LOG_ERROR, "write() returned error: %s\n", strerror(errno));
+			}
 			return -1;
 		}
 
@@ -2932,3 +2938,46 @@ int ast_eid_cmp(const struct ast_eid *eid1, const struct ast_eid *eid2)
 {
 	return memcmp(eid1, eid2, sizeof(*eid1));
 }
+
+int ast_file_is_readable(const char *filename)
+{
+#if defined(HAVE_EACCESS) || defined(HAVE_EUIDACCESS)
+#if defined(HAVE_EUIDACCESS) && !defined(HAVE_EACCESS)
+#define eaccess euidaccess
+#endif
+	return eaccess(filename, R_OK) == 0;
+#else
+	int fd = open(filename, O_RDONLY |  O_NONBLOCK);
+	if (fd < 0) {
+		return 0;
+	}
+	close(fd);
+	return 1;
+#endif
+}
+
+int ast_compare_versions(const char *version1, const char *version2)
+{
+	unsigned int major[2] = { 0 };
+	unsigned int minor[2] = { 0 };
+	unsigned int patch[2] = { 0 };
+	unsigned int extra[2] = { 0 };
+	int res;
+
+	sscanf(version1, "%u.%u.%u.%u", &major[0], &minor[0], &patch[0], &extra[0]);
+	sscanf(version2, "%u.%u.%u.%u", &major[1], &minor[1], &patch[1], &extra[1]);
+
+	res = major[0] - major[1];
+	if (res) {
+		return res;
+	}
+	res = minor[0] - minor[1];
+	if (res) {
+		return res;
+	}
+	res = patch[0] - patch[1];
+	if (res) {
+		return res;
+	}
+	return extra[0] - extra[1];
+}
diff --git a/main/xmldoc.c b/main/xmldoc.c
index fcf1b2c..da753cd 100644
--- a/main/xmldoc.c
+++ b/main/xmldoc.c
@@ -45,18 +45,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 /*! \brief Default documentation language. */
 static const char default_documentation_language[] = "en_US";
 
-/*!
- * \brief Number of columns to print when showing the XML documentation with a
- *         'core show application/function *' CLI command. Used in text wrapping.
- */
-static const int xmldoc_text_columns = 74;
-
-/*!
- * \brief This is a value that we will use to let the wrapping mechanism move the cursor
- *         backward and forward xmldoc_max_diff positions before cutting the middle of a
- *         word, trying to find a space or a \n.
- */
-static const int xmldoc_max_diff = 5;
+/*! \brief Number of columns to print when showing the XML documentation with a
+ *         'core show application/function *' CLI command. Used in text wrapping.*/
+static const int xmldoc_text_columns = 79;
 
 /*! \brief XML documentation language. */
 static char documentation_language[6];
@@ -176,100 +167,22 @@ static void xmldoc_setpostbr(char *postbr, size_t len, const char *text)
 
 /*!
  * \internal
- * \brief Try to find a space or a break in text starting at currentpost
- *         and moving at most maxdiff positions.
- *         Helper for xmldoc_string_wrap().
- *
- * \param text Input string where it will search.
- * \param currentpos Current position within text.
- * \param maxdiff Not move more than maxdiff inside text.
- *
- * \retval 1 if a space or break is found inside text while moving.
- * \retval 0 if no space or break is found.
- */
-static int xmldoc_wait_nextspace(const char *text, int currentpos, int maxdiff)
-{
-	int i, textlen;
-
-	if (!text) {
-		return 0;
-	}
-
-	textlen = strlen(text);
-	for (i = currentpos; i < textlen; i++) {
-		if (text[i] == ESC) {
-			/* Move to the end of the escape sequence */
-			while (i < textlen && text[i] != 'm') {
-				i++;
-			}
-		} else if (text[i] == ' ' || text[i] == '\n') {
-			/* Found the next space or linefeed */
-			return 1;
-		} else if (i - currentpos > maxdiff) {
-			/* We have looked the max distance and didn't find it */
-			return 0;
-		}
-	}
-
-	/* Reached the end and did not find it */
-
-	return 0;
-}
-
-/*!
- * \internal
- * \brief Helper function for xmldoc_string_wrap().
- *    Try to found a space or a break inside text moving backward
- *    not more than maxdiff positions.
- *
- * \param text The input string where to search for a space.
- * \param currentpos The current cursor position.
- * \param maxdiff The max number of positions to move within text.
- *
- * \retval 0 If no space is found (Notice that text[currentpos] is not a space or a break)
- * \retval > 0 If a space or a break is found, and the result is the position relative to
- *  currentpos.
- */
-static int xmldoc_foundspace_backward(const char *text, int currentpos, int maxdiff)
-{
-	int i;
-
-	for (i = currentpos; i > 0; i--) {
-		if (text[i] == ' ' || text[i] == '\n') {
-			return (currentpos - i);
-		} else if (text[i] == 'm' && (text[i - 1] >= '0' || text[i - 1] <= '9')) {
-			/* give up, we found the end of a possible ESC sequence. */
-			return 0;
-		} else if (currentpos - i > maxdiff) {
-			/* give up, we can't move anymore. */
-			return 0;
-		}
-	}
-
-	/* we found the beginning of the text */
-
-	return 0;
-}
-
-/*!
- * \internal
  * \brief Justify a text to a number of columns.
  *
  * \param text Input text to be justified.
  * \param columns Number of columns to preserve in the text.
- * \param maxdiff Try to not cut a word when goinf down.
  *
  * \retval NULL on error.
  * \retval The wrapped text.
  */
-static char *xmldoc_string_wrap(const char *text, int columns, int maxdiff)
+static char *xmldoc_string_wrap(const char *text, int columns)
 {
 	struct ast_str *tmp;
 	char *ret, postbr[160];
-	int count = 1, i, backspace, needtobreak = 0, colmax, textlen;
+	int count, i, textlen, postbrlen, lastbreak;
 
 	/* sanity check */
-	if (!text || columns <= 0 || maxdiff < 0) {
+	if (!text || columns <= 0) {
 		ast_log(LOG_WARNING, "Passing wrong arguments while trying to wrap the text\n");
 		return NULL;
 	}
@@ -282,55 +195,44 @@ static char *xmldoc_string_wrap(const char *text, int columns, int maxdiff)
 
 	/* Check for blanks and tabs and put them in postbr. */
 	xmldoc_setpostbr(postbr, sizeof(postbr), text);
-	colmax = columns - xmldoc_postbrlen(postbr);
+	postbrlen = xmldoc_postbrlen(postbr);
+
+	count = 0;
+	lastbreak = 0;
 
 	textlen = strlen(text);
 	for (i = 0; i < textlen; i++) {
-		if (needtobreak || !(count % colmax)) {
-			if (text[i] == ' ') {
-				ast_str_append(&tmp, 0, "\n%s", postbr);
-				needtobreak = 0;
-				count = 1;
-			} else if (text[i] != '\n') {
-				needtobreak = 1;
-				if (xmldoc_wait_nextspace(text, i, maxdiff)) {
-					/* wait for the next space */
-					ast_str_append(&tmp, 0, "%c", text[i]);
-					continue;
-				}
-				/* Try to look backwards */
-				backspace = xmldoc_foundspace_backward(text, i, maxdiff);
-				if (backspace) {
-					needtobreak = 1;
-					ast_str_truncate(tmp, -backspace);
-					i -= backspace + 1;
-					continue;
-				}
-				ast_str_append(&tmp, 0, "\n%s", postbr);
-				needtobreak = 0;
-				count = 1;
-			}
-			/* skip blanks after a \n */
-			while (text[i] == ' ') {
-				i++;
-			}
-		}
 		if (text[i] == '\n') {
 			xmldoc_setpostbr(postbr, sizeof(postbr), &text[i] + 1);
-			colmax = columns - xmldoc_postbrlen(postbr);
-			needtobreak = 0;
-			count = 1;
-		}
-		if (text[i] == ESC) {
-			/* Ignore Escape sequences. */
+			postbrlen = xmldoc_postbrlen(postbr);
+			count = 0;
+			lastbreak = 0;
+		} else if (text[i] == ESC) {
+			/* Walk over escape sequences without counting them. */
 			do {
 				ast_str_append(&tmp, 0, "%c", text[i]);
 				i++;
 			} while (i < textlen && text[i] != 'm');
 		} else {
+			if (text[i] == ' ') {
+				lastbreak = i;
+			}
 			count++;
 		}
-		ast_str_append(&tmp, 0, "%c", text[i]);
+
+		if (count > columns) {
+			/* Seek backwards if it was at most 30 characters ago. */
+			int back = i - lastbreak;
+			if (lastbreak && back > 0 && back < 30) {
+				ast_str_truncate(tmp, -back);
+				i = lastbreak; /* go back a bit */
+			}
+			ast_str_append(&tmp, 0, "\n%s", postbr);
+			count = postbrlen;
+			lastbreak = 0;
+		} else {
+			ast_str_append(&tmp, 0, "%c", text[i]);
+		}
 	}
 
 	ret = ast_strdup(ast_str_buffer(tmp));
@@ -442,7 +344,7 @@ char *ast_xmldoc_printable(const char *bwinput, int withcolors)
 	}
 
 	/* Wrap the text, notice that string wrap will avoid cutting an ESC sequence. */
-	wrapped = xmldoc_string_wrap(ast_str_buffer(colorized), xmldoc_text_columns, xmldoc_max_diff);
+	wrapped = xmldoc_string_wrap(ast_str_buffer(colorized), xmldoc_text_columns);
 
 	ast_free(colorized);
 
@@ -2646,14 +2548,18 @@ struct ast_xml_xpath_results *__attribute__((format(printf, 1, 2))) ast_xmldoc_q
 	struct documentation_tree *doctree;
 	RAII_VAR(struct ast_str *, xpath_str, ast_str_create(128), ast_free);
 	va_list ap;
+	int res;
 
 	if (!xpath_str) {
 		return NULL;
 	}
 
 	va_start(ap, fmt);
-	ast_str_set_va(&xpath_str, 0, fmt, ap);
+	res = ast_str_set_va(&xpath_str, 0, fmt, ap);
 	va_end(ap);
+	if (res == AST_DYNSTR_BUILD_FAILED) {
+		return NULL;
+	}
 
 	AST_RWLIST_RDLOCK(&xmldoc_tree);
 	AST_LIST_TRAVERSE(&xmldoc_tree, doctree, entry) {
diff --git a/pbx/pbx_dundi.c b/pbx/pbx_dundi.c
index fc9e182..04da247 100644
--- a/pbx/pbx_dundi.c
+++ b/pbx/pbx_dundi.c
@@ -3264,6 +3264,7 @@ static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, i
 	pack = ast_calloc(1, len);
 	if (pack) {
 		pack->h = (struct dundi_hdr *)(pack->data);
+		pack->retransid = -1;
 		if (cmdresp != DUNDI_COMMAND_ACK) {
 			pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
 			pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
diff --git a/res/res_agi.c b/res/res_agi.c
index 921eeff..ff33580 100644
--- a/res/res_agi.c
+++ b/res/res_agi.c
@@ -2630,8 +2630,8 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const
 			}
 			f = ast_read(chan);
 			if (!f) {
-				ast_agi_send(agi->fd, chan, "200 result=%d (hangup) endpos=%ld\n", -1, sample_offset);
 				ast_closestream(fs);
+				ast_agi_send(agi->fd, chan, "200 result=%d (hangup) endpos=%ld\n", -1, sample_offset);
 				if (sildet)
 					ast_dsp_free(sildet);
 				return RESULT_FAILURE;
@@ -2645,8 +2645,8 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const
 					ast_stream_rewind(fs, 200);
 					ast_truncstream(fs);
 					sample_offset = ast_tellstream(fs);
-					ast_agi_send(agi->fd, chan, "200 result=%d (dtmf) endpos=%ld\n", f->subclass.integer, sample_offset);
 					ast_closestream(fs);
+					ast_agi_send(agi->fd, chan, "200 result=%d (dtmf) endpos=%ld\n", f->subclass.integer, sample_offset);
 					ast_frfree(f);
 					if (sildet)
 						ast_dsp_free(sildet);
@@ -2690,8 +2690,8 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const
 			ast_truncstream(fs);
 			sample_offset = ast_tellstream(fs);
 		}
-		ast_agi_send(agi->fd, chan, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
 		ast_closestream(fs);
+		ast_agi_send(agi->fd, chan, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
 	}
 
 	if (silence > 0) {
diff --git a/res/res_chan_stats.c b/res/res_chan_stats.c
index df34718..8d446b3 100644
--- a/res/res_chan_stats.c
+++ b/res/res_chan_stats.c
@@ -111,7 +111,7 @@ static void updates(void *data, struct stasis_subscription *sub,
 
 	if (!update->old_snapshot && update->new_snapshot) {
 		/* Initial cache entry; count a channel creation */
-		ast_statsd_log("channels.count", AST_STATSD_COUNTER, 1);
+		ast_statsd_log_string("channels.count", AST_STATSD_GAUGE, "+1", 1.0);
 	} else if (update->old_snapshot && !update->new_snapshot) {
 		/* Cache entry removed. Compute the age of the channel and post
 		 * that, as well as decrementing the channel count.
@@ -125,7 +125,7 @@ static void updates(void *data, struct stasis_subscription *sub,
 		ast_statsd_log("channels.calltime", AST_STATSD_TIMER, age);
 
 		/* And decrement the channel count */
-		ast_statsd_log("channels.count", AST_STATSD_COUNTER, -1);
+		ast_statsd_log_string("channels.count", AST_STATSD_GAUGE, "-1", 1.0);
 	}
 }
 
diff --git a/res/res_config_pgsql.c b/res/res_config_pgsql.c
index 23b6a46..52c8ede 100644
--- a/res/res_config_pgsql.c
+++ b/res/res_config_pgsql.c
@@ -336,7 +336,9 @@ static struct tables *find_table(const char *database, const char *orig_tablenam
 		ast_str_set(&sql, 0, "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM pg_class c, pg_type t, pg_attribute a LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum WHERE c.oid = a.attrelid AND a.atttypid = t.oid AND (a.attnum > 0) AND c.relname = '%s' ORDER BY c.relname, attnum", orig_tablename);
 	}
 
+	ast_mutex_lock(&pgsql_lock);
 	exec_result = pgsql_exec(database, orig_tablename, ast_str_buffer(sql), &result);
+	ast_mutex_unlock(&pgsql_lock);
 	ast_debug(1, "Query of table structure complete.  Now retrieving results.\n");
 	if (exec_result != 0) {
 		ast_log(LOG_ERROR, "Failed to query database columns for table %s\n", orig_tablename);
@@ -1354,6 +1356,9 @@ static int unload_module(void)
 	ast_cli_unregister_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
 	ast_config_engine_deregister(&pgsql_engine);
 
+	/* Unlock so something else can destroy the lock. */
+	ast_mutex_unlock(&pgsql_lock);
+
 	/* Destroy cached table info */
 	AST_LIST_LOCK(&psql_tables);
 	while ((table = AST_LIST_REMOVE_HEAD(&psql_tables, list))) {
@@ -1361,9 +1366,6 @@ static int unload_module(void)
 	}
 	AST_LIST_UNLOCK(&psql_tables);
 
-	/* Unlock so something else can destroy the lock. */
-	ast_mutex_unlock(&pgsql_lock);
-
 	return 0;
 }
 
diff --git a/res/res_endpoint_stats.c b/res/res_endpoint_stats.c
new file mode 100644
index 0000000..6b84794
--- /dev/null
+++ b/res/res_endpoint_stats.c
@@ -0,0 +1,157 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Digium, Inc.
+ *
+ * Matthew Jordan <mjordan at 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.
+ */
+
+/*!
+ * \brief Statsd Endpoint stats.
+ *
+ * This module subscribes to Stasis endpoints and send statistics
+ * based on their state.
+ *
+ * \author Matthew Jordan <mjordan at digium.com>
+ * \since 13.7.0
+ */
+
+/*** MODULEINFO
+	<depend>res_statsd</depend>
+	<defaultenabled>no</defaultenabled>
+	<support_level>extended</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/stasis_endpoints.h"
+#include "asterisk/stasis_message_router.h"
+#include "asterisk/statsd.h"
+
+/*! Stasis message router */
+static struct stasis_message_router *router;
+
+static void update_endpoint_state(struct ast_endpoint_snapshot *snapshot, const char *delta)
+{
+	switch (snapshot->state) {
+	case AST_ENDPOINT_UNKNOWN:
+		ast_statsd_log_string("endpoints.state.unknown", AST_STATSD_GAUGE, delta, 1.0);
+		break;
+	case AST_ENDPOINT_OFFLINE:
+		ast_statsd_log_string("endpoints.state.offline", AST_STATSD_GAUGE, delta, 1.0);
+		break;
+	case AST_ENDPOINT_ONLINE:
+		ast_statsd_log_string("endpoints.state.online", AST_STATSD_GAUGE, delta, 1.0);
+		break;
+	}
+}
+
+static void handle_endpoint_update(struct ast_endpoint_snapshot *old_snapshot, struct ast_endpoint_snapshot *new_snapshot)
+{
+	if (!old_snapshot && new_snapshot) {
+		ast_statsd_log_string("endpoints.count", AST_STATSD_GAUGE, "+1", 1.0);
+		update_endpoint_state(new_snapshot, "+1");
+	} else if (old_snapshot && !new_snapshot) {
+		ast_statsd_log_string("endpoints.count", AST_STATSD_GAUGE, "-1", 1.0);
+		update_endpoint_state(old_snapshot, "-1");
+	} else {
+		if (old_snapshot->state != new_snapshot->state) {
+			update_endpoint_state(old_snapshot, "-1");
+			update_endpoint_state(new_snapshot, "+1");
+		}
+		ast_statsd_log_full_va("endpoints.%s.%s.channels", AST_STATSD_GAUGE, new_snapshot->num_channels, 1.0,
+			new_snapshot->tech, new_snapshot->resource);
+	}
+}
+
+static void cache_update_cb(void *data, struct stasis_subscription *sub,
+	struct stasis_message *message)
+{
+	struct stasis_cache_update *update = stasis_message_data(message);
+	struct ast_endpoint_snapshot *old_snapshot;
+	struct ast_endpoint_snapshot *new_snapshot;
+
+	if (ast_endpoint_snapshot_type() != update->type) {
+		return;
+	}
+
+	old_snapshot = stasis_message_data(update->old_snapshot);
+	new_snapshot = stasis_message_data(update->new_snapshot);
+
+	handle_endpoint_update(old_snapshot, new_snapshot);
+}
+
+static int dump_cache_load(void *obj, void *arg, int flags)
+{
+	struct stasis_message *msg = obj;
+	struct ast_endpoint_snapshot *snapshot = stasis_message_data(msg);
+
+	handle_endpoint_update(NULL, snapshot);
+
+	return 0;
+}
+
+static int dump_cache_unload(void *obj, void *arg, int flags)
+{
+	struct stasis_message *msg = obj;
+	struct ast_endpoint_snapshot *snapshot = stasis_message_data(msg);
+
+	handle_endpoint_update(snapshot, NULL);
+
+	return 0;
+}
+
+static int load_module(void)
+{
+	struct ao2_container *endpoints;
+
+	router = stasis_message_router_create(ast_endpoint_topic_all_cached());
+	if (!router) {
+		return AST_MODULE_LOAD_FAILURE;
+	}
+	stasis_message_router_add(router, stasis_cache_update_type(), cache_update_cb, NULL);
+
+	endpoints = stasis_cache_dump(ast_endpoint_cache(), ast_endpoint_snapshot_type());
+	if (endpoints) {
+		ao2_callback(endpoints, OBJ_MULTIPLE | OBJ_NODATA | OBJ_NOLOCK, dump_cache_load, NULL);
+		ao2_ref(endpoints, -1);
+	}
+
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+	struct ao2_container *endpoints;
+
+	endpoints = stasis_cache_dump(ast_endpoint_cache(), ast_endpoint_snapshot_type());
+	if (endpoints) {
+		ao2_callback(endpoints, OBJ_MULTIPLE | OBJ_NODATA | OBJ_NOLOCK, dump_cache_unload, NULL);
+		ao2_ref(endpoints, -1);
+	}
+
+	stasis_message_router_unsubscribe_and_join(router);
+	router = NULL;
+
+	return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Endpoint statistics",
+	.support_level = AST_MODULE_SUPPORT_EXTENDED,
+	.load = load_module,
+	.unload = unload_module,
+	.nonoptreq = "res_statsd"
+	);
diff --git a/res/res_fax.c b/res/res_fax.c
index e947c91..ef0e276 100644
--- a/res/res_fax.c
+++ b/res/res_fax.c
@@ -2028,14 +2028,14 @@ static int report_receive_fax_status(struct ast_channel *chan, const char *filen
 			fax_bitrate = ast_strdupa(fax_bitrate);
 		}
 
-		json_object = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: O}",
+		json_object = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: o}",
 				"type", "receive",
 				"remote_station_id", S_OR(remote_station_id, ""),
 				"local_station_id", S_OR(local_station_id, ""),
 				"fax_pages", S_OR(fax_pages, ""),
 				"fax_resolution", S_OR(fax_resolution, ""),
 				"fax_bitrate", S_OR(fax_bitrate, ""),
-				"filenames", json_array);
+				"filenames", ast_json_ref(json_array));
 		if (!json_object) {
 			return -1;
 		}
diff --git a/res/res_format_attr_h264.c b/res/res_format_attr_h264.c
index d263a55..34793ef 100644
--- a/res/res_format_attr_h264.c
+++ b/res/res_format_attr_h264.c
@@ -236,7 +236,7 @@ static struct ast_format *h264_parse_sdp_fmtp(const struct ast_format *format, c
 	if (field != H264_ATTR_KEY_UNSET) {	\
 		if (added) {	\
 			ast_str_append(str, 0, ";");	\
-		} else {	\
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {	\
 			added = 1;	\
 		}	\
 		ast_str_append(str, 0, "%s=%u", name, field);	\
@@ -247,7 +247,7 @@ static struct ast_format *h264_parse_sdp_fmtp(const struct ast_format *format, c
 	if (field) {	\
 		if (added) {	\
 			ast_str_append(str, 0, ";");	\
-		} else {	\
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {	\
 			added = 1;	\
 		}	\
 		ast_str_append(str, 0, "%s=%u", name, field);	\
@@ -263,8 +263,6 @@ static void h264_generate_sdp_fmtp(const struct ast_format *format, unsigned int
 		return;
 	}
 
-	ast_str_append(str, 0, "a=fmtp:%u ", payload);
-
 	APPEND_IF_NONZERO(attr->MAX_MBPS, str, "max-mbps");
 	APPEND_IF_NONZERO(attr->MAX_FS, str, "max-fs");
 	APPEND_IF_NONZERO(attr->MAX_CPB, str, "max-cpb");
@@ -287,7 +285,7 @@ static void h264_generate_sdp_fmtp(const struct ast_format *format, unsigned int
 	if (attr->PROFILE_IDC && attr->PROFILE_IOP && attr->LEVEL) {
 		if (added) {
 			ast_str_append(str, 0, ";");
-		} else {
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
 			added = 1;
 		}
 		ast_str_append(str, 0, "profile-level-id=%02X%02X%02X", attr->PROFILE_IDC, attr->PROFILE_IOP, attr->LEVEL);
@@ -296,15 +294,13 @@ static void h264_generate_sdp_fmtp(const struct ast_format *format, unsigned int
 	if (!ast_strlen_zero(attr->SPS) && !ast_strlen_zero(attr->PPS)) {
 		if (added) {
 			ast_str_append(str, 0, ";");
-		} else {
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
 			added = 1;
 		}
-		ast_str_append(str, 0, ";sprop-parameter-sets=%s,%s", attr->SPS, attr->PPS);
+		ast_str_append(str, 0, "sprop-parameter-sets=%s,%s", attr->SPS, attr->PPS);
 	}
 
-	if (!added) {
-		ast_str_reset(*str);
-	} else {
+	if (added) {
 		ast_str_append(str, 0, "\r\n");
 	}
 
diff --git a/res/res_format_attr_opus.c b/res/res_format_attr_opus.c
index 7748ecc..4e81df0 100644
--- a/res/res_format_attr_opus.c
+++ b/res/res_format_attr_opus.c
@@ -33,28 +33,36 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/format.h"
+#include "asterisk/logger.h"            /* for ast_log, LOG_WARNING */
+#include "asterisk/strings.h"           /* for ast_str_append */
+#include "asterisk/utils.h"             /* for MIN, ast_malloc, ast_free */
 
 /*!
  * \brief Opus attribute structure.
  *
- * \note http://tools.ietf.org/html/draft-ietf-payload-rtp-opus-00.
+ * \note http://tools.ietf.org/html/rfc7587#section-6
  */
 struct opus_attr {
-	unsigned int maxbitrate;	        /* Default 64-128 kb/s for FB stereo music */
-	unsigned int maxplayrate	        /* Default 48000 */;
-	unsigned int minptime;	            /* Default 3, but it's 10 in format.c */
-	unsigned int stereo;	            /* Default 0 */
-	unsigned int cbr;	                /* Default 0 */
-	unsigned int fec;	                /* Default 0 */
-	unsigned int dtx;	                /* Default 0 */
-	unsigned int spropmaxcapturerate;	/* Default 48000 */
-	unsigned int spropstereo;	        /* Default 0 */
+	unsigned int maxbitrate;
+	unsigned int maxplayrate;
+	unsigned int unused; /* was minptime, kept for binary compatibility */
+	unsigned int stereo;
+	unsigned int cbr;
+	unsigned int fec;
+	unsigned int dtx;
+	unsigned int spropmaxcapturerate;
+	unsigned int spropstereo;
 };
 
 static struct opus_attr default_opus_attr = {
-	.fec    = 0,
-	.dtx    = 0,
-	.stereo = 0,
+	.maxplayrate         = 48000,
+	.spropmaxcapturerate = 48000,
+	.maxbitrate          = 510000,
+	.stereo              = 0,
+	.spropstereo         = 0,
+	.cbr                 = 0,
+	.fec                 = 1,
+	.dtx                 = 0,
 };
 
 static void opus_destroy(struct ast_format *format)
@@ -67,7 +75,7 @@ static void opus_destroy(struct ast_format *format)
 static int opus_clone(const struct ast_format *src, struct ast_format *dst)
 {
 	struct opus_attr *original = ast_format_get_attribute_data(src);
-	struct opus_attr *attr = ast_calloc(1, sizeof(*attr));
+	struct opus_attr *attr = ast_malloc(sizeof(*attr));
 
 	if (!attr) {
 		return -1;
@@ -75,6 +83,8 @@ static int opus_clone(const struct ast_format *src, struct ast_format *dst)
 
 	if (original) {
 		*attr = *original;
+	} else {
+		*attr = default_opus_attr;
 	}
 
 	ast_format_set_attribute_data(dst, attr);
@@ -97,33 +107,54 @@ static struct ast_format *opus_parse_sdp_fmtp(const struct ast_format *format, c
 
 	if ((kvp = strstr(attributes, "maxplaybackrate")) && sscanf(kvp, "maxplaybackrate=%30u", &val) == 1) {
 		attr->maxplayrate = val;
+	} else {
+		attr->maxplayrate = 48000;
 	}
+
 	if ((kvp = strstr(attributes, "sprop-maxcapturerate")) && sscanf(kvp, "sprop-maxcapturerate=%30u", &val) == 1) {
 		attr->spropmaxcapturerate = val;
+	} else {
+		attr->spropmaxcapturerate = 48000;
 	}
-	if ((kvp = strstr(attributes, "minptime")) && sscanf(kvp, "minptime=%30u", &val) == 1) {
-		attr->minptime = val;
-	}
+
 	if ((kvp = strstr(attributes, "maxaveragebitrate")) && sscanf(kvp, "maxaveragebitrate=%30u", &val) == 1) {
 		attr->maxbitrate = val;
+	} else {
+		attr->maxbitrate = 510000;
 	}
-	if ((kvp = strstr(attributes, " stereo")) && sscanf(kvp, " stereo=%30u", &val) == 1) {
-		attr->stereo = val;
-	}
-	if ((kvp = strstr(attributes, ";stereo")) && sscanf(kvp, ";stereo=%30u", &val) == 1) {
-		attr->stereo = val;
+
+	if (!strncmp(attributes, "stereo=1", 8)) {
+		attr->stereo = 1;
+	} else if (strstr(attributes, " stereo=1")) {
+		attr->stereo = 1;
+	} else if (strstr(attributes, ";stereo=1")) {
+		attr->stereo = 1;
+	} else {
+		attr->stereo = 0;
 	}
-	if ((kvp = strstr(attributes, "sprop-stereo")) && sscanf(kvp, "sprop-stereo=%30u", &val) == 1) {
-		attr->spropstereo = val;
+
+	if (strstr(attributes, "sprop-stereo=1")) {
+		attr->spropstereo = 1;
+	} else {
+		attr->spropstereo = 0;
 	}
-	if ((kvp = strstr(attributes, "cbr")) && sscanf(kvp, "cbr=%30u", &val) == 1) {
-		attr->cbr = val;
+
+	if (strstr(attributes, "cbr=1")) {
+		attr->cbr = 1;
+	} else {
+		attr->cbr = 0;
 	}
-	if ((kvp = strstr(attributes, "useinbandfec")) && sscanf(kvp, "useinbandfec=%30u", &val) == 1) {
-		attr->fec = val;
+
+	if (strstr(attributes, "useinbandfec=1")) {
+		attr->fec = 1;
+	} else {
+		attr->fec = 0;
 	}
-	if ((kvp = strstr(attributes, "usedtx")) && sscanf(kvp, "usedtx=%30u", &val) == 1) {
-		attr->dtx = val;
+
+	if (strstr(attributes, "usedtx=1")) {
+		attr->dtx = 1;
+	} else {
+		attr->dtx = 0;
 	}
 
 	return cloned;
@@ -132,34 +163,92 @@ static struct ast_format *opus_parse_sdp_fmtp(const struct ast_format *format, c
 static void opus_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
 {
 	struct opus_attr *attr = ast_format_get_attribute_data(format);
+	int added = 0;
 
 	if (!attr) {
-		return;
+		/*
+		 * (Only) cached formats do not have attribute data assigned because
+		 * they were created before this attribute module was registered.
+		 * Therefore, we assume the default attribute values here.
+		 */
+		attr = &default_opus_attr;
+	}
+
+	if (48000 != attr->maxplayrate) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "maxplaybackrate=%u", attr->maxplayrate);
+	}
+
+	if (48000 != attr->spropmaxcapturerate) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "sprop-maxcapturerate=%u", attr->spropmaxcapturerate);
+	}
+
+	if (510000 != attr->maxbitrate) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "maxaveragebitrate=%u", attr->maxbitrate);
+	}
+
+	if (0 != attr->stereo) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "stereo=%u", attr->stereo);
 	}
 
-	/* FIXME should we only generate attributes that were explicitly set? */
-	ast_str_append(str, 0,
-				"a=fmtp:%u "
-					"maxplaybackrate=%u;"
-					"sprop-maxcapturerate=%u;"
-					"minptime=%u;"
-					"maxaveragebitrate=%u;"
-					"stereo=%d;"
-					"sprop-stereo=%d;"
-					"cbr=%d;"
-					"useinbandfec=%d;"
-					"usedtx=%d\r\n",
-			payload,
-			attr->maxplayrate ? attr->maxplayrate : 48000,	/* maxplaybackrate */
-			attr->spropmaxcapturerate ? attr->spropmaxcapturerate : 48000,	/* sprop-maxcapturerate */
-			attr->minptime > 10 ? attr->minptime : 10,	/* minptime */
-			attr->maxbitrate ? attr->maxbitrate : 20000,	/* maxaveragebitrate */
-			attr->stereo ? 1 : 0,		/* stereo */
-			attr->spropstereo ? 1 : 0,		/* sprop-stereo */
-			attr->cbr ? 1 : 0,		/* cbr */
-			attr->fec ? 1 : 0,		/* useinbandfec */
-			attr->dtx ? 1 : 0		/* usedtx */
-	);
+	if (0 != attr->spropstereo) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "sprop-stereo=%u", attr->spropstereo);
+	}
+
+	if (0 != attr->cbr) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "cbr=%u", attr->cbr);
+	}
+
+	if (0 != attr->fec) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "useinbandfec=%u", attr->fec);
+	}
+
+	if (0 != attr->dtx) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "usedtx=%u", attr->dtx);
+	}
+
+	if (added) {
+		ast_str_append(str, 0, "\r\n");
+	}
 }
 
 static struct ast_format *opus_getjoint(const struct ast_format *format1, const struct ast_format *format2)
@@ -183,19 +272,22 @@ static struct ast_format *opus_getjoint(const struct ast_format *format1, const
 	}
 	attr_res = ast_format_get_attribute_data(jointformat);
 
-	/* Only do dtx if both sides want it. DTX is a trade off between
-	 * computational complexity and bandwidth. */
-	attr_res->dtx = attr1->dtx && attr2->dtx ? 1 : 0;
+	attr_res->dtx = attr1->dtx || attr2->dtx ? 1 : 0;
 
 	/* Only do FEC if both sides want it.  If a peer specifically requests not
 	 * to receive with FEC, it may be a waste of bandwidth. */
 	attr_res->fec = attr1->fec && attr2->fec ? 1 : 0;
 
+	attr_res->cbr = attr1->cbr || attr2->cbr ? 1 : 0;
+	attr_res->spropstereo = attr1->spropstereo || attr2->spropstereo ? 1 : 0;
+
 	/* Only do stereo if both sides want it.  If a peer specifically requests not
 	 * to receive stereo signals, it may be a waste of bandwidth. */
 	attr_res->stereo = attr1->stereo && attr2->stereo ? 1 : 0;
 
-	/* FIXME: do we need to join other attributes as well, e.g., minptime, cbr, etc.? */
+	attr_res->maxbitrate = MIN(attr1->maxbitrate, attr2->maxbitrate);
+	attr_res->spropmaxcapturerate = MIN(attr1->spropmaxcapturerate, attr2->spropmaxcapturerate);
+	attr_res->maxplayrate = MIN(attr1->maxplayrate, attr2->maxplayrate);
 
 	return jointformat;
 }
@@ -223,7 +315,7 @@ static struct ast_format *opus_set(const struct ast_format *format, const char *
 	} else if (!strcasecmp(name, "max_playrate")) {
 		attr->maxplayrate = val;
 	} else if (!strcasecmp(name, "minptime")) {
-		attr->minptime = val;
+		attr->unused = val;
 	} else if (!strcasecmp(name, "stereo")) {
 		attr->stereo = val;
 	} else if (!strcasecmp(name, "cbr")) {
diff --git a/res/res_format_attr_vp8.c b/res/res_format_attr_vp8.c
new file mode 100644
index 0000000..78a6889
--- /dev/null
+++ b/res/res_format_attr_vp8.c
@@ -0,0 +1,228 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Alexander Traud
+ *
+ * Alexander Traud <pabstraud at compuserve.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.
+ */
+
+/*!
+ * \file
+ * \brief VP8 format attribute interface
+ *
+ * \author Alexander Traud <pabstraud at compuserve.com>
+ *
+ * \note http://tools.ietf.org/html/draft-ietf-payload-vp8
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/format.h"
+#include "asterisk/logger.h"            /* for ast_log, LOG_WARNING */
+#include "asterisk/strings.h"           /* for ast_str_append */
+#include "asterisk/utils.h"             /* for MIN, ast_malloc, ast_free */
+
+struct vp8_attr {
+	unsigned int maximum_frame_rate;
+	unsigned int maximum_frame_size;
+};
+
+static struct vp8_attr default_vp8_attr = {
+	.maximum_frame_rate = UINT_MAX,
+	.maximum_frame_size = UINT_MAX,
+};
+
+static void vp8_destroy(struct ast_format *format)
+{
+	struct vp8_attr *attr = ast_format_get_attribute_data(format);
+
+	ast_free(attr);
+}
+
+static int vp8_clone(const struct ast_format *src, struct ast_format *dst)
+{
+	struct vp8_attr *original = ast_format_get_attribute_data(src);
+	struct vp8_attr *attr = ast_malloc(sizeof(*attr));
+
+	if (!attr) {
+		return -1;
+	}
+
+	if (original) {
+		*attr = *original;
+	} else {
+		*attr = default_vp8_attr;
+	}
+
+	ast_format_set_attribute_data(dst, attr);
+
+	return 0;
+}
+
+static struct ast_format *vp8_parse_sdp_fmtp(const struct ast_format *format, const char *attributes)
+{
+	struct ast_format *cloned;
+	struct vp8_attr *attr;
+	const char *kvp;
+	unsigned int val;
+
+	cloned = ast_format_clone(format);
+	if (!cloned) {
+		return NULL;
+	}
+	attr = ast_format_get_attribute_data(cloned);
+
+	if ((kvp = strstr(attributes, "max-fr")) && sscanf(kvp, "max-fr=%30u", &val) == 1) {
+		attr->maximum_frame_rate = val;
+	} else {
+		attr->maximum_frame_rate = UINT_MAX;
+	}
+
+	if ((kvp = strstr(attributes, "max-fs")) && sscanf(kvp, "max-fs=%30u", &val) == 1) {
+		attr->maximum_frame_size = val;
+	} else {
+		attr->maximum_frame_size = UINT_MAX;
+	}
+
+	return cloned;
+}
+
+static void vp8_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
+{
+	struct vp8_attr *attr = ast_format_get_attribute_data(format);
+	int added = 0;
+
+	if (!attr) {
+		/*
+		 * (Only) cached formats do not have attribute data assigned because
+		 * they were created before this attribute module was registered.
+		 * Therefore, we assume the default attribute values here.
+		 */
+		attr = &default_vp8_attr;
+	}
+
+	if (UINT_MAX != attr->maximum_frame_rate) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "max-fr=%u", attr->maximum_frame_rate);
+	}
+
+	if (UINT_MAX != attr->maximum_frame_size) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "max-fs=%u", attr->maximum_frame_size);
+	}
+
+	if (added) {
+		ast_str_append(str, 0, "\r\n");
+	}
+}
+
+static struct ast_format *vp8_getjoint(const struct ast_format *format1, const struct ast_format *format2)
+{
+	struct vp8_attr *attr1 = ast_format_get_attribute_data(format1);
+	struct vp8_attr *attr2 = ast_format_get_attribute_data(format2);
+	struct ast_format *jointformat;
+	struct vp8_attr *attr_res;
+
+	if (!attr1) {
+		attr1 = &default_vp8_attr;
+	}
+
+	if (!attr2) {
+		attr2 = &default_vp8_attr;
+	}
+
+	jointformat = ast_format_clone(format1);
+	if (!jointformat) {
+		return NULL;
+	}
+	attr_res = ast_format_get_attribute_data(jointformat);
+
+	attr_res->maximum_frame_rate = MIN(attr1->maximum_frame_rate, attr2->maximum_frame_rate);
+	attr_res->maximum_frame_size = MIN(attr1->maximum_frame_size, attr2->maximum_frame_size);
+
+	return jointformat;
+}
+
+static struct ast_format *vp8_set(const struct ast_format *format, const char *name, const char *value)
+{
+	struct ast_format *cloned;
+	struct vp8_attr *attr;
+	unsigned int val;
+
+	if (sscanf(value, "%30u", &val) != 1) {
+		ast_log(LOG_WARNING, "Unknown value '%s' for attribute type '%s'\n",
+			value, name);
+		return NULL;
+	}
+
+	cloned = ast_format_clone(format);
+	if (!cloned) {
+		return NULL;
+	}
+	attr = ast_format_get_attribute_data(cloned);
+
+	if (!strcasecmp(name, "maximum_frame_rate")) {
+		attr->maximum_frame_rate = val;
+	} else if (!strcasecmp(name, "maximum_frame_size")) {
+		attr->maximum_frame_size = val;
+	} else {
+		ast_log(LOG_WARNING, "unknown attribute type %s\n", name);
+	}
+
+	return cloned;
+}
+
+static struct ast_format_interface vp8_interface = {
+	.format_destroy = vp8_destroy,
+	.format_clone = vp8_clone,
+	.format_get_joint = vp8_getjoint,
+	.format_attribute_set = vp8_set,
+	.format_parse_sdp_fmtp = vp8_parse_sdp_fmtp,
+	.format_generate_sdp_fmtp = vp8_generate_sdp_fmtp,
+};
+
+static int load_module(void)
+{
+	if (ast_format_interface_register("vp8", &vp8_interface)) {
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+	return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "VP8 Format Attribute Module",
+	.support_level = AST_MODULE_SUPPORT_CORE,
+	.load = load_module,
+	.unload = unload_module,
+	.load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 46edd31..8e99c55 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -2563,6 +2563,8 @@ pjsip_dialog *ast_sip_create_dialog_uac(const struct ast_sip_endpoint *endpoint,
 
 		pj_strdup2_with_null(dlg->pool, &tmp, outbound_proxy);
 		if (!(route = pjsip_parse_hdr(dlg->pool, &ROUTE_HNAME, tmp.ptr, tmp.slen, NULL))) {
+			ast_log(LOG_ERROR, "Could not create dialog to endpoint '%s' as outbound proxy URI '%s' is not valid\n",
+				ast_sorcery_object_get_id(endpoint), outbound_proxy);
 			dlg->sess_count--;
 			pjsip_dlg_terminate(dlg);
 			return NULL;
@@ -2756,6 +2758,7 @@ static int create_out_of_dialog_request(const pjsip_method *method, struct ast_s
 	pj_str_t from;
 	pj_pool_t *pool;
 	pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
+	pjsip_uri *sip_uri;
 
 	if (ast_strlen_zero(uri)) {
 		if (!endpoint && (!contact || ast_strlen_zero(contact->uri))) {
@@ -2792,6 +2795,16 @@ static int create_out_of_dialog_request(const pjsip_method *method, struct ast_s
 		return -1;
 	}
 
+	sip_uri = pjsip_parse_uri(pool, remote_uri.ptr, remote_uri.slen, 0);
+	if (!sip_uri || (!PJSIP_URI_SCHEME_IS_SIP(sip_uri) && !PJSIP_URI_SCHEME_IS_SIPS(sip_uri))) {
+		ast_log(LOG_ERROR, "Unable to create outbound %.*s request to endpoint %s as URI '%s' is not valid\n",
+			(int) pj_strlen(&method->name), pj_strbuf(&method->name),
+			endpoint ? ast_sorcery_object_get_id(endpoint) : "<none>",
+			pj_strbuf(&remote_uri));
+		pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
+		return -1;
+	}
+
 	if (sip_dialog_create_from(pool, &from, endpoint ? endpoint->fromuser : NULL,
 				endpoint ? endpoint->fromdomain : NULL, &remote_uri, &selector)) {
 		ast_log(LOG_ERROR, "Unable to create From header for %.*s request to endpoint %s\n",
@@ -2816,8 +2829,9 @@ static int create_out_of_dialog_request(const pjsip_method *method, struct ast_s
 	/* If an outbound proxy is specified on the endpoint apply it to this request */
 	if (endpoint && !ast_strlen_zero(endpoint->outbound_proxy) &&
 		ast_sip_set_outbound_proxy((*tdata), endpoint->outbound_proxy)) {
-		ast_log(LOG_ERROR, "Unable to apply outbound proxy on request %.*s to endpoint %s\n",
-			(int) pj_strlen(&method->name), pj_strbuf(&method->name), ast_sorcery_object_get_id(endpoint));
+		ast_log(LOG_ERROR, "Unable to apply outbound proxy on request %.*s to endpoint %s as outbound proxy URI '%s' is not valid\n",
+			(int) pj_strlen(&method->name), pj_strbuf(&method->name), ast_sorcery_object_get_id(endpoint),
+			endpoint->outbound_proxy);
 		pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
 		return -1;
 	}
@@ -2962,8 +2976,6 @@ struct send_request_wrapper {
 	pj_timer_entry *timeout_timer;
 	/*! Original timeout. */
 	pj_int32_t timeout;
-	/*! Timeout/cleanup lock. */
-	pj_mutex_t *lock;
 	/*! The transmit data. */
 	pjsip_tx_data *tdata;
 };
@@ -2989,7 +3001,7 @@ static void endpt_send_request_cb(void *token, pjsip_event *e)
 		ast_debug(2, "%p: PJSIP tsx response received\n", req_wrapper);
 	}
 
-	pj_mutex_lock(req_wrapper->lock);
+	ao2_lock(req_wrapper);
 
 	/* It's possible that our own timer was already processing while
 	 * we were waiting on the lock so check the timer id.  If it's
@@ -3030,7 +3042,7 @@ static void endpt_send_request_cb(void *token, pjsip_event *e)
 		req_wrapper->cb_called = 1;
 		ast_debug(2, "%p: Callbacks executed\n", req_wrapper);
 	}
-	pj_mutex_unlock(req_wrapper->lock);
+	ao2_unlock(req_wrapper);
 	ao2_ref(req_wrapper, -1);
 }
 
@@ -3047,12 +3059,12 @@ static void send_request_timer_callback(pj_timer_heap_t *theap, pj_timer_entry *
 	ast_debug(2, "%p: Internal tsx timer expired after %d msec\n",
 		req_wrapper, req_wrapper->timeout);
 
-	pj_mutex_lock(req_wrapper->lock);
+	ao2_lock(req_wrapper);
 	/* If the id is not TIMEOUT_TIMER2 then the timer was cancelled above
 	 * while the lock was being held so just clean up.
 	 */
 	if (entry->id != TIMEOUT_TIMER2) {
-		pj_mutex_unlock(req_wrapper->lock);
+		ao2_unlock(req_wrapper);
 		ast_debug(3, "%p: Timeout already handled\n", req_wrapper);
 		ao2_ref(req_wrapper, -1);
 		return;
@@ -3070,7 +3082,7 @@ static void send_request_timer_callback(pj_timer_heap_t *theap, pj_timer_entry *
 		ast_debug(2, "%p: Callbacks executed\n", req_wrapper);
 	}
 
-	pj_mutex_unlock(req_wrapper->lock);
+	ao2_unlock(req_wrapper);
 	ao2_ref(req_wrapper, -1);
 }
 
@@ -3078,7 +3090,6 @@ static void send_request_wrapper_destructor(void *obj)
 {
 	struct send_request_wrapper *req_wrapper = obj;
 
-	pj_mutex_destroy(req_wrapper->lock);
 	pjsip_tx_data_dec_ref(req_wrapper->tdata);
 	ast_debug(2, "%p: wrapper destroyed\n", req_wrapper);
 }
@@ -3091,8 +3102,7 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
 	pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
 
 	/* Create wrapper to detect if the callback was actually called on an error. */
-	req_wrapper = ao2_alloc_options(sizeof(*req_wrapper), send_request_wrapper_destructor,
-		AO2_ALLOC_OPT_LOCK_NOLOCK);
+	req_wrapper = ao2_alloc(sizeof(*req_wrapper), send_request_wrapper_destructor);
 	if (!req_wrapper) {
 		pjsip_tx_data_dec_ref(tdata);
 		return PJ_ENOMEM;
@@ -3104,25 +3114,11 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
 	req_wrapper->callback = cb;
 	req_wrapper->timeout = timeout;
 	req_wrapper->timeout_timer = NULL;
-	req_wrapper->lock = NULL;
 	req_wrapper->tdata = tdata;
 	/* Add a reference to tdata.  The wrapper destructor cleans it up. */
 	pjsip_tx_data_add_ref(tdata);
 
-	ret_val = pj_mutex_create_simple(tdata->pool, "tsx_timeout", &req_wrapper->lock);
-	if (ret_val != PJ_SUCCESS) {
-		char errmsg[PJ_ERR_MSG_SIZE];
-		pj_strerror(ret_val, errmsg, sizeof(errmsg));
-		ast_log(LOG_ERROR, "Error %d '%s' sending %.*s request to endpoint %s\n",
-			(int) ret_val, errmsg, (int) pj_strlen(&tdata->msg->line.req.method.name),
-			pj_strbuf(&tdata->msg->line.req.method.name),
-			endpoint ? ast_sorcery_object_get_id(endpoint) : "<unknown>");
-		pjsip_tx_data_dec_ref(tdata);
-		ao2_ref(req_wrapper, -1);
-		return PJ_ENOMEM;
-	}
-
-	pj_mutex_lock(req_wrapper->lock);
+	ao2_lock(req_wrapper);
 
 	if (timeout > 0) {
 		pj_time_val timeout_timer_val = { timeout / 1000, timeout % 1000 };
@@ -3159,9 +3155,11 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
 		char errmsg[PJ_ERR_MSG_SIZE];
 
 		if (timeout > 0) {
-			pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(endpt),
+			int timers_cancelled = pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(endpt),
 				req_wrapper->timeout_timer, TIMER_INACTIVE);
-			ao2_ref(req_wrapper, -1);
+			if (timers_cancelled > 0) {
+				ao2_ref(req_wrapper, -1);
+			}
 		}
 
 		/* Complain of failure to send the request. */
@@ -3184,7 +3182,7 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
 			ao2_ref(req_wrapper, -1);
 		}
 	}
-	pj_mutex_unlock(req_wrapper->lock);
+	ao2_unlock(req_wrapper);
 	ao2_ref(req_wrapper, -1);
 	return ret_val;
 }
@@ -3759,6 +3757,11 @@ static void remove_request_headers(pjsip_endpoint *endpt)
 	}
 }
 
+long ast_sip_threadpool_queue_size(void)
+{
+	return ast_threadpool_queue_size(sip_threadpool);
+}
+
 AST_TEST_DEFINE(xml_sanitization_end_null)
 {
 	char sanitized[8];
diff --git a/res/res_pjsip/config_auth.c b/res/res_pjsip/config_auth.c
index 773889c..9160e67 100644
--- a/res/res_pjsip/config_auth.c
+++ b/res/res_pjsip/config_auth.c
@@ -195,13 +195,12 @@ static struct ast_sip_endpoint_formatter endpoint_auth_formatter = {
 	.format_ami = format_ami_endpoint_auth
 };
 
-static struct ao2_container *cli_get_container(void)
+static struct ao2_container *cli_get_container(const char *regex)
 {
 	RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
 	struct ao2_container *s_container;
 
-	container = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "auth",
-		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "auth", regex);
 	if (!container) {
 		return NULL;
 	}
@@ -272,12 +271,14 @@ static int cli_print_body(void *obj, void *arg, int flags)
 static struct ast_cli_entry cli_commands[] = {
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Auths",
 		.command = "pjsip list auths",
-		.usage = "Usage: pjsip list auths\n"
-				 "       List the configured PJSIP Auths\n"),
+		.usage = "Usage: pjsip list auths [ like <pattern> ]\n"
+				"       List the configured PJSIP Auths\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Auths",
 		.command = "pjsip show auths",
-		.usage = "Usage: pjsip show auths\n"
-				 "       Show the configured PJSIP Auths\n"),
+		.usage = "Usage: pjsip show auths [ like <pattern> ]\n"
+				"       Show the configured PJSIP Auths\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Auth",
 		.command = "pjsip show auth",
 		.usage = "Usage: pjsip show auth <id>\n"
diff --git a/res/res_pjsip/config_system.c b/res/res_pjsip/config_system.c
index 473eca8..dfd9240 100644
--- a/res/res_pjsip/config_system.c
+++ b/res/res_pjsip/config_system.c
@@ -180,7 +180,7 @@ int ast_sip_initialize_system(void)
 			OPT_UINT_T, 0, FLDSET(struct system_config, threadpool.auto_increment));
 	ast_sorcery_object_field_register(system_sorcery, "system", "threadpool_idle_timeout", "60",
 			OPT_UINT_T, 0, FLDSET(struct system_config, threadpool.idle_timeout));
-	ast_sorcery_object_field_register(system_sorcery, "system", "threadpool_max_size", "0",
+	ast_sorcery_object_field_register(system_sorcery, "system", "threadpool_max_size", "50",
 			OPT_UINT_T, 0, FLDSET(struct system_config, threadpool.max_size));
 	ast_sorcery_object_field_register(system_sorcery, "system", "disable_tcp_switch", "yes",
 			OPT_BOOL_T, 1, FLDSET(struct system_config, disable_tcp_switch));
diff --git a/res/res_pjsip/config_transport.c b/res/res_pjsip/config_transport.c
index 73030b1..840824b 100644
--- a/res/res_pjsip/config_transport.c
+++ b/res/res_pjsip/config_transport.c
@@ -27,6 +27,7 @@
 #include "asterisk/astobj2.h"
 #include "asterisk/sorcery.h"
 #include "asterisk/acl.h"
+#include "asterisk/utils.h"
 #include "include/res_pjsip_private.h"
 #include "asterisk/http_websocket.h"
 
@@ -216,8 +217,27 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
 
 		res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg, &transport->state->factory);
 	} else if (transport->type == AST_TRANSPORT_TLS) {
+		if (transport->async_operations > 1 && ast_compare_versions(pj_get_version(), "2.5.0") < 0) {
+			ast_log(LOG_ERROR, "Transport: %s: When protocol=tls and pjproject version < 2.5.0, async_operations can't be > 1\n",
+					ast_sorcery_object_get_id(obj));
+			return -1;
+		}
+		if (!ast_strlen_zero(transport->ca_list_file)) {
+			if (!ast_file_is_readable(transport->ca_list_file)) {
+				ast_log(LOG_ERROR, "Transport: %s: ca_list_file %s is either missing or not readable\n",
+						ast_sorcery_object_get_id(obj), transport->ca_list_file);
+				return -1;
+			}
+		}
 		transport->tls.ca_list_file = pj_str((char*)transport->ca_list_file);
 #ifdef HAVE_PJ_SSL_CERT_LOAD_FROM_FILES2
+		if (!ast_strlen_zero(transport->ca_list_path)) {
+			if (!ast_file_is_readable(transport->ca_list_path)) {
+				ast_log(LOG_ERROR, "Transport: %s: ca_list_path %s is either missing or not readable\n",
+						ast_sorcery_object_get_id(obj), transport->ca_list_path);
+				return -1;
+			}
+		}
 		transport->tls.ca_list_path = pj_str((char*)transport->ca_list_path);
 #else
 		if (!ast_strlen_zero(transport->ca_list_path)) {
@@ -225,7 +245,21 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
 					"support the 'ca_list_path' option. Please upgrade to version 2.4 or later.\n");
 		}
 #endif
+		if (!ast_strlen_zero(transport->cert_file)) {
+			if (!ast_file_is_readable(transport->cert_file)) {
+				ast_log(LOG_ERROR, "Transport: %s: cert_file %s is either missing or not readable\n",
+						ast_sorcery_object_get_id(obj), transport->cert_file);
+				return -1;
+			}
+		}
 		transport->tls.cert_file = pj_str((char*)transport->cert_file);
+		if (!ast_strlen_zero(transport->privkey_file)) {
+			if (!ast_file_is_readable(transport->privkey_file)) {
+				ast_log(LOG_ERROR, "Transport: %s: privkey_file %s is either missing or not readable\n",
+						ast_sorcery_object_get_id(obj), transport->privkey_file);
+				return -1;
+			}
+		}
 		transport->tls.privkey_file = pj_str((char*)transport->privkey_file);
 		transport->tls.password = pj_str((char*)transport->password);
 		set_qos(transport, &transport->tls.qos_params);
@@ -633,13 +667,13 @@ static int tos_to_str(const void *obj, const intptr_t *args, char **buf)
 	return 0;
 }
 
-static struct ao2_container *cli_get_container(void)
+static struct ao2_container *cli_get_container(const char *regex)
 {
 	RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
 	struct ao2_container *s_container;
 
-	container = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "transport",
-		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "transport",
+		regex);
 	if (!container) {
 		return NULL;
 	}
@@ -720,12 +754,14 @@ static struct ast_cli_entry cli_commands[] = {
 	AST_CLI_DEFINE(handle_pjsip_list_ciphers, "List available OpenSSL cipher names"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Transports",
 		.command = "pjsip list transports",
-		.usage = "Usage: pjsip list transports\n"
-				 "       List the configured PJSIP Transports\n"),
+		.usage = "Usage: pjsip list transports [ like <pattern> ]\n"
+				"       List the configured PJSIP Transports\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Transports",
 		.command = "pjsip show transports",
-		.usage = "Usage: pjsip show transports\n"
-				 "       Show the configured PJSIP Transport\n"),
+		.usage = "Usage: pjsip show transports [ like <pattern> ]\n"
+				"       Show the configured PJSIP Transport\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Transport",
 		.command = "pjsip show transport",
 		.usage = "Usage: pjsip show transport <id>\n"
diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c
index 9625f04..2908f6f 100644
--- a/res/res_pjsip/location.c
+++ b/res/res_pjsip/location.c
@@ -26,6 +26,7 @@
 #include "asterisk/sorcery.h"
 #include "include/res_pjsip_private.h"
 #include "asterisk/res_pjsip_cli.h"
+#include "asterisk/statsd.h"
 
 /*! \brief Destructor for AOR */
 static void aor_destroy(void *obj)
@@ -59,17 +60,20 @@ static int destroy_contact(void *obj, void *arg, int flags)
 
 static void aor_deleted_observer(const void *object)
 {
+	const struct ast_sip_aor *aor = object;
 	const char *aor_id = ast_sorcery_object_get_id(object);
 	/* Give enough space for ^ at the beginning and ;@ at the end, since that is our object naming scheme */
 	char regex[strlen(aor_id) + 4];
 	struct ao2_container *contacts;
 
-	snprintf(regex, sizeof(regex), "^%s;@", aor_id);
+	if (aor->permanent_contacts) {
+		ao2_callback(aor->permanent_contacts, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, destroy_contact, NULL);
+	}
 
+	snprintf(regex, sizeof(regex), "^%s;@", aor_id);
 	if (!(contacts = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "contact", regex))) {
 		return;
 	}
-
 	/* Destroy any contacts that may still exist that were made for this AoR */
 	ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, destroy_contact, NULL);
 
@@ -88,6 +92,7 @@ static void contact_destroy(void *obj)
 	struct ast_sip_contact *contact = obj;
 
 	ast_string_field_free_memory(contact);
+	ast_free(contact->aor);
 	ao2_cleanup(contact->endpoint);
 }
 
@@ -95,6 +100,9 @@ static void contact_destroy(void *obj)
 static void *contact_alloc(const char *name)
 {
 	struct ast_sip_contact *contact = ast_sorcery_generic_alloc(sizeof(*contact), contact_destroy);
+	char *id = ast_strdupa(name);
+	char *aor = id;
+	char *aor_separator = NULL;
 
 	if (!contact) {
 		return NULL;
@@ -105,6 +113,18 @@ static void *contact_alloc(const char *name)
 		return NULL;
 	}
 
+	/* Dynamic contacts are delimited with ";@" and static ones with "@@" */
+	if ((aor_separator = strstr(id, ";@")) || (aor_separator = strstr(id, "@@"))) {
+		*aor_separator = '\0';
+	}
+	ast_assert(aor_separator != NULL);
+
+	contact->aor = ast_strdup(aor);
+	if (!contact->aor) {
+		ao2_cleanup(contact);
+		return NULL;
+	}
+
 	return contact;
 }
 
@@ -319,32 +339,6 @@ static int expiration_struct2str(const void *obj, const intptr_t *args, char **b
 	return (ast_asprintf(buf, "%ld", contact->expiration_time.tv_sec) < 0) ? -1 : 0;
 }
 
-/*! \brief Helper function which validates a permanent contact */
-static int permanent_contact_validate(void *data)
-{
-	const char *value = data;
-	pj_pool_t *pool;
-	pj_str_t contact_uri;
-	static const pj_str_t HCONTACT = { "Contact", 7 };
-	pjsip_contact_hdr *contact_hdr;
-	int rc = 0;
-
-	pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Permanent Contact Validation", 256, 256);
-	if (!pool) {
-		return -1;
-	}
-
-	pj_strdup2_with_null(pool, &contact_uri, value);
-	if (!(contact_hdr = pjsip_parse_hdr(pool, &HCONTACT, contact_uri.ptr, contact_uri.slen, NULL))
-		|| !(PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri)
-			|| PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri))) {
-		rc = -1;
-	}
-
-	pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
-	return rc;
-}
-
 static int permanent_uri_sort_fn(const void *obj_left, const void *obj_right, int flags)
 {
 	const struct ast_sip_contact *object_left = obj_left;
@@ -391,13 +385,8 @@ static int permanent_uri_handler(const struct aco_option *opt, struct ast_variab
 	while ((contact_uri = strsep(&contacts, ","))) {
 		struct ast_sip_contact *contact;
 		struct ast_sip_contact_status *status;
-		char contact_id[strlen(aor_id) + strlen(contact_uri) + 2 + 1];
-
-		if (ast_sip_push_task_synchronous(NULL, permanent_contact_validate, contact_uri)) {
-			ast_log(LOG_ERROR, "Permanent URI on aor '%s' with contact '%s' failed to parse\n",
-				ast_sorcery_object_get_id(aor), contact_uri);
-			return -1;
-		}
+		char hash[33];
+		char contact_id[strlen(aor_id) + sizeof(hash) + 2];
 
 		if (!aor->permanent_contacts) {
 			aor->permanent_contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
@@ -407,12 +396,15 @@ static int permanent_uri_handler(const struct aco_option *opt, struct ast_variab
 			}
 		}
 
-		snprintf(contact_id, sizeof(contact_id), "%s@@%s", aor_id, contact_uri);
+		ast_md5_hash(hash, contact_uri);
+		snprintf(contact_id, sizeof(contact_id), "%s@@%s", aor_id, hash);
 		contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", contact_id);
 		if (!contact) {
 			return -1;
 		}
 
+		ast_string_field_set(contact, uri, contact_uri);
+
 		status = ast_res_pjsip_find_or_create_contact_status(contact);
 		if (!status) {
 			ao2_ref(contact, -1);
@@ -420,7 +412,6 @@ static int permanent_uri_handler(const struct aco_option *opt, struct ast_variab
 		}
 		ao2_ref(status, -1);
 
-		ast_string_field_set(contact, uri, contact_uri);
 		ao2_link(aor->permanent_contacts, contact);
 		ao2_ref(contact, -1);
 	}
@@ -626,13 +617,12 @@ struct ast_sip_endpoint_formatter endpoint_aor_formatter = {
 	.format_ami = format_ami_endpoint_aor
 };
 
-static struct ao2_container *cli_aor_get_container(void)
+static struct ao2_container *cli_aor_get_container(const char *regex)
 {
 	RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
 	struct ao2_container *s_container;
 
-	container = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "aor",
-		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "aor", regex);
 	if (!container) {
 		return NULL;
 	}
@@ -730,12 +720,25 @@ static int cli_contact_iterate(void *container, ao2_callback_fn callback, void *
 	return ast_sip_for_each_contact(container, callback, args);
 }
 
-static struct ao2_container *cli_contact_get_container(void)
+static int cli_filter_contacts(void *obj, void *arg, int flags)
+{
+	struct ast_sip_contact_wrapper *wrapper = obj;
+	regex_t *regexbuf = arg;
+
+	if (!regexec(regexbuf, wrapper->contact_id, 0, NULL, 0)) {
+		return 0;
+	}
+
+	return CMP_MATCH;
+}
+
+static struct ao2_container *cli_contact_get_container(const char *regex)
 {
 	RAII_VAR(struct ao2_container *, parent_container, NULL, ao2_cleanup);
 	struct ao2_container *child_container;
+	regex_t regexbuf;
 
-	parent_container =  cli_aor_get_container();
+	parent_container =  cli_aor_get_container("");
 	if (!parent_container) {
 		return NULL;
 	}
@@ -748,24 +751,35 @@ static struct ao2_container *cli_contact_get_container(void)
 
 	ao2_callback(parent_container, OBJ_NODATA, cli_aor_gather_contacts, child_container);
 
+	if (!ast_strlen_zero(regex)) {
+		if (regcomp(&regexbuf, regex, REG_EXTENDED | REG_NOSUB)) {
+			ao2_ref(child_container, -1);
+			return NULL;
+		}
+		ao2_callback(child_container, OBJ_UNLINK | OBJ_MULTIPLE | OBJ_NODATA, cli_filter_contacts, &regexbuf);
+		regfree(&regexbuf);
+	}
+
 	return child_container;
 }
 
 static void *cli_contact_retrieve_by_id(const char *id)
 {
-	return ao2_find(cli_contact_get_container(), id, OBJ_KEY | OBJ_NOLOCK);
+	RAII_VAR(struct ao2_container *, container, cli_contact_get_container(""), ao2_cleanup);
+
+	return ao2_find(container, id, OBJ_KEY | OBJ_NOLOCK);
 }
 
 static int cli_contact_print_header(void *obj, void *arg, int flags)
 {
 	struct ast_sip_cli_context *context = arg;
 	int indent = CLI_INDENT_TO_SPACES(context->indent_level);
-	int filler = CLI_LAST_TABSTOP - indent - 18;
+	int filler = CLI_LAST_TABSTOP - indent - 23;
 
 	ast_assert(context->output_buffer != NULL);
 
 	ast_str_append(&context->output_buffer, 0,
-		"%*s:  <Aor/ContactUri%*.*s>  <Status....>  <RTT(ms)..>\n",
+		"%*s:  <Aor/ContactUri%*.*s> <Hash....> <Status> <RTT(ms)..>\n",
 		indent, "Contact", filler, filler, CLI_HEADER_FILLER);
 
 	return 0;
@@ -778,22 +792,26 @@ static int cli_contact_print_body(void *obj, void *arg, int flags)
 	struct ast_sip_cli_context *context = arg;
 	int indent;
 	int flexwidth;
+	const char *contact_id = ast_sorcery_object_get_id(contact);
+	const char *hash_start = contact_id + strlen(contact->aor) + 2;
 
 	RAII_VAR(struct ast_sip_contact_status *, status,
-		ast_sorcery_retrieve_by_id( ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact)),
+		ast_sorcery_retrieve_by_id( ast_sip_get_sorcery(), CONTACT_STATUS, contact_id),
 		ao2_cleanup);
 
 	ast_assert(contact->uri != NULL);
 	ast_assert(context->output_buffer != NULL);
 
 	indent = CLI_INDENT_TO_SPACES(context->indent_level);
-	flexwidth = CLI_LAST_TABSTOP - indent - 2;
+	flexwidth = CLI_LAST_TABSTOP - indent - 9 - strlen(contact->aor) + 1;
 
-	ast_str_append(&context->output_buffer, 0, "%*s:  %-*.*s  %-12.12s  %11.3f\n",
+	ast_str_append(&context->output_buffer, 0, "%*s:  %s/%-*.*s %-10.10s %-7.7s %11.3f\n",
 		indent,
 		"Contact",
+		contact->aor,
 		flexwidth, flexwidth,
-		wrapper->contact_id,
+		contact->uri,
+		hash_start,
 		ast_sip_get_contact_short_status_label(status ? status->status : UNKNOWN),
 		(status && (status->status != UNKNOWN) ? ((long long) status->rtt) / 1000.0 : NAN));
 
@@ -890,12 +908,14 @@ static int cli_aor_print_body(void *obj, void *arg, int flags)
 static struct ast_cli_entry cli_commands[] = {
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Aors",
 		.command = "pjsip list aors",
-		.usage = "Usage: pjsip list aors\n"
-				 "       List the configured PJSIP Aors\n"),
+		.usage = "Usage: pjsip list aors [ like <pattern> ]\n"
+				"       List the configured PJSIP Aors\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Aors",
 		.command = "pjsip show aors",
-		.usage = "Usage: pjsip show aors\n"
-				 "       Show the configured PJSIP Aors\n"),
+		.usage = "Usage: pjsip show aors [ like <pattern> ]\n"
+				"       Show the configured PJSIP Aors\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Aor",
 		.command = "pjsip show aor",
 		.usage = "Usage: pjsip show aor <id>\n"
@@ -903,12 +923,14 @@ static struct ast_cli_entry cli_commands[] = {
 
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Contacts",
 		.command = "pjsip list contacts",
-		.usage = "Usage: pjsip list contacts\n"
-				 "       List the configured PJSIP contacts\n"),
+		.usage = "Usage: pjsip list contacts [ like <pattern> ]\n"
+				"       List the configured PJSIP contacts\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Contacts",
 		.command = "pjsip show contacts",
-		.usage = "Usage: pjsip show contacts\n"
-				 "       Show the configured PJSIP contacts\n"),
+		.usage = "Usage: pjsip show contacts [ like <pattern> ]\n"
+				"       Show the configured PJSIP contacts\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Contact",
 		.command = "pjsip show contact",
 		.usage = "Usage: pjsip show contact\n"
@@ -934,6 +956,8 @@ static int contact_apply_handler(const struct ast_sorcery *sorcery, void *object
 int ast_sip_initialize_sorcery_location(void)
 {
 	struct ast_sorcery *sorcery = ast_sip_get_sorcery();
+	int i;
+
 	ast_sorcery_apply_default(sorcery, "contact", "astdb", "registrar");
 	ast_sorcery_apply_default(sorcery, "aor", "config", "pjsip.conf,criteria=type=aor");
 
@@ -1000,6 +1024,15 @@ int ast_sip_initialize_sorcery_location(void)
 	ast_sip_register_cli_formatter(aor_formatter);
 	ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
 
+	/*
+	 * Reset StatsD gauges in case we didn't shut down cleanly.
+	 * Note that this must done here, as contacts will create the contact_status
+	 * object before PJSIP options handling is initialized.
+	 */
+	for (i = 0; i <= REMOVED; i++) {
+		ast_statsd_log_full_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE, 0, 1.0, ast_sip_get_contact_status_label(i));
+	}
+
 	return 0;
 }
 
diff --git a/res/res_pjsip/pjsip_cli.c b/res/res_pjsip/pjsip_cli.c
index 16df3f5..bbd0ac4 100644
--- a/res/res_pjsip/pjsip_cli.c
+++ b/res/res_pjsip/pjsip_cli.c
@@ -125,6 +125,7 @@ char *ast_sip_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_
 	const char *cmd2;
 	const char *object_id;
 	char formatter_type[64];
+	const char *regex;
 
 	struct ast_sip_cli_context context = {
 		.indent_level = 0,
@@ -162,6 +163,18 @@ char *ast_sip_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_
 		is_container = 1;
 	}
 
+	if (cmd != CLI_GENERATE
+		&& is_container
+		&& a->argc >= 4
+		&& strcmp(object_id, "like") == 0) {
+		if (ast_strlen_zero(a->argv[4])) {
+			return CLI_SHOWUSAGE;
+		}
+		regex = a->argv[4];
+	} else {
+		regex = "";
+	}
+
 	if (cmd == CLI_GENERATE
 		&& (is_container
 			|| a->argc > 4
@@ -187,7 +200,7 @@ char *ast_sip_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_
 		" =========================================================================================\n\n");
 
 	if (is_container || cmd == CLI_GENERATE) {
-		container = formatter_entry->get_container();
+		container = formatter_entry->get_container(regex);
 		if (!container) {
 			ast_cli(a->fd, "No container returned for object type %s.\n",
 				formatter_type);
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 2fdfc9d..746a457 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -20,6 +20,7 @@
 #include "asterisk/sorcery.h"
 #include "asterisk/callerid.h"
 #include "asterisk/test.h"
+#include "asterisk/statsd.h"
 
 /*! \brief Number of buckets for persistent endpoint information */
 #define PERSISTENT_BUCKETS 53
@@ -56,47 +57,37 @@ static int persistent_endpoint_cmp(void *obj, void *arg, int flags)
 	return !strcmp(ast_endpoint_get_resource(persistent1->endpoint), id) ? CMP_MATCH | CMP_STOP : 0;
 }
 
-/*! \brief Structure for communicating contact status to
- * persistent_endpoint_update_state from the contact/contact_status
- * observers.
- */
-struct sip_contact_status {
-	char *uri;
-	enum ast_sip_contact_status_type status;
-	int64_t rtt;
-};
-
 /*! \brief Callback function for changing the state of an endpoint */
-static int persistent_endpoint_update_state(void *obj, void *arg, void *data, int flags)
+static int persistent_endpoint_update_state(void *obj, void *arg, int flags)
 {
 	struct sip_persistent_endpoint *persistent = obj;
 	struct ast_endpoint *endpoint = persistent->endpoint;
-	char *aor = arg;
-	struct sip_contact_status *status = data;
+	struct ast_sip_contact_status *status = arg;
 	struct ao2_container *contacts;
 	struct ast_json *blob;
 	struct ao2_iterator i;
 	struct ast_sip_contact *contact;
 	enum ast_endpoint_state state = AST_ENDPOINT_OFFLINE;
 
-	if (!ast_strlen_zero(aor)) {
-		if (!strstr(persistent->aors, aor)) {
+	if (status) {
+		char rtt[32];
+
+		/* If the status' aor isn't one of the endpoint's, we skip */
+		if (!strstr(persistent->aors, status->aor)) {
 			return 0;
 		}
 
-		if (status) {
-			char rtt[32];
-			snprintf(rtt, 31, "%" PRId64, status->rtt);
-			blob = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",
-				"contact_status", ast_sip_get_contact_status_label(status->status),
-				"aor", aor,
-				"uri", status->uri,
-				"roundtrip_usec", rtt,
-				"endpoint_name", ast_endpoint_get_resource(endpoint));
-			ast_endpoint_blob_publish(endpoint, ast_endpoint_contact_state_type(), blob);
-			ast_json_unref(blob);
-		}
+		snprintf(rtt, sizeof(rtt), "%" PRId64, status->rtt);
+		blob = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",
+			"contact_status", ast_sip_get_contact_status_label(status->status),
+			"aor", status->aor,
+			"uri", status->uri,
+			"roundtrip_usec", rtt,
+			"endpoint_name", ast_endpoint_get_resource(endpoint));
+		ast_endpoint_blob_publish(endpoint, ast_endpoint_contact_state_type(), blob);
+		ast_json_unref(blob);
 	}
+
 	/* Find all the contacts for this endpoint.  If ANY are available,
 	 * mark the endpoint as ONLINE.
 	 */
@@ -142,57 +133,54 @@ static int persistent_endpoint_update_state(void *obj, void *arg, void *data, in
 	return 0;
 }
 
-/*! \brief Function called when stuff relating to a contact happens (created/deleted) */
+/*! \brief Function called when a contact is created */
 static void persistent_endpoint_contact_created_observer(const void *object)
 {
 	const struct ast_sip_contact *contact = object;
-	char *id = ast_strdupa(ast_sorcery_object_get_id(contact));
-	char *aor = NULL;
-	char *contact_uri = NULL;
-	struct sip_contact_status status;
-
-	aor = id;
-	/* Dynamic contacts are delimited with ";@" and static ones with "@@" */
-	if ((contact_uri = strstr(id, ";@")) || (contact_uri = strstr(id, "@@"))) {
-		*contact_uri = '\0';
-		contact_uri += 2;
-	} else {
-		contact_uri = id;
-	}
+	struct ast_sip_contact_status *contact_status;
 
-	status.uri = contact_uri;
-	status.status = CREATED;
-	status.rtt = 0;
+	contact_status = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,
+		ast_sorcery_object_get_id(contact));
+	if (!contact_status) {
+		ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s/%s\n",
+			contact->aor, contact->uri);
+		return;
+	}
+	contact_status->uri = ast_strdup(contact->uri);
+	if (!contact_status->uri) {
+		ao2_cleanup(contact_status);
+		return;
+	}
+	contact_status->status = CREATED;
 
-	ast_verb(1, "Contact %s/%s has been created\n", aor, contact_uri);
+	ast_verb(1, "Contact %s/%s has been created\n",contact->aor, contact->uri);
 
-	ao2_callback_data(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor, &status);
+	ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, contact_status);
+	ao2_cleanup(contact_status);
 }
 
-/*! \brief Function called when stuff relating to a contact happens (created/deleted) */
+/*! \brief Function called when a contact is deleted */
 static void persistent_endpoint_contact_deleted_observer(const void *object)
 {
-	char *id = ast_strdupa(ast_sorcery_object_get_id(object));
-	char *aor = NULL;
-	char *contact_uri = NULL;
-	struct sip_contact_status status;
+	const struct ast_sip_contact *contact = object;
+	struct ast_sip_contact_status *contact_status;
 
-	aor = id;
-	/* Dynamic contacts are delimited with ";@" and static ones with "@@" */
-	if ((contact_uri = strstr(id, ";@")) || (contact_uri = strstr(id, "@@"))) {
-		*contact_uri = '\0';
-		contact_uri += 2;
-	} else {
-		contact_uri = id;
+	contact_status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact));
+	if (!contact_status) {
+		ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s/%s\n",
+			contact->aor, contact->uri);
+		return;
 	}
 
-	ast_verb(1, "Contact %s/%s has been deleted\n", aor, contact_uri);
-
-	status.uri = contact_uri;
-	status.status = REMOVED;
-	status.rtt = 0;
+	ast_verb(1, "Contact %s/%s has been deleted\n", contact->aor, contact->uri);
+	ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,
+		"-1", 1.0, ast_sip_get_contact_status_label(contact_status->status));
+	ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,
+		"+1", 1.0, ast_sip_get_contact_status_label(REMOVED));
 
-	ao2_callback_data(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor, &status);
+	ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, contact_status);
+	ast_sorcery_delete(ast_sip_get_sorcery(), contact_status);
+	ao2_cleanup(contact_status);
 }
 
 /*! \brief Observer for contacts so state can be updated on respective endpoints */
@@ -201,51 +189,41 @@ static const struct ast_sorcery_observer state_contact_observer = {
 	.deleted = persistent_endpoint_contact_deleted_observer,
 };
 
-/*! \brief Function called when stuff relating to a contact status happens (updated) */
+/*! \brief Function called when a contact_status is updated */
 static void persistent_endpoint_contact_status_observer(const void *object)
 {
-	const struct ast_sip_contact_status *contact_status = object;
-	char *id = ast_strdupa(ast_sorcery_object_get_id(object));
-	char *aor = NULL;
-	char *contact_uri = NULL;
-	struct sip_contact_status status;
+	struct ast_sip_contact_status *contact_status = (struct ast_sip_contact_status *)object;
 
 	/* If rtt_start is set (this is the outgoing OPTIONS), ignore. */
 	if (contact_status->rtt_start.tv_sec > 0) {
 		return;
 	}
 
-	aor = id;
-	/* Dynamic contacts are delimited with ";@" and static ones with "@@" */
-	if ((contact_uri = strstr(id, ";@")) || (contact_uri = strstr(id, "@@"))) {
-		*contact_uri = '\0';
-		contact_uri += 2;
-	} else {
-		contact_uri = id;
-	}
-
-	if (contact_status->status == contact_status->last_status) {
-		ast_debug(3, "Contact %s status didn't change: %s, RTT: %.3f msec\n",
-			contact_uri, ast_sip_get_contact_status_label(contact_status->status),
-			contact_status->rtt / 1000.0);
-		return;
-	} else {
-		ast_verb(1, "Contact %s/%s is now %s.  RTT: %.3f msec\n", aor, contact_uri,
+	if (contact_status->status != contact_status->last_status) {
+		ast_verb(1, "Contact %s/%s is now %s.  RTT: %.3f msec\n", contact_status->aor, contact_status->uri,
 			ast_sip_get_contact_status_label(contact_status->status),
 			contact_status->rtt / 1000.0);
-	}
 
-	ast_test_suite_event_notify("AOR_CONTACT_UPDATE",
-		"Contact: %s\r\n"
-			"Status: %s",
-		ast_sorcery_object_get_id(contact_status),
-		ast_sip_get_contact_status_label(contact_status->status));
+		ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,
+			"-1", 1.0, ast_sip_get_contact_status_label(contact_status->last_status));
+		ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,
+			"+1", 1.0, ast_sip_get_contact_status_label(contact_status->status));
 
-	status.uri = contact_uri;
-	status.status = contact_status->status;
-	status.rtt = contact_status->rtt;
+		ast_test_suite_event_notify("AOR_CONTACT_UPDATE",
+			"Contact: %s\r\n"
+				"Status: %s",
+			ast_sorcery_object_get_id(contact_status),
+			ast_sip_get_contact_status_label(contact_status->status));
 
-	ao2_callback_data(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor, &status);
+		ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, contact_status);
+	} else {
+		ast_debug(3, "Contact %s/%s status didn't change: %s, RTT: %.3f msec\n",
+			contact_status->aor, contact_status->uri, ast_sip_get_contact_status_label(contact_status->status),
+			contact_status->rtt / 1000.0);
+	}
+
+	ast_statsd_log_full_va("PJSIP.contacts.%s.rtt", AST_STATSD_TIMER,
+		contact_status->status != AVAILABLE ? -1 : contact_status->rtt / 1000, 1.0, ast_sorcery_object_get_id(contact_status));
 }
 
 /*! \brief Observer for contacts so state can be updated on respective endpoints */
@@ -253,6 +231,17 @@ static const struct ast_sorcery_observer state_contact_status_observer = {
 	.updated = persistent_endpoint_contact_status_observer,
 };
 
+static void endpoint_deleted_observer(const void *object)
+{
+	const struct ast_sip_endpoint *endpoint = object;
+
+	ao2_find(persistent_endpoints, ast_endpoint_get_resource(endpoint->persistent), OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA);
+}
+
+static const struct ast_sorcery_observer endpoint_observers = {
+	.deleted = endpoint_deleted_observer,
+};
+
 static int dtmf_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
 {
 	struct ast_sip_endpoint *endpoint = obj;
@@ -694,7 +683,7 @@ static int media_encryption_handler(const struct aco_option *opt, struct ast_var
 		endpoint->media.rtp.encryption = AST_SIP_MEDIA_ENCRYPT_SDES;
 	} else if (!strcasecmp("dtls", var->value)) {
 		endpoint->media.rtp.encryption = AST_SIP_MEDIA_ENCRYPT_DTLS;
-		ast_rtp_dtls_cfg_parse(&endpoint->media.rtp.dtls_cfg, "dtlsenable", "yes");
+		return ast_rtp_dtls_cfg_parse(&endpoint->media.rtp.dtls_cfg, "dtlsenable", "yes");
 	} else {
 		return -1;
 	}
@@ -1074,7 +1063,7 @@ static struct ast_endpoint *persistent_endpoint_find_or_create(const struct ast_
 		if (ast_strlen_zero(persistent->aors)) {
 			ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_UNKNOWN);
 		} else {
-			persistent_endpoint_update_state(persistent, NULL, NULL, 0);
+			persistent_endpoint_update_state(persistent, NULL, 0);
 		}
 
 		ao2_link_flags(persistent_endpoints, persistent, OBJ_NOLOCK);
@@ -1084,29 +1073,6 @@ static struct ast_endpoint *persistent_endpoint_find_or_create(const struct ast_
 	return persistent->endpoint;
 }
 
-/*! \brief Helper function which validates an outbound proxy */
-static int outbound_proxy_validate(void *data)
-{
-	const char *proxy = data;
-	pj_pool_t *pool;
-	pj_str_t tmp;
-	static const pj_str_t ROUTE_HNAME = { "Route", 5 };
-
-	pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Outbound Proxy Validation", 256, 256);
-	if (!pool) {
-		return -1;
-	}
-
-	pj_strdup2_with_null(pool, &tmp, proxy);
-	if (!pjsip_parse_hdr(pool, &ROUTE_HNAME, tmp.ptr, tmp.slen, NULL)) {
-		pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
-		return -1;
-	}
-
-	pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
-	return 0;
-}
-
 /*! \brief Callback function for when an object is finalized */
 static int sip_endpoint_apply_handler(const struct ast_sorcery *sorcery, void *obj)
 {
@@ -1116,12 +1082,7 @@ static int sip_endpoint_apply_handler(const struct ast_sorcery *sorcery, void *o
 		return -1;
 	}
 
-	if (!ast_strlen_zero(endpoint->outbound_proxy) &&
-		ast_sip_push_task_synchronous(NULL, outbound_proxy_validate, (char*)endpoint->outbound_proxy)) {
-		ast_log(LOG_ERROR, "Invalid outbound proxy '%s' specified on endpoint '%s'\n",
-			endpoint->outbound_proxy, ast_sorcery_object_get_id(endpoint));
-		return -1;
-	} else if (endpoint->extensions.timer.min_se < 90) {
+	if (endpoint->extensions.timer.min_se < 90) {
 		ast_log(LOG_ERROR, "Session timer minimum expires time must be 90 or greater on endpoint '%s'\n",
 			ast_sorcery_object_get_id(endpoint));
 		return -1;
@@ -1423,13 +1384,12 @@ static int ami_show_endpoints(struct mansession *s, const struct message *m)
 	return 0;
 }
 
-static struct ao2_container *cli_endpoint_get_container(void)
+static struct ao2_container *cli_endpoint_get_container(const char *regex)
 {
 	RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
 	struct ao2_container *s_container;
 
-	container = ast_sorcery_retrieve_by_fields(sip_sorcery, "endpoint",
-		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	container = ast_sorcery_retrieve_by_regex(sip_sorcery, "endpoint", regex);
 	if (!container) {
 		return NULL;
 	}
@@ -1543,12 +1503,26 @@ static int cli_endpoint_gather_channels(void *obj, void *arg, int flags)
 	return 0;
 }
 
-static struct ao2_container *cli_channel_get_container(void)
+static int cli_filter_channels(void *obj, void *arg, int flags)
+{
+	struct ast_channel_snapshot *channel = obj;
+	regex_t *regexbuf = arg;
+
+	if (!regexec(regexbuf, channel->name, 0, NULL, 0)
+		|| !regexec(regexbuf, channel->appl, 0, NULL, 0)) {
+		return 0;
+	}
+
+	return CMP_MATCH;
+}
+
+static struct ao2_container *cli_channel_get_container(const char *regex)
 {
 	RAII_VAR(struct ao2_container *, parent_container, NULL, ao2_cleanup);
 	struct ao2_container *child_container;
+	regex_t regexbuf;
 
-	parent_container = cli_endpoint_get_container();
+	parent_container = cli_endpoint_get_container("");
 	if (!parent_container) {
 		return NULL;
 	}
@@ -1560,6 +1534,15 @@ static struct ao2_container *cli_channel_get_container(void)
 
 	ao2_callback(parent_container, OBJ_NODATA, cli_endpoint_gather_channels, child_container);
 
+	if (!ast_strlen_zero(regex)) {
+		if (regcomp(&regexbuf, regex, REG_EXTENDED | REG_NOSUB)) {
+			ao2_ref(child_container, -1);
+			return NULL;
+		}
+		ao2_callback(child_container, OBJ_UNLINK | OBJ_MULTIPLE | OBJ_NODATA, cli_filter_channels, &regexbuf);
+		regfree(&regexbuf);
+	}
+
 	return child_container;
 }
 
@@ -1572,7 +1555,7 @@ static const char *cli_channel_get_id(const void *obj)
 
 static void *cli_channel_retrieve_by_id(const char *id)
 {
-	RAII_VAR(struct ao2_container *, container, cli_channel_get_container(), ao2_cleanup);
+	RAII_VAR(struct ao2_container *, container, cli_channel_get_container(""), ao2_cleanup);
 
 	return ao2_find(container, id, OBJ_KEY | OBJ_NOLOCK);
 }
@@ -1774,12 +1757,14 @@ static int cli_endpoint_print_body(void *obj, void *arg, int flags)
 static struct ast_cli_entry cli_commands[] = {
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Channels",
 		.command = "pjsip list channels",
-		.usage = "Usage: pjsip list channels\n"
-				 "       List the active PJSIP channels\n"),
+		.usage = "Usage: pjsip list channels [ like <pattern> ]\n"
+				"       List the active PJSIP channels\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Channels",
 		.command = "pjsip show channels",
-		.usage = "Usage: pjsip show channels\n"
-				 "       List(detailed) the active PJSIP channels\n"),
+		.usage = "Usage: pjsip show channels [ like <pattern> ]\n"
+				"       List(detailed) the active PJSIP channels\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Channel",
 		.command = "pjsip show channel",
 		.usage = "Usage: pjsip show channel\n"
@@ -1787,12 +1772,14 @@ static struct ast_cli_entry cli_commands[] = {
 
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Endpoints",
 		.command = "pjsip list endpoints",
-		.usage = "Usage: pjsip list endpoints\n"
-				 "       List the configured PJSIP endpoints\n"),
+		.usage = "Usage: pjsip list endpoints [ like <pattern> ]\n"
+				"       List the configured PJSIP endpoints\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Endpoints",
 		.command = "pjsip show endpoints",
-		.usage = "Usage: pjsip show endpoints\n"
-				 "       List(detailed) the configured PJSIP endpoints\n"),
+		.usage = "Usage: pjsip show endpoints [ like <pattern> ]\n"
+				"       List(detailed) the configured PJSIP endpoints\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Endpoint",
 		.command = "pjsip show endpoint",
 		.usage = "Usage: pjsip show endpoint <id>\n"
@@ -1956,6 +1943,7 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
 		return -1;
 	}
 
+	ast_sorcery_observer_add(sip_sorcery, "endpoint", &endpoint_observers);
 	ast_sorcery_observer_add(sip_sorcery, "contact", &state_contact_observer);
 	ast_sorcery_observer_add(sip_sorcery, CONTACT_STATUS, &state_contact_status_observer);
 
diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c
index 9b05260..0e0e90f 100644
--- a/res/res_pjsip/pjsip_distributor.c
+++ b/res/res_pjsip/pjsip_distributor.c
@@ -246,6 +246,8 @@ static pjsip_module endpoint_mod = {
 	.on_rx_request = endpoint_lookup,
 };
 
+#define SIP_MAX_QUEUE (AST_TASKPROCESSOR_HIGH_WATER_LEVEL * 3)
+
 static pj_bool_t distributor(pjsip_rx_data *rdata)
 {
 	pjsip_dialog *dlg = find_dialog(rdata);
@@ -280,7 +282,19 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
 		clone->endpt_info.mod_data[endpoint_mod.id] = ao2_bump(dist->endpoint);
 	}
 
-	ast_sip_push_task(serializer, distribute, clone);
+	if (ast_sip_threadpool_queue_size() > SIP_MAX_QUEUE) {
+		/* When the threadpool is backed up this much, there is a good chance that we have encountered
+		 * some sort of terrible condition and don't need to be adding more work to the threadpool.
+		 * It's in our best interest to send back a 503 response and be done with it.
+		 */
+		if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
+			pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 503, NULL, NULL, NULL);
+		}
+		ao2_cleanup(clone->endpt_info.mod_data[endpoint_mod.id]);
+		pjsip_rx_data_free_cloned(clone);
+	} else {
+		ast_sip_push_task(serializer, distribute, clone);
+	}
 
 end:
 	if (dlg) {
diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c
index f888cc4..8b75a5f 100644
--- a/res/res_pjsip/pjsip_options.c
+++ b/res/res_pjsip/pjsip_options.c
@@ -29,6 +29,7 @@
 #include "asterisk/cli.h"
 #include "asterisk/time.h"
 #include "asterisk/test.h"
+#include "asterisk/statsd.h"
 #include "include/res_pjsip_private.h"
 
 #define DEFAULT_LANGUAGE "en"
@@ -41,7 +42,6 @@ static const char *status_map [] = {
 	[UNKNOWN] = "Unknown",
 	[CREATED] = "Created",
 	[REMOVED] = "Removed",
-
 };
 
 static const char *short_status_map [] = {
@@ -64,18 +64,43 @@ const char *ast_sip_get_contact_short_status_label(const enum ast_sip_contact_st
 
 /*!
  * \internal
+ * \brief Destroy a ast_sip_contact_status object.
+ */
+static void contact_status_destroy(void * obj)
+{
+	struct ast_sip_contact_status *status = obj;
+
+	ast_free(status->aor);
+	ast_free(status->uri);
+}
+
+/*!
+ * \internal
  * \brief Create a ast_sip_contact_status object.
  */
 static void *contact_status_alloc(const char *name)
 {
-	struct ast_sip_contact_status *status = ast_sorcery_generic_alloc(sizeof(*status), NULL);
+	struct ast_sip_contact_status *status = ast_sorcery_generic_alloc(sizeof(*status), contact_status_destroy);
+	char *id = ast_strdupa(name);
+	char *aor = id;
+	char *aor_separator = NULL;
 
 	if (!status) {
 		ast_log(LOG_ERROR, "Unable to allocate ast_sip_contact_status\n");
 		return NULL;
 	}
 
-	status->status = UNKNOWN;
+	/* Dynamic contacts are delimited with ";@" and static ones with "@@" */
+	if ((aor_separator = strstr(id, ";@")) || (aor_separator = strstr(id, "@@"))) {
+		*aor_separator = '\0';
+	}
+	ast_assert(aor_separator != NULL);
+
+	status->aor = ast_strdup(aor);
+	if (!status->aor) {
+		ao2_cleanup(status);
+		return NULL;
+	}
 
 	return status;
 }
@@ -97,12 +122,17 @@ struct ast_sip_contact_status *ast_res_pjsip_find_or_create_contact_status(const
 	status = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,
 		ast_sorcery_object_get_id(contact));
 	if (!status) {
-		ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s\n",
-			contact->uri);
+		ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s/%s\n",
+			contact->aor, contact->uri);
+		return NULL;
+	}
+
+	status->uri = ast_strdup(contact->uri);
+	if (!status->uri) {
+		ao2_cleanup(status);
 		return NULL;
 	}
 
-	status->status = UNKNOWN;
 	status->rtt_start = ast_tv(0, 0);
 	status->rtt = 0;
 
@@ -113,6 +143,9 @@ struct ast_sip_contact_status *ast_res_pjsip_find_or_create_contact_status(const
 		return NULL;
 	}
 
+	ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,
+		"+1", 1.0, ast_sip_get_contact_status_label(status->status));
+
 	return status;
 }
 
@@ -123,8 +156,8 @@ struct ast_sip_contact_status *ast_res_pjsip_find_or_create_contact_status(const
 static void update_contact_status(const struct ast_sip_contact *contact,
 	enum ast_sip_contact_status_type value)
 {
-	struct ast_sip_contact_status *status;
-	struct ast_sip_contact_status *update;
+	RAII_VAR(struct ast_sip_contact_status *, status, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_sip_contact_status *, update, NULL, ao2_cleanup);
 
 	status = ast_res_pjsip_find_or_create_contact_status(contact);
 	if (!status) {
@@ -141,6 +174,11 @@ static void update_contact_status(const struct ast_sip_contact *contact,
 		return;
 	}
 
+	update->uri = ast_strdup(contact->uri);
+	if (!update->uri) {
+		return;
+	}
+
 	update->last_status = status->status;
 	update->status = value;
 
@@ -148,13 +186,12 @@ static void update_contact_status(const struct ast_sip_contact *contact,
 	   the diff between the last start time and "now" */
 	update->rtt = update->status == AVAILABLE && status->rtt_start.tv_sec > 0 ?
 		ast_tvdiff_us(ast_tvnow(), status->rtt_start) : 0;
-
 	update->rtt_start = ast_tv(0, 0);
 
 	ast_test_suite_event_notify("AOR_CONTACT_QUALIFY_RESULT",
 		"Contact: %s\r\n"
-			"Status: %s\r\n"
-			"RTT: %" PRId64,
+		"Status: %s\r\n"
+		"RTT: %" PRId64,
 		ast_sorcery_object_get_id(update),
 		ast_sip_get_contact_status_label(update->status),
 		update->rtt);
@@ -163,9 +200,6 @@ static void update_contact_status(const struct ast_sip_contact *contact,
 		ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n",
 			contact->uri);
 	}
-
-	ao2_ref(status, -1);
-	ao2_ref(update, -1);
 }
 
 /*!
@@ -175,8 +209,8 @@ static void update_contact_status(const struct ast_sip_contact *contact,
  */
 static void init_start_time(const struct ast_sip_contact *contact)
 {
-	struct ast_sip_contact_status *status;
-	struct ast_sip_contact_status *update;
+	RAII_VAR(struct ast_sip_contact_status *, status, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_sip_contact_status *, update, NULL, ao2_cleanup);
 
 	status = ast_res_pjsip_find_or_create_contact_status(contact);
 	if (!status) {
@@ -193,6 +227,11 @@ static void init_start_time(const struct ast_sip_contact *contact)
 		return;
 	}
 
+	update->uri = ast_strdup(contact->uri);
+	if (!update->uri) {
+		return;
+	}
+
 	update->status = status->status;
 	update->last_status = status->last_status;
 	update->rtt = status->rtt;
@@ -202,9 +241,6 @@ static void init_start_time(const struct ast_sip_contact *contact)
 		ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n",
 			contact->uri);
 	}
-
-	ao2_ref(status, -1);
-	ao2_ref(update, -1);
 }
 
 /*!
@@ -981,6 +1017,9 @@ static int rtt_start_to_str(const void *obj, const intptr_t *args, char **buf)
 	return 0;
 }
 
+static char status_value_unknown[2];
+static char status_value_created[2];
+
 int ast_sip_initialize_sorcery_qualify(void)
 {
 	struct ast_sorcery *sorcery = ast_sip_get_sorcery();
@@ -994,10 +1033,12 @@ int ast_sip_initialize_sorcery_qualify(void)
 		return -1;
 	}
 
+	snprintf(status_value_unknown, sizeof(status_value_unknown), "%u", UNKNOWN);
 	ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "last_status",
-		"0", OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, last_status));
+		status_value_unknown, OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, last_status));
+	snprintf(status_value_created, sizeof(status_value_created), "%u", CREATED);
 	ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "status",
-		"0", OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, status));
+		status_value_created, OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, status));
 	ast_sorcery_object_field_register_custom_nodoc(sorcery, CONTACT_STATUS, "rtt_start",
 		"0.0", rtt_start_handler, rtt_start_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "rtt",
@@ -1026,6 +1067,7 @@ static int qualify_and_schedule_cb(void *obj, void *arg, int flags)
 
 	initial_interval = (int)((initial_interval * 1000) * ast_random_double());
 
+	unschedule_qualify(contact);
 	if (contact->qualify_frequency) {
 		schedule_qualify(contact, initial_interval);
 	} else {
@@ -1155,6 +1197,43 @@ static struct ast_sip_endpoint_formatter contact_status_formatter = {
 	.format_ami = format_ami_contact_status
 };
 
+static void aor_observer_modified(const void *obj)
+{
+	struct ast_sip_aor *aor = (void *)obj;
+	struct ao2_container *contacts;
+
+	contacts = ast_sip_location_retrieve_aor_contacts(aor);
+	if (contacts) {
+		ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb, aor);
+		ao2_ref(contacts, -1);
+	}
+}
+
+static int unschedule_contact_cb(void *obj, void *arg, int flags)
+{
+	unschedule_qualify(obj);
+
+	return CMP_MATCH;
+}
+
+static void aor_observer_deleted(const void *obj)
+{
+	const struct ast_sip_aor *aor = obj;
+	struct ao2_container *contacts;
+
+	contacts = ast_sip_location_retrieve_aor_contacts(aor);
+	if (contacts) {
+		ao2_callback(contacts, OBJ_NODATA, unschedule_contact_cb, NULL);
+		ao2_ref(contacts, -1);
+	}
+}
+
+static const struct ast_sorcery_observer observer_callbacks_options = {
+	.created = aor_observer_modified,
+	.updated = aor_observer_modified,
+	.deleted = aor_observer_deleted
+};
+
 int ast_res_pjsip_init_options_handling(int reload)
 {
 	static const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
@@ -1185,6 +1264,13 @@ int ast_res_pjsip_init_options_handling(int reload)
 		return -1;
 	}
 
+	if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "aor", &observer_callbacks_options)) {
+		pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module);
+		ao2_cleanup(sched_qualifies);
+		sched_qualifies = NULL;
+		return -1;
+	}
+
 	internal_sip_register_endpoint_formatter(&contact_status_formatter);
 	ast_manager_register2("PJSIPQualify", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_sip_qualify, NULL, NULL, NULL);
 	ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options));
@@ -1200,6 +1286,7 @@ void ast_res_pjsip_cleanup_options_handling(void)
 	ast_manager_unregister("PJSIPQualify");
 	internal_sip_unregister_endpoint_formatter(&contact_status_formatter);
 
+	ast_sorcery_observer_remove(ast_sip_get_sorcery(), "aor", &observer_callbacks_options);
 	pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module);
 	ao2_cleanup(sched_qualifies);
 	sched_qualifies = NULL;
diff --git a/res/res_pjsip_caller_id.c b/res/res_pjsip_caller_id.c
index 63ef1f4..f1908a7 100644
--- a/res/res_pjsip_caller_id.c
+++ b/res/res_pjsip_caller_id.c
@@ -351,10 +351,11 @@ static void update_incoming_connected_line(struct ast_sip_session *session, pjsi
  */
 static int caller_id_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata)
 {
-	if (session->inv_session->state < PJSIP_INV_STATE_CONFIRMED) {
+	if (!session->channel) {
 		/*
-		 * Initial inbound INVITE.  Set the session ID directly
-		 * because the channel has not been created yet.
+		 * Since we have no channel this must be the initial inbound
+		 * INVITE.  Set the session ID directly because the channel
+		 * has not been created yet.
 		 */
 		if (session->endpoint->id.trust_inbound
 			&& (!set_id_from_pai(rdata, &session->id)
@@ -367,9 +368,10 @@ static int caller_id_incoming_request(struct ast_sip_session *session, pjsip_rx_
 		if (!session->endpoint->id.self.number.valid) {
 			set_id_from_from(rdata, &session->id);
 		}
-	} else if (session->channel) {
-		/* Reinvite. Check for changes to the ID and queue a connected line
-		 * update if necessary
+	} else {
+		/*
+		 * ReINVITE or UPDATE.  Check for changes to the ID and queue
+		 * a connected line update if necessary.
 		 */
 		update_incoming_connected_line(session, rdata);
 	}
diff --git a/res/res_pjsip_endpoint_identifier_ip.c b/res/res_pjsip_endpoint_identifier_ip.c
index 5c6e2cc..11559ad 100644
--- a/res/res_pjsip_endpoint_identifier_ip.c
+++ b/res/res_pjsip_endpoint_identifier_ip.c
@@ -298,13 +298,6 @@ struct ast_sip_endpoint_formatter endpoint_identify_formatter = {
 	.format_ami = format_ami_endpoint_identify
 };
 
-static int cli_populate_container(void *obj, void *arg, int flags)
-{
-	ao2_link(arg, obj);
-
-	return 0;
-}
-
 static int cli_iterator(void *container, ao2_callback_fn callback, void *args)
 {
 	const struct ast_sip_endpoint *endpoint = container;
@@ -328,47 +321,28 @@ static int cli_iterator(void *container, ao2_callback_fn callback, void *args)
 	return 0;
 }
 
-static int cli_endpoint_gather_identifies(void *obj, void *arg, int flags)
-{
-	struct ast_sip_endpoint *endpoint = obj;
-	struct ao2_container *container = arg;
-
-	cli_iterator(endpoint, cli_populate_container, container);
-
-	return 0;
-}
-
-static struct ao2_container *cli_get_container(void)
+static struct ao2_container *cli_get_container(const char *regex)
 {
-	RAII_VAR(struct ao2_container *, parent_container, NULL, ao2_cleanup);
-	RAII_VAR(struct ao2_container *, s_parent_container, NULL, ao2_cleanup);
-	struct ao2_container *child_container;
+	RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
+	struct ao2_container *s_container;
 
-	parent_container =  ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "endpoint",
-		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
-	if (!parent_container) {
+	container =  ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "identify", regex);
+	if (!container) {
 		return NULL;
 	}
 
-	s_parent_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
+	s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
 		ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);
-	if (!s_parent_container) {
+	if (!s_container) {
 		return NULL;
 	}
 
-	if (ao2_container_dup(s_parent_container, parent_container, 0)) {
+	if (ao2_container_dup(s_container, container, 0)) {
+		ao2_ref(s_container, -1);
 		return NULL;
 	}
 
-	child_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
-		ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);
-	if (!child_container) {
-		return NULL;
-	}
-
-	ao2_callback(s_parent_container, OBJ_NODATA, cli_endpoint_gather_identifies, child_container);
-
-	return child_container;
+	return s_container;
 }
 
 static void *cli_retrieve_by_id(const char *id)
@@ -461,12 +435,14 @@ static char *my_cli_traverse_objects(struct ast_cli_entry *e, int cmd,
 static struct ast_cli_entry cli_identify[] = {
 AST_CLI_DEFINE(my_cli_traverse_objects, "List PJSIP Identifies",
 	.command = "pjsip list identifies",
-	.usage = "Usage: pjsip list identifies\n"
-	"       List the configured PJSIP Identifies\n"),
+	.usage = "Usage: pjsip list identifies [ like <pattern> ]\n"
+	"       List the configured PJSIP Identifies\n"
+	"       Optional regular expression pattern is used to filter the list.\n"),
 AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Identifies",
 	.command = "pjsip show identifies",
-	.usage = "Usage: pjsip show identifies\n"
-	"       Show the configured PJSIP Identifies\n"),
+	.usage = "Usage: pjsip show identifies [ like <pattern> ]\n"
+	"       Show the configured PJSIP Identifies\n"
+	"       Optional regular expression pattern is used to filter the list.\n"),
 AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Identify",
 	.command = "pjsip show identify",
 	.usage = "Usage: pjsip show identify <id>\n"
@@ -489,7 +465,7 @@ static int load_module(void)
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "type", "", OPT_NOOP_T, 0, 0);
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, endpoint_name));
 	ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "identify", "match", "", ip_identify_match_handler, match_to_str, match_to_var_list, 0, 0);
-	ast_sorcery_reload_object(ast_sip_get_sorcery(), "identify");
+	ast_sorcery_load_object(ast_sip_get_sorcery(), "identify");
 
 	ast_sip_register_endpoint_identifier_with_name(&ip_identifier, "ip");
 	ast_sip_register_endpoint_formatter(&endpoint_identify_formatter);
diff --git a/res/res_pjsip_exten_state.c b/res/res_pjsip_exten_state.c
index 3dc8c1a..4e225dd 100644
--- a/res/res_pjsip_exten_state.c
+++ b/res/res_pjsip_exten_state.c
@@ -115,7 +115,7 @@ static void exten_state_subscription_destructor(void *obj)
 	struct exten_state_subscription *sub = obj;
 
 	ast_free(sub->user_agent);
-	ao2_cleanup(sub->sip_sub);
+	ast_sip_subscription_destroy(sub->sip_sub);
 	ast_taskprocessor_unreference(sub->serializer);
 }
 
@@ -160,7 +160,7 @@ static struct exten_state_subscription *exten_state_subscription_alloc(
 		return NULL;
 	}
 
-	exten_state_sub->sip_sub = ao2_bump(sip_sub);
+	exten_state_sub->sip_sub = sip_sub;
 
 	/* We keep our own reference to the serializer as there is no guarantee in state_changed
 	 * that the subscription tree is still valid when it is called. This can occur when
diff --git a/res/res_pjsip_mwi.c b/res/res_pjsip_mwi.c
index 06587da..f349324 100644
--- a/res/res_pjsip_mwi.c
+++ b/res/res_pjsip_mwi.c
@@ -204,7 +204,9 @@ static void mwi_subscription_destructor(void *obj)
 	struct mwi_subscription *sub = obj;
 
 	ast_debug(3, "Destroying MWI subscription for endpoint %s\n", sub->id);
-	ao2_cleanup(sub->sip_sub);
+	if (sub->is_solicited) {
+		ast_sip_subscription_destroy(sub->sip_sub);
+	}
 	ao2_cleanup(sub->stasis_subs);
 	ast_free(sub->aors);
 }
@@ -233,7 +235,7 @@ static struct mwi_subscription *mwi_subscription_alloc(struct ast_sip_endpoint *
 	 * state not being updated on the device
 	 */
 	if (is_solicited) {
-		sub->sip_sub = ao2_bump(sip_sub);
+		sub->sip_sub = sip_sub;
 	}
 
 	sub->stasis_subs = ao2_container_alloc(STASIS_BUCKETS, stasis_sub_hash, stasis_sub_cmp);
diff --git a/res/res_pjsip_notify.c b/res/res_pjsip_notify.c
index e494daf..96367cf 100644
--- a/res/res_pjsip_notify.c
+++ b/res/res_pjsip_notify.c
@@ -840,7 +840,7 @@ static char *cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a
 		e->usage =
 			"Usage: pjsip send notify <type> {endpoint|uri} <peer> [<peer>...]\n"
 			"       Send a NOTIFY request to an endpoint\n"
-			"       Message types are defined in sip_notify.conf\n";
+			"       Message types are defined in pjsip_notify.conf\n";
 		return NULL;
 	case CLI_GENERATE:
 		if (a->argc > 4 && (!strcasecmp(a->argv[4], "uri"))) {
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index c4d0249..4ba5748 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -35,6 +35,7 @@
 #include "asterisk/stasis_system.h"
 #include "asterisk/threadstorage.h"
 #include "asterisk/threadpool.h"
+#include "asterisk/statsd.h"
 #include "res_pjsip/include/res_pjsip_private.h"
 
 /*** DOCUMENTATION
@@ -100,6 +101,21 @@
 						buggy registrars.
 					</para></description>
 				</configOption>
+				<configOption name="fatal_retry_interval" default="0">
+					<synopsis>Interval used when receiving a Fatal response.</synopsis>
+					<description><para>
+						If a fatal response is received, chan_pjsip will wait
+						<replaceable>fatal_retry_interval</replaceable> seconds before
+						attempting registration again. If 0 is specified, chan_pjsip will not
+						retry after receiving a fatal (non-temporary 4xx, 5xx, 6xx) response.
+						Setting this to a non-zero value may go against a "SHOULD NOT" in RFC3261,
+						but can be used to work around buggy registrars.</para>
+						<note><para>if also set the <replaceable>forbidden_retry_interval</replaceable>
+						takes precedence over this one when a 403 is received.
+						Also, if <replaceable>auth_rejection_permanent</replaceable> equals 'yes' then
+						a 401 and 407 become subject to this retry interval.</para></note>
+					</description>
+				</configOption>
 				<configOption name="server_uri">
 					<synopsis>SIP URI of the server to register against</synopsis>
 					<description><para>
@@ -277,6 +293,8 @@ struct sip_outbound_registration {
 	unsigned int retry_interval;
 	/*! \brief Interval at which retries should occur for permanent responses */
 	unsigned int forbidden_retry_interval;
+	/*! \brief Interval at which retries should occur for all permanent responses */
+	unsigned int fatal_retry_interval;
 	/*! \brief Treat authentication challenges that we cannot handle as permanent failures */
 	unsigned int auth_rejection_permanent;
 	/*! \brief Maximum number of retries permitted */
@@ -312,10 +330,14 @@ struct sip_outbound_registration_client_state {
 	unsigned int retry_interval;
 	/*! \brief Interval at which retries should occur for permanent responses */
 	unsigned int forbidden_retry_interval;
+	/*! \brief Interval at which retries should occur for all permanent responses */
+	unsigned int fatal_retry_interval;
 	/*! \brief Treat authentication challenges that we cannot handle as permanent failures */
 	unsigned int auth_rejection_permanent;
 	/*! \brief Determines whether SIP Path support should be advertised */
 	unsigned int support_path;
+	/*! CSeq number of last sent auth request. */
+	unsigned int auth_cseq;
 	/*! \brief Serializer for stuff and things */
 	struct ast_taskprocessor *serializer;
 	/*! \brief Configured authentication credentials */
@@ -591,6 +613,19 @@ static void schedule_registration(struct sip_outbound_registration_client_state
 	}
 }
 
+static void update_client_state_status(struct sip_outbound_registration_client_state *client_state, enum sip_outbound_registration_status status)
+{
+	if (client_state->status == status) {
+		return;
+	}
+
+	ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "-1", 1.0,
+		sip_outbound_registration_status_str(client_state->status));
+	ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "+1", 1.0,
+		sip_outbound_registration_status_str(status));
+	client_state->status = status;
+}
+
 /*! \brief Callback function for unregistering (potentially) and destroying state */
 static int handle_client_state_destruction(void *data)
 {
@@ -624,7 +659,7 @@ static int handle_client_state_destruction(void *data)
 				(int) info.server_uri.slen, info.server_uri.ptr,
 				(int) info.client_uri.slen, info.client_uri.ptr);
 
-			client_state->status = SIP_REGISTRATION_STOPPING;
+			update_client_state_status(client_state, SIP_REGISTRATION_STOPPING);
 			client_state->destroy = 1;
 			if (pjsip_regc_unregister(client_state->client, &tdata) == PJ_SUCCESS
 				&& registration_client_send(client_state, tdata) == PJ_SUCCESS) {
@@ -643,7 +678,7 @@ static int handle_client_state_destruction(void *data)
 		client_state->client = NULL;
 	}
 
-	client_state->status = SIP_REGISTRATION_STOPPED;
+	update_client_state_status(client_state, SIP_REGISTRATION_STOPPED);
 	ast_sip_auth_vector_destroy(&client_state->outbound_auths);
 	ao2_ref(client_state, -1);
 
@@ -705,7 +740,7 @@ static int sip_outbound_registration_is_temporal(unsigned int code,
 static void schedule_retry(struct registration_response *response, unsigned int interval,
 			   const char *server_uri, const char *client_uri)
 {
-	response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY;
+	update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY);
 	schedule_registration(response->client_state, interval);
 
 	if (response->rdata) {
@@ -739,15 +774,27 @@ static int handle_registration_response(void *data)
 	ast_debug(1, "Processing REGISTER response %d from server '%s' for client '%s'\n",
 			response->code, server_uri, client_uri);
 
-	if (!response->client_state->auth_attempted &&
-			(response->code == 401 || response->code == 407)) {
+	if ((response->code == 401 || response->code == 407)
+		&& (!response->client_state->auth_attempted
+			|| response->rdata->msg_info.cseq->cseq != response->client_state->auth_cseq)) {
+		int res;
+		pjsip_cseq_hdr *cseq_hdr;
 		pjsip_tx_data *tdata;
+
 		if (!ast_sip_create_request_with_auth_from_old(&response->client_state->outbound_auths,
 				response->rdata, response->old_request, &tdata)) {
 			response->client_state->auth_attempted = 1;
 			ast_debug(1, "Sending authenticated REGISTER to server '%s' from client '%s'\n",
 					server_uri, client_uri);
-			if (registration_client_send(response->client_state, tdata) == PJ_SUCCESS) {
+			pjsip_tx_data_add_ref(tdata);
+			res = registration_client_send(response->client_state, tdata);
+
+			/* Save the cseq that actually got sent. */
+			cseq_hdr = (pjsip_cseq_hdr *) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ,
+				NULL);
+			response->client_state->auth_cseq = cseq_hdr->cseq;
+			pjsip_tx_data_dec_ref(tdata);
+			if (res == PJ_SUCCESS) {
 				ao2_ref(response, -1);
 				return 0;
 			}
@@ -763,14 +810,21 @@ static int handle_registration_response(void *data)
 	if (PJSIP_IS_STATUS_IN_CLASS(response->code, 200)) {
 		/* Check if this is in regards to registering or unregistering */
 		if (response->expiration) {
+			int next_registration_round;
+
 			/* If the registration went fine simply reschedule registration for the future */
 			ast_debug(1, "Outbound registration to '%s' with client '%s' successful\n", server_uri, client_uri);
-			response->client_state->status = SIP_REGISTRATION_REGISTERED;
+			update_client_state_status(response->client_state, SIP_REGISTRATION_REGISTERED);
 			response->client_state->retries = 0;
-			schedule_registration(response->client_state, response->expiration - REREGISTER_BUFFER_TIME);
+			next_registration_round = response->expiration - REREGISTER_BUFFER_TIME;
+			if (next_registration_round < 0) {
+				/* Re-register immediately. */
+				next_registration_round = 0;
+			}
+			schedule_registration(response->client_state, next_registration_round);
 		} else {
 			ast_debug(1, "Outbound unregistration to '%s' with client '%s' successful\n", server_uri, client_uri);
-			response->client_state->status = SIP_REGISTRATION_UNREGISTERED;
+			update_client_state_status(response->client_state, SIP_REGISTRATION_UNREGISTERED);
 		}
 	} else if (response->client_state->destroy) {
 		/* We need to deal with the pending destruction instead. */
@@ -781,7 +835,7 @@ static int handle_registration_response(void *data)
 		&& sip_outbound_registration_is_temporal(response->code, response->client_state)) {
 		if (response->client_state->retries == response->client_state->max_retries) {
 			/* If we received enough temporal responses to exceed our maximum give up permanently */
-			response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
+			update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_PERMANENT);
 			ast_log(LOG_WARNING, "Maximum retries reached when attempting outbound registration to '%s' with client '%s', stopping registration attempt\n",
 				server_uri, client_uri);
 		} else {
@@ -794,14 +848,22 @@ static int handle_registration_response(void *data)
 			&& response->client_state->forbidden_retry_interval
 			&& response->client_state->retries < response->client_state->max_retries) {
 			/* A forbidden response retry interval is configured and there are retries remaining */
-			response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY;
+			update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY);
 			response->client_state->retries++;
 			schedule_registration(response->client_state, response->client_state->forbidden_retry_interval);
 			ast_log(LOG_WARNING, "403 Forbidden fatal response received from '%s' on registration attempt to '%s', retrying in '%u' seconds\n",
 				server_uri, client_uri, response->client_state->forbidden_retry_interval);
+		} else if (response->client_state->fatal_retry_interval
+			   && response->client_state->retries < response->client_state->max_retries) {
+			/* Some kind of fatal failure response received, so retry according to configured interval */
+			update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY);
+			response->client_state->retries++;
+			schedule_registration(response->client_state, response->client_state->fatal_retry_interval);
+			ast_log(LOG_WARNING, "'%d' fatal response received from '%s' on registration attempt to '%s', retrying in '%u' seconds\n",
+				response->code, server_uri, client_uri, response->client_state->fatal_retry_interval);
 		} else {
 			/* Finally if there's no hope of registering give up */
-			response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
+			update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_PERMANENT);
 			if (response->rdata) {
 				ast_log(LOG_WARNING, "Fatal response '%d' received from '%s' on registration attempt to '%s', stopping outbound registration\n",
 					response->code, server_uri, client_uri);
@@ -886,7 +948,6 @@ static void sip_outbound_registration_state_destroy(void *obj)
 
 	ast_debug(3, "Destroying registration state for registration to server '%s' from client '%s'\n",
 			state->registration->server_uri, state->registration->client_uri);
-
 	ao2_cleanup(state->registration);
 
 	if (!state->client_state) {
@@ -905,6 +966,10 @@ static void sip_outbound_registration_client_state_destroy(void *obj)
 {
 	struct sip_outbound_registration_client_state *client_state = obj;
 
+	ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "-1", 1.0);
+	ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "-1", 1.0,
+		sip_outbound_registration_status_str(client_state->status));
+
 	ast_taskprocessor_unreference(client_state->serializer);
 }
 
@@ -933,6 +998,10 @@ static struct sip_outbound_registration_state *sip_outbound_registration_state_a
 	state->client_state->timer.user_data = state->client_state;
 	state->client_state->timer.cb = sip_outbound_registration_timer_cb;
 
+	ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "+1", 1.0);
+	ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "+1", 1.0,
+		sip_outbound_registration_status_str(state->client_state->status));
+
 	state->registration = ao2_bump(registration);
 	return state;
 }
@@ -1186,6 +1255,7 @@ static int sip_outbound_registration_perform(void *data)
 	}
 	state->client_state->retry_interval = registration->retry_interval;
 	state->client_state->forbidden_retry_interval = registration->forbidden_retry_interval;
+	state->client_state->fatal_retry_interval = registration->fatal_retry_interval;
 	state->client_state->max_retries = registration->max_retries;
 	state->client_state->retries = 0;
 	state->client_state->support_path = registration->support_path;
@@ -1611,11 +1681,12 @@ static int ami_show_outbound_registrations(struct mansession *s,
 	return 0;
 }
 
-static struct ao2_container *cli_get_container(void)
+static struct ao2_container *cli_get_container(const char *regex)
 {
-	RAII_VAR(struct ao2_container *, container, get_registrations(), ao2_cleanup);
+	RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
 	struct ao2_container *s_container;
 
+	container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "registration", regex);
 	if (!container) {
 		return NULL;
 	}
@@ -1719,12 +1790,14 @@ static struct ast_cli_entry cli_outbound_registration[] = {
 	AST_CLI_DEFINE(cli_register, "Registers an outbound registration target"),
 	AST_CLI_DEFINE(my_cli_traverse_objects, "List PJSIP Registrations",
 		.command = "pjsip list registrations",
-		.usage = "Usage: pjsip list registrations\n"
-				 "       List the configured PJSIP Registrations\n"),
+		.usage = "Usage: pjsip list registrations [ like <pattern> ]\n"
+				"       List the configured PJSIP Registrations\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Registrations",
 		.command = "pjsip show registrations",
-		.usage = "Usage: pjsip show registrations\n"
-				 "       Show the configured PJSIP Registrations\n"),
+		.usage = "Usage: pjsip show registrations [ like <pattern> ]\n"
+				"       Show the configured PJSIP Registrations\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Registration",
 		.command = "pjsip show registration",
 		.usage = "Usage: pjsip show registration <id>\n"
@@ -1815,6 +1888,13 @@ static void registration_loaded_observer(const char *name, const struct ast_sorc
 		return;
 	}
 
+	/*
+	 * Refresh the current configured registrations. We don't need to hold
+	 * onto the objects, as the apply handler will cause their states to
+	 * be created appropriately.
+	 */
+	ao2_cleanup(get_registrations());
+
 	/* Now to purge dead registrations. */
 	ao2_callback(states, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, check_state, NULL);
 	ao2_ref(states, -1);
@@ -1909,6 +1989,7 @@ static int load_module(void)
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "expiration", "3600", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, expiration));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_interval", "60", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_interval));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "forbidden_retry_interval", "0", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, forbidden_retry_interval));
+	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "fatal_retry_interval", "0", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, fatal_retry_interval));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "max_retries", "10", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, max_retries));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "auth_rejection_permanent", "yes", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, auth_rejection_permanent));
 	ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "registration", "outbound_auth", "", outbound_auth_handler, outbound_auths_to_str, outbound_auths_to_var_list, 0, 0);
@@ -1953,6 +2034,12 @@ static int load_module(void)
 	ast_manager_register_xml("PJSIPRegister", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_register);
 	ast_manager_register_xml("PJSIPShowRegistrationsOutbound", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_show_outbound_registrations);
 
+	/* Clear any previous statsd gauges in case we weren't shutdown cleanly */
+	ast_statsd_log("PJSIP.registrations.count", AST_STATSD_GAUGE, 0);
+	ast_statsd_log("PJSIP.registrations.state.Registered", AST_STATSD_GAUGE, 0);
+	ast_statsd_log("PJSIP.registrations.state.Unregistered", AST_STATSD_GAUGE, 0);
+	ast_statsd_log("PJSIP.registrations.state.Rejected", AST_STATSD_GAUGE, 0);
+
 	/* Load configuration objects */
 	ast_sorcery_load_object(ast_sip_get_sorcery(), "registration");
 
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index eb49aaf..99376b1 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -411,6 +411,8 @@ struct sip_subscription_tree {
 	int is_list;
 	/*! Next item in the list */
 	AST_LIST_ENTRY(sip_subscription_tree) next;
+	/*! Indicates that a NOTIFY is currently being sent on the SIP subscription */
+	int last_notify;
 };
 
 /*!
@@ -596,6 +598,7 @@ static void subscription_persistence_remove(struct sip_subscription_tree *sub_tr
 
 	ast_sorcery_delete(ast_sip_get_sorcery(), sub_tree->persistence);
 	ao2_ref(sub_tree->persistence, -1);
+	sub_tree->persistence = NULL;
 }
 
 
@@ -1020,26 +1023,6 @@ static int datastore_cmp(void *obj, void *arg, int flags)
 	return strcmp(datastore1->uid, uid2) ? 0 : CMP_MATCH | CMP_STOP;
 }
 
-static int subscription_remove_serializer(void *obj)
-{
-	struct sip_subscription_tree *sub_tree = obj;
-
-	/* This is why we keep the dialog on the subscription. When the subscription
-	 * is destroyed, there is no guarantee that the underlying dialog is ready
-	 * to be destroyed. Furthermore, there's no guarantee in the opposite direction
-	 * either. The dialog could be destroyed before our subscription is. We fix
-	 * this problem by keeping a reference to the dialog until it is time to
-	 * destroy the subscription. We need to have the dialog available when the
-	 * subscription is destroyed so that we can guarantee that our attempt to
-	 * remove the serializer will be successful.
-	 */
-	ast_sip_dialog_set_serializer(sub_tree->dlg, NULL);
-	ast_sip_dialog_set_endpoint(sub_tree->dlg, NULL);
-	pjsip_dlg_dec_session(sub_tree->dlg, &pubsub_module);
-
-	return 0;
-}
-
 static void add_subscription(struct sip_subscription_tree *obj)
 {
 	SCOPED_LOCK(lock, &subscriptions, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
@@ -1063,14 +1046,32 @@ static void remove_subscription(struct sip_subscription_tree *obj)
 	AST_RWLIST_TRAVERSE_SAFE_END;
 }
 
-static void subscription_destructor(void *obj)
+static void destroy_subscription(struct ast_sip_subscription *sub)
 {
-	struct ast_sip_subscription *sub = obj;
-
 	ast_debug(3, "Destroying SIP subscription to resource %s\n", sub->resource);
 	ast_free(sub->body_text);
 
+	AST_VECTOR_FREE(&sub->children);
 	ao2_cleanup(sub->datastores);
+	ast_free(sub);
+}
+
+static void destroy_subscriptions(struct ast_sip_subscription *root)
+{
+	int i;
+
+	if (!root) {
+		return;
+	}
+
+	for (i = 0; i < AST_VECTOR_SIZE(&root->children); ++i) {
+		struct ast_sip_subscription *child;
+
+		child = AST_VECTOR_GET(&root->children, i);
+		destroy_subscriptions(child);
+	}
+
+	destroy_subscription(root);
 }
 
 static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_subscription_handler *handler,
@@ -1079,7 +1080,7 @@ static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_s
 	struct ast_sip_subscription *sub;
 	pjsip_sip_uri *contact_uri;
 
-	sub = ao2_alloc(sizeof(*sub) + strlen(resource) + 1, subscription_destructor);
+	sub = ast_calloc(1, sizeof(*sub) + strlen(resource) + 1);
 	if (!sub) {
 		return NULL;
 	}
@@ -1087,13 +1088,13 @@ static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_s
 
 	sub->datastores = ao2_container_alloc(DATASTORE_BUCKETS, datastore_hash, datastore_cmp);
 	if (!sub->datastores) {
-		ao2_ref(sub, -1);
+		destroy_subscription(sub);
 		return NULL;
 	}
 
 	sub->body_text = ast_str_create(128);
 	if (!sub->body_text) {
-		ao2_ref(sub, -1);
+		destroy_subscription(sub);
 		return NULL;
 	}
 
@@ -1104,7 +1105,7 @@ static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_s
 
 	sub->handler = handler;
 	sub->subscription_state = PJSIP_EVSUB_STATE_ACTIVE;
-	sub->tree = tree;
+	sub->tree = ao2_bump(tree);
 
 	return sub;
 }
@@ -1132,6 +1133,7 @@ static struct ast_sip_subscription *create_virtual_subscriptions(const struct as
 
 	sub->full_state = current->full_state;
 	sub->body_generator = generator;
+	AST_VECTOR_INIT(&sub->children, AST_VECTOR_SIZE(&current->children));
 
 	for (i = 0; i < AST_VECTOR_SIZE(&current->children); ++i) {
 		struct ast_sip_subscription *child;
@@ -1166,7 +1168,6 @@ static void shutdown_subscriptions(struct ast_sip_subscription *sub)
 	if (AST_VECTOR_SIZE(&sub->children) > 0) {
 		for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
 			shutdown_subscriptions(AST_VECTOR_GET(&sub->children, i));
-			ao2_cleanup(AST_VECTOR_GET(&sub->children, i));
 		}
 		return;
 	}
@@ -1176,37 +1177,58 @@ static void shutdown_subscriptions(struct ast_sip_subscription *sub)
 		sub->handler->subscription_shutdown(sub);
 	}
 }
+static int subscription_unreference_dialog(void *obj)
+{
+	struct sip_subscription_tree *sub_tree = obj;
+
+	/* This is why we keep the dialog on the subscription. When the subscription
+	 * is destroyed, there is no guarantee that the underlying dialog is ready
+	 * to be destroyed. Furthermore, there's no guarantee in the opposite direction
+	 * either. The dialog could be destroyed before our subscription is. We fix
+	 * this problem by keeping a reference to the dialog until it is time to
+	 * destroy the subscription. We need to have the dialog available when the
+	 * subscription is destroyed so that we can guarantee that our attempt to
+	 * remove the serializer will be successful.
+	 */
+	pjsip_dlg_dec_session(sub_tree->dlg, &pubsub_module);
+	sub_tree->dlg = NULL;
+
+	return 0;
+}
 
 static void subscription_tree_destructor(void *obj)
 {
 	struct sip_subscription_tree *sub_tree = obj;
 
+	ast_debug(3, "Destroying subscription tree %p\n", sub_tree);
+
 	remove_subscription(sub_tree);
 
-	subscription_persistence_remove(sub_tree);
 	ao2_cleanup(sub_tree->endpoint);
 
+	destroy_subscriptions(sub_tree->root);
+
 	if (sub_tree->dlg) {
-		ast_sip_push_task_synchronous(NULL, subscription_remove_serializer, sub_tree);
+		ast_sip_push_task_synchronous(sub_tree->serializer, subscription_unreference_dialog, sub_tree);
 	}
 
-	shutdown_subscriptions(sub_tree->root);
-	ao2_cleanup(sub_tree->root);
-
 	ast_taskprocessor_unreference(sub_tree->serializer);
 	ast_module_unref(ast_module_info->self);
 }
 
+void ast_sip_subscription_destroy(struct ast_sip_subscription *sub)
+{
+	ast_debug(3, "Removing subscription %p reference to subscription tree %p\n", sub, sub->tree);
+	ao2_cleanup(sub->tree);
+}
+
 static void subscription_setup_dialog(struct sip_subscription_tree *sub_tree, pjsip_dialog *dlg)
 {
-	/* We keep a reference to the dialog until our subscription is destroyed. See
-	 * the subscription_destructor for more details
-	 */
-	pjsip_dlg_inc_session(dlg, &pubsub_module);
 	sub_tree->dlg = dlg;
 	ast_sip_dialog_set_serializer(dlg, sub_tree->serializer);
 	ast_sip_dialog_set_endpoint(dlg, sub_tree->endpoint);
 	pjsip_evsub_set_mod_data(sub_tree->evsub, pubsub_module.id, sub_tree);
+	pjsip_dlg_inc_session(dlg, &pubsub_module);
 }
 
 static struct sip_subscription_tree *allocate_subscription_tree(struct ast_sip_endpoint *endpoint)
@@ -1497,7 +1519,11 @@ static void sip_subscription_to_ami(struct sip_subscription_tree *sub_tree,
 	ast_str_append(buf, 0, "Endpoint: %s\r\n",
 		       ast_sorcery_object_get_id(sub_tree->endpoint));
 
-	ast_copy_pj_str(str, &sub_tree->dlg->call_id->id, sizeof(str));
+	if (sub_tree->dlg) {
+		ast_copy_pj_str(str, &sub_tree->dlg->call_id->id, sizeof(str));
+	} else {
+		ast_copy_string(str, "<unknown>", sizeof(str));
+	}
 	ast_str_append(buf, 0, "Callid: %s\r\n", str);
 
 	ast_str_append(buf, 0, "State: %s\r\n", pjsip_evsub_get_state_name(sub_tree->evsub));
@@ -1518,10 +1544,12 @@ static void sip_subscription_to_ami(struct sip_subscription_tree *sub_tree,
 
 void *ast_sip_subscription_get_header(const struct ast_sip_subscription *sub, const char *header)
 {
-	pjsip_dialog *dlg = sub->tree->dlg;
-	pjsip_msg *msg = ast_sip_mod_data_get(dlg->mod_data, pubsub_module.id, MOD_DATA_MSG);
+	pjsip_dialog *dlg;
+	pjsip_msg *msg;
 	pj_str_t name;
 
+	dlg = sub->tree->dlg;
+	msg = ast_sip_mod_data_get(dlg->mod_data, pubsub_module.id, MOD_DATA_MSG);
 	pj_cstr(&name, header);
 
 	return pjsip_msg_find_hdr_by_name(msg, &name, NULL);
@@ -1654,6 +1682,7 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree,
 #ifdef TEST_FRAMEWORK
 	struct ast_sip_endpoint *endpoint = sub_tree->endpoint;
 #endif
+	pjsip_evsub *evsub = sub_tree->evsub;
 	int res;
 
 	if (allocate_tdata_buffer(tdata)) {
@@ -1661,13 +1690,13 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree,
 		return -1;
 	}
 
-	res = pjsip_evsub_send_request(sub_tree->evsub, tdata) == PJ_SUCCESS ? 0 : -1;
+	res = pjsip_evsub_send_request(evsub, tdata) == PJ_SUCCESS ? 0 : -1;
 	subscription_persistence_update(sub_tree, NULL);
 
 	ast_test_suite_event_notify("SUBSCRIPTION_STATE_SET",
 		"StateText: %s\r\n"
 		"Endpoint: %s\r\n",
-		pjsip_evsub_get_state_name(sub_tree->evsub),
+		pjsip_evsub_get_state_name(evsub),
 		ast_sorcery_object_get_id(endpoint));
 
 	return res;
@@ -2075,6 +2104,8 @@ static pjsip_require_hdr *create_require_eventlist(pj_pool_t *pool)
 /*!
  * \brief Send a NOTIFY request to a subscriber
  *
+ * \pre sub_tree->dlg is locked
+ *
  * \param sub_tree The subscription tree representing the subscription
  * \param force_full_state If true, ignore resource list settings and send full resource list state.
  * \retval 0 Success
@@ -2107,6 +2138,9 @@ static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int forc
 		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) require);
 	}
 
+	if (sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED) {
+		sub_tree->last_notify = 1;
+	}
 	if (sip_subscription_send_request(sub_tree, tdata)) {
 		return -1;
 	}
@@ -2119,13 +2153,16 @@ static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int forc
 static int serialized_send_notify(void *userdata)
 {
 	struct sip_subscription_tree *sub_tree = userdata;
+	pjsip_dialog *dlg = sub_tree->dlg;
 
+	pjsip_dlg_inc_lock(dlg);
 	/* It's possible that between when the notification was scheduled
 	 * and now, that a new SUBSCRIBE arrived, requiring full state to be
 	 * sent out in an immediate NOTIFY. If that has happened, we need to
 	 * bail out here instead of sending the batched NOTIFY.
 	 */
 	if (!sub_tree->send_scheduled_notify) {
+		pjsip_dlg_dec_lock(dlg);
 		ao2_cleanup(sub_tree);
 		return 0;
 	}
@@ -2135,6 +2172,7 @@ static int serialized_send_notify(void *userdata)
 			"Resource: %s",
 			sub_tree->root->resource);
 	sub_tree->notify_sched_id = -1;
+	pjsip_dlg_dec_lock(dlg);
 	ao2_cleanup(sub_tree);
 	return 0;
 }
@@ -2167,8 +2205,19 @@ static int schedule_notification(struct sip_subscription_tree *sub_tree)
 int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip_body_data *notify_data,
 		int terminate)
 {
+	int res;
+	pjsip_dialog *dlg = sub->tree->dlg;
+
+	pjsip_dlg_inc_lock(dlg);
+
+	if (!sub->tree->evsub) {
+		pjsip_dlg_dec_lock(dlg);
+		return 0;
+	}
+
 	if (ast_sip_pubsub_generate_body_content(ast_sip_subscription_get_body_type(sub),
 				ast_sip_subscription_get_body_subtype(sub), notify_data, &sub->body_text)) {
+		pjsip_dlg_dec_lock(dlg);
 		return -1;
 	}
 
@@ -2178,9 +2227,8 @@ int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip
 	}
 
 	if (sub->tree->notification_batch_interval) {
-		return schedule_notification(sub->tree);
+		res = schedule_notification(sub->tree);
 	} else {
-		int res;
 		/* See the note in pubsub_on_rx_refresh() for why sub->tree is refbumped here */
 		ao2_ref(sub->tree, +1);
 		res = send_notify(sub->tree, 0);
@@ -2188,9 +2236,10 @@ int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip
 				"Resource: %s",
 				sub->tree->root->resource);
 		ao2_ref(sub->tree, -1);
-
-		return res;
 	}
+
+	pjsip_dlg_dec_lock(dlg);
+	return res;
 }
 
 void ast_sip_subscription_get_local_uri(struct ast_sip_subscription *sub, char *buf, size_t size)
@@ -2200,7 +2249,9 @@ void ast_sip_subscription_get_local_uri(struct ast_sip_subscription *sub, char *
 
 void ast_sip_subscription_get_remote_uri(struct ast_sip_subscription *sub, char *buf, size_t size)
 {
-	pjsip_dialog *dlg = sub->tree->dlg;
+	pjsip_dialog *dlg;
+
+	dlg = sub->tree->dlg;
 	ast_copy_pj_str(buf, &dlg->remote.info_str, size);
 }
 
@@ -3139,10 +3190,88 @@ static pj_bool_t pubsub_on_rx_request(pjsip_rx_data *rdata)
 	return PJ_FALSE;
 }
 
+static void set_state_terminated(struct ast_sip_subscription *sub)
+{
+	int i;
+
+	sub->subscription_state = PJSIP_EVSUB_STATE_TERMINATED;
+	for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
+		set_state_terminated(AST_VECTOR_GET(&sub->children, i));
+	}
+}
+
+/* XXX This function and serialized_pubsub_on_rx_refresh are nearly identical */
+static int serialized_pubsub_on_server_timeout(void *userdata)
+{
+	struct sip_subscription_tree *sub_tree = userdata;
+	pjsip_dialog *dlg = sub_tree->dlg;
+
+	pjsip_dlg_inc_lock(dlg);
+	if (!sub_tree->evsub) {
+		pjsip_dlg_dec_lock(dlg);
+		return 0;
+	}
+	set_state_terminated(sub_tree->root);
+	send_notify(sub_tree, 1);
+	ast_test_suite_event_notify("SUBSCRIPTION_TERMINATED",
+			"Resource: %s",
+			sub_tree->root->resource);
+
+	pjsip_dlg_dec_lock(dlg);
+	ao2_cleanup(sub_tree);
+	return 0;
+}
+
+/*!
+ * \brief PJSIP callback when underlying SIP subscription changes state
+ *
+ * This callback is a bit of a mess, because it's not always called when
+ * you might expect it to be, and it can be called multiple times for the
+ * same state.
+ *
+ * For instance, this function is not called at all when an incoming SUBSCRIBE
+ * arrives to refresh a subscription. That makes sense in a way, since the
+ * subscription state has not made a change; it was active and remains active.
+ *
+ * However, if an incoming SUBSCRIBE arrives to end a subscription, then this
+ * will be called into once upon receiving the SUBSCRIBE (after the call to
+ * pubsub_on_rx_refresh) and again when sending a NOTIFY to end the subscription.
+ * In both cases, the apparent state of the subscription is "terminated".
+ *
+ * However, the double-terminated state changes don't happen in all cases. For
+ * instance, if a subscription expires, then the only time this callback is
+ * called is when we send the NOTIFY to end the subscription.
+ *
+ * As far as state changes are concerned, we only ever care about transitions
+ * to the "terminated" state. The action we take here is dependent on the
+ * conditions behind why the state change to "terminated" occurred. If the
+ * state change has occurred because we are sending a NOTIFY to end the
+ * subscription, we consider this to be the final hurrah of the subscription
+ * and take measures to start shutting things down. If the state change to
+ * terminated occurs for a different reason (e.g. transaction timeout,
+ * incoming SUBSCRIBE to end the subscription), then we push a task to
+ * send out a NOTIFY. When that NOTIFY is sent, this callback will be
+ * called again and we will actually shut down the subscription. The
+ * subscription tree's last_notify field let's us know if this is being
+ * called as a result of a terminating NOTIFY or not.
+ *
+ * There is no guarantee that this function will be called from a serializer
+ * thread since it can be called due to a transaction timeout. Therefore
+ * synchronization primitives are necessary to ensure that no operations
+ * step on each others' toes. The dialog lock is always held when this
+ * callback is called, so we ensure that relevant structures that may
+ * be touched in this function are always protected by the dialog lock
+ * elsewhere as well. The dialog lock in particular protects
+ *
+ * \li The subscription tree's last_notify field
+ * \li The subscription tree's evsub pointer
+ */
 static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event)
 {
 	struct sip_subscription_tree *sub_tree;
 
+	ast_debug(3, "on_evsub_state called with state %s\n", pjsip_evsub_get_state_name(evsub));
+
 	if (pjsip_evsub_get_state(evsub) != PJSIP_EVSUB_STATE_TERMINATED) {
 		return;
 	}
@@ -3152,21 +3281,63 @@ static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event)
 		return;
 	}
 
-	ao2_cleanup(sub_tree);
+	if (!sub_tree->last_notify) {
+		if (ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_server_timeout, ao2_bump(sub_tree))) {
+			ast_log(LOG_ERROR, "Failed to push task to send final NOTIFY.\n");
+			ao2_ref(sub_tree, -1);
+		} else {
+			return;
+		}
+	}
 
 	pjsip_evsub_set_mod_data(evsub, pubsub_module.id, NULL);
+	sub_tree->evsub = NULL;
+	ast_sip_dialog_set_serializer(sub_tree->dlg, NULL);
+	ast_sip_dialog_set_endpoint(sub_tree->dlg, NULL);
+	subscription_persistence_remove(sub_tree);
+	shutdown_subscriptions(sub_tree->root);
+
+	/* Remove evsub's reference to the sub_tree */
+	ao2_ref(sub_tree, -1);
 }
 
-static void set_state_terminated(struct ast_sip_subscription *sub)
+static int serialized_pubsub_on_rx_refresh(void *userdata)
 {
-	int i;
+	struct sip_subscription_tree *sub_tree = userdata;
+	pjsip_dialog *dlg = sub_tree->dlg;
 
-	sub->subscription_state = PJSIP_EVSUB_STATE_TERMINATED;
-	for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
-		set_state_terminated(AST_VECTOR_GET(&sub->children, i));
+	pjsip_dlg_inc_lock(dlg);
+	if (!sub_tree->evsub) {
+		pjsip_dlg_dec_lock(dlg);
+		return 0;
+	}
+
+	if (pjsip_evsub_get_state(sub_tree->evsub) == PJSIP_EVSUB_STATE_TERMINATED) {
+		set_state_terminated(sub_tree->root);
 	}
+
+	send_notify(sub_tree, 1);
+
+	ast_test_suite_event_notify(sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED ?
+			"SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_REFRESHED",
+			"Resource: %s", sub_tree->root->resource);
+
+	pjsip_dlg_dec_lock(dlg);
+	ao2_cleanup(sub_tree);
+	return 0;
 }
 
+/*!
+ * \brief Called whenever an in-dialog SUBSCRIBE is received
+ *
+ * This includes both SUBSCRIBE requests that actually refresh the subscription
+ * as well as SUBSCRIBE requests that end the subscription.
+ *
+ * In the case where the SUBSCRIBE is actually refreshing the subscription we
+ * push a task to send an appropriate NOTIFY request. In the case where the
+ * SUBSCRIBE is ending the subscription, we let the pubsub_on_evsub_state
+ * callback take care of sending the terminal NOTIFY request instead.
+ */
 static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata,
 		int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body)
 {
@@ -3177,31 +3348,19 @@ static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata,
 		return;
 	}
 
-	/* If sending a NOTIFY to terminate a subscription, then pubsub_on_evsub_state()
-	 * will be called when we send the NOTIFY, and that will result in dropping the
-	 * refcount of sub_tree by one, and possibly destroying the sub_tree. We need to
-	 * hold a reference to the sub_tree until this function returns so that we don't
-	 * try to read from or write to freed memory by accident
+	/* PJSIP will set the evsub's state to terminated before calling into this function
+	 * if the Expires value of the incoming SUBSCRIBE is 0.
 	 */
-	ao2_ref(sub_tree, +1);
-
-	if (pjsip_evsub_get_state(evsub) == PJSIP_EVSUB_STATE_TERMINATED) {
-		set_state_terminated(sub_tree->root);
-	}
-
-	if (send_notify(sub_tree, 1)) {
-		*p_st_code = 500;
+	if (pjsip_evsub_get_state(sub_tree->evsub) != PJSIP_EVSUB_STATE_TERMINATED) {
+		if (ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_rx_refresh, ao2_bump(sub_tree))) {
+			/* If we can't push the NOTIFY refreshing task...we'll just go with it. */
+			ao2_ref(sub_tree, -1);
+		}
 	}
 
-	ast_test_suite_event_notify(sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED ?
-			"SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_REFRESHED",
-			"Resource: %s", sub_tree->root->resource);
-
 	if (sub_tree->is_list) {
 		pj_list_insert_before(res_hdr, create_require_eventlist(rdata->tp_info.pool));
 	}
-
-	ao2_ref(sub_tree, -1);
 }
 
 static void pubsub_on_rx_notify(pjsip_evsub *evsub, pjsip_rx_data *rdata, int *p_st_code,
@@ -3239,31 +3398,24 @@ static void pubsub_on_client_refresh(pjsip_evsub *evsub)
 	ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_client_refresh, sub_tree);
 }
 
-static int serialized_pubsub_on_server_timeout(void *userdata)
-{
-	struct sip_subscription_tree *sub_tree = userdata;
-
-	set_state_terminated(sub_tree->root);
-	send_notify(sub_tree, 1);
-	ast_test_suite_event_notify("SUBSCRIPTION_TERMINATED",
-			"Resource: %s",
-			sub_tree->root->resource);
-
-	ao2_cleanup(sub_tree);
-	return 0;
-}
-
 static void pubsub_on_server_timeout(pjsip_evsub *evsub)
 {
-	struct sip_subscription_tree *sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
 
+	struct sip_subscription_tree *sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
 	if (!sub_tree) {
-		/* if a subscription has been terminated and the subscription
-		   timeout/expires is less than the time it takes for all pending
-		   transactions to end then the subscription timer will not have
-		   been canceled yet and sub will be null, so do nothing since
-		   the subscription has already been terminated. */
-		return;
+		/* PJSIP does not terminate the server timeout timer when a SUBSCRIBE
+		 * with Expires: 0 arrives to end a subscription, nor does it terminate
+		 * this timer when we send a NOTIFY request in response to receiving such
+		 * a SUBSCRIBE. PJSIP does not stop the server timeout timer until the
+		 * NOTIFY transaction has finished (either through receiving a response
+		 * or through a transaction timeout).
+		 *
+		 * Therefore, it is possible that we can be told that a server timeout
+		 * occurred after we already thought that the subscription had been
+		 * terminated. In such a case, we will have already removed the sub_tree
+		 * from the evsub's mod_data array.
+		 */
+        return;
 	}
 
 	ao2_ref(sub_tree, +1);
diff --git a/res/res_pjsip_pubsub.exports.in b/res/res_pjsip_pubsub.exports.in
index 58702d6..6616524 100644
--- a/res/res_pjsip_pubsub.exports.in
+++ b/res/res_pjsip_pubsub.exports.in
@@ -38,6 +38,7 @@
 		LINKER_SYMBOL_PREFIXast_sip_subscription_get_remote_uri;
 		LINKER_SYMBOL_PREFIXast_sip_subscription_get_header;
 		LINKER_SYMBOL_PREFIXast_sip_subscription_is_terminated;
+		LINKER_SYMBOL_PREFIXast_sip_subscription_destroy;
 	local:
 		*;
 };
diff --git a/res/res_pjsip_rfc3326.c b/res/res_pjsip_rfc3326.c
index 3ed8de6..ab92d6f 100644
--- a/res/res_pjsip_rfc3326.c
+++ b/res/res_pjsip_rfc3326.c
@@ -32,6 +32,7 @@
 #include "asterisk/res_pjsip_session.h"
 #include "asterisk/module.h"
 #include "asterisk/causes.h"
+#include "asterisk/threadpool.h"
 
 static void rfc3326_use_reason_header(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
 {
@@ -101,9 +102,15 @@ static void rfc3326_add_reason_header(struct ast_sip_session *session, struct pj
 
 static void rfc3326_outgoing_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
 {
-	if ((pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_bye_method) &&
-	     pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_cancel_method)) ||
-	    !session->channel) {
+	if ((pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_bye_method)
+			&& pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_cancel_method))
+		|| !session->channel
+		/*
+		 * The session->channel has been seen to go away on us between
+		 * checks so we must also be running under the call's serializer
+		 * thread.
+		 */
+		|| session->serializer != ast_threadpool_serializer_get_current()) {
 		return;
 	}
 
@@ -114,7 +121,9 @@ static void rfc3326_outgoing_response(struct ast_sip_session *session, struct pj
 {
 	struct pjsip_status_line status = tdata->msg->line.status;
 
-	if ((status.code < 300) || !session->channel) {
+	if (status.code < 300
+		|| !session->channel
+		|| session->serializer != ast_threadpool_serializer_get_current()) {
 		return;
 	}
 
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index df28204..f733ea4 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -321,8 +321,8 @@ static int set_caps(struct ast_sip_session *session, struct ast_sip_session_medi
 	/* get the joint capabilities between peer and endpoint */
 	ast_format_cap_get_compatible(caps, peer, joint);
 	if (!ast_format_cap_count(joint)) {
-		struct ast_str *usbuf = ast_str_alloca(256);
-		struct ast_str *thembuf = ast_str_alloca(256);
+		struct ast_str *usbuf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *thembuf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 		ast_rtp_codecs_payloads_destroy(&codecs);
 		ast_log(LOG_NOTICE, "No joint capabilities for '%s' media stream between our configuration(%s) and incoming SDP(%s)\n",
@@ -387,8 +387,11 @@ static pjmedia_sdp_attr* generate_rtpmap_attr(struct ast_sip_session *session, p
 	rtpmap.pt = media->desc.fmt[media->desc.fmt_count - 1];
 	rtpmap.clock_rate = ast_rtp_lookup_sample_rate2(asterisk_format, format, code);
 	pj_strdup2(pool, &rtpmap.enc_name, ast_rtp_lookup_mime_subtype2(asterisk_format, format, code, options));
-	rtpmap.param.slen = 0;
-	rtpmap.param.ptr = NULL;
+	if (!pj_stricmp2(&rtpmap.enc_name, "opus")) {
+		pj_cstr(&rtpmap.param, "2");
+	} else {
+		pj_cstr(&rtpmap.param, NULL);
+	}
 
 	pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr);
 
diff --git a/res/res_pjsip_t38.c b/res/res_pjsip_t38.c
index c867f7f..2c544bb 100644
--- a/res/res_pjsip_t38.c
+++ b/res/res_pjsip_t38.c
@@ -161,6 +161,9 @@ static void t38_change_state(struct ast_sip_session *session, struct ast_sip_ses
 		parameters.max_ifp = ast_udptl_get_far_max_ifp(session_media->udptl);
 		parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
 		ast_udptl_set_tag(session_media->udptl, "%s", ast_channel_name(session->channel));
+
+		/* Inform the bridge the channel is in that it needs to be reconfigured */
+		ast_channel_set_unbridged(session->channel, 1);
 		break;
 	case T38_ENABLED:
 		parameters = state->their_parms;
@@ -177,7 +180,8 @@ static void t38_change_state(struct ast_sip_session *session, struct ast_sip_ses
 		}
 		break;
 	case T38_LOCAL_REINVITE:
-		/* wait until we get a peer response before responding to local reinvite */
+		/* Inform the bridge the channel is in that it needs to be reconfigured */
+		ast_channel_set_unbridged(session->channel, 1);
 		break;
 	case T38_MAX_ENUM:
 		/* Well, that shouldn't happen */
@@ -264,6 +268,7 @@ static int t38_initialize_session(struct ast_sip_session *session, struct ast_si
 	ast_udptl_set_error_correction_scheme(session_media->udptl, session->endpoint->media.t38.error_correction);
 	ast_udptl_setnat(session_media->udptl, session->endpoint->media.t38.nat);
 	ast_udptl_set_far_max_datagram(session_media->udptl, session->endpoint->media.t38.maxdatagram);
+	ast_debug(3, "UDPTL initialized on session for %s\n", ast_channel_name(session->channel));
 
 	return 0;
 }
@@ -462,6 +467,11 @@ static void t38_masq(void *data, int framehook_id,
 	ast_framehook_detach(new_chan, framehook_id);
 }
 
+static int t38_consume(void *data, enum ast_frame_type type)
+{
+	return 0;
+}
+
 static const struct ast_datastore_info t38_framehook_datastore = {
 	.type = "T38 framehook",
 };
@@ -474,6 +484,7 @@ static void t38_attach_framehook(struct ast_sip_session *session)
 	static struct ast_framehook_interface hook = {
 		.version = AST_FRAMEHOOK_INTERFACE_VERSION,
 		.event_cb = t38_framehook,
+		.consume_cb = t38_consume,
 		.chan_fixup_cb = t38_masq,
 		.chan_breakdown_cb = t38_masq,
 	};
@@ -559,6 +570,41 @@ static struct ast_sip_session_supplement t38_supplement = {
 	.outgoing_request = t38_outgoing_invite_request,
 };
 
+static int t38_incoming_bye_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
+{
+	struct ast_datastore *datastore;
+	struct ast_sip_session_media *session_media;
+
+	if (!session->channel) {
+		return 0;
+	}
+
+	datastore = ast_sip_session_get_datastore(session, "t38");
+	if (!datastore) {
+		return 0;
+	}
+
+	session_media = ao2_find(session->media, "image", OBJ_KEY);
+	if (!session_media) {
+		ao2_ref(datastore, -1);
+		return 0;
+	}
+
+	t38_change_state(session, session_media, datastore->data, T38_REJECTED);
+
+	ao2_ref(datastore, -1);
+	ao2_ref(session_media, -1);
+
+	return 0;
+}
+
+/*! \brief Supplement for handling a remote termination of T.38 state */
+static struct ast_sip_session_supplement t38_bye_supplement = {
+	.method = "BYE",
+	.priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 1,
+	.incoming_request = t38_incoming_bye_request,
+};
+
 /*! \brief Parse a T.38 image stream and store the attribute information */
 static void t38_interpret_sdp(struct t38_state *state, struct ast_sip_session *session, struct ast_sip_session_media *session_media,
 	const struct pjmedia_sdp_media *stream)
@@ -630,10 +676,12 @@ static enum ast_sip_session_sdp_stream_defer defer_incoming_sdp_stream(
 	struct t38_state *state;
 
 	if (!session->endpoint->media.t38.enabled) {
+		ast_debug(3, "Not deferring incoming SDP stream: T.38 not enabled on %s\n", ast_channel_name(session->channel));
 		return AST_SIP_SESSION_SDP_DEFER_NOT_HANDLED;
 	}
 
 	if (t38_initialize_session(session, session_media)) {
+		ast_debug(3, "Not deferring incoming SDP stream: Failed to initialize UDPTL on %s\n", ast_channel_name(session->channel));
 		return AST_SIP_SESSION_SDP_DEFER_ERROR;
 	}
 
@@ -646,6 +694,7 @@ static enum ast_sip_session_sdp_stream_defer defer_incoming_sdp_stream(
 	/* If they are initiating the re-invite we need to defer responding until later */
 	if (session->t38state == T38_DISABLED) {
 		t38_change_state(session, session_media, state, T38_PEER_REINVITE);
+		ast_debug(3, "Deferring incoming SDP stream on %s for peer re-invite\n", ast_channel_name(session->channel));
 		return AST_SIP_SESSION_SDP_DEFER_NEEDED;
 	}
 
@@ -661,6 +710,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
 	RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free);
 
 	if (!session->endpoint->media.t38.enabled) {
+		ast_debug(3, "Declining; T.38 not enabled on session\n");
 		return -1;
 	}
 
@@ -669,6 +719,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
 	}
 
 	if ((session->t38state == T38_REJECTED) || (session->t38state == T38_DISABLED)) {
+		ast_debug(3, "Declining; T.38 state is rejected or declined\n");
 		t38_change_state(session, session_media, state, T38_DISABLED);
 		return -1;
 	}
@@ -678,6 +729,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
 	/* Ensure that the address provided is valid */
 	if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_INET) <= 0) {
 		/* The provided host was actually invalid so we error out this negotiation */
+		ast_debug(3, "Declining; provided host is invalid\n");
 		return -1;
 	}
 
@@ -685,6 +737,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
 	if ((ast_sockaddr_is_ipv6(addrs) && !session->endpoint->media.t38.ipv6) ||
 		(ast_sockaddr_is_ipv4(addrs) && session->endpoint->media.t38.ipv6)) {
 		/* The address does not match configured */
+		ast_debug(3, "Declining, provided host does not match configured address family\n");
 		return -1;
 	}
 
@@ -713,13 +766,16 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
 	pj_str_t stmp;
 
 	if (!session->endpoint->media.t38.enabled) {
+		ast_debug(3, "Not creating outgoing SDP stream: T.38 not enabled\n");
 		return 1;
 	} else if ((session->t38state != T38_LOCAL_REINVITE) && (session->t38state != T38_PEER_REINVITE) &&
 		(session->t38state != T38_ENABLED)) {
+		ast_debug(3, "Not creating outgoing SDP stream: T.38 not enabled\n");
 		return 1;
 	} else if (!(state = t38_state_get_or_alloc(session))) {
 		return -1;
 	} else if (t38_initialize_session(session, session_media)) {
+		ast_debug(3, "Not creating outgoing SDP stream: Failed to initialize T.38 session\n");
 		return -1;
 	}
 
@@ -738,6 +794,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
 	}
 
 	if (ast_strlen_zero(hostip)) {
+		ast_debug(3, "Not creating outgoing SDP stream: no known host IP\n");
 		return -1;
 	}
 
@@ -805,6 +862,7 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
 	struct t38_state *state;
 
 	if (!session_media->udptl) {
+		ast_debug(3, "Not applying negotiated SDP stream: no UDTPL session\n");
 		return 0;
 	}
 
@@ -817,6 +875,7 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
 	/* Ensure that the address provided is valid */
 	if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) {
 		/* The provided host was actually invalid so we error out this negotiation */
+		ast_debug(3, "Not applying negotiated SDP stream: failed to resolve remote stream host\n");
 		return -1;
 	}
 
@@ -875,6 +934,7 @@ static int unload_module(void)
 {
 	ast_sip_session_unregister_sdp_handler(&image_sdp_handler, "image");
 	ast_sip_session_unregister_supplement(&t38_supplement);
+	ast_sip_session_unregister_supplement(&t38_bye_supplement);
 
 	return 0;
 }
@@ -901,6 +961,11 @@ static int load_module(void)
 		goto end;
 	}
 
+	if (ast_sip_session_register_supplement(&t38_bye_supplement)) {
+		ast_log(LOG_ERROR, "Unable to register T.38 BYE session supplement\n");
+		goto end;
+	}
+
 	if (ast_sip_session_register_sdp_handler(&image_sdp_handler, "image")) {
 		ast_log(LOG_ERROR, "Unable to register SDP handler for image stream type\n");
 		goto end;
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index 390fd08..3dfaf87 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -2118,6 +2118,8 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
 			SSL_set_accept_state(dtls->ssl);
 		}
 
+		ast_mutex_lock(&dtls->lock);
+
 		dtls_srtp_check_pending(instance, rtp, rtcp);
 
 		BIO_write(dtls->read_bio, buf, len);
@@ -2128,6 +2130,7 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
 			unsigned long error = ERR_get_error();
 			ast_log(LOG_ERROR, "DTLS failure occurred on RTP instance '%p' due to reason '%s', terminating\n",
 				instance, ERR_reason_error_string(error));
+			ast_mutex_unlock(&dtls->lock);
 			return -1;
 		}
 
@@ -2145,6 +2148,8 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
 			dtls_srtp_start_timeout_timer(instance, rtp, rtcp);
 		}
 
+		ast_mutex_unlock(&dtls->lock);
+
 		return res;
 	}
 #endif
@@ -4703,7 +4708,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
 		rtp->f.delivery.tv_sec = 0;
 		rtp->f.delivery.tv_usec = 0;
 		/* Pass the RTP marker bit as bit */
-		rtp->f.subclass.frame_ending = mark;
+		rtp->f.subclass.frame_ending = mark ? 1 : 0;
 	} else if (ast_format_get_type(rtp->f.subclass.format) == AST_MEDIA_TYPE_TEXT) {
 		/* TEXT -- samples is # of samples vs. 1000 */
 		if (!rtp->lastitexttimestamp)
@@ -4780,7 +4785,7 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
 			return;
 		} else {
 			if (rtp->rtcp) {
-				if (rtp->rtcp->schedid > 0) {
+				if (rtp->rtcp->schedid > -1) {
 					if (!ast_sched_del(rtp->sched, rtp->rtcp->schedid)) {
 						/* Successfully cancelled scheduler entry. */
 						ao2_ref(instance, -1);
@@ -4817,6 +4822,9 @@ static int ast_rtp_fd(struct ast_rtp_instance *instance, int rtcp)
 static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct ast_sockaddr *addr)
 {
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+#ifdef HAVE_OPENSSL_SRTP
+	struct dtls_details *dtls;
+#endif
 
 	if (rtp->rtcp) {
 		ast_debug(1, "Setting RTCP address on RTP instance '%p'\n", instance);
@@ -4834,6 +4842,28 @@ static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct
 		rtp_learning_seq_init(&rtp->rtp_source_learn, rtp->seqno);
 	}
 
+#ifdef HAVE_OPENSSL_SRTP
+	/* Trigger pending outbound DTLS packets received before the address was set.  Avoid unnecessary locking
+	 * by checking if we're passive. Without this, we only send the pending packets once a new SSL packet is
+	 * received in __rtp_recvfrom.
+	 */
+	dtls = &rtp->dtls;
+	if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) {
+		ast_mutex_lock(&dtls->lock);
+		dtls_srtp_check_pending(instance, rtp, 0);
+		ast_mutex_unlock(&dtls->lock);
+	}
+
+	if (rtp->rtcp) {
+		dtls = &rtp->rtcp->dtls;
+		if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) {
+			ast_mutex_lock(&dtls->lock);
+			dtls_srtp_check_pending(instance, rtp, 1);
+			ast_mutex_unlock(&dtls->lock);
+		}
+	}
+#endif
+
 	return;
 }
 
@@ -4997,7 +5027,7 @@ static void ast_rtp_stop(struct ast_rtp_instance *instance)
 	}
 #endif
 
-	if (rtp->rtcp && rtp->rtcp->schedid > 0) {
+	if (rtp->rtcp && rtp->rtcp->schedid > -1) {
 		if (!ast_sched_del(rtp->sched, rtp->rtcp->schedid)) {
 			/* successfully cancelled scheduler entry. */
 			ao2_ref(instance, -1);
diff --git a/res/res_sorcery_memory_cache.c b/res/res_sorcery_memory_cache.c
index 33e78d1..99db0ce 100644
--- a/res/res_sorcery_memory_cache.c
+++ b/res/res_sorcery_memory_cache.c
@@ -102,6 +102,20 @@
 			<para>Marks ALL objects in a sorcery memory cache as stale.</para>
 		</description>
 	</manager>
+	<manager name="SorceryMemoryCachePopulate" language="en_US">
+		<synopsis>
+			Expire all objects from a memory cache and populate it with all objects from the backend.
+		</synopsis>
+		<syntax>
+			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+			<parameter name="Cache" required="true">
+				<para>The name of the cache to populate.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Expires all objects from a memory cache and populate it with all objects from the backend.</para>
+		</description>
+	</manager>
  ***/
 
 /*! \brief Structure for storing a memory cache */
@@ -116,12 +130,22 @@ struct sorcery_memory_cache {
 	unsigned int object_lifetime_maximum;
 	/*! \brief The amount of time (in seconds) before an object is marked as stale, 0 if disabled */
 	unsigned int object_lifetime_stale;
-	/** \brief Whether all objects are expired when the object type is reloaded, 0 if disabled */
+	/*! \brief Whether all objects are expired when the object type is reloaded, 0 if disabled */
 	unsigned int expire_on_reload;
+	/*! \brief Whether this is a cache of the entire backend, 0 if disabled */
+	unsigned int full_backend_cache;
 	/*! \brief Heap of cached objects. Oldest object is at the top. */
 	struct ast_heap *object_heap;
 	/*! \brief Scheduler item for expiring oldest object. */
 	int expire_id;
+	/*! \brief scheduler id of stale update task */
+	int stale_update_sched_id;
+	/*! \brief An unreffed pointer to the sorcery instance, accessible only with lock held */
+	const struct ast_sorcery *sorcery;
+	/*! \brief The type of object we are caching */
+	char *object_type;
+	/*! TRUE if trying to stop the oldest object expiration scheduler item. */
+	unsigned int del_expire:1;
 #ifdef TEST_FRAMEWORK
 	/*! \brief Variable used to indicate we should notify a test when we reach empty */
 	unsigned int cache_notify;
@@ -144,6 +168,22 @@ struct sorcery_memory_cached_object {
 	ssize_t __heap_index;
 	/*! \brief scheduler id of stale update task */
 	int stale_update_sched_id;
+	/*! \brief Cached objectset for field and regex retrieval */
+	struct ast_variable *objectset;
+};
+
+/*! \brief Structure used for fields comparison */
+struct sorcery_memory_cache_fields_cmp_params {
+	/*! \brief Pointer to the sorcery structure */
+	const struct ast_sorcery *sorcery;
+	/*! \brief The sorcery memory cache */
+	struct sorcery_memory_cache *cache;
+	/*! \brief Pointer to the fields to check */
+	const struct ast_variable *fields;
+	/*! \brief Regular expression for checking object id */
+	regex_t *regex;
+	/*! \brief Optional container to put object into */
+	struct ao2_container *container;
 };
 
 static void *sorcery_memory_cache_open(const char *data);
@@ -152,6 +192,12 @@ static void sorcery_memory_cache_load(void *data, const struct ast_sorcery *sorc
 static void sorcery_memory_cache_reload(void *data, const struct ast_sorcery *sorcery, const char *type);
 static void *sorcery_memory_cache_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type,
 	const char *id);
+static void *sorcery_memory_cache_retrieve_fields(const struct ast_sorcery *sorcery, void *data, const char *type,
+	const struct ast_variable *fields);
+static void sorcery_memory_cache_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type,
+	struct ao2_container *objects, const struct ast_variable *fields);
+static void sorcery_memory_cache_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type,
+	struct ao2_container *objects, const char *regex);
 static int sorcery_memory_cache_delete(const struct ast_sorcery *sorcery, void *data, void *object);
 static void sorcery_memory_cache_close(void *data);
 
@@ -164,6 +210,9 @@ static struct ast_sorcery_wizard memory_cache_object_wizard = {
 	.load = sorcery_memory_cache_load,
 	.reload = sorcery_memory_cache_reload,
 	.retrieve_id = sorcery_memory_cache_retrieve_id,
+	.retrieve_fields = sorcery_memory_cache_retrieve_fields,
+	.retrieve_multiple = sorcery_memory_cache_retrieve_multiple,
+	.retrieve_regex = sorcery_memory_cache_retrieve_regex,
 	.close = sorcery_memory_cache_close,
 };
 
@@ -182,48 +231,44 @@ static struct ao2_container *caches;
 /*! \brief Scheduler for cache management */
 static struct ast_sched_context *sched;
 
-#define STALE_UPDATE_THREAD_ID 0x5EED1E55
-AST_THREADSTORAGE(stale_update_id_storage);
+#define PASSTHRU_UPDATE_THREAD_ID 0x5EED1E55
+AST_THREADSTORAGE(passthru_update_id_storage);
 
-static int is_stale_update(void)
+static int is_passthru_update(void)
 {
-	uint32_t *stale_update_thread_id;
+	uint32_t *passthru_update_thread_id;
 
-	stale_update_thread_id = ast_threadstorage_get(&stale_update_id_storage,
-		sizeof(*stale_update_thread_id));
-	if (!stale_update_thread_id) {
+	passthru_update_thread_id = ast_threadstorage_get(&passthru_update_id_storage,
+		sizeof(*passthru_update_thread_id));
+	if (!passthru_update_thread_id) {
 		return 0;
 	}
 
-	return *stale_update_thread_id == STALE_UPDATE_THREAD_ID;
+	return *passthru_update_thread_id == PASSTHRU_UPDATE_THREAD_ID;
 }
 
-static void start_stale_update(void)
+static void set_passthru_update(uint32_t value)
 {
-	uint32_t *stale_update_thread_id;
+	uint32_t *passthru_update_thread_id;
 
-	stale_update_thread_id = ast_threadstorage_get(&stale_update_id_storage,
-		sizeof(*stale_update_thread_id));
-	if (!stale_update_thread_id) {
-		ast_log(LOG_ERROR, "Could not set stale update ID for sorcery memory cache thread\n");
+	passthru_update_thread_id = ast_threadstorage_get(&passthru_update_id_storage,
+		sizeof(*passthru_update_thread_id));
+	if (!passthru_update_thread_id) {
+		ast_log(LOG_ERROR, "Could not set passthru update ID for sorcery memory cache thread\n");
 		return;
 	}
 
-	*stale_update_thread_id = STALE_UPDATE_THREAD_ID;
+	*passthru_update_thread_id = value;
 }
 
-static void end_stale_update(void)
+static void start_passthru_update(void)
 {
-	uint32_t *stale_update_thread_id;
-
-	stale_update_thread_id = ast_threadstorage_get(&stale_update_id_storage,
-		sizeof(*stale_update_thread_id));
-	if (!stale_update_thread_id) {
-		ast_log(LOG_ERROR, "Could not set stale update ID for sorcery memory cache thread\n");
-		return;
-	}
+	set_passthru_update(PASSTHRU_UPDATE_THREAD_ID);
+}
 
-	*stale_update_thread_id = 0;
+static void end_passthru_update(void)
+{
+	set_passthru_update(0);
 }
 
 /*!
@@ -241,7 +286,7 @@ static int sorcery_memory_cache_hash(const void *obj, int flags)
 	const char *name = obj;
 	int hash;
 
-	switch (flags & (OBJ_SEARCH_OBJECT | OBJ_SEARCH_KEY | OBJ_SEARCH_PARTIAL_KEY)) {
+	switch (flags & OBJ_SEARCH_MASK) {
 	default:
 	case OBJ_SEARCH_OBJECT:
 		name = cache->name;
@@ -276,7 +321,7 @@ static int sorcery_memory_cache_cmp(void *obj, void *arg, int flags)
 	const char *right_name = arg;
 	int cmp;
 
-	switch (flags & (OBJ_SEARCH_OBJECT | OBJ_SEARCH_KEY | OBJ_SEARCH_PARTIAL_KEY)) {
+	switch (flags & OBJ_SEARCH_MASK) {
 	default:
 	case OBJ_SEARCH_OBJECT:
 		right_name = right->name;
@@ -306,7 +351,7 @@ static int sorcery_memory_cached_object_hash(const void *obj, int flags)
 	const char *name = obj;
 	int hash;
 
-	switch (flags & (OBJ_SEARCH_OBJECT | OBJ_SEARCH_KEY | OBJ_SEARCH_PARTIAL_KEY)) {
+	switch (flags & OBJ_SEARCH_MASK) {
 	default:
 	case OBJ_SEARCH_OBJECT:
 		name = ast_sorcery_object_get_id(cached->object);
@@ -341,7 +386,7 @@ static int sorcery_memory_cached_object_cmp(void *obj, void *arg, int flags)
 	const char *right_name = arg;
 	int cmp;
 
-	switch (flags & (OBJ_SEARCH_OBJECT | OBJ_SEARCH_KEY | OBJ_SEARCH_PARTIAL_KEY)) {
+	switch (flags & OBJ_SEARCH_MASK) {
 	default:
 	case OBJ_SEARCH_OBJECT:
 		right_name = ast_sorcery_object_get_id(right->object);
@@ -367,10 +412,11 @@ static void sorcery_memory_cache_destructor(void *obj)
 	struct sorcery_memory_cache *cache = obj;
 
 	ast_free(cache->name);
-	ao2_cleanup(cache->objects);
 	if (cache->object_heap) {
 		ast_heap_destroy(cache->object_heap);
 	}
+	ao2_cleanup(cache->objects);
+	ast_free(cache->object_type);
 }
 
 /*!
@@ -384,6 +430,7 @@ static void sorcery_memory_cached_object_destructor(void *obj)
 	struct sorcery_memory_cached_object *cached = obj;
 
 	ao2_cleanup(cached->object);
+	ast_variables_destroy(cached->objectset);
 }
 
 static int schedule_cache_expiration(struct sorcery_memory_cache *cache);
@@ -409,8 +456,7 @@ static int remove_from_cache(struct sorcery_memory_cache *cache, const char *id,
 	struct sorcery_memory_cached_object *oldest_object;
 	struct sorcery_memory_cached_object *heap_object;
 
-	hash_object = ao2_find(cache->objects, id,
-		OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NOLOCK);
+	hash_object = ao2_find(cache->objects, id, OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NOLOCK);
 	if (!hash_object) {
 		return -1;
 	}
@@ -442,11 +488,25 @@ static int expire_objects_from_cache(const void *data)
 	struct sorcery_memory_cache *cache = (struct sorcery_memory_cache *)data;
 	struct sorcery_memory_cached_object *cached;
 
-	ao2_wrlock(cache->objects);
+	/*
+	 * We need to do deadlock avoidance between a non-scheduler thread
+	 * blocking when trying to delete the scheduled entry for this
+	 * callback because the scheduler thread is running this callback
+	 * and this callback waiting for the cache->objects container lock
+	 * that the blocked non-scheduler thread already holds.
+	 */
+	while (ao2_trywrlock(cache->objects)) {
+		if (cache->del_expire) {
+			cache->expire_id = -1;
+			ao2_ref(cache, -1);
+			return 0;
+		}
+		sched_yield();
+	}
 
 	cache->expire_id = -1;
 
-	/* This is an optimization for objects which have been cached close to eachother */
+	/* This is an optimization for objects which have been cached close to each other */
 	while ((cached = ast_heap_peek(cache->object_heap, 1))) {
 		int expiration;
 
@@ -481,12 +541,15 @@ static int expire_objects_from_cache(const void *data)
  */
 static void remove_all_from_cache(struct sorcery_memory_cache *cache)
 {
-	while (ast_heap_pop(cache->object_heap));
+	while (ast_heap_pop(cache->object_heap)) {
+	}
 
 	ao2_callback(cache->objects, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE,
 		NULL, NULL);
 
+	cache->del_expire = 1;
 	AST_SCHED_DEL_UNREF(sched, cache->expire_id, ao2_ref(cache, -1));
+	cache->del_expire = 0;
 }
 
 /*!
@@ -579,18 +642,9 @@ static int schedule_cache_expiration(struct sorcery_memory_cache *cache)
 		return 0;
 	}
 
-	if (cache->expire_id != -1) {
-		/* If we can't unschedule this expiration then it is currently attempting to run,
-		 * so let it run - it just means that it'll be the one scheduling instead of us.
-		 */
-		if (ast_sched_del(sched, cache->expire_id)) {
-			return 0;
-		}
-
-		/* Since it successfully cancelled we need to drop the ref to the cache it had */
-		ao2_ref(cache, -1);
-		cache->expire_id = -1;
-	}
+	cache->del_expire = 1;
+	AST_SCHED_DEL_UNREF(sched, cache->expire_id, ao2_ref(cache, -1));
+	cache->del_expire = 0;
 
 	cached = ast_heap_peek(cache->object_heap, 1);
 	if (!cached) {
@@ -662,10 +716,17 @@ static int remove_oldest_from_cache(struct sorcery_memory_cache *cache)
 static int add_to_cache(struct sorcery_memory_cache *cache,
 		struct sorcery_memory_cached_object *cached_object)
 {
+	struct sorcery_memory_cached_object *front;
+
 	if (!ao2_link_flags(cache->objects, cached_object, OBJ_NOLOCK)) {
 		return -1;
 	}
 
+	if (cache->full_backend_cache && (front = ast_heap_peek(cache->object_heap, 1))) {
+		/* For a full backend cache all objects share the same lifetime */
+		cached_object->created = front->created;
+	}
+
 	if (ast_heap_push(cache->object_heap, cached_object)) {
 		ao2_find(cache->objects, cached_object,
 			OBJ_SEARCH_OBJECT | OBJ_UNLINK | OBJ_NODATA | OBJ_NOLOCK);
@@ -681,6 +742,45 @@ static int add_to_cache(struct sorcery_memory_cache *cache,
 
 /*!
  * \internal
+ * \brief Allocate a cached object for caching an object
+ *
+ * \param sorcery The sorcery instance
+ * \param cache The sorcery memory cache
+ * \param object The object to cache
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+static struct sorcery_memory_cached_object *sorcery_memory_cached_object_alloc(const struct ast_sorcery *sorcery,
+	const struct sorcery_memory_cache *cache, void *object)
+{
+	struct sorcery_memory_cached_object *cached;
+
+	cached = ao2_alloc(sizeof(*cached), sorcery_memory_cached_object_destructor);
+	if (!cached) {
+		return NULL;
+	}
+
+	cached->object = ao2_bump(object);
+	cached->created = ast_tvnow();
+	cached->stale_update_sched_id = -1;
+
+	if (cache->full_backend_cache) {
+		/* A cached objectset allows us to easily perform all retrieval operations in a
+		 * minimal of time.
+		 */
+		cached->objectset = ast_sorcery_objectset_create(sorcery, object);
+		if (!cached->objectset) {
+			ao2_ref(cached, -1);
+			return NULL;
+		}
+	}
+
+	return cached;
+}
+
+/*!
+ * \internal
  * \brief Callback function to cache an object in a memory cache
  *
  * \param sorcery The sorcery instance
@@ -695,13 +795,10 @@ static int sorcery_memory_cache_create(const struct ast_sorcery *sorcery, void *
 	struct sorcery_memory_cache *cache = data;
 	struct sorcery_memory_cached_object *cached;
 
-	cached = ao2_alloc(sizeof(*cached), sorcery_memory_cached_object_destructor);
+	cached = sorcery_memory_cached_object_alloc(sorcery, cache, object);
 	if (!cached) {
 		return -1;
 	}
-	cached->object = ao2_bump(object);
-	cached->created = ast_tvnow();
-	cached->stale_update_sched_id = -1;
 
 	/* As there is no guarantee that this won't be called by multiple threads wanting to cache
 	 * the same object we remove any old ones, which turns this into a create/update function
@@ -715,8 +812,8 @@ static int sorcery_memory_cache_create(const struct ast_sorcery *sorcery, void *
 		if (remove_oldest_from_cache(cache)) {
 			ast_log(LOG_ERROR, "Unable to make room in cache for sorcery object '%s'.\n",
 				ast_sorcery_object_get_id(object));
-			ao2_ref(cached, -1);
 			ao2_unlock(cache->objects);
+			ao2_ref(cached, -1);
 			return -1;
 		}
 		ast_assert(ao2_container_count(cache->objects) != cache->maximum_objects);
@@ -724,8 +821,8 @@ static int sorcery_memory_cache_create(const struct ast_sorcery *sorcery, void *
 	if (add_to_cache(cache, cached)) {
 		ast_log(LOG_ERROR, "Unable to add object '%s' to the cache\n",
 			ast_sorcery_object_get_id(object));
-		ao2_ref(cached, -1);
 		ao2_unlock(cache->objects);
+		ao2_ref(cached, -1);
 		return -1;
 	}
 	ao2_unlock(cache->objects);
@@ -734,6 +831,116 @@ static int sorcery_memory_cache_create(const struct ast_sorcery *sorcery, void *
 	return 0;
 }
 
+/*!
+ * \internal
+ * \brief AO2 callback function for adding an object to a memory cache
+ *
+ * \param obj The cached object
+ * \param arg The sorcery instance
+ * \param data The cache itself
+ * \param flags Unused flags
+ */
+static int object_add_to_cache_callback(void *obj, void *arg, void *data, int flags)
+{
+	struct sorcery_memory_cache *cache = data;
+	struct sorcery_memory_cached_object *cached;
+
+	cached = sorcery_memory_cached_object_alloc(arg, cache, obj);
+	if (!cached) {
+		return CMP_STOP;
+	}
+
+	add_to_cache(cache, cached);
+	ao2_ref(cached, -1);
+
+	return 0;
+}
+
+struct stale_cache_update_task_data {
+	struct ast_sorcery *sorcery;
+	struct sorcery_memory_cache *cache;
+	char *type;
+};
+
+static void stale_cache_update_task_data_destructor(void *obj)
+{
+	struct stale_cache_update_task_data *task_data = obj;
+
+	ao2_cleanup(task_data->cache);
+	ast_sorcery_unref(task_data->sorcery);
+	ast_free(task_data->type);
+}
+
+static struct stale_cache_update_task_data *stale_cache_update_task_data_alloc(struct ast_sorcery *sorcery,
+		struct sorcery_memory_cache *cache, const char *type)
+{
+	struct stale_cache_update_task_data *task_data;
+
+	task_data = ao2_alloc_options(sizeof(*task_data), stale_cache_update_task_data_destructor,
+		AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!task_data) {
+		return NULL;
+	}
+
+	task_data->sorcery = ao2_bump(sorcery);
+	task_data->cache = ao2_bump(cache);
+	task_data->type = ast_strdup(type);
+	if (!task_data->type) {
+		ao2_ref(task_data, -1);
+		return NULL;
+	}
+
+	return task_data;
+}
+
+static int stale_cache_update(const void *data)
+{
+	struct stale_cache_update_task_data *task_data = (struct stale_cache_update_task_data *) data;
+	struct ao2_container *backend_objects;
+
+	start_passthru_update();
+	backend_objects = ast_sorcery_retrieve_by_fields(task_data->sorcery, task_data->type,
+		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	end_passthru_update();
+
+	if (!backend_objects) {
+		task_data->cache->stale_update_sched_id = -1;
+		ao2_ref(task_data, -1);
+		return 0;
+	}
+
+	if (task_data->cache->maximum_objects && ao2_container_count(backend_objects) >= task_data->cache->maximum_objects) {
+		ast_log(LOG_ERROR, "The backend contains %d objects while the sorcery memory cache '%s' is explicitly configured to only allow %d\n",
+			ao2_container_count(backend_objects), task_data->cache->name, task_data->cache->maximum_objects);
+		task_data->cache->stale_update_sched_id = -1;
+		ao2_ref(task_data, -1);
+		return 0;
+	}
+
+	ao2_wrlock(task_data->cache->objects);
+	remove_all_from_cache(task_data->cache);
+	ao2_callback_data(backend_objects, OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE, object_add_to_cache_callback,
+		task_data->sorcery, task_data->cache);
+
+	/* If the number of cached objects does not match the number of backend objects we encountered a memory allocation
+	 * failure and the cache is incomplete, so drop everything and fall back to querying the backend directly
+	 * as it may be able to provide what is wanted.
+	 */
+	if (ao2_container_count(task_data->cache->objects) != ao2_container_count(backend_objects)) {
+		ast_log(LOG_WARNING, "The backend contains %d objects while only %d could be added to sorcery memory cache '%s'\n",
+			ao2_container_count(backend_objects), ao2_container_count(task_data->cache->objects), task_data->cache->name);
+		remove_all_from_cache(task_data->cache);
+	}
+
+	ao2_unlock(task_data->cache->objects);
+	ao2_ref(backend_objects, -1);
+
+	task_data->cache->stale_update_sched_id = -1;
+	ao2_ref(task_data, -1);
+
+	return 0;
+}
+
 struct stale_update_task_data {
 	struct ast_sorcery *sorcery;
 	struct sorcery_memory_cache *cache;
@@ -772,7 +979,7 @@ static int stale_item_update(const void *data)
 	struct stale_update_task_data *task_data = (struct stale_update_task_data *) data;
 	void *object;
 
-	start_stale_update();
+	start_passthru_update();
 
 	object = ast_sorcery_retrieve_by_id(task_data->sorcery,
 		ast_sorcery_object_get_type(task_data->object),
@@ -796,13 +1003,196 @@ static int stale_item_update(const void *data)
 		ast_sorcery_object_get_id(task_data->object));
 
 	ao2_ref(task_data, -1);
-	end_stale_update();
+	end_passthru_update();
 
 	return 0;
 }
 
 /*!
  * \internal
+ * \brief Populate the cache with all objects from the backend
+ *
+ * \pre cache->objects is write-locked
+ *
+ * \param sorcery The sorcery instance
+ * \param type The type of object
+ * \param cache The sorcery memory cache
+ */
+static void memory_cache_populate(const struct ast_sorcery *sorcery, const char *type, struct sorcery_memory_cache *cache)
+{
+	struct ao2_container *backend_objects;
+
+	start_passthru_update();
+	backend_objects = ast_sorcery_retrieve_by_fields(sorcery, type, AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	end_passthru_update();
+
+	if (!backend_objects) {
+		/* This will occur in off-nominal memory allocation failure scenarios */
+		return;
+	}
+
+	if (cache->maximum_objects && ao2_container_count(backend_objects) >= cache->maximum_objects) {
+		ast_log(LOG_ERROR, "The backend contains %d objects while the sorcery memory cache '%s' is explicitly configured to only allow %d\n",
+			ao2_container_count(backend_objects), cache->name, cache->maximum_objects);
+		return;
+	}
+
+	ao2_callback_data(backend_objects, OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE, object_add_to_cache_callback,
+		(struct ast_sorcery*)sorcery, cache);
+
+	/* If the number of cached objects does not match the number of backend objects we encountered a memory allocation
+	 * failure and the cache is incomplete, so drop everything and fall back to querying the backend directly
+	 * as it may be able to provide what is wanted.
+	 */
+	if (ao2_container_count(cache->objects) != ao2_container_count(backend_objects)) {
+		ast_log(LOG_WARNING, "The backend contains %d objects while only %d could be added to sorcery memory cache '%s'\n",
+			ao2_container_count(backend_objects), ao2_container_count(cache->objects), cache->name);
+		remove_all_from_cache(cache);
+	}
+
+	ao2_ref(backend_objects, -1);
+}
+
+/*!
+ * \internal
+ * \brief Determine if a full backend cache update is needed and do it
+ *
+ * \param sorcery The sorcery instance
+ * \param type The type of object
+ * \param cache The sorcery memory cache
+ */
+static void memory_cache_full_update(const struct ast_sorcery *sorcery, const char *type, struct sorcery_memory_cache *cache)
+{
+	if (!cache->full_backend_cache) {
+		return;
+	}
+
+	ao2_wrlock(cache->objects);
+	if (!ao2_container_count(cache->objects)) {
+		memory_cache_populate(sorcery, type, cache);
+	}
+	ao2_unlock(cache->objects);
+}
+
+/*!
+ * \internal
+ * \brief Queue a full cache update
+ *
+ * \param sorcery The sorcery instance
+ * \param cache The sorcery memory cache
+ * \param type The type of object
+ */
+static void memory_cache_stale_update_full(const struct ast_sorcery *sorcery, struct sorcery_memory_cache *cache,
+	const char *type)
+{
+	ao2_wrlock(cache->objects);
+	if (cache->stale_update_sched_id == -1) {
+		struct stale_cache_update_task_data *task_data;
+
+		task_data = stale_cache_update_task_data_alloc((struct ast_sorcery *) sorcery,
+			cache, type);
+		if (task_data) {
+			cache->stale_update_sched_id = ast_sched_add(sched, 1,
+				stale_cache_update, task_data);
+		}
+		if (cache->stale_update_sched_id < 0) {
+			ao2_cleanup(task_data);
+		}
+	}
+	ao2_unlock(cache->objects);
+}
+
+/*!
+ * \internal
+ * \brief Queue a stale object update
+ *
+ * \param sorcery The sorcery instance
+ * \param cache The sorcery memory cache
+ * \param cached The cached object
+ */
+static void memory_cache_stale_update_object(const struct ast_sorcery *sorcery, struct sorcery_memory_cache *cache,
+	struct sorcery_memory_cached_object *cached)
+{
+	ao2_lock(cached);
+	if (cached->stale_update_sched_id == -1) {
+		struct stale_update_task_data *task_data;
+
+		task_data = stale_update_task_data_alloc((struct ast_sorcery *) sorcery,
+			cache, ast_sorcery_object_get_type(cached->object), cached->object);
+		if (task_data) {
+			ast_debug(1, "Cached sorcery object type '%s' ID '%s' is stale. Refreshing\n",
+				ast_sorcery_object_get_type(cached->object), ast_sorcery_object_get_id(cached->object));
+			cached->stale_update_sched_id = ast_sched_add(sched, 1,
+				stale_item_update, task_data);
+		}
+		if (cached->stale_update_sched_id < 0) {
+			ao2_cleanup(task_data);
+			ast_log(LOG_ERROR, "Unable to update stale cached object type '%s', ID '%s'.\n",
+				ast_sorcery_object_get_type(cached->object), ast_sorcery_object_get_id(cached->object));
+		}
+	}
+	ao2_unlock(cached);
+}
+
+/*!
+ * \internal
+ * \brief Check whether an object (or cache) is stale and queue an update
+ *
+ * \param sorcery The sorcery instance
+ * \param cache The sorcery memory cache
+ * \param cached The cached object
+ */
+static void memory_cache_stale_check_object(const struct ast_sorcery *sorcery, struct sorcery_memory_cache *cache,
+	struct sorcery_memory_cached_object *cached)
+{
+	struct timeval elapsed;
+
+	if (!cache->object_lifetime_stale) {
+		return;
+	}
+
+	/* For a full cache as every object has the same expiration/staleness we can do the same check */
+	elapsed = ast_tvsub(ast_tvnow(), cached->created);
+
+	if (elapsed.tv_sec < cache->object_lifetime_stale) {
+		return;
+	}
+
+	if (cache->full_backend_cache) {
+		memory_cache_stale_update_full(sorcery, cache, ast_sorcery_object_get_type(cached->object));
+	} else {
+		memory_cache_stale_update_object(sorcery, cache, cached);
+	}
+
+}
+
+/*!
+ * \internal
+ * \brief Check whether the entire cache is stale or not and queue an update
+ *
+ * \param sorcery The sorcery instance
+ * \param cache The sorcery memory cache
+ *
+ * \note Unlike \ref memory_cache_stale_check this does not require  an explicit object
+ */
+static void memory_cache_stale_check(const struct ast_sorcery *sorcery, struct sorcery_memory_cache *cache)
+{
+	struct sorcery_memory_cached_object *cached;
+
+	ao2_rdlock(cache->objects);
+	cached = ao2_bump(ast_heap_peek(cache->object_heap, 1));
+	ao2_unlock(cache->objects);
+
+	if (!cached) {
+		return;
+	}
+
+	memory_cache_stale_check_object(sorcery, cache, cached);
+	ao2_ref(cached, -1);
+}
+
+/*!
+ * \internal
  * \brief Callback function to retrieve an object from a memory cache
  *
  * \param sorcery The sorcery instance
@@ -819,10 +1209,12 @@ static void *sorcery_memory_cache_retrieve_id(const struct ast_sorcery *sorcery,
 	struct sorcery_memory_cached_object *cached;
 	void *object;
 
-	if (is_stale_update()) {
+	if (is_passthru_update()) {
 		return NULL;
 	}
 
+	memory_cache_full_update(sorcery, type, cache);
+
 	cached = ao2_find(cache->objects, id, OBJ_SEARCH_KEY);
 	if (!cached) {
 		return NULL;
@@ -830,29 +1222,7 @@ static void *sorcery_memory_cache_retrieve_id(const struct ast_sorcery *sorcery,
 
 	ast_assert(!strcmp(ast_sorcery_object_get_id(cached->object), id));
 
-	if (cache->object_lifetime_stale) {
-		struct timeval elapsed;
-
-		elapsed = ast_tvsub(ast_tvnow(), cached->created);
-		if (elapsed.tv_sec > cache->object_lifetime_stale) {
-			ao2_lock(cached);
-			if (cached->stale_update_sched_id == -1) {
-				struct stale_update_task_data *task_data;
-
-				task_data = stale_update_task_data_alloc((struct ast_sorcery *)sorcery, cache,
-					type, cached->object);
-				if (task_data) {
-					ast_debug(1, "Cached sorcery object type '%s' ID '%s' is stale. Refreshing\n",
-						type, id);
-					cached->stale_update_sched_id = ast_sched_add(sched, 1, stale_item_update, task_data);
-				} else {
-					ast_log(LOG_ERROR, "Unable to update stale cached object type '%s', ID '%s'.\n",
-						type, id);
-				}
-			}
-			ao2_unlock(cached);
-		}
-	}
+	memory_cache_stale_check_object(sorcery, cache, cached);
 
 	object = ao2_bump(cached->object);
 	ao2_ref(cached, -1);
@@ -862,6 +1232,150 @@ static void *sorcery_memory_cache_retrieve_id(const struct ast_sorcery *sorcery,
 
 /*!
  * \internal
+ * \brief AO2 callback function for comparing a retrieval request and finding applicable objects
+ *
+ * \param obj The cached object
+ * \param arg The comparison parameters
+ * \param flags Unused flags
+ */
+static int sorcery_memory_cache_fields_cmp(void *obj, void *arg, int flags)
+{
+	struct sorcery_memory_cached_object *cached = obj;
+	const struct sorcery_memory_cache_fields_cmp_params *params = arg;
+	RAII_VAR(struct ast_variable *, diff, NULL, ast_variables_destroy);
+
+	if (params->regex) {
+		/* If a regular expression has been provided see if it matches, otherwise move on */
+		if (!regexec(params->regex, ast_sorcery_object_get_id(cached->object), 0, NULL, 0)) {
+			ao2_link(params->container, cached->object);
+		}
+		return 0;
+	} else if (params->fields &&
+	     (ast_sorcery_changeset_create(cached->objectset, params->fields, &diff) ||
+	     diff)) {
+		/* If we can't turn the object into an object set OR if differences exist between the fields
+		 * passed in and what are present on the object they are not a match.
+		 */
+		return 0;
+	}
+
+	if (params->container) {
+		ao2_link(params->container, cached->object);
+
+		/* As multiple objects are being returned keep going */
+		return 0;
+	} else {
+		/* Immediately stop and return, we only want a single object */
+		return CMP_MATCH | CMP_STOP;
+	}
+}
+
+/*!
+ * \internal
+ * \brief Callback function to retrieve a single object based on fields
+ *
+ * \param sorcery The sorcery instance
+ * \param data The sorcery memory cache
+ * \param type The type of the object to retrieve
+ * \param fields Any explicit fields to search for
+ */
+static void *sorcery_memory_cache_retrieve_fields(const struct ast_sorcery *sorcery, void *data, const char *type,
+	const struct ast_variable *fields)
+{
+	struct sorcery_memory_cache *cache = data;
+	struct sorcery_memory_cache_fields_cmp_params params = {
+		.sorcery = sorcery,
+		.cache = cache,
+		.fields = fields,
+	};
+	struct sorcery_memory_cached_object *cached;
+	void *object = NULL;
+
+	if (is_passthru_update() || !cache->full_backend_cache || !fields) {
+		return NULL;
+	}
+
+	cached = ao2_callback(cache->objects, 0, sorcery_memory_cache_fields_cmp, &params);
+
+	if (cached) {
+		memory_cache_stale_check_object(sorcery, cache, cached);
+		object = ao2_bump(cached->object);
+		ao2_ref(cached, -1);
+	}
+
+	return object;
+}
+
+/*!
+ * \internal
+ * \brief Callback function to retrieve multiple objects from a memory cache
+ *
+ * \param sorcery The sorcery instance
+ * \param data The sorcery memory cache
+ * \param type The type of the object to retrieve
+ * \param objects Container to place the objects into
+ * \param fields Any explicit fields to search for
+ */
+static void sorcery_memory_cache_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type,
+	struct ao2_container *objects, const struct ast_variable *fields)
+{
+	struct sorcery_memory_cache *cache = data;
+	struct sorcery_memory_cache_fields_cmp_params params = {
+		.sorcery = sorcery,
+		.cache = cache,
+		.fields = fields,
+		.container = objects,
+	};
+
+	if (is_passthru_update() || !cache->full_backend_cache) {
+		return;
+	}
+
+	memory_cache_full_update(sorcery, type, cache);
+	ao2_callback(cache->objects, 0, sorcery_memory_cache_fields_cmp, &params);
+
+	if (ao2_container_count(objects)) {
+		memory_cache_stale_check(sorcery, cache);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Callback function to retrieve multiple objects using a regex on the object id
+ *
+ * \param sorcery The sorcery instance
+ * \param data The sorcery memory cache
+ * \param type The type of the object to retrieve
+ * \param objects Container to place the objects into
+ * \param regex Regular expression to apply to the object id
+ */
+static void sorcery_memory_cache_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type,
+	struct ao2_container *objects, const char *regex)
+{
+	struct sorcery_memory_cache *cache = data;
+	regex_t expression;
+	struct sorcery_memory_cache_fields_cmp_params params = {
+		.sorcery = sorcery,
+		.cache = cache,
+		.container = objects,
+		.regex = &expression,
+	};
+
+	if (is_passthru_update() || !cache->full_backend_cache || regcomp(&expression, regex, REG_EXTENDED | REG_NOSUB)) {
+		return;
+	}
+
+	memory_cache_full_update(sorcery, type, cache);
+	ao2_callback(cache->objects, 0, sorcery_memory_cache_fields_cmp, &params);
+	regfree(&expression);
+
+	if (ao2_container_count(objects)) {
+		memory_cache_stale_check(sorcery, cache);
+	}
+}
+
+/*!
+ * \internal
  * \brief Callback function to finish configuring the memory cache
  *
  * \param data The sorcery memory cache
@@ -880,6 +1394,11 @@ static void sorcery_memory_cache_load(void *data, const struct ast_sorcery *sorc
 	ao2_link(caches, cache);
 	ast_debug(1, "Memory cache '%s' associated with sorcery instance '%p' of module '%s' with object type '%s'\n",
 		cache->name, sorcery, ast_sorcery_get_module(sorcery), type);
+
+	if (cache->full_backend_cache) {
+		cache->sorcery = sorcery;
+		cache->object_type = ast_strdup(type);
+	}
 }
 
 /*!
@@ -948,6 +1467,7 @@ static void *sorcery_memory_cache_open(const char *data)
 	}
 
 	cache->expire_id = -1;
+	cache->stale_update_sched_id = -1;
 
 	/* If no configuration options have been provided this memory cache will operate in a default
 	 * configuration.
@@ -982,6 +1502,8 @@ static void *sorcery_memory_cache_open(const char *data)
 			}
 		} else if (!strcasecmp(name, "expire_on_reload")) {
 			cache->expire_on_reload = ast_true(value);
+		} else if (!strcasecmp(name, "full_backend_cache")) {
+			cache->full_backend_cache = ast_true(value);
 		} else {
 			ast_log(LOG_ERROR, "Unsupported option '%s' used for memory cache\n", name);
 			return NULL;
@@ -1061,9 +1583,13 @@ static void sorcery_memory_cache_close(void *data)
 		 * a prolonged period of time.
 		 */
 		ao2_wrlock(cache->objects);
-		ao2_callback(cache->objects, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE,
-			NULL, NULL);
-		AST_SCHED_DEL_UNREF(sched, cache->expire_id, ao2_ref(cache, -1));
+		remove_all_from_cache(cache);
+		ao2_unlock(cache->objects);
+	}
+
+	if (cache->full_backend_cache) {
+		ao2_wrlock(cache->objects);
+		cache->sorcery = NULL;
 		ao2_unlock(cache->objects);
 	}
 
@@ -1394,11 +1920,72 @@ static char *sorcery_memory_cache_stale(struct ast_cli_entry *e, int cmd, struct
 	return CLI_SUCCESS;
 }
 
+/*!
+ * \internal
+ * \brief CLI command implementation for 'sorcery memory cache populate'
+ */
+static char *sorcery_memory_cache_populate(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct sorcery_memory_cache *cache;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "sorcery memory cache populate";
+		e->usage =
+		    "Usage: sorcery memory cache populate <cache name>\n"
+		    "       Expire all objects in the cache and populate it with ALL objects from backend.\n";
+		return NULL;
+	case CLI_GENERATE:
+		if (a->pos == 4) {
+			return sorcery_memory_cache_complete_name(a->word, a->n);
+		} else {
+			return NULL;
+		}
+	}
+
+	if (a->argc > 5) {
+		return CLI_SHOWUSAGE;
+	}
+
+	cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
+	if (!cache) {
+		ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
+		return CLI_FAILURE;
+	}
+
+	if (!cache->full_backend_cache) {
+		ast_cli(a->fd, "Specified sorcery memory cache '%s' does not have full backend caching enabled\n", a->argv[4]);
+		ao2_ref(cache, -1);
+		return CLI_FAILURE;
+	}
+
+	ao2_wrlock(cache->objects);
+	if (!cache->sorcery) {
+		ast_cli(a->fd, "Specified sorcery memory cache '%s' is no longer active\n", a->argv[4]);
+		ao2_unlock(cache->objects);
+		ao2_ref(cache, -1);
+		return CLI_FAILURE;
+	}
+
+	remove_all_from_cache(cache);
+	memory_cache_populate(cache->sorcery, cache->object_type, cache);
+
+	ast_cli(a->fd, "Specified sorcery memory cache '%s' has been populated with '%d' objects from the backend\n",
+		a->argv[4], ao2_container_count(cache->objects));
+
+	ao2_unlock(cache->objects);
+
+	ao2_ref(cache, -1);
+
+	return CLI_SUCCESS;
+}
+
 static struct ast_cli_entry cli_memory_cache[] = {
 	AST_CLI_DEFINE(sorcery_memory_cache_show, "Show sorcery memory cache information"),
 	AST_CLI_DEFINE(sorcery_memory_cache_dump, "Dump all objects within a sorcery memory cache"),
 	AST_CLI_DEFINE(sorcery_memory_cache_expire, "Expire a specific object or ALL objects within a sorcery memory cache"),
 	AST_CLI_DEFINE(sorcery_memory_cache_stale, "Mark a specific object or ALL objects as stale within a sorcery memory cache"),
+	AST_CLI_DEFINE(sorcery_memory_cache_populate, "Clear and populate the sorcery memory cache with objects from the backend"),
 };
 
 /*!
@@ -1543,6 +2130,52 @@ static int sorcery_memory_cache_ami_stale(struct mansession *s, const struct mes
 	return 0;
 }
 
+/*!
+ * \internal
+ * \brief AMI command implementation for 'SorceryMemoryCachePopulate'
+ */
+static int sorcery_memory_cache_ami_populate(struct mansession *s, const struct message *m)
+{
+	const char *cache_name = astman_get_header(m, "Cache");
+	struct sorcery_memory_cache *cache;
+
+	if (ast_strlen_zero(cache_name)) {
+		astman_send_error(s, m, "SorceryMemoryCachePopulate requires that a cache name be provided.\n");
+		return 0;
+	}
+
+	cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
+	if (!cache) {
+		astman_send_error(s, m, "The provided cache does not exist\n");
+		return 0;
+	}
+
+	if (!cache->full_backend_cache) {
+		astman_send_error(s, m, "The provided cache does not have full backend caching enabled\n");
+		ao2_ref(cache, -1);
+		return 0;
+	}
+
+	ao2_wrlock(cache->objects);
+	if (!cache->sorcery) {
+		astman_send_error(s, m, "The provided cache is no longer active\n");
+		ao2_unlock(cache->objects);
+		ao2_ref(cache, -1);
+		return 0;
+	}
+
+	remove_all_from_cache(cache);
+	memory_cache_populate(cache->sorcery, cache->object_type, cache);
+
+	ao2_unlock(cache->objects);
+
+	ao2_ref(cache, -1);
+
+	astman_send_ack(s, m, "Cache has been expired and populated\n");
+
+	return 0;
+}
+
 #ifdef TEST_FRAMEWORK
 
 /*! \brief Dummy sorcery object */
@@ -2308,11 +2941,51 @@ static void *mock_retrieve_id(const struct ast_sorcery *sorcery, void *data,
 }
 
 /*!
+ * \brief Callback for retrieving multiple sorcery objects
+ *
+ * The mock wizard uses the \ref real_backend_data in order to construct
+ * objects. If the backend data is "nonexisent" then no object is returned.
+ * Otherwise, the number of objects matching the exists value will be returned.
+ *
+ * \param sorcery The sorcery instance
+ * \param data Unused
+ * \param type The object type. Will always be "test".
+ * \param objects Container to place objects into.
+ * \param fields Fields to search for.
+ */
+static void mock_retrieve_multiple(const struct ast_sorcery *sorcery, void *data,
+		const char *type, struct ao2_container *objects, const struct ast_variable *fields)
+{
+	int i;
+
+	if (fields) {
+		return;
+	}
+
+	for (i = 0; i < real_backend_data->exists; ++i) {
+		char uuid[AST_UUID_STR_LEN];
+		struct test_data *b_data;
+
+		b_data = ast_sorcery_alloc(sorcery, type, ast_uuid_generate_str(uuid, sizeof(uuid)));
+		if (!b_data) {
+			continue;
+		}
+
+		b_data->salt = real_backend_data->salt;
+		b_data->pepper = real_backend_data->pepper;
+
+		ao2_link(objects, b_data);
+		ao2_ref(b_data, -1);
+	}
+}
+
+/*!
  * \brief A mock sorcery wizard used for the stale test
  */
 static struct ast_sorcery_wizard mock_wizard = {
 	.name = "mock",
 	.retrieve_id = mock_retrieve_id,
+	.retrieve_multiple = mock_retrieve_multiple,
 };
 
 /*!
@@ -2476,26 +3149,263 @@ cleanup:
 	return res;
 }
 
-#endif
+AST_TEST_DEFINE(full_backend_cache_expiration)
+{
+	int res = AST_TEST_FAIL;
+	struct ast_sorcery *sorcery = NULL;
+	struct backend_data initial = {
+		.salt = 0,
+		.pepper = 0,
+		.exists = 4,
+	};
+	struct ao2_container *objects;
+	ast_mutex_t lock;
+	ast_cond_t cond;
+	struct timeval start;
+	struct timespec end;
 
-static int unload_module(void)
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "full_backend_cache_expiration";
+		info->category = "/res/res_sorcery_memory_cache/";
+		info->summary = "Ensure that the full backend cache actually caches the backend";
+		info->description = "This test performs the following:\n"
+			"\t* Create a sorcery instance with two wizards"
+			"\t\t* The first is a memory cache that expires objects after 3 seconds and does full backend caching\n"
+			"\t\t* The second is a mock of a back-end\n"
+			"\t* Populates the cache by requesting all objects which returns 4.\n"
+			"\t* Updates the backend to contain a different number of objects, 8.\n"
+			"\t* Requests all objects and confirms the number returned is only 4.\n"
+			"\t* Wait for cached objects to expire.\n"
+			"\t* Requests all objects and confirms the number returned is 8.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	ast_sorcery_wizard_register(&mock_wizard);
+
+	sorcery = ast_sorcery_open();
+	if (!sorcery) {
+		ast_test_status_update(test, "Failed to create sorcery instance\n");
+		goto cleanup;
+	}
+
+	ast_sorcery_apply_wizard_mapping(sorcery, "test", "memory_cache",
+			"object_lifetime_maximum=3,full_backend_cache=yes", 1);
+	ast_sorcery_apply_wizard_mapping(sorcery, "test", "mock", NULL, 0);
+	ast_sorcery_internal_object_register(sorcery, "test", test_data_alloc, NULL, NULL);
+	ast_sorcery_object_field_register_nodoc(sorcery, "test", "salt", "0", OPT_UINT_T, 0, FLDSET(struct test_data, salt));
+	ast_sorcery_object_field_register_nodoc(sorcery, "test", "pepper", "0", OPT_UINT_T, 0, FLDSET(struct test_data, pepper));
+
+	/* Prepopulate the cache */
+	real_backend_data = &initial;
+
+	/* Get all current objects in the backend */
+	objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	if (!objects) {
+		ast_test_status_update(test, "Unable to retrieve all objects in backend and populate cache\n");
+		goto cleanup;
+	}
+	ao2_ref(objects, -1);
+
+	/* Update the backend to have a different number of objects */
+	initial.exists = 8;
+
+	/* Get all current objects in the backend */
+	objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	if (!objects) {
+		ast_test_status_update(test, "Unable to retrieve all objects in backend and populate cache\n");
+		goto cleanup;
+	}
+
+	if (ao2_container_count(objects) == initial.exists) {
+		ast_test_status_update(test, "Number of objects returned is of the current backend and not the cache\n");
+		ao2_ref(objects, -1);
+		goto cleanup;
+	}
+
+	ao2_ref(objects, -1);
+
+	ast_mutex_init(&lock);
+	ast_cond_init(&cond, NULL);
+
+	start = ast_tvnow();
+	end.tv_sec = start.tv_sec + 5;
+	end.tv_nsec = start.tv_usec * 1000;
+
+	ast_mutex_lock(&lock);
+	while (ast_cond_timedwait(&cond, &lock, &end) != ETIMEDOUT) {
+	}
+	ast_mutex_unlock(&lock);
+
+	ast_mutex_destroy(&lock);
+	ast_cond_destroy(&cond);
+
+	/* Get all current objects in the backend */
+	objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	if (!objects) {
+		ast_test_status_update(test, "Unable to retrieve all objects in backend and populate cache\n");
+		goto cleanup;
+	}
+
+	if (ao2_container_count(objects) != initial.exists) {
+		ast_test_status_update(test, "Number of objects returned is NOT of the current backend when it should be\n");
+		ao2_ref(objects, -1);
+		goto cleanup;
+	}
+
+	ao2_ref(objects, -1);
+
+	res = AST_TEST_PASS;
+
+cleanup:
+	if (sorcery) {
+		ast_sorcery_unref(sorcery);
+	}
+	ast_sorcery_wizard_unregister(&mock_wizard);
+	return res;
+}
+
+AST_TEST_DEFINE(full_backend_cache_stale)
 {
-	if (sched) {
-		ast_sched_context_destroy(sched);
-		sched = NULL;
+	int res = AST_TEST_FAIL;
+	struct ast_sorcery *sorcery = NULL;
+	struct backend_data initial = {
+		.salt = 0,
+		.pepper = 0,
+		.exists = 4,
+	};
+	struct ao2_container *objects;
+	ast_mutex_t lock;
+	ast_cond_t cond;
+	struct timeval start;
+	struct timespec end;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "full_backend_cache_stale";
+		info->category = "/res/res_sorcery_memory_cache/";
+		info->summary = "Ensure that the full backend cache works with staleness";
+		info->description = "This test performs the following:\n"
+			"\t* Create a sorcery instance with two wizards"
+			"\t\t* The first is a memory cache that stales objects after 1 second and does full backend caching\n"
+			"\t\t* The second is a mock of a back-end\n"
+			"\t* Populates the cache by requesting all objects which returns 4.\n"
+			"\t* Wait for objects to go stale.\n"
+			"\t* Updates the backend to contain a different number of objects, 8.\""
+			"\t* Requests all objects and confirms the number returned is only 4.\n"
+			"\t* Wait for objects to be refreshed from backend.\n"
+			"\t* Requests all objects and confirms the number returned is 8.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
 	}
 
-	ao2_cleanup(caches);
+	ast_sorcery_wizard_register(&mock_wizard);
 
-	ast_sorcery_wizard_unregister(&memory_cache_object_wizard);
+	ast_mutex_init(&lock);
+	ast_cond_init(&cond, NULL);
 
-	ast_cli_unregister_multiple(cli_memory_cache, ARRAY_LEN(cli_memory_cache));
+	sorcery = ast_sorcery_open();
+	if (!sorcery) {
+		ast_test_status_update(test, "Failed to create sorcery instance\n");
+		goto cleanup;
+	}
 
-	ast_manager_unregister("SorceryMemoryCacheExpireObject");
-	ast_manager_unregister("SorceryMemoryCacheExpire");
-	ast_manager_unregister("SorceryMemoryCacheStaleObject");
-	ast_manager_unregister("SorceryMemoryCacheStale");
+	ast_sorcery_apply_wizard_mapping(sorcery, "test", "memory_cache",
+			"object_lifetime_stale=1,full_backend_cache=yes", 1);
+	ast_sorcery_apply_wizard_mapping(sorcery, "test", "mock", NULL, 0);
+	ast_sorcery_internal_object_register(sorcery, "test", test_data_alloc, NULL, NULL);
+	ast_sorcery_object_field_register_nodoc(sorcery, "test", "salt", "0", OPT_UINT_T, 0, FLDSET(struct test_data, salt));
+	ast_sorcery_object_field_register_nodoc(sorcery, "test", "pepper", "0", OPT_UINT_T, 0, FLDSET(struct test_data, pepper));
+
+	/* Prepopulate the cache */
+	real_backend_data = &initial;
+
+	/* Get all current objects in the backend */
+	objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	if (!objects) {
+		ast_test_status_update(test, "Unable to retrieve all objects in backend and populate cache\n");
+		goto cleanup;
+	}
+	ao2_ref(objects, -1);
+
+	start = ast_tvnow();
+	end.tv_sec = start.tv_sec + 5;
+	end.tv_nsec = start.tv_usec * 1000;
+
+	ast_mutex_lock(&lock);
+	while (ast_cond_timedwait(&cond, &lock, &end) != ETIMEDOUT) {
+	}
+	ast_mutex_unlock(&lock);
+
+	initial.exists = 8;
+
+	/* Get all current objects in the backend */
+	objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	if (!objects) {
+		ast_test_status_update(test, "Unable to retrieve all objects in backend and populate cache\n");
+		goto cleanup;
+	}
 
+	if (ao2_container_count(objects) == initial.exists) {
+		ast_test_status_update(test, "Number of objects returned is of the backend and not the cache\n");
+		ao2_ref(objects, -1);
+		goto cleanup;
+	}
+
+	ao2_ref(objects, -1);
+
+	start = ast_tvnow();
+	end.tv_sec = start.tv_sec + 5;
+	end.tv_nsec = start.tv_usec * 1000;
+
+	ast_mutex_lock(&lock);
+	while (ast_cond_timedwait(&cond, &lock, &end) != ETIMEDOUT) {
+	}
+	ast_mutex_unlock(&lock);
+
+	/* Get all current objects in the backend */
+	objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	if (!objects) {
+		ast_test_status_update(test, "Unable to retrieve all objects in backend and populate cache\n");
+		goto cleanup;
+	}
+
+	if (ao2_container_count(objects) != initial.exists) {
+		ast_test_status_update(test, "Number of objects returned is not of backend\n");
+		ao2_ref(objects, -1);
+		goto cleanup;
+	}
+
+	ao2_ref(objects, -1);
+
+	start = ast_tvnow();
+	end.tv_sec = start.tv_sec + 5;
+	end.tv_nsec = start.tv_usec * 1000;
+
+	ast_mutex_lock(&lock);
+	while (ast_cond_timedwait(&cond, &lock, &end) != ETIMEDOUT) {
+	}
+	ast_mutex_unlock(&lock);
+
+	res = AST_TEST_PASS;
+
+cleanup:
+	if (sorcery) {
+		ast_sorcery_unref(sorcery);
+	}
+	ast_sorcery_wizard_unregister(&mock_wizard);
+	ast_mutex_destroy(&lock);
+	ast_cond_destroy(&cond);
+	return res;
+}
+
+#endif
+
+static int unload_module(void)
+{
 	AST_TEST_UNREGISTER(open_with_valid_options);
 	AST_TEST_UNREGISTER(open_with_invalid_options);
 	AST_TEST_UNREGISTER(create_and_retrieve);
@@ -2504,6 +3414,30 @@ static int unload_module(void)
 	AST_TEST_UNREGISTER(maximum_objects);
 	AST_TEST_UNREGISTER(expiration);
 	AST_TEST_UNREGISTER(stale);
+	AST_TEST_UNREGISTER(full_backend_cache_expiration);
+	AST_TEST_UNREGISTER(full_backend_cache_stale);
+
+	ast_manager_unregister("SorceryMemoryCacheExpireObject");
+	ast_manager_unregister("SorceryMemoryCacheExpire");
+	ast_manager_unregister("SorceryMemoryCacheStaleObject");
+	ast_manager_unregister("SorceryMemoryCacheStale");
+	ast_manager_unregister("SorceryMemoryCachePopulate");
+
+	ast_cli_unregister_multiple(cli_memory_cache, ARRAY_LEN(cli_memory_cache));
+
+	ast_sorcery_wizard_unregister(&memory_cache_object_wizard);
+
+	/*
+	 * XXX There is the potential to leak memory if there are pending
+	 * next-cache-expiration and stale-cache-update tasks in the scheduler.
+	 */
+	if (sched) {
+		ast_sched_context_destroy(sched);
+		sched = NULL;
+	}
+
+	ao2_cleanup(caches);
+	caches = NULL;
 
 	return 0;
 }
@@ -2512,6 +3446,14 @@ static int load_module(void)
 {
 	int res;
 
+	caches = ao2_container_alloc(CACHES_CONTAINER_BUCKET_SIZE, sorcery_memory_cache_hash,
+		sorcery_memory_cache_cmp);
+	if (!caches) {
+		ast_log(LOG_ERROR, "Failed to create container for configured caches\n");
+		unload_module();
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
 	sched = ast_sched_context_create();
 	if (!sched) {
 		ast_log(LOG_ERROR, "Failed to create scheduler for cache management\n");
@@ -2525,14 +3467,6 @@ static int load_module(void)
 		return AST_MODULE_LOAD_DECLINE;
 	}
 
-	caches = ao2_container_alloc(CACHES_CONTAINER_BUCKET_SIZE, sorcery_memory_cache_hash,
-		sorcery_memory_cache_cmp);
-	if (!caches) {
-		ast_log(LOG_ERROR, "Failed to create container for configured caches\n");
-		unload_module();
-		return AST_MODULE_LOAD_DECLINE;
-	}
-
 	if (ast_sorcery_wizard_register(&memory_cache_object_wizard)) {
 		unload_module();
 		return AST_MODULE_LOAD_DECLINE;
@@ -2543,6 +3477,7 @@ static int load_module(void)
 	res |= ast_manager_register_xml("SorceryMemoryCacheExpire", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_expire);
 	res |= ast_manager_register_xml("SorceryMemoryCacheStaleObject", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_stale_object);
 	res |= ast_manager_register_xml("SorceryMemoryCacheStale", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_stale);
+	res |= ast_manager_register_xml("SorceryMemoryCachePopulate", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_populate);
 
 	if (res) {
 		unload_module();
@@ -2560,6 +3495,8 @@ static int load_module(void)
 	AST_TEST_REGISTER(delete);
 	AST_TEST_REGISTER(maximum_objects);
 	AST_TEST_REGISTER(expiration);
+	AST_TEST_REGISTER(full_backend_cache_expiration);
+	AST_TEST_REGISTER(full_backend_cache_stale);
 
 	return AST_MODULE_LOAD_SUCCESS;
 }
diff --git a/res/res_sorcery_realtime.c b/res/res_sorcery_realtime.c
index fc22170..3412b92 100644
--- a/res/res_sorcery_realtime.c
+++ b/res/res_sorcery_realtime.c
@@ -142,7 +142,7 @@ static struct ast_variable *sorcery_realtime_filter_objectset(struct ast_variabl
 		}
 	}
 
-	ao2_ref(object_type, -1);
+	ao2_cleanup(object_type);
 
 	return objectset;
 }
diff --git a/res/res_stasis.c b/res/res_stasis.c
index 1cab8c3..94b037e 100644
--- a/res/res_stasis.c
+++ b/res/res_stasis.c
@@ -150,10 +150,10 @@ static struct ast_json *stasis_start_to_json(struct stasis_message *message,
 		return NULL;
 	}
 
-	msg = ast_json_pack("{s: s, s: O, s: O, s: o}",
+	msg = ast_json_pack("{s: s, s: o, s: o, s: o}",
 		"type", "StasisStart",
-		"timestamp", ast_json_object_get(payload->blob, "timestamp"),
-		"args", ast_json_object_get(payload->blob, "args"),
+		"timestamp", ast_json_copy(ast_json_object_get(payload->blob, "timestamp")),
+		"args", ast_json_deep_copy(ast_json_object_get(payload->blob, "args")),
 		"channel", ast_channel_snapshot_to_json(payload->channel, NULL));
 	if (!msg) {
 		ast_log(LOG_ERROR, "Failed to pack JSON for StasisStart message\n");
diff --git a/res/res_stasis_playback.c b/res/res_stasis_playback.c
index dc6e8f2..779dd77 100644
--- a/res/res_stasis_playback.c
+++ b/res/res_stasis_playback.c
@@ -105,9 +105,9 @@ static struct ast_json *playback_to_json(struct stasis_message *message,
 		return NULL;
 	}
 
-	return ast_json_pack("{s: s, s: O}",
+	return ast_json_pack("{s: s, s: o}",
 		"type", type,
-		"playback", blob);
+		"playback", ast_json_deep_copy(blob));
 }
 
 STASIS_MESSAGE_TYPE_DEFN(stasis_app_playback_snapshot_type,
diff --git a/res/res_stasis_recording.c b/res/res_stasis_recording.c
index 50e0697..392d92c 100644
--- a/res/res_stasis_recording.c
+++ b/res/res_stasis_recording.c
@@ -91,9 +91,9 @@ static struct ast_json *recording_to_json(struct stasis_message *message,
 		return NULL;
 	}
 
-	return ast_json_pack("{s: s, s: O}",
+	return ast_json_pack("{s: s, s: o}",
 		"type", type,
-		"recording", blob);
+		"recording", ast_json_deep_copy(blob));
 }
 
 STASIS_MESSAGE_TYPE_DEFN(stasis_app_recording_snapshot_type,
diff --git a/res/res_statsd.c b/res/res_statsd.c
index 2cbd7f0..4eb5260 100644
--- a/res/res_statsd.c
+++ b/res/res_statsd.c
@@ -97,11 +97,11 @@ static void conf_server(const struct conf *cfg, struct ast_sockaddr *addr)
 	}
 }
 
-void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name,
-	const char *metric_type, intmax_t value, double sample_rate)
+void AST_OPTIONAL_API_NAME(ast_statsd_log_string)(const char *metric_name,
+	const char *metric_type, const char *value, double sample_rate)
 {
-	RAII_VAR(struct conf *, cfg, NULL, ao2_cleanup);
-	RAII_VAR(struct ast_str *, msg, NULL, ast_free);
+	struct conf *cfg;
+	struct ast_str *msg;
 	size_t len;
 	struct ast_sockaddr statsd_server;
 
@@ -109,9 +109,6 @@ void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name,
 		return;
 	}
 
-	cfg = ao2_global_obj_ref(confs);
-	conf_server(cfg, &statsd_server);
-
 	/* Rates <= 0.0 never get logged.
 	 * Rates >= 1.0 always get logged.
 	 * All others leave it to chance.
@@ -122,9 +119,11 @@ void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name,
 	}
 
 	cfg = ao2_global_obj_ref(confs);
+	conf_server(cfg, &statsd_server);
 
 	msg = ast_str_create(40);
 	if (!msg) {
+		ao2_cleanup(cfg);
 		return;
 	}
 
@@ -132,7 +131,7 @@ void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name,
 		ast_str_append(&msg, 0, "%s.", cfg->global->prefix);
 	}
 
-	ast_str_append(&msg, 0, "%s:%jd|%s", metric_name, value, metric_type);
+	ast_str_append(&msg, 0, "%s:%s|%s", metric_name, value, metric_type);
 
 	if (sample_rate < 1.0) {
 		ast_str_append(&msg, 0, "|@%.2f", sample_rate);
@@ -144,20 +143,87 @@ void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name,
 
 	len = ast_str_strlen(msg);
 
-	ast_debug(6, "send: %s\n", ast_str_buffer(msg));
+	ast_debug(6, "Sending statistic %s to StatsD server\n", ast_str_buffer(msg));
 	ast_sendto(socket_fd, ast_str_buffer(msg), len, 0, &statsd_server);
+
+	ao2_cleanup(cfg);
+	ast_free(msg);
+}
+
+void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name,
+	const char *metric_type, intmax_t value, double sample_rate)
+{
+	char char_value[30];
+	snprintf(char_value, sizeof(char_value), "%jd", value);
+
+	ast_statsd_log_string(metric_name, metric_type, char_value, sample_rate);
+
+}
+
+AST_THREADSTORAGE(statsd_buf);
+
+void AST_OPTIONAL_API_NAME(ast_statsd_log_string_va)(const char *metric_name,
+	const char *metric_type, const char *value, double sample_rate, ...)
+{
+	struct ast_str *buf;
+	va_list ap;
+	int res;
+
+	buf = ast_str_thread_get(&statsd_buf, 128);
+	if (!buf) {
+		return;
+	}
+
+	va_start(ap, sample_rate);
+	res = ast_str_set_va(&buf, 0, metric_name, ap);
+	va_end(ap);
+
+	if (res == AST_DYNSTR_BUILD_FAILED) {
+		return;
+	}
+
+	ast_statsd_log_string(ast_str_buffer(buf), metric_type, value, sample_rate);
+}
+
+void AST_OPTIONAL_API_NAME(ast_statsd_log_full_va)(const char *metric_name,
+	const char *metric_type, intmax_t value, double sample_rate, ...)
+{
+	struct ast_str *buf;
+	va_list ap;
+	int res;
+
+	buf = ast_str_thread_get(&statsd_buf, 128);
+	if (!buf) {
+		return;
+	}
+
+	va_start(ap, sample_rate);
+	res = ast_str_set_va(&buf, 0, metric_name, ap);
+	va_end(ap);
+
+	if (res == AST_DYNSTR_BUILD_FAILED) {
+		return;
+	}
+
+	ast_statsd_log_full(ast_str_buffer(buf), metric_type, value, sample_rate);
 }
 
 void AST_OPTIONAL_API_NAME(ast_statsd_log)(const char *metric_name,
 	const char *metric_type, intmax_t value)
 {
-	ast_statsd_log_full(metric_name, metric_type, value, 1.0);
+	char char_value[30];
+	snprintf(char_value, sizeof(char_value), "%jd", value);
+
+	ast_statsd_log_string(metric_name, metric_type, char_value, 1.0);
 }
 
 void AST_OPTIONAL_API_NAME(ast_statsd_log_sample)(const char *metric_name,
 	intmax_t value, double sample_rate)
 {
-	ast_statsd_log_full(metric_name, AST_STATSD_COUNTER, value,
+	char char_value[30];
+	snprintf(char_value, sizeof(char_value), "%jd", value);
+
+	ast_statsd_log_string(metric_name, AST_STATSD_COUNTER, char_value,
 		sample_rate);
 }
 
diff --git a/res/res_statsd.exports.in b/res/res_statsd.exports.in
index 6f02b25..d4a79c1 100644
--- a/res/res_statsd.exports.in
+++ b/res/res_statsd.exports.in
@@ -3,6 +3,7 @@
 		LINKER_SYMBOL_PREFIX*ast_statsd_log;
 		LINKER_SYMBOL_PREFIX*ast_statsd_log_full;
 		LINKER_SYMBOL_PREFIX*ast_statsd_log_sample;
+		LINKER_SYMBOL_PREFIX*ast_statsd_log_string;
 	local:
 		*;
 };
diff --git a/res/stasis/app.c b/res/stasis/app.c
index 3539182..4e18aa5 100644
--- a/res/stasis/app.c
+++ b/res/stasis/app.c
@@ -610,11 +610,11 @@ static int message_received_handler(const char *endpoint_id, struct ast_json *js
 		return -1;
 	}
 
-	app_send(app, ast_json_pack("{s: s, s: o, s: o, s: O}",
+	app_send(app, ast_json_pack("{s: s, s: o, s: o, s: o}",
 		"type", "TextMessageReceived",
 		"timestamp", ast_json_timeval(ast_tvnow(), NULL),
 		"endpoint", json_endpoint,
-		"message", json_msg));
+		"message", ast_json_ref(json_msg)));
 
 	return 0;
 }
diff --git a/rest-api-templates/api.wiki.mustache b/rest-api-templates/api.wiki.mustache
index 73aa244..0a54a64 100644
--- a/rest-api-templates/api.wiki.mustache
+++ b/rest-api-templates/api.wiki.mustache
@@ -67,7 +67,7 @@ h3. Header parameters
 
 h3. Error Responses
 {{#error_responses}}
-* {{code}} - {{{reason}}}
+* {{code}} - {{{wiki_reason}}}
 {{/error_responses}}
 {{/has_error_responses}}
 {{/operations}}
diff --git a/rest-api-templates/asterisk_processor.py b/rest-api-templates/asterisk_processor.py
index ab8a8af..68a6799 100644
--- a/rest-api-templates/asterisk_processor.py
+++ b/rest-api-templates/asterisk_processor.py
@@ -199,6 +199,8 @@ class AsteriskProcessor(SwaggerPostProcessor):
             raise SwaggerError("Summary should end with .", context)
         operation.wiki_summary = wikify(operation.summary or "")
         operation.wiki_notes = wikify(operation.notes or "")
+        for error_response in operation.error_responses:
+            error_response.wiki_reason = wikify(error_response.reason or "")
         operation.parse_body = (operation.body_parameter or operation.has_query_parameters) and True
 
     def process_parameter(self, parameter, context):
diff --git a/tests/test_config.c b/tests/test_config.c
index b9935dc..3ad5772 100644
--- a/tests/test_config.c
+++ b/tests/test_config.c
@@ -1492,8 +1492,8 @@ AST_TEST_DEFINE(config_options_test)
 			res = AST_TEST_FAIL;
 		}
 		if (!ast_format_cap_identical(arr[x]->codeccapopt, control->codeccapopt)) {
-			struct ast_str *codec_buf1 = ast_str_alloca(64);
-			struct ast_str *codec_buf2 = ast_str_alloca(64);
+			struct ast_str *codec_buf1 = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+			struct ast_str *codec_buf2 = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_test_status_update(test, "format did not match: '%s' vs '%s' on loop %d\n",
 				ast_format_cap_get_names(arr[x]->codeccapopt, &codec_buf1),
diff --git a/tests/test_format_cap.c b/tests/test_format_cap.c
index 9e8eac4..ed5427f 100644
--- a/tests/test_format_cap.c
+++ b/tests/test_format_cap.c
@@ -245,7 +245,7 @@ AST_TEST_DEFINE(format_cap_append_all_unknown)
 	} else if (!ast_format_cap_has_type(caps, AST_MEDIA_TYPE_VIDEO)) {
 		ast_test_status_update(test, "Added all media formats but no video formats exist when they should\n");
 		return AST_TEST_FAIL;
-	} else if ((ast_format_cap_count(caps) + 1) != ast_codec_get_max()) {
+	} else if ((ast_format_cap_count(caps) + 1) != (ast_codec_get_max() - 1)) {
 		ast_test_status_update(test, "The number of formats in the capabilities structure does not match known number\n");
 		return AST_TEST_FAIL;
 	}
@@ -1079,7 +1079,7 @@ AST_TEST_DEFINE(format_cap_get_names)
 	RAII_VAR(struct ast_format *, ulaw_format, NULL, ao2_cleanup);
 	RAII_VAR(struct ast_codec *, alaw, NULL, ao2_cleanup);
 	RAII_VAR(struct ast_format *, alaw_format, NULL, ao2_cleanup);
-	struct ast_str *buffer = ast_str_alloca(128);
+	struct ast_str *buffer = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 	switch (cmd) {
 	case TEST_INIT:
diff --git a/tests/test_threadpool.c b/tests/test_threadpool.c
index f5b1073..42181a2 100644
--- a/tests/test_threadpool.c
+++ b/tests/test_threadpool.c
@@ -571,6 +571,87 @@ end:
 	return res;
 }
 
+AST_TEST_DEFINE(threadpool_thread_timeout_thrash)
+{
+	struct ast_threadpool *pool = NULL;
+	struct ast_threadpool_listener *listener = NULL;
+	enum ast_test_result_state res = AST_TEST_FAIL;
+	struct test_listener_data *tld = NULL;
+	struct ast_threadpool_options options = {
+		.version = AST_THREADPOOL_OPTIONS_VERSION,
+		.idle_timeout = 1,
+		.auto_increment = 1,
+		.initial_size = 0,
+		.max_size = 1,
+	};
+	int iteration;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "thread_timeout_thrash";
+		info->category = "/main/threadpool/";
+		info->summary = "Thrash threadpool thread timeout";
+		info->description =
+			"Repeatedly queue a task when a threadpool thread should timeout.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	tld = test_alloc();
+	if (!tld) {
+		return AST_TEST_FAIL;
+	}
+
+	listener = ast_threadpool_listener_alloc(&test_callbacks, tld);
+	if (!listener) {
+		goto end;
+	}
+
+	pool = ast_threadpool_create(info->name, listener, &options);
+	if (!pool) {
+		goto end;
+	}
+
+	ast_threadpool_set_size(pool, 1);
+
+	for (iteration = 0; iteration < 30; ++iteration) {
+		struct simple_task_data *std = NULL;
+		struct timeval start = ast_tvnow();
+		struct timespec end = {
+			.tv_sec = start.tv_sec + options.idle_timeout,
+			.tv_nsec = start.tv_usec * 1000
+		};
+
+		std = simple_task_data_alloc();
+		if (!std) {
+			goto end;
+		}
+
+		/* Wait until the threadpool thread should timeout due to being idle */
+		ast_mutex_lock(&tld->lock);
+		while (ast_cond_timedwait(&tld->cond, &tld->lock, &end) != ETIMEDOUT) {
+			/* This purposely left empty as we want to loop waiting for a time out */
+		}
+		ast_mutex_unlock(&tld->lock);
+
+		ast_threadpool_push(pool, simple_task, std);
+	}
+
+	res = wait_until_thread_state(test, tld, 0, 0);
+	if (res == AST_TEST_FAIL) {
+		goto end;
+	}
+
+	res = listener_check(test, listener, 1, 1, 30, 0, 0, 1);
+
+end:
+	ast_threadpool_shutdown(pool);
+	ao2_cleanup(listener);
+	ast_free(tld);
+	return res;
+}
+
 AST_TEST_DEFINE(threadpool_one_task_one_thread)
 {
 	struct ast_threadpool *pool = NULL;
@@ -1610,6 +1691,7 @@ static int unload_module(void)
 	ast_test_unregister(threadpool_thread_creation);
 	ast_test_unregister(threadpool_thread_destruction);
 	ast_test_unregister(threadpool_thread_timeout);
+	ast_test_unregister(threadpool_thread_timeout_thrash);
 	ast_test_unregister(threadpool_one_task_one_thread);
 	ast_test_unregister(threadpool_one_thread_one_task);
 	ast_test_unregister(threadpool_one_thread_multiple_tasks);
@@ -1630,6 +1712,7 @@ static int load_module(void)
 	ast_test_register(threadpool_thread_creation);
 	ast_test_register(threadpool_thread_destruction);
 	ast_test_register(threadpool_thread_timeout);
+	ast_test_register(threadpool_thread_timeout_thrash);
 	ast_test_register(threadpool_one_task_one_thread);
 	ast_test_register(threadpool_one_thread_one_task);
 	ast_test_register(threadpool_one_thread_multiple_tasks);

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-voip/asterisk.git



More information about the Pkg-voip-commits mailing list