[Pkg-voip-commits] [asterisk] 03/13: New upstream version 13.18.5~dfsg

tzafrir at debian.org tzafrir at debian.org
Wed Dec 27 23:16:20 UTC 2017


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

tzafrir pushed a commit to branch master
in repository asterisk.

commit 8a5493ad8a49154d798ed448fd7f0d28eab3b1b5
Author: Tzafrir Cohen <tzafrir at debian.org>
Date:   Sun Dec 24 11:02:35 2017 +0200

    New upstream version 13.18.5~dfsg
---
 .version                                           |    2 +-
 CHANGES                                            |   68 +
 ChangeLog                                          | 2293 +++++++++++++++++++-
 README-SERIOUSLY.bestpractices.txt                 |    7 +
 UPGRADE.txt                                        |    6 +
 addons/cdr_mysql.c                                 |   10 +-
 addons/chan_ooh323.c                               |    8 +-
 addons/ooh323c/src/ooSocket.c                      |    2 +-
 apps/Makefile                                      |    2 +
 apps/app_chanspy.c                                 |    2 +-
 apps/app_confbridge.c                              |   12 +-
 apps/app_directory.c                               |    7 +-
 apps/app_followme.c                                |    2 +-
 apps/app_meetme.c                                  |   25 +-
 apps/app_minivm.c                                  |  159 +-
 apps/app_mixmonitor.c                              |   15 +
 apps/app_originate.c                               |   32 +-
 apps/app_playback.c                                |    2 +-
 apps/app_privacy.c                                 |    1 -
 apps/app_queue.c                                   |   75 +-
 apps/app_record.c                                  |  113 +-
 apps/app_system.c                                  |   10 +
 apps/app_voicemail.c                               |  875 ++++----
 apps/app_waitforsilence.c                          |  137 +-
 asterisk-13.17.0-summary.html                      |  306 ---
 asterisk-13.17.0-summary.txt                       |  814 -------
 asterisk-13.18.5-summary.html                      |   11 +
 asterisk-13.18.5-summary.txt                       |   82 +
 bridges/bridge_native_rtp.c                        |   27 +-
 bridges/bridge_softmix.c                           |    8 +-
 build_tools/download_externals                     |   23 +-
 build_tools/list_valid_installed_externals         |   20 +-
 channels/Makefile                                  |    2 +
 channels/chan_dahdi.c                              |    3 +-
 channels/chan_iax2.c                               |    4 +-
 channels/chan_motif.c                              |    2 +-
 channels/chan_pjsip.c                              |   43 +-
 channels/chan_rtp.c                                |   20 +-
 channels/chan_sip.c                                |   18 +-
 channels/chan_skinny.c                             |   11 +-
 channels/chan_unistim.c                            |    4 +-
 channels/chan_vpb.cc                               |    2 +-
 channels/iax2/firmware.c                           |   13 +-
 channels/pjsip/dialplan_functions.c                |  169 ++
 channels/pjsip/include/dialplan_functions.h        |   25 +
 channels/sig_pri.c                                 |    8 +-
 channels/sig_pri.h                                 |    2 +-
 channels/sip/dialplan_functions.c                  |    9 +
 configs/basic-pbx/modules.conf                     |    1 -
 configs/samples/config_test.conf.sample            |    8 +
 configs/samples/minivm.conf.sample                 |    2 +-
 configs/samples/musiconhold.conf.sample            |   23 +
 configs/samples/pjsip.conf.sample                  |   18 +-
 configs/samples/res_config_sqlite.conf.sample      |    2 +-
 configs/samples/xmpp.conf.sample                   |   23 +
 configure                                          |   55 +-
 configure.ac                                       |   15 +-
 .../config/versions/15db7b91a97a_add_rtcp_mux.py   |    2 +
 ...abbd708c_add_auto_info_to_endpoint_dtmf_mode.py |    2 +-
 .../versions/23530d604b96_add_rpid_immediate.py    |    2 +
 ...f0fa5_add_bind_rtp_to_media_address_to_pjsip.py |    2 +
 .../28ab27a7826d_add_srv_lookups_to_identify.py    |    2 +
 .../versions/28b8e71e541f_add_g726_non_standard.py |    2 +
 ...930b41b3_add_pjsip_endpoint_options_for_12_1.py |    4 +
 ...1a3bf4143e_add_user_eq_phone_option_to_pjsip.py |    2 +
 .../versions/3772f8f828da_update_identify_by.py    |    2 +
 ...cc0b5bc2c9_add_allow_reload_to_ps_transports.py |    2 +
 .../4468b4a91372_add_pjsip_asymmetric_rtp_codec.py |    2 +
 .../versions/4c573e7135bd_fix_tos_field_types.py   |    4 +-
 .../5139253c0423_make_q_member_uniqueid_autoinc.py |    4 +-
 .../51f8cb66540e_add_further_dtls_options.py       |    3 +
 .../5950038a6ead_fix_pjsip_verifiy_typo.py         |   23 +-
 ...67461fb_ps_contacts_add_authenticate_qualify.py |    2 +
 ...fa278d_add_ps_endpoints_refer_blind_progress.py |    2 +
 .../8d478ab86e29_pjsip_add_disable_multi_domain.py |    2 +
 .../8fce4c573e15_add_pjsip_allow_overlap.py        |    2 +
 .../a1698e8bb9c5_add_incoming_mwi_mailbox.py       |   21 +
 ...f1309_ps_globals_add_ignore_uri_user_options.py |    2 +
 ...5976fdd_add_dtls_fingerprint_to_ps_endpoints.py |   40 +
 .../c7a44a5a0851_pjsip_add_global_mwi_options.py   |    2 +
 ...954dd96_add_ps_endpoints_notify_early_inuse_.py |    2 +
 ...c44d5a908_add_missing_columns_to_sys_and_reg.py |    3 +
 .../e96a0b8071c_increase_pjsip_column_size.py      |    8 +-
 ...f2a_add_media_encryption_optimistic_to_pjsip.py |    2 +
 ...c2d3964_ps_contacts_add_endpoint_and_modify_.py |    8 +-
 ...entify.py => f3d1c5d38b56_add_prune_on_boot.py} |   18 +-
 .../versions/f638dbe2eb23_symmetric_transport.py   |    2 +
 contrib/ast-db-manage/env.py                       |   17 +-
 contrib/realtime/mssql/mssql_config.sql            |   60 +-
 contrib/realtime/mysql/mysql_config.sql            |   18 +
 contrib/realtime/oracle/oracle_config.sql          |   38 +
 contrib/realtime/postgresql/postgresql_config.sql  |   20 +
 contrib/scripts/install_prereq                     |    2 +-
 contrib/scripts/sip_to_pjsip/sip_to_pjsip.py       |   30 +
 formats/format_g719.c                              |   17 +-
 formats/format_g723.c                              |   15 +-
 formats/format_g726.c                              |   15 +-
 formats/format_g729.c                              |   16 +-
 formats/format_gsm.c                               |   15 +-
 formats/format_h263.c                              |   15 +-
 formats/format_h264.c                              |   15 +-
 formats/format_ilbc.c                              |   16 +-
 formats/format_pcm.c                               |   20 +-
 formats/format_siren14.c                           |   17 +-
 formats/format_siren7.c                            |   17 +-
 formats/format_sln.c                               |   19 +-
 formats/format_vox.c                               |   17 +-
 formats/format_wav.c                               |   17 +-
 formats/format_wav_gsm.c                           |   17 +-
 funcs/func_cdr.c                                   |    2 +-
 funcs/func_shell.c                                 |    5 +
 include/asterisk/app.h                             |   31 +-
 include/asterisk/bridge_after.h                    |    2 +
 include/asterisk/bridge_technology.h               |    4 +-
 include/asterisk/calendar.h                        |    2 +-
 include/asterisk/cdr.h                             |   44 +-
 include/asterisk/config.h                          |   11 +
 include/asterisk/config_options.h                  |   25 +
 include/asterisk/features_config.h                 |   15 +
 include/asterisk/format.h                          |    4 +-
 include/asterisk/format_cache.h                    |    5 +
 include/asterisk/logger.h                          |    4 +-
 include/asterisk/manager.h                         |    2 +-
 include/asterisk/res_pjsip.h                       |  184 +-
 include/asterisk/res_pjsip_session.h               |   26 +
 include/asterisk/rtp_engine.h                      |    2 +
 include/asterisk/strings.h                         |   20 +
 include/asterisk/vector.h                          |   18 +-
 main/Makefile                                      |    3 +-
 main/acl.c                                         |    4 +-
 main/app.c                                         |   15 +
 main/ast_expr2.c                                   |    6 +-
 main/ast_expr2.y                                   |    6 +-
 main/asterisk.c                                    |  102 +-
 main/bridge.c                                      |    6 +-
 main/bridge_after.c                                |   30 +-
 main/bridge_channel.c                              |    4 +
 main/ccss.c                                        |    2 +-
 main/cdr.c                                         |  220 +-
 main/channel.c                                     |    4 +-
 main/cli.c                                         |    4 +-
 main/codec_builtin.c                               |    8 +
 main/config.c                                      |   49 +
 main/config_options.c                              |   36 +
 main/features_config.c                             |   15 +
 main/format_cache.c                                |    8 +
 main/heap.c                                        |    4 +-
 main/http.c                                        |   46 +-
 main/json.c                                        |    1 +
 main/libasteriskssl.c                              |   24 +-
 main/manager.c                                     |   64 +-
 main/netsock2.c                                    |   16 +-
 main/rtp_engine.c                                  |   13 +-
 main/say.c                                         |   42 +-
 main/stdtime/localtime.c                           |    2 +-
 main/strings.c                                     |   21 +-
 main/stun.c                                        |    4 +-
 main/tcptls.c                                      |    4 +-
 main/utils.c                                       |    2 +-
 makeopts.in                                        |    6 +-
 res/res_ari.c                                      |    2 +-
 res/res_calendar.c                                 |  105 +-
 res/res_calendar_caldav.c                          |   41 +-
 res/res_calendar_icalendar.c                       |   41 +-
 res/res_config_pgsql.c                             |    5 +-
 res/res_http_post.c                                |   19 +-
 res/res_monitor.c                                  |   54 +-
 res/res_musiconhold.c                              |  129 +-
 res/res_pjproject.c                                |    2 +-
 res/res_pjsip.c                                    |  165 +-
 res/res_pjsip/config_transport.c                   |   46 +-
 res/res_pjsip/include/res_pjsip_private.h          |   65 +-
 res/res_pjsip/location.c                           |   64 +-
 res/res_pjsip/pjsip_configuration.c                |   97 +-
 res/res_pjsip/pjsip_distributor.c                  |    2 +-
 ...message_ip_updater.c => pjsip_message_filter.c} |  282 ++-
 res/res_pjsip/pjsip_session.c                      |  121 ++
 res/res_pjsip/pjsip_transport_events.c             |  366 ++++
 res/res_pjsip/presence_xml.c                       |   16 +-
 res/res_pjsip_caller_id.c                          |    8 +-
 res/res_pjsip_messaging.c                          |    6 +-
 res/res_pjsip_nat.c                                |   12 +-
 res/res_pjsip_outbound_publish.c                   |   18 +
 res/res_pjsip_outbound_registration.c              |  140 +-
 res/res_pjsip_pidf_body_generator.c                |    2 +-
 res/res_pjsip_pidf_eyebeam_body_supplement.c       |   32 +-
 res/res_pjsip_publish_asterisk.c                   |    6 +-
 res/res_pjsip_pubsub.c                             |  152 +-
 res/res_pjsip_registrar.c                          |  319 ++-
 res/res_pjsip_sdp_rtp.c                            |   29 +-
 res/res_pjsip_session.c                            |  226 +-
 res/res_pjsip_session.exports.in                   |    1 +
 res/res_pjsip_t38.c                                |   49 +-
 res/res_pjsip_transport_management.c               |   58 +-
 res/res_pjsip_transport_websocket.c                |   17 +
 res/res_rtp_asterisk.c                             |  697 ++++--
 res/res_smdi.c                                     |   10 +-
 res/res_srtp.c                                     |   22 +-
 res/res_stasis_device_state.c                      |    4 +-
 res/res_stasis_snoop.c                             |   22 +-
 res/res_xmpp.c                                     |   99 +-
 res/srtp/srtp_compat.h                             |    4 +
 res/stasis/control.c                               |  118 +-
 sounds/Makefile                                    |    7 +-
 sounds/sounds.xml                                  |   27 +
 tests/test_config.c                                |   88 +
 tests/test_core_format.c                           |    5 +-
 tests/test_taskprocessor.c                         |    2 +-
 tests/test_vector.c                                |    2 +-
 third-party/pjproject/Makefile                     |   10 +-
 third-party/pjproject/configure.m4                 |    6 +-
 ...-Improve-error-handling-in-OpenSSL-socket.patch |  247 +++
 .../patches/0080-STUN-Fingerprint-with-ICE.patch   |   35 +
 ...Add-validity-checking-for-numeric-header-.patch |  910 ++++++++
 third-party/pjproject/patches/config_site.h        |    2 +-
 utils/astman.c                                     |    2 +-
 utils/extconf.c                                    |    4 +
 217 files changed, 9325 insertions(+), 3117 deletions(-)

diff --git a/.version b/.version
index 5504738..7cba6c2 100644
--- a/.version
+++ b/.version
@@ -1 +1 @@
-13.17.0
\ No newline at end of file
+13.18.5
\ No newline at end of file
diff --git a/CHANGES b/CHANGES
index c3a2d0a..913b36a 100644
--- a/CHANGES
+++ b/CHANGES
@@ -9,6 +9,74 @@
 ==============================================================================
 
 ------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 13.17.0 to Asterisk 13.18.0 ----------
+------------------------------------------------------------------------------
+
+Core
+------------------
+ * VP9 is now a supported passthrough video codec and it can be used by
+   specifying "vp9" in the allow line.
+
+Build System
+------------------
+ * A '--with-download-cache' option is now available which is equivalent to
+   setting '--with-sounds-cache' and '--with-externals-cache' to the same
+   value.  The download cache can also be set via the AST_DOWNLOAD_CACHE
+   environment variable.
+
+res_pjsip
+------------------
+ * The "external_media_address" on transports is now resolved using dnsmgr and
+   when dnsmgr refreshes are enabled will be automatically updated with the new
+   IP address of a given hostname.
+
+ * A new endpoint parameter "incoming_mwi_mailbox" allows Asterisk to receive
+   unsolicited MWI NOTIFY requests and make them available to other modules via
+   the stasis message bus.
+
+ * The "remove_existing" option now allows a registration to succeed by
+   displacing any existing contacts that now exceed the "max_contacts" count.
+   Any removed contacts are the next to expire.  The behaviour change is
+   beneficial when "rewrite_contact" is enabled and "max_contacts" is greater
+   than one.  The removed contact is likely the old contact created by
+   "rewrite_contact" that the device is refreshing.
+
+res_musiconhold
+------------------
+ * By default, when res_musiconhold reloads or unloads, it sends a HUP signal
+   to custom applications (and all descendants), waits 100ms, then sends a
+   TERM signal, waits 100ms, then finally sends a KILL signal.  An application
+   which is interacting with an external device and/or spawns children of its
+   own may not be able to exit cleanly in the default times, expecially if sent
+   a KILL signal, or if it's children are getting signals directly from
+   res_musiconhoild.  To allow extra time, the 'kill_escalation_delay'
+   class option can be used to set the number of milliseconds res_musiconhold
+   waits before escalating kill signals, with the default being the current
+   100ms.  To control to whom the signals are sent, the "kill_method"
+   class option can be set to "process_group" (the default, existing behavior),
+   which sends signals to the application and its descendants directly, or
+   "process" which sends signals only to the application itself.
+
+res_pjsip
+------------------
+ * New dialplan function PJSIP_DTMF_MODE added to get or change the DTMF mode
+   of a channel on a per-call basis.
+
+res_xmpp
+-----------------
+ * OAuth 2.0 authentication is now supported when contacting Google. Follow the
+   instructions in xmpp.conf.sample to retrieve and configure the necessary
+   tokens.
+
+app_queue
+------------------
+ * Add priority to callers in AMI QueueStatus response.
+
+AMI
+------------------
+ * Added a new CancelAtxfer action that cancels an attended transfer.
+
+------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 13.16.0 to Asterisk 13.17.0 ----------
 ------------------------------------------------------------------------------
 
diff --git a/ChangeLog b/ChangeLog
index f630fed..e90d3e0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,2295 @@
-2017-07-12 11:12 +0000  Asterisk Development Team <asteriskteam at digium.com>
+2017-12-22 22:21 +0000  Asterisk Development Team <asteriskteam at digium.com>
 
-	* asterisk 13.17.0 Released.
+	* asterisk 13.18.5 Released.
+
+2017-12-20 16:17 +0000 [5b5cb3dfe8]  Kevin Harwell <kharwell at digium.com>
+
+	* AST-2017-014: res_pjsip - Missing contact header can cause crash
+
+	  Those SIP messages that create dialogs require a contact header to be present.
+	  If the contact header was missing from the message it could cause Asterisk to
+	  crash.
+
+	  This patch checks to make sure SIP messages that create a dialog contain the
+	  contact header. If the message does not and it is required Asterisk now returns
+	  a "400 Missing Contact header" response. Also added NULL checks when retrieving
+	  the contact header that were missing as a "just in case".
+
+	  ASTERISK-27480 #close
+
+	  Change-Id: I1810db87683fc637a9e3e1384a746037fec20afe
+	  (cherry picked from commit 53799318bc040a2082904df86d42ab08790b47ec)
+
+2017-12-13 14:33 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.18.4 Released.
+
+2017-11-30 10:12 +0000 [8052b5d76e]  Joshua Colp <jcolp at digium.com>
+
+	* AST-2017-012: Place single RTCP report block at beginning of report.
+
+	  When the RTCP code was transitioned over to Stasis a code change
+	  was made to keep track of how many reports are present. This count
+	  controlled where report blocks were placed in the RTCP report.
+
+	  If a compound RTCP packet was received this logic would incorrectly
+	  place a report block in the wrong location resulting in a write
+	  to an invalid location.
+
+	  This change removes this counting logic and always places the report
+	  block at the first position. If in the future multiple reports are
+	  supported the logic can be extended but for now keeping a count
+	  serves no purpose.
+
+	  ASTERISK-27382
+	  ASTERISK-27429
+
+	  Change-Id: Iad6c8a9985c4b608ef493e19c421211615485116
+	  (cherry picked from commit 5705e8ae0e05602d5399faa561c3119cfb83eca3)
+
+2017-12-01 19:42 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.18.3 Released.
+
+2017-11-30 14:38 +0000 [65ca0785fa]  George Joseph <gjoseph at digium.com>
+
+	* AST-2017-013: chan_skinny: Call pthread_detach when sess threads end
+
+	  chan_skinny creates a new thread for each new session.  In trying
+	  to be a good cleanup citizen, the threads are joinable and the
+	  unload_module function does a pthread_cancel() and a pthread_join()
+	  on any sessions that are active at that time.  This has an
+	  unintended side effect though. Since you can call pthread_join on a
+	  thread that's already terminated, pthreads keeps the thread's
+	  storage around until you explicitly call pthread_join (or
+	  pthread_detach()).   Since only the module_unload function was
+	  calling pthread_join, and even then only on the ones active at the
+	  tme, the storage for every thread/session ever created sticks
+	  around until asterisk exits.
+
+	  * A thread can detach itself so the session_destroy() function
+	    now calls pthread_detach() just before it frees the session
+	    memory allocation.  The module_unload function still takes care
+	    of the ones that are still active should the module be unloaded.
+
+	  ASTERISK-27452
+	  Reported by: Juan Sacco
+
+	  Change-Id: I9af7268eba14bf76960566f891320f97b974e6dd
+
+2017-11-10 15:41 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.18.2 Released.
+
+2017-11-01 11:12 +0000 [c3c75b729a]  Ben Ford <bford at digium.com>
+
+	* res_pjsip: Add to list of valid characters for from_user.
+
+	  Fixes a regression where some characters were unable to be used in
+	  the from_user field of an endpoint. Additionally, the backtick was
+	  removed from the list of valid characters, since it is not valid,
+	  and it was replaced with a single quote, which is a valid character.
+
+	  ASTERISK-27387
+
+	  Change-Id: Id80c10a644508365c87b3182e99ea49da11b0281
+	  (cherry picked from commit ffcb7e2a2540181ea41062ca0e1bc3e4fed9b3a5)
+
+2017-11-06 16:37 +0000 [f08a444eb3]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_registrar.c: Fix named AOR and pjproject group deadlock.
+
+	  One of the patches for ASTERISK_27147 introduced a deadlock regression.
+	  When the connection oriented transport shut down, the code attempted to
+	  remove the associated contact.  However, that same transport had just
+	  requested a registration that we hadn't responded to yet.  Depending
+	  upon timing we could deadlock.
+
+	  * Made send the REGISTER response after we completed processing the
+	  request contacts and released the named AOR lock to avoid the deadlock.
+
+	  ASTERISK-27391
+
+	  Change-Id: I89a90f87cb7a02facbafb44c75d8845f93417364
+
+2017-11-10 15:41 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.18.2 Released.
+
+2017-11-01 11:12 +0000 [c3c75b729a]  Ben Ford <bford at digium.com>
+
+	* res_pjsip: Add to list of valid characters for from_user.
+
+	  Fixes a regression where some characters were unable to be used in
+	  the from_user field of an endpoint. Additionally, the backtick was
+	  removed from the list of valid characters, since it is not valid,
+	  and it was replaced with a single quote, which is a valid character.
+
+	  ASTERISK-27387
+
+	  Change-Id: Id80c10a644508365c87b3182e99ea49da11b0281
+	  (cherry picked from commit ffcb7e2a2540181ea41062ca0e1bc3e4fed9b3a5)
+
+2017-11-06 16:37 +0000 [f08a444eb3]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_registrar.c: Fix named AOR and pjproject group deadlock.
+
+	  One of the patches for ASTERISK_27147 introduced a deadlock regression.
+	  When the connection oriented transport shut down, the code attempted to
+	  remove the associated contact.  However, that same transport had just
+	  requested a registration that we hadn't responded to yet.  Depending
+	  upon timing we could deadlock.
+
+	  * Made send the REGISTER response after we completed processing the
+	  request contacts and released the named AOR lock to avoid the deadlock.
+
+	  ASTERISK-27391
+
+	  Change-Id: I89a90f87cb7a02facbafb44c75d8845f93417364
+
+2017-11-08 16:26 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.18.1 Released.
+
+2017-10-19 13:53 +0000 [ed0acc1fff]  George Joseph <gjoseph at digium.com>
+
+	* AST-2017-009: pjproject: Add validation of numeric header values
+
+	  Parsing the numeric header fields like cseq, ttl, port, etc. all
+	  had the potential to overflow, either causing unintended values to
+	  be captured or, if the values were subsequently converted back to
+	  strings, a buffer overrun.  To address this, new "strto" functions
+	  have been created that do range checking and those functions are
+	  used wherever possible in the parser.
+
+	   * Created pjlib/include/limits.h and pjlib/include/compat/limits.h
+	     to either include the system limits.h or define common numeric
+	     limits if there is no system limits.h.
+
+	   * Created strto*_validate functions in sip_parser that take bounds
+	     and on failure call the on_str_parse_error function which prints
+	     an error message and calls PJ_THROW.
+
+	   * Updated sip_parser to validate the numeric fields.
+
+	   * Fixed an issue in sip_transport that prevented error messages
+	     from being properly displayed.
+
+	   * Added "volatile" to some variables referenced in PJ_CATCH blocks
+	     as the optimizer was sometimes optimizing them away.
+
+	   * Fixed length calculation in sip_transaction/create_tsx_key_2543
+	     to account for signed ints being 11 characters, not 9.
+
+	  ASTERISK-27319
+	  Reported by: Youngsung Kim at LINE Corporation
+
+	  Change-Id: I48de2e4ccf196990906304e8d7061f4ffdd772ff
+
+2017-10-19 13:35 +0000 [cd7c10c646]  Kevin Harwell <kharwell at digium.com>
+
+	* AST-2017-011 - res_pjsip_session: session leak when a call is rejected
+
+	  A previous commit made it so when an invite session transitioned into a
+	  disconnected state destruction of the Asterisk pjsip session object was
+	  postponed until either a transport error occurred or the event timer
+	  expired. However, if a call was rejected (for instance a 488) before the
+	  session was fully established the event timer may not have been initiated,
+	  or it was canceled without triggering either of the session finalizing states
+	  mentioned above.
+
+	  Really the only time destruction of the session should be delayed is when a
+	  BYE is being transacted. This is because it's possible in some cases for the
+	  session to be disconnected, but the BYE is still transacting.
+
+	  This patch makes it so the session object always gets released (no more
+	  memory leak) when the pjsip session is in a disconnected state. Except when
+	  the method is a BYE. Then it waits until a transport error occurs or an event
+	  timeout.
+
+	  ASTERISK-27345 #close
+
+	  Reported by: Corey Farrell
+
+	  Change-Id: I1e724737b758c20ac76d19d3611e3d2876ae10ed
+
+2017-10-03 16:19 +0000 [8f7073be6d]  Richard Mudgett <rmudgett at digium.com>
+
+	* AST-2017-010: Fix cdr_object_update_party_b_userfield_cb() buf overrun
+
+	  cdr_object_update_party_b_userfield_cb() could overrun the fixed buffer if
+	  the supplied string is too long.  The long string could be supplied by
+	  external means using the CDR(userfield) function.
+
+	  This may seem reminiscent to AST-2017-001 (ASTERISK_26897) and it is.  The
+	  earlier patch fixed the buffer overrun for Party A's userfield while this
+	  patch fixes the same thing for Party B's userfield.
+
+	  ASTERISK-27337
+
+	  Change-Id: I0fa767f65ecec7e676ca465306ff9e0edbf3b652
+
+2017-10-30 15:33 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.18.0 Released.
+
+2017-10-25 20:01 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.18.0-rc2 Released.
+
+2017-10-22 17:32 +0000 [db233704f4]  Joshua Colp <jcolp at digium.com>
+
+	* res_xmpp: Ensure the connection filter is available.
+
+	  Users of the API that res_xmpp provides expect that a
+	  filter be available on the client at all times. When
+	  OAuth authentication support was added this requirement
+	  was not maintained.
+
+	  This change merely moves the OAuth authentication to
+	  after the filter is created, ensuring users of res_xmpp
+	  can add things to the filter as needed.
+
+	  ASTERISK-27346
+
+	  Change-Id: I4ac474afe220e833288ff574e32e2b9a23394886
+	  (cherry picked from commit 07e17fd04ffcf204400898660a4c118666596d5d)
+
+2017-10-23 13:42 +0000 [72bf65f44f]  Ben Ford <bford at digium.com>
+
+	* http.c: Fix http header send content.
+
+	  Currently ast_http_send barricades a portion of the content that
+	  needs to be sent in order to establish a connection for things
+	  like the ARI client. The conditional and contents have been changed
+	  to ensure that everything that needs to be sent, will be sent.
+
+	  ASTERISK-27372
+
+	  Change-Id: I8816d2d8f80f4fefc6dcae4b5fdfc97f1e46496d
+
+2017-10-13 17:46 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.18.0-rc1 Released.
+
+2017-10-13 12:09 +0000 [4bc2aca9b7]  Kevin Harwell <kharwell at digium.com>
+
+	* AMI: Increase version number
+
+	  Bump the AMI patch number since the following new addition was made:
+
+	  * Added a new CancelAtxfer action that cancels an attended transfer.
+
+	  Change-Id: I9bac528791bd62ef0e99243903b6bc7a6c7ab182
+
+2017-08-25 08:19 +0000 [6d3ee9fb93]  Thomas Sevestre <thomassevestre at free.fr>
+
+	* features, manager : Add CancelAtxfer AMI action
+
+	  Add action to cancel feature attended transfer with AMI interface
+
+	  ASTERISK-27215 #close
+
+	  Change-Id: Iab8a81362b5a1757e2608f70b014ef863200cb42
+
+2017-10-06 04:55 +0000 [21d502818f]  Daniel Tryba <daniel at tryba.nl>
+
+	* res_pjsip_session: Prevent user=phone being added to anonimized URIs.
+
+	  Move ast_sip_add_usereqphone to be called after anonymization of URIs,
+	  to prevent the user_eq_phone adding "user=phone" to URIs containing a
+	  username that is not a phonenumber (RFC3261 19.1.1). An extra call to
+	  ast_sip_add_usereqphone on the saved version before anonymization is
+	  added to add user=phone" to the PAI.
+
+	  ASTERISK-27047 #close
+
+	  Change-Id: Ie5644bc66341b86dc08b1f7442210de2e6acdec6
+
+2017-10-06 05:14 +0000 [af09996178]  Daniel Tryba <daniel at tryba.nl>
+
+	* res_pjsip: Prevent "user=phone" being added multiple times to header
+
+	  ast_sip_add_usereqphone adds "user=phone" to the header every time is is
+	  called without checking whether the param already exists. Preventing
+	  this by searching to string representation of header for "user=phone".
+
+	  ASTERISK-26988 #close
+
+	  Change-Id: Ib84383b07254de357dc6a98d91fc1d2c2c3719e6
+
+2017-10-10 09:49 +0000 [8e05796e81]  Tzafrir Cohen <tzafrir.cohen at xorcom.com>
+
+	* cdr_mysql: avoid releasing a config string
+
+	  Fixes a memory corruption issue after a reload of cdr_mysql.
+
+	  Issue was accidentally included in 747beb1ed159f89a3b58742e4257740b3d6d6bba .
+
+	  ASTERISK-27270 #close
+
+	  Change-Id: I90b6a9d18710c0f9009466370bd5f4bac5d5d12e
+
+2017-10-05 18:12 +0000 [5f6bad6733]  Richard Mudgett <rmudgett at digium.com>
+
+	* cdr.c: Defer misc checks.
+
+	  Try to defer some checks until needed in case there is an early exit.
+
+	  Change-Id: Ibc6b34c38a4f60ad4f9b67984b7d070a07257064
+
+2017-10-11 07:03 +0000 [f3f141781c]  George Joseph <gjoseph at digium.com>
+
+	* chan_vpb:  Fix a gcc 7 out-of-bounds complaint
+
+	  chan_vpb was trying to use sizeof(*p->play_dtmf), where
+	  p->play_dtmf is defined as char[16], to get the length of the array
+	  but since p->play_dtmf is an actual array, sizeof(*p->play_dtmf)
+	  returns the size of the first array element, which is 1.  gcc7
+	  validly complains because the context in which it's used could
+	  cause an out-of-bounds condition.
+
+	  Change-Id: If9c4bfdb6b02fa72d39e0c09bf88900663c000ba
+
+2017-10-06 02:39 +0000 [416e35589e]  Nathan Bruning <nathan at iperity.com>
+
+	* app_queue.c: clear moh field in init_queue
+
+	  ASTERISK-27301 #close
+
+	  Change-Id: Ic31361f34e2de3b6470e68fc37205a7711082eba
+
+2017-10-10 12:01 +0000 [e71a65a358]  Sean Bright <sean.bright at gmail.com>
+
+	* app_originate: Set ORIGINATE_STATUS correctly on failure
+
+	  We were ignoring the return value from ast_pbx_outgoing_exten() and
+	  ast_pbx_outgoing_app() which could fail before setting the reason code.
+	  This resulted in failures being reported as success.
+
+	  ASTERISK-25266 #close
+	  Reported by: Allen Ford
+
+	  Change-Id: Idf16237b7e41b527d2c69c865829128686beeb3b
+
+2017-10-02 16:46 +0000 [42fdfffefc]  Richard Mudgett <rmudgett at digium.com>
+
+	* cdr.c: Eliminated simple RAII_VAR usages.
+
+	  Change-Id: I150505db307249a962987e7b941bdd369bb91f35
+
+2017-10-09 22:51 +0000 [48971e4d43]  Corey Farrell <git at cfware.com>
+
+	* res_pjproject: Fix cleanup of buildopts vector.
+
+	  ASTERISK-27306
+
+	  Change-Id: I3bed0edf3f55b1d4adcbabb25ec14f11dc766c72
+
+2017-10-03 16:09 +0000 [128f7ffaa2]  Richard Mudgett <rmudgett at digium.com>
+
+	* cdr.c: Replace redundant check with an ast_assert()
+
+	  The only caller of cdr_object_fn_table.process_party_b() explicitly does
+	  the check before calling.
+
+	  Change-Id: Ib0c53cdf5048227842846e0df9d2c19117c45618
+
+2017-10-02 17:41 +0000 [3525081a7c]  Richard Mudgett <rmudgett at digium.com>
+
+	* cdr.c: Replace inlined code with ao2_t_replace()
+
+	  Change-Id: I9f424f5282ca7d833592f958d95f1b2bafb549b0
+
+2017-09-29 12:07 +0000 [7366657a9a]  Richard Mudgett <rmudgett at digium.com>
+
+	* cdr.c: Use current ao2 flag names
+
+	  Change-Id: Ib59d7d2f2a4a822754628f2c48a308d6791a6e6e
+
+2017-09-29 12:31 +0000 [34d55352a5]  Richard Mudgett <rmudgett at digium.com>
+
+	* cdr.h: Fix doxygen comments.
+
+	  * Also some misc formatting in cdr.c.
+
+	  Change-Id: Ied89a28802a662c37c43326a1aafdce596e0df4a
+
+2017-09-20 18:36 +0000 [d388c18abf]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_registrar.c: Update remove_existing AOR contact handling.
+
+	  When "rewrite_contact" is enabled, the "max_contacts" count option can
+	  block re-registrations because the source port from the endpoint can be
+	  random.  When the re-registration is blocked, the endpoint may give up
+	  re-registering and require manual intervention.
+
+	  * The "remove_existing" option now allows a registration to succeed by
+	  displacing any existing contacts that now exceed the "max_contacts" count.
+	  Any removed contacts are the next to expire.  The behaviour change is
+	  beneficial when "rewrite_contact" is enabled and "max_contacts" is greater
+	  than one.  The removed contact is likely the old contact created by
+	  "rewrite_contact" that the device is refreshing.
+
+	  ASTERISK-27192
+
+	  Change-Id: I64c107a10b70db1697d17136051ae6bf22b5314b
+
+2017-10-04 10:46 +0000 [82592c3673]  Corey Farrell <git at cfware.com>
+
+	* res_pjsip: Fix issues that prevented shutdown of modules.
+
+	  res_pjsip and res_pjsip_session had circular references, preventing both
+	  modules from shutting down.
+	  * Move session supplement registration to res_pjsip.
+	  * Use create internal functions for use by pjsip_message_filter.c.
+
+	  ASTERISK-27306
+
+	  Change-Id: Ifbd5c19ec848010111afeab2436f9699da06ba6b
+
+2017-10-09 08:15 +0000 [6b16fa12c8]  Sean Bright <sean.bright at gmail.com>
+
+	* res_config_sqlite: Don't enable SQLite CDRs when running 'make samples'
+
+	  Change-Id: I65a5190b2732b2246d67472db70dd37db64ddad4
+
+2017-10-08 14:05 +0000 [39b68a41f7]  David Hajek <david.hajek at daktela.com>
+
+	* res/res_ari.c Fix: Memory leaks in ARI when using Content-Type: application/json
+
+	  ASTERISK-27305
+	  Reported by: David Hajek
+	  Tested by: David Hajek
+
+	  Change-Id: Ife3e289062e6cf7d0e7d342dbf79ed96feff441e
+
+2017-10-08 09:11 +0000 [209916981a]  Alexander Traud <pabstraud at compuserve.com>
+
+	* tcptls: Do not re-bind to wildcard on client creation.
+
+	  Since ASTERISK-26922, this issue affected only those chan_sip which were
+	  * enabled for dual-stack (bindaddr=::), and
+	  * enabled for TCP (tcpenable=yes) and/or TLS (tlsenable=yes), and
+	  * tried to register and/or invite a IPv4-only service,
+	  * via TCP and/or TLS.
+	  Now, ast_tcptls_client_create does not re-bind to [::] anymore.
+
+	  ASTERISK-27324 #close
+
+	  Change-Id: I4b242837bdeb1ec7130dc82505c6180a946fd9b5
+
+2017-10-05 16:26 +0000 [f1163c0f6f]  Corey Farrell <git at cfware.com>
+
+	* res_pjsip: Fix leak of persistent endpoint references.
+
+	  Do not manually call sip_endpoint_apply_handler from load_all_endpoints.
+	  This is not necessary and causes memory leaks.
+
+	  Additionally reinitialize persistent->aors when we reuse a persistent
+	  object with a new endpoint.
+
+	  ASTERISK-27306
+
+	  Change-Id: I59bbfc8da8a14d5f4af8c5bb1e71f8592ae823eb
+
+2017-10-05 17:59 +0000 [8bf4be1048]  Corey Farrell <git at cfware.com>
+
+	* vector: multiple evaluation of elem in AST_VECTOR_ADD_SORTED.
+
+	  Use temporary variable to prevent multiple evaluations of elem argument.
+	  This resolves a memory leak in res_pjproject startup.
+
+	  ASTERISK-27317 #close
+
+	  Change-Id: Ib960d7f5576f9e1a3c478ecb48995582a574e06d
+
+2017-10-05 15:54 +0000 [5110600f1e]  Corey Farrell <git at cfware.com>
+
+	* res_pjsip: Fix leak of fake_auth references.
+
+	  pjsip_distributor leaks references to fake_auth when the default realm
+	  has not changed.
+
+	  ASTERISK-27306
+
+	  Change-Id: I3fcf103b3680ad2d1d4610dcd6738eeaebf4d202
+
+2017-10-05 20:23 +0000 [462dd7c2de]  Corey Farrell <git at cfware.com>
+
+	* main/strings: Fix uninitialized value.
+
+	  ast_strings_match uses sscanf and checks for non-zero return to verify a
+	  token was parsed. This is incorrect as sscanf returns EOF (-1) for errors.
+
+	  ASTERISK-27318 #close
+
+	  Change-Id: Ifcece92605f58116eff24c5a0a3b0ee08b3c87b1
+
+2017-09-28 02:56 +0000 [29c442b587]  Benoît Dereck-Tricot <benoit.dereck-tricot at eyepea.eu>
+
+	* res_calendar_icalendar: Filter out occurrences superceded by another VEVENT
+
+	  When we are loading the calendars, we call libical's
+	  icalcomponent_foreach_recurrence method for each VEVENT component that
+	  we have in our calendar.
+
+	  That method has no knowledge concerning the existence of the other
+	  VEVENT components and will feed our callback with all ocurrences
+	  matching the requested time span.
+
+	  The occurrences generated by icalcomponent_foreach_recurrence while
+	  expanding a recurring VEVENT's RRULE and RDATE properties can be
+	  superceded by an other VEVENT sharing the same UID.
+
+	  I use an external iterator (in libical terminology) to avoid messing
+	  with the internal ones from the calling function, and search for
+	  VEVENTS which could supersede the current occurrence.
+
+	  The event which can invalidate this occurence needs to have:
+
+	  - the same UID as our recurrent component (comp)
+	  - a RECURRENCE-ID property, which represents the start time of this
+	    occurrence
+
+	  If one component is found, just clean and return.
+
+	  ASTERISK-27296 #close
+	  Reported by: Benoît Dereck-Tricot
+
+	  Change-Id: I8587ae3eaa765af7cb21eda3b6bf84e8a1c87af8
+
+2017-10-03 15:16 +0000 [6c30f4a2d1]  Torrey Searle <torrey at voxbone.com>
+
+	* contrib/thirdparty/sip_to_pjsip: add additional flag mappings
+
+	  add mappings for udptl redundancy, rtptimeout, and debug flags
+
+	  Change-Id: Ie73cf5c83c05dee01eb9624ede76c1a30225d73a
+
+2017-10-02 07:48 +0000 [6dfe5b29b6]  Daniel Tryba <daniel at pocos.nl>
+
+	* res_pjsip_caller_id chan_sip: Comply to RFC 3323 values for privacy
+
+	  Currently privacy requests are only granted if the Privacy header
+	  value is exactly "id" (defined in RFC 3325). It ignores any other
+	  possible value (or a combination there of). This patch reverses the
+	  logic from testing for "id" to grant privacy, to testing for "none" and
+	  granting privacy for any other value. "none" must not be used in
+	  combination with any other value (RFC 3323 section 4.2).
+
+	  ASTERISK-27284 #close
+
+	  Change-Id: If438a21f31a962da32d7a33ff33bdeb1e776fe56
+
+2017-09-28 17:37 +0000 [0945f10d3b]  Richard Mudgett <rmudgett at digium.com>
+
+	* app_queue.c: Fix announcements when announce-to-first-user not enabled.
+
+	  The previous patch for ASTERISK-27216 made it so you wouldn't get any
+	  position or periodic announcements unless you had announce-to-first-user
+	  enabled.  The announce-to-first-user feature was added by ASTERISK_21782
+	  as a result of the patch which introduced the redundant announcements that
+	  ASTERISK-27216 removes.
+
+	  * By noting that the makeannouncement variable is used to suppresses the
+	  first user announcement, we set its initial value to the
+	  announce-to-first-user enable setting.
+
+	  ASTERISK-27216
+
+	  Change-Id: Ieaeb7dbea8ae7073086b775fbafe0625b000b10a
+
+2017-09-21 14:43 +0000 [a433bb38b5]  Richard Mudgett <rmudgett at digium.com>
+
+	* heap.c: No need to calloc heap pointer array.
+
+	  Change-Id: I5ae2f316229f336eb90d99c7af7ed07a33097e68
+
+2017-09-27 13:45 +0000 [47620ea862]  George Joseph <gjoseph at digium.com>
+
+	* logger:  Bring back ability to  turn debug on by source file
+
+	  Somewhere along the way we lost the ability to debug individual
+	  source files.  For modules, this wasn't a big deal but all the
+	  source files in ./main are in the one "core" module so debugging
+	  individual core capabilities was almost impossible.
+
+	  * Added a test to DEBUG_ATLEAST that also checks __FILE__ instead
+	  of just module name.  Any source file will work even if it's in
+	  a module subdirectory.
+
+	  Change-Id: Icc0af41837f3b1679dec7af21fa32cd1f7469f6e
+
+2017-09-26 11:01 +0000 [d70d7b2f5d]  George Joseph <gjoseph at digium.com>
+
+	* pjsip_message_filter: Fix regression causing bad contact address
+
+	  The "res_pjsip:  Filter out non SIP(S) requests" commit moved the
+	  filtering of messages to pjproject's PJSIP_MOD_PRIORITY_TRANSPORT_LAYER
+	  in order to filter out incoming bad uri schemes as early as possible.
+	  Since the change affected outgoing messages as well and the TRANSPORT
+	  layer is the last to be run on outgoing messages, we were overwriting
+	  the setting of external_signaling_address (which is set earlier by
+	  res_pjsip_nat) with an internal address.
+
+	  * pjsip_message_filter now registers itself as a pjproject module
+	  twice.  Once in the TSX layer for the outgoing messages (as it was
+	  originally), then a second time in the TRANSPORT layer for the
+	  incoming messages to catch the invalid uri schemes.
+
+	  ASTERISK-27295
+	  Reported by: Sean Bright
+
+	  Change-Id: I2c90190c43370f8a9d1c4693a19fd65840689c8c
+
+2017-09-13 21:31 +0000 [221d8a5c24]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_rtp_asterisk.c: Fix bridge_p2p_rtp_write() reentrancy potential.
+
+	  The bridge_p2p_rtp_write() has potential reentrancy problems.
+
+	  * Accessing the bridged RTP members must be done with the instance1 lock
+	  held.  The DTMF and asymmetric codec checks must be split to be done with
+	  the correct RTP instance struct locked.  i.e., They must be done when
+	  working on the appropriate side of the point to point bridge.
+
+	  * Forcing the RTP mark bit was referencing the wrong side of the point to
+	  point bridge.  The set mark bit is used everywhere else to set the mark
+	  bit when sending not receiving.
+
+	  The patches for ASTERISK_26745 and ASTERISK_27158 did not take into
+	  account that not everything carried by RTP uses a codec.  The telephony
+	  DTMF events are not exchanged with a codec.  As a result when
+	  RFC2833/RFC4733 sent digits you would crash if "core set debug 1" is
+	  enabled, the DTMF digits would always get passed to the core even though
+	  the local native RTP bridge is active, and the DTMF digits would go out
+	  using the wrong SSRC id.
+
+	  * Add protection for non-format payload types like DTMF when updating the
+	  lastrxformat and lasttxformat.  Also protect against non-format payload
+	  types when checking for asymmetric codecs.
+
+	  ASTERISK-27292
+
+	  Change-Id: I6344ab7de21e26f84503c4d1fca1a41579364186
+
+2017-09-25 13:09 +0000 [f3b1b64d21]  Sean Bright <sean.bright at gmail.com>
+
+	* pjproject: Patch to correct STUN FINGERPRINT usage
+
+	  Change-Id: I0e453253dff1388b0186b36c754457c1d0d12db6
+
+2017-09-25 10:59 +0000 [8d2c3effc2]  Richard Mudgett <rmudgett at digium.com>
+
+	* channel.c: Fix invalid reference in conditionaled out code.
+
+	  ASTERISK-27289
+
+	  Change-Id: I7a415948116493050614d9f4fa91ffbe0c21ec4c
+
+2017-09-25 07:25 +0000 [690f7f7c76]  George Joseph <gjoseph at digium.com>
+
+	* build:  A few gcc 7 error fixes
+
+	  Change-Id: I7b5300fbf1af7d88d47129db13ad6dbdc9b553ec
+
+2017-09-22 10:02 +0000 [f39af4d36d]  Sean Bright <sean.bright at gmail.com>
+
+	* res_pjsip: Use ast_sip_is_content_type() where appropriate
+
+	  Change-Id: If3ab0d73d79ac4623308bd48508af2bfd554937d
+
+2017-09-19 05:22 +0000 [c98e980fff]  Rodrigo Ramírez Norambuena <a at rodrigoramirez.com>
+
+	* res_config_pgsql: Fix removed support to previous for versions PostgreSQL 9.1
+
+	  In PostgreSQL 9.1 the backslash are string literals and not the escape
+	  of characters.
+
+	  In previous issue ASTERISK_26057 was fixed the use of escape LIKE but the
+	  support for old version of Postgresql than 9.1 was dropped. The sentence
+	  before make was "ESCAPE '\'" but in version before than 9.1  need it to be
+	  as follow "ESCAPE '\\'".
+
+	  ASTERISK-27283
+
+	  Change-Id: I96d9ee1ed7693ab17503cb36a9cd72847165f949
+
+2017-09-15 02:59 +0000 [0adf6f3bd9]  Stefan Engström <stefanen at kth.se>
+
+	* app_queue: Only do announcement logic between ringing cycles
+
+	  This patch reverts the change by patch 2263 from old reviewboard.
+	  Note that reverting that 2263-patch still preserves the behaviour that
+	  the commit log of the 2263-patch claimed to add. The reason for this is:
+
+	  The function wait_for_answer is only called from try_calling which
+	  in turn is only called from the main for loop in queue_exec, and
+	  earlier in that loop we already check the things that's removed by
+	  this patch. There's no need to check those things twice each loop
+	  iteration, and I think the proper place to check it is before each
+	  ringing cycle. By checking it in wait_for_answer, you allow the issue
+	  explained in the jira - that the head caller hears announcements while
+	  the agents' sip phones are actively ringing.
+
+	  Reported-by: Stefan Engström
+	  Tested-by: Stefan Engström
+	  ASTERISK-27216 #close
+
+	  Change-Id: Ic4290dc75256f9743900c6762ee1bb915f672db0
+
+2017-09-07 04:41 +0000 [da40976987]  Jean Aunis <jean.aunis at prescom.fr>
+
+	* bridge : Fix one-way direct-media when early bridging with native_rtp
+
+	  When two channels were early bridged in a native_rtp bridge, the RTP description
+	  on one side was not updated when the other side answered.
+	  This patch forbids non-answered channels to enter a native_rtp bridge, and
+	  triggers a bridge reconfiguration when an ANSWER frame is received.
+
+	  ASTERISK-27257
+
+	  Change-Id: If1aaee1b4ed9658a1aa91ab715ee0a6413b878df
+
+2017-09-19 10:38 +0000 [828a0611bc]  George Joseph <gjoseph at digium.com>
+
+	* res_pjsip_pubsub:  Check for Content-Type header in rx_notify_request
+
+	  pubsub_on_rx_notify_request wasn't checking for a null
+	  Content-Type header before checking that it was
+	  application/simple-message-summary.
+
+	  ASTERISK-27279
+	  Reported by: Ross Beer
+
+	  Change-Id: Iec2a6c4d2e74af37ff779ecc9fd35644c5c4ea52
+
+2017-09-19 09:34 +0000 [94f616e5e2]  David J. Pryke <david+extra.asterisk at pryke.us>
+
+	* chan_sip: Expose read-only access to the full SIP INVITE Request-URI
+
+	  Provide a way to get the contents of the the Request URI from the initial SIP
+	  INVITE in dial plan function call. (In this case "${CHANNEL(ruri)}")
+
+	  ASTERISK-27278
+	  Reported by: David J. Pryke
+	  Tested by: David J. Pryke
+
+	  Change-Id: I1dd4d6988eed1b6c98a9701e0e833a15ef0dac3e
+
+2017-09-18 10:27 +0000 [cfc0ca1fb5]  Alexander Traud <pabstraud at compuserve.com>
+
+	* tcptls: Fixed a white space error.
+
+	  ASTERISK-26606
+
+	  Change-Id: I81a7268ef7ba012d4d80d44c70b6276d48e397fa
+
+2017-09-18 10:00 +0000 [99a08eb7ab]  Alexander Traud <pabstraud at compuserve.com>
+
+	* res_srtp: lower log level of auth failures
+
+	  Previously, sRTP authentication failures were reported on log level WARNING.
+	  When such failures happen, each RT(C)P packet is affected, spamming the log.
+	  Now, those failures are reported at log level VERBOSE 2. Furthermore, the
+	  amount is further reduced (previously all two seconds, now all three seconds).
+	  Additionally, the new log entry informs whether media (RTP) or statistics (RTCP)
+	  are affected.
+
+	  ASTERISK-16898 #close
+
+	  Change-Id: I6c98d46b711f56e08655abeb01c951ab8e8d7fa0
+
+2017-09-13 03:46 +0000 [f1eb36ea51]  alex <alexandr.revin at gmail.com>
+
+	* cdr_mysql.c: Apply cdrzone to start and answer
+
+	  Change-Id: I7de0a5adc89824a5f2b696fc22c80fc22dff36b0
+
+2017-08-25 17:01 +0000 [6d4b801c83]  Richard Mudgett <rmudgett at digium.com>
+
+	* AST-2017-008: Improve RTP and RTCP packet processing.
+
+	  Validate RTCP packets before processing them.
+
+	  * Validate that the received packet is of a minimum length and apply the
+	  RFC3550 RTCP packet validation checks.
+
+	  * Fixed potentially reading garbage beyond the received RTCP record data.
+
+	  * Fixed rtp->themssrc only being set once when the remote could change
+	  the SSRC.  We would effectively stop handling the RTCP statistic records.
+
+	  * Fixed rtp->themssrc to not treat a zero value as special by adding
+	  rtp->themssrc_valid to indicate if rtp->themssrc is available.
+
+	  ASTERISK-27274
+
+	  Make strict RTP learning more flexible.
+
+	  Direct media can cause strict RTP to attempt to learn a remote address
+	  again before it has had a chance to learn the remote address the first
+	  time.  Because of the rapid relearn requests, strict RTP could latch onto
+	  the first remote address and fail to latch onto the direct media remote
+	  address.  As a result, you have one way audio until the call is placed on
+	  and off hold.
+
+	  The new algorithm learns remote addresses for a set time (1.5 seconds)
+	  before locking the remote address.  In addition, we must see a configured
+	  number of remote packets from the same address in a row before switching.
+
+	  * Fixed strict RTP learning from always accepting the first new address
+	  packet as the new stream.
+
+	  * Fixed strict RTP to initialize the expected sequence number with the
+	  last received sequence number instead of the last transmitted sequence
+	  number.
+
+	  * Fixed the predicted next sequence number calculation in
+	  rtp_learning_rtp_seq_update() to handle overflow.
+
+	  ASTERISK-27252
+
+	  Change-Id: Ia2d3aa6e0f22906c25971e74f10027d96525f31c
+
+2017-09-13 14:14 +0000 [5075cc8eed]  Sean Bright <sean.bright at gmail.com>
+
+	* res_calendar: On reload, update all configuration
+
+	  This changes the behavior of res_calendar to drop all existing calendars
+	  and re-create them whenever a reload is done. The Calendar API provides
+	  no way for configuration information to be pushed down to calendar
+	  'techs' so updated settings would not take affect until a module
+	  unload/load was done or Asterisk was restarted.
+
+	  Asterisk 15+ already has a configuration option 'fetch_again_at_reload'
+	  that performs a similar function.
+
+	  Also fix a tiny memory leak in res_calendar_caldav while we're at it.
+
+	  ASTERISK-25524 #close
+	  Reported by: Jesper
+
+	  Change-Id: Ib0f8057642e9d471960f1a79fd42e5a3ce587d3b
+
+2017-09-13 16:23 +0000 [63900374fa]  George Joseph <gjoseph at digium.com>
+
+	* res_pjsip:  Filter out non SIP(S) requests
+
+	  Incoming requests with non sip(s) URIs in the Request, To, From
+	  or Contact URIs are now rejected with
+	  PJSIP_SC_UNSUPPORTED_URI_SCHEME (416).  This is performed in
+	  pjsip_message_filter (formerly pjsip_message_ip_updater) and is
+	  done at pjproject's "TRANSPORT" layer before a request can even
+	  reach the distributor.
+
+	  URIs read by res_pjsip_outbound_publish from pjsip.conf are now
+	  also checked for both length and sip(s) scheme.  Those URIs read
+	  by outbound registration and aor were already being checked for
+	  scheme but their error messages needed to be updated to include
+	  scheme failure as well as length failure.
+
+	  Change-Id: Ibb2f9f1d2dc7549da562af4cbd9156c44ffdd460
+
+2017-09-13 14:08 +0000 [db785ddb92]  Sean Bright <sean.bright at gmail.com>
+
+	* res_calendar: Various fixes
+
+	  * The way that we were looking at XML elements for CalDAV was extremely
+	    fragile, so use SAX2 for increased robustness.
+
+	  * Don't complain about a 'channel' not be specified if autoreminder is
+	    not set. Assume that if 'channel' is not set, we don't want to be
+	    notified.
+
+	  * Fix some truncated CLI output in 'calendar show calendar' and make the
+	    'Autoreminder' description a bit more clear
+
+	  ASTERISK-24588 #close
+	  Reported by: Stefan Gofferje
+
+	  ASTERISK-25523 #close
+	  Reported by: Jesper
+
+	  Change-Id: I200d11afca6a47e7d97888f286977e2e69874b2c
+
+2017-09-13 09:38 +0000 [0688f61a01]  Sean Bright <sean.bright at gmail.com>
+
+	* chan_rtp: Use μ-law by default instead of signed linear
+
+	  Multicast/Unicast RTP do not use SDP so we need to use a format that
+	  cleanly maps to one of the static RTP payload types. Without this
+	  change, an Originate to a Multicast or Unicast channel without a format
+	  specified would produce no audio on the receiving device.
+
+	  ASTERISK-21399 #close
+	  Reported by: Tzafrir Cohen
+
+	  Change-Id: I97e332b566e85da04b0004b9b0daae746cfca0e3
+
+2017-09-11 05:46 +0000 [ed2a4ee81e]  George Joseph <gjoseph at digium.com>
+
+	* res_pjsip:  Add handling for incoming unsolicited MWI NOTIFY
+
+	  A new endpoint parameter "incoming_mwi_mailbox" allows Asterisk to
+	  receive unsolicited MWI NOTIFY requests and make them available to
+	  other modules via the stasis message bus.
+
+	  res_pjsip_pubsub has a new handler "pubsub_on_rx_mwi_notify_request"
+	  that parses a simple-message-summary body and, if
+	  endpoint->incoming_mwi_account is set, calls ast_publish_mwi_state
+	  with the voice-message counts from the message.
+
+	  Change-Id: I08bae3d16e77af48fcccc2c936acce8fc0ef0f3c
+
+2017-09-08 21:41 +0000 [044674c0cd]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_rtp_asterisk.c: Add doxygen to RTCP payload types.
+
+	  Change-Id: I3f20ce428777cc4ce9c13b2f808d29ff8c873998
+
+2017-09-11 05:52 +0000 [5ff2d06aa6]  George Joseph <gjoseph at digium.com>
+
+	* alembic:  Fix typo in add_auto_info_to_endpoint_dtmf_mode
+
+	  The downgrade function was missing "_v2" at the end of the
+	  alter column type.
+
+	  Change-Id: Iaa9bcef48d6f3590ce07a61342d8e66f00263d8e
+
+2017-09-10 06:17 +0000 [babb617f20]  Walter Doekes <walter+asterisk at wjd.nu>
+
+	* res/res_pjsip: Fix localnet checks in pjsip, part 2.
+
+	  In 45744fc53, I mistakenly broke SDP media address rewriting by
+	  misinterpreting which address was checked in the localnet comparison.
+
+	  Instead of checking the remote peer address to decide whether we need
+	  media address rewriting, we check our local media address: if it's
+	  local, then we rewrite. This feels awkward, but works and even made
+	  directmedia work properly if you set local_net. (For the record: for
+	  local peers, the SDP media rewrite code is not called, so the
+	  comparison does no harm there.)
+
+	  ASTERISK-27248 #close
+
+	  Change-Id: I566be1c33f4d0a689567d451ed46bab9c3861d4f
+
+2017-09-05 11:13 +0000 [ad606844be]  Florian Floimair <f.floimair at commend.com>
+
+	* alembic: Add support for MS-SQL
+
+	  MS-SQL has no native Enum-type support and therefore
+	  needs to work with constraints.
+	  Since these constraints need unique names the suggested approach
+	  referenced in the following alembic documentation has been applied:
+	  http://bit.ly/2x9r8pb
+
+	  ASTERISK-27255 #close
+
+	  Change-Id: I4a399ba3eed41a33ce8cb294968ad340221580ee
+
+2017-09-05 07:31 +0000 [2aefc6e5fe]  Jacek Konieczny <j.konieczny at eggsoft.pl>
+
+	* func_cdr: honour 'u' flag on dummy channel
+
+	  Fixes ${CDR(...,u)} when used in cdr_custom.conf
+
+	  ASTERISK-27165 #close
+
+	  Change-Id: Ia4e0b6ba93e03d27886354c279737790e2cd6a83
+
+2017-09-06 16:05 +0000 [c0d4f1880e]  Scott Griepentrog <scott at griepentrog.com>
+
+	* chan_sip: when getting sip pvt return failure if not found
+
+	  In handle_request_invite, when processing a pickup, a call
+	  is made to get_sip_pvt_from_replaces to locate the pvt for
+	  the subscription. The pvt is assumed to be valid when zero
+	  is returned indicating no error, and is dereferenced which
+	  can cause a crash if it was not found.
+
+	  This change checks the not found case and returns -1 which
+	  allows the calling code to fail appropriately.
+
+	  ASTERISK-27217 #close
+	  Reported-by: Bryan Walters
+
+	  Change-Id: I6bee92b8b8b85fcac3fd66f8c00ab18bc1765612
+
+2017-09-06 10:50 +0000 [e4797b2cbd]  Sean Bright <sean.bright at gmail.com>
+
+	* app_waitforsilence: Cleanup & don't treat missing frames as 'noise'
+
+	  * WaitForSilence completes successfully if it receives no media in the
+	    specified timeout, but when acting as WaitForNoise that logic needs
+	    to be reversed.
+
+	  * Use standard argument parsing macros and add some error checking for
+	    invalid values.
+
+	  * The documentation indicated that the first argument to both
+	    WaitForSilence and WaitForNoise was required when it was not. Update
+	    the documentation to reflect that.
+
+	  * Wrap up some behavior in structs to avoid boolean checks all over the
+	    place.
+
+	  ASTERISK-24066 #close
+	  Reported by: M vd S
+
+	  Change-Id: I01d40adc5b63342bb5018a1bea2081a0aa191ef9
+
+2017-09-01 05:17 +0000 [186ef1a657]  George Joseph <gjoseph at digium.com>
+
+	* stasis/control:  Fix possible deadlock with swap channel
+
+	  If an error occurs during a bridge impart it's possible that
+	  the "bridge_after" callback might try to run before
+	  control_swap_channel_in_bridge has been signalled to continue.
+	  Since control_swap_channel_in_bridge is holding the control lock
+	  and the callback needs it, a deadlock will occur.
+
+	  * control_swap_channel_in_bridge now only holds the control
+	    lock while it's actually modifying the control structure and
+	    releases it while the bridge impart is running.
+	  * bridge_after_cb is now tolerant of impart failures.
+
+	  Change-Id: Ifd239aa93955b3eb475521f61e284fcb0da2c3b3
+
+2017-09-06 05:23 +0000 [597d1f8951]  Vitezslav Novy <a1 at vnovy.net>
+
+	* chan_sip: Do not change IP address in SDP origin line (o=) in SIP reINVITE
+
+	  If directmedia=yes is configured, when call is answered, Asterisk sends reINVITE
+	  to both parties to set up media path directly between the endpoints.
+	  In this reINVITE msg SDP origin line (o=) contains IP address of endpoint
+	  instead of IP of asterisk. This behavior violates RFC3264, sec 8:
+	  "When issuing an offer that modifies the session,
+	  the "o=" line of the new SDP MUST be identical to that in the
+	  previous SDP, except that the version in the origin field MUST
+	  increment by one from the previous SDP."
+	  This patch assures IP address of Asterisk is always sent in
+	  SDP origin line.
+
+	  ASTERISK-17540
+	  Reported by:  saghul
+
+	  Change-Id: I533a047490c43dcff32eeca8378b2ba02345b64e
+
+2017-09-06 07:54 +0000 [15ddc9acb3]  George Joseph <gjoseph at digium.com>
+
+	* alembic: Fix enum creation for dtls_fingerprint
+
+	  Change-Id: Ic061c5066a146616a68376881c7e4cf6d6e7e7db
+
+2017-09-05 11:08 +0000 [2370469645]  Florian Floimair <f.floimair at commend.com>
+
+	* alembic: fix erroneous commit for add_prune_on_boot
+
+	  Added include for postgresql ENUM type and
+	  redefined values in the same way as in the
+	  other migration scripts.
+
+	  ASTERISK-27254 #close
+
+	  Change-Id: Id667304cdf3891b1c2f7d35fab3e2a84026159fa
+
+2017-09-06 03:15 +0000 [13aa1241c3]  Alexander Traud <pabstraud at compuserve.com>
+
+	* res_srtp: Add support for libsrtp2.1.
+
+	  Asterisk is able to use libSRTP 2.0.x. However since libSRTP 2.1.x, the macro
+	  SRTP_AES_ICM got renamed to SRTP_AES_ICM_128. Beside to still compile with
+	  previous versions of libSRTP, this change allows libSRTP 2.1.x as well.
+
+	  ASTERISK-27253 #close
+
+	  Change-Id: I2e6eb3c3bc844fee8a624060a2eb6f182dc70315
+
+2017-09-05 09:35 +0000 [598a18ffee]  Ben Ford <bford at digium.com>
+
+	* chan_pjsip: Suppress frame warnings.
+
+	  When rtp_keepalive is on for a PJSIP endpoint dialing to another
+	  Asterisk instance also using PJSIP, Asterisk will continue to print
+	  warning messages about not being able to send frames of a certain
+	  type. This suppresses that warning message.
+
+	  Change-Id: I0332a05519d7bda9cacfa26d433909ff1909be67
+
+2017-08-25 17:05 +0000 [6c922b3157]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_rtp_asterisk.c: Check RTP packet version earlier.
+
+	  Change-Id: Ic6493a7d79683f3e5845dff1cee49445fd5a0adf
+
+2017-09-05 10:05 +0000 [3f7d0b63fc]  Sean Bright <sean.bright at gmail.com>
+
+	* formats: Restore previous fread() behavior
+
+	  Some formats are able to handle short reads while others are not, so
+	  restore the previous behavior for the format modules so that we don't
+	  have spurious errors when playing back files.
+
+	  ASTERISK-27232 #close
+	  Reported by: Jens T.
+
+	  Change-Id: Iab7f52b25a394f277566c8a2a4b15a692280a300
+
+2017-09-05 09:16 +0000 [45744fc53d]  Walter Doekes <walter+asterisk at wjd.nu>
+
+	* res/res_pjsip: Standardize/fix localnet checks across pjsip.
+
+	  In 2dee95cc (ASTERISK-27024) and 776ffd77 (ASTERISK-26879) there was
+	  confusion about whether the transport_state->localnet ACL has ALLOW or
+	  DENY semantics.
+
+	  For the record: the localnet has DENY semantics, meaning that "not in
+	  the list" means ALLOW, and the local nets are in the list.
+
+	  Therefore, checks like this look wrong, but are right:
+
+	      /* See if where we are sending this request is local or not, and if
+	         not that we can get a Contact URI to modify */
+	      if (ast_apply_ha(transport_state->localnet, &addr) != AST_SENSE_ALLOW) {
+	          ast_debug(5, "Request is being sent to local address, "
+	                       "skipping NAT manipulation\n");
+
+	  (In the list == localnet == DENY == skip NAT manipulation.)
+
+	  And conversely, other checks that looked right, were wrong.
+
+	  This change adds two macro's to reduce the confusion and uses those
+	  instead:
+
+	      ast_sip_transport_is_nonlocal(transport_state, addr)
+	      ast_sip_transport_is_local(transport_state, addr)
+
+	  ASTERISK-27248 #close
+
+	  Change-Id: Ie7767519eb5a822c4848e531a53c0fd054fae934
+
+2017-09-05 05:23 +0000 [786c4791f9]  George Joseph <gjoseph at digium.com>
+
+	* res_pjsip_t38:  Make t38_reinvite_response_cb tolerant of NULL channel
+
+	  t38_reinvite_response_cb can get called by res_pjsip_session's
+	  session_inv_on_tsx_state_changed in situations where session->channel
+	  is NULL.  If it is, the ast_log warning segfaults because it tries
+	  to get the channel name from a NULL channel.
+
+	  * Check session->channel and print "unknown channel" when it's NULL.
+
+	  ASTERISK-27236
+	  Reported by: Ross Beer
+
+	  Change-Id: I4326e288d36327f6c79ab52226d54905cdc87dc7
+
+2017-09-01 16:17 +0000 [55f30c29fd]  Sean Bright <sean.bright at gmail.com>
+
+	* rtp_engine: Prevent possible double free with DTLS config
+
+	  ASTERISK-27225 #close
+	  Reported by: Richard Kenner
+
+	  Change-Id: I097b81734ef730f8603c0b972909d212a3a5cf89
+
+2017-09-01 13:15 +0000 [f36db2dbdc]  Sean Bright <sean.bright at gmail.com>
+
+	* chan_ooh323: Fix confusing indentation warning
+
+	  ASTERISK-27177 #close
+	  Reported by: Tzafrir Cohen
+
+	  Change-Id: I40311c404edb2302a7543ad5ca7a06b2a38f2d97
+
+2017-09-01 09:51 +0000 [5f4863d4f9]  Sean Bright <sean.bright at gmail.com>
+
+	* app_directory: Handle a NULL mailbox without crashing
+
+	  ASTERISK-27241 #close
+	  Reported by: David Moore
+
+	  Change-Id: Ibbbca85517b04c315406ebfe3b6f7e0763daedc6
+
+2017-07-24 10:48 +0000 [990b017668]  George Joseph <gjoseph at digium.com>
+
+	* pjsip_message_ip_updater:  Fix issue handling "tel" URIs
+
+	  sanitize_tdata was assuming all URIs were SIP URIs so when a non
+	  SIP uri was in the From, To or Contact headers, the unconditional
+	  cast of a non-pjsip_sip_uri structure to pjsip_sip_uri caused
+	  a segfault when trying to access uri->other_param.
+
+	  * Added PJSIP_URI_SCHEME_IS_SIP(uri) || PJSIP_URI_SCHEME_IS_SIPS(uri)
+	    checks before attempting to cast or use the returned uri.
+
+	  ASTERISK-27152
+	  Reported-by: Ross Beer
+
+	  Change-Id: Id380df790e6622c8058a96035f8b8f4aa0b8551f
+
+2017-07-01 19:24 +0000 [04ee3eb774]  Corey Farrell <git at cfware.com>
+
+	* AST-2017-006: Fix app_minivm application MinivmNotify command injection
+
+	  An admin can configure app_minivm with an externnotify program to be run
+	  when a voicemail is received.  The app_minivm application MinivmNotify
+	  uses ast_safe_system() for this purpose which is vulnerable to command
+	  injection since the Caller-ID name and number values given to externnotify
+	  can come from an external untrusted source.
+
+	  * Add ast_safe_execvp() function.  This gives modules the ability to run
+	  external commands with greater safety compared to ast_safe_system().
+	  Specifically when some parameters are filled by untrusted sources the new
+	  function does not allow malicious input to break argument encoding.  This
+	  may be of particular concern where CALLERID(name) or CALLERID(num) may be
+	  used as a parameter to a script run by ast_safe_system() which could
+	  potentially allow arbitrary command execution.
+
+	  * Changed app_minivm.c:run_externnotify() to use the new ast_safe_execvp()
+	  instead of ast_safe_system() to avoid command injection.
+
+	  * Document code injection potential from untrusted data sources for other
+	  shell commands that are under user control.
+
+	  ASTERISK-27103
+
+	  Change-Id: I7552472247a84cde24e1358aaf64af160107aef1
+
+2017-05-22 10:36 +0000 [1a022285dd]  Joshua Colp <jcolp at digium.com>
+
+	* res_rtp_asterisk: Only learn a new source in learn state.
+
+	  This change moves the logic which learns a new source address
+	  for RTP so it only occurs in the learning state. The learning
+	  state is entered on initial allocation of RTP or if we are
+	  told that the remote address for the media has changed. While
+	  in the learning state if we continue to receive media from
+	  the original source we restart the learning process. It is
+	  only once we receive a sufficient number of RTP packets from
+	  the new source that we will switch to it. Once this is done
+	  the closed state is entered where all packets that do not
+	  originate from the expected source are dropped.
+
+	  The learning process has also been improved to take into
+	  account the time between received packets so a flood of them
+	  while in the learning state does not cause media to be switched.
+
+	  Finally RTCP now drops packets which are not for the learned
+	  SSRC if strict RTP is enabled.
+
+	  ASTERISK-27013
+
+	  Change-Id: I56a96e993700906355e79bc880ad9d4ad3ab129c
+
+2017-08-29 14:22 +0000 [4aaccb7795]  Richard Mudgett <rmudgett at digium.com>
+
+	* bridge_native_rtp.c: Fixup native_rtp_framehook()
+
+	  * Fix framehook to test frame type for control frame.
+	  * Made framehook exit early if frame type is not a control frame.
+	  * Eliminated RAII_VAR in framehook.
+	  * Use switch instead of else-if ladder for control frame handling.
+
+	  Change-Id: Ia555fc3600bd85470e3c0141147dbe3ad07c1d18
+
+2017-08-29 09:26 +0000 [d2ace23248]  Sean Bright <sean.bright at gmail.com>
+
+	* confbridge: Handle user hangup during name recording
+
+	  This prevents orphaned CBAnn channels from getting stuck in the bridge.
+
+	  ASTERISK-26994 #close
+	  Reported by: James Terhune
+
+	  Change-Id: I5e43e832a9507ec3f2c59752cd900b41dab80457
+
+2017-08-25 21:06 +0000 [a45af32983]  Andre Nazario <samoied at users.sourceforge.net>
+
+	* chan_pjsip: Add tag info in CHANNEL function
+
+	  Create local_tag and remote_tag in CHANNEL info to get tag from From and
+	  To headers of a SIP dialog.
+
+	  ASTERISK-27220
+
+	  Change-Id: I59b16c4b928896fcbde02ad88f0e98922b15d524
+
+2017-08-25 13:44 +0000 [9e6efcace5]  Sean Bright <sean.bright at gmail.com>
+
+	* voicemail: Fix various abuses of mkstemp
+
+	  mkstemp() returns a unique filename, but appending an extension to that
+	  filename does not guarantee uniqueness. Instead, use mkdtemp() and we
+	  can put whatever extension we want on the files that we create inside
+	  the directory.
+
+	  In the case of app_minivm, we also now properly clean up any temporary
+	  files that we create.
+
+	  ASTERISK-20858 #close
+	  Reported by: Walter Doekes
+
+	  Change-Id: I30ad04f0e115f0b11693ff678ba5184d8b938e43
+
+2017-08-25 12:20 +0000 [01b5913ce0]  Sean Bright <sean.bright at gmail.com>
+
+	* app_record: Resolve some absolute vs. relative filename bugs
+
+	  If the Record() application is called with a relative filename that
+	  includes directories, we were not properly creating the intermediate
+	  directories and Record() would fail.
+
+	  Secondarily, updated the documentation for RECORDED_FILE to mention
+	  that it does not include a filename extension.
+
+	  Finally, rewrote the '%d' functionality to be a bit more straight
+	  forward and less noisy.
+
+	  ASTERISK-16777 #close
+	  Reported by: klaus3000
+
+	  Change-Id: Ibc2640cba3a8c7f17d97b02f76b7608b1e7ffde2
+
+2017-08-23 10:01 +0000 [bf178a0f4f]  Florian Floimair <f.floimair at commend.com>
+
+	* alembic: Add dtls_fingerprint column in ps_endpoints table
+
+	  The ps_endpoints table was missing the dtls_fingerprint column
+	  introduced with commit adba2a8d7fd.
+
+	  ASTERISK-27168 #close
+
+	  Change-Id: I9cb5006f7f50718b5239919562773adabb334cfd
+
+2016-02-28 19:05 +0000 [fff2f68616]  Matt Jordan <mjordan at digium.com>
+
+	* main/app: Only look to end of file if ':end' is specified, and not just ':'
+
+	  There is a little known feature in app_controlplayback that will cause the
+	  specified offset to be used relative to the end of a file if a ':end' is
+	  detected within the filename.
+
+	  This feature is pretty bad, but okay.
+
+	  However, a bug exists in this code where a ':' detected in the filename
+	  will cause the end pointer to be non-NULL, even if the full ':end' isn't
+	  specified. This causes us to treat an unspecified offset (0) as being
+	  "start playing from the end of the file", resulting in no file playback
+	  occurring.
+
+	  This patch fixes this bug by resetting the end pointer if ':end' is not
+	  found in the filename.
+
+	  ASTERISK-23608 #close
+	  Reported by: Jonathan White
+
+	  Change-Id: Ib4c7b1b45283e4effd622a970055c51146892f35
+	  (cherry picked from commit 13efea24f7ce6ccc01d1a5a0603be2636d83a408)
+
+2017-08-24 09:42 +0000 [579d4593ac]  Sean Bright <sean.bright at gmail.com>
+
+	* app_queue: Evaluate realtime queues when running dialplan functions
+
+	  ASTERISK-19103 #close
+	  Reported by: Jim Van Meggelen
+
+	  Change-Id: I4bd32a9d1fcebb8ac56bff0e084d4f53e31b692b
+
+2017-08-23 09:19 +0000 [0af145de2d]  Sean Bright <sean.bright at gmail.com>
+
+	* app_voicemail: Honor escape digits in "greeting only" mode
+
+	  ASTERISK-21241 #close
+	  Reported by: Eelco Brolman
+	  Patches:
+	  	Patch uploaded by Eelco Brolman (License 6442)
+
+	  Change-Id: Icbe39b5c82a49b46cf1d168dc17766f3d84f54fe
+
+2017-08-24 08:35 +0000 [d251a961ac]  Sean Bright <sean.bright at gmail.com>
+
+	* res_smdi: Clean up memory leak
+
+	  Change-Id: I1e33290929e1aa7c5b9cb513f8254f2884974de8
+
+2017-08-11 11:40 +0000 [3f22b53349]  Richard Mudgett <rmudgett at digium.com>
+
+	* bridge_softmix.c: Remove always true test.
+
+	  Change-Id: I26238df2ff0d0f6dfe95c3aa35da588f1ee71727
+
+2017-08-17 16:46 +0000 [b88c3a4209]  Sungtae Kim <pchero21 at gmail.com>
+
+	* app_queue: Fix initial hold time queue statistic
+
+	  Fixed to use correct initial value and fixed to use the
+	  correct queue info to check the first value.
+
+	  ASTERISK-27204
+
+	  Change-Id: Ia9e36c828e566e1cc25c66f73307566e4acb8e73
+
+2017-08-21 04:28 +0000 [8e99969000]  Torrey Searle <torrey at voxbone.com>
+
+	* res/res_pjsip_session: allow SDP answer to be regenerated
+
+	  If an SDP answer hasn't been sent yet, it's legal to change it.
+	  This is required for PJSIP_DTMF_MODE to work correctly, and can
+	  also have use in the future for updating codecs too.
+
+	  ASTERISK-27209 #close
+
+	  Change-Id: Idbbfb7cb3f72fbd96c94d10d93540f69bd51e7a1
+
+2017-08-20 08:15 +0000 [4faf77feec]  Michael Kuron <m.kuron at gmx.de>
+
+	* res_xmpp: fix inverted return code check in OAuth
+
+	  fetch_access_token calls func_curl via ast_func_read. The latter returns 0 upon
+	  success and -1 if the function is not available.
+	  This commit inverts the return code check so that an error is printed if the
+	  module is not loaded and not if it is loaded.
+
+	  ASTERISK-27207 #close
+
+	  Change-Id: I9ef903f80702d1218e8701f65a4e5e918e6548fb
+
+2017-08-17 12:00 +0000 [a6251ec373]  Sean Bright <sean.bright at gmail.com>
+
+	* res_calendar_icalendar: Properly handle recurring events
+
+	  When looking for recurring events, use the correct end time based on the
+	  configured 'timeframe.'
+
+	  ASTERISK-27174 #close
+	  Reported by: Mark Thompson
+
+	  Change-Id: Id90c3cfc79d561a5521d79be176683e225f2edef
+
+2017-08-16 15:43 +0000 [572b5307e0]  George Joseph <gjoseph at digium.com>
+
+	* Fix downloader not working with curl
+
+	  The codec/dpma downloader wasn't handling curl correctly.  The logic
+	  that transforms makeopts into a bash-sourceable file wasn't
+	  handling the make 'or' command in DOWNLOAD_TIMEOUT so bash was
+	  looking for an 'or' command.
+
+	  That logic has been eliminated.  Instead of trying to transform
+	  and source makeopts, the downloader now calls a make scriptlet
+	  to print the value of a specific variable.  This way, make handles
+	  the ors (or any other make construct that happens to creep into
+	  that file).
+
+	  ASTERISK-27202
+	  Reported by: Sean McCord
+
+	  Change-Id: Iadfb6693528e4d4da7b8bb201fa66da2c71c7f99
+
+2017-08-15 15:15 +0000 [8594f73a81]  Richard Mudgett <rmudgett at digium.com>
+
+	* configure: Check cache for valid pjproject tarball before downloading.
+
+	  On a fresh Asterisk source directory, the bundled pjproject tarball is
+	  unconditionally downloaded even if the tarball is already in a specified
+	  cache directory.
+
+	  * Made check if the pjproject tarball is valid in the cache directory
+	  before downloading the tarball on a fresh source directory.
+
+	  Change-Id: Ic7ec842d3c97ecd8dafbad6f056b7fdbce41cae5
+
+2017-08-15 11:14 +0000 [d08342b0cb]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip: Fix prune_on_boot to remove only contacts for the host.
+
+	  * Check that the contact's reg_server matches the host's name before
+	  deleting any prune_on_boot contacts.  We don't want to delete reliable
+	  transport contacts made with other servers if the ps_contacts database
+	  table is shared with other servers.
+
+	  Thanks to Ross Beer for pointing out that the original prune logic would
+	  delete reliable transport contacts from other servers.
+
+	  ASTERISK-27147
+
+	  Change-Id: I8e439d0d1c266ffdfd7b73d1e5e466180a689bd0
+
+2017-08-04 09:25 +0000 [54e3ac402f]  Andrey Egorov <andr06 at gmail.com>
+
+	* res_xmpp: Google OAuth 2.0 protocol support for XMPP / Motif
+
+	  Add ability to use tokens instead of passwords according to Google OAuth 2.0
+	  protocol.
+
+	  ASTERISK-27169
+	  Reported by: Andrey Egorov
+	  Tested by: Andrey Egorov
+
+	  Change-Id: I07f7052a502457ab55010a4d3686653b60f4c8db
+
+2017-08-10 14:18 +0000 [bac3e8c08b]  Richard Mudgett <rmudgett at digium.com>
+
+	* STUN/netsock2: Fix some valgrind uninitialized memory findings.
+
+	  * netsock2.c: Test the addr->len member first as it may be the only member
+	  initialized in the struct.
+
+	  * stun.c:ast_stun_handle_packet(): The combinded[] local array could get
+	  used uninitialized by ast_stun_request().  The uninitialized string gets
+	  copied to another location and could overflow the destination memory
+	  buffer.
+
+	  These valgrind findings were found for ASTERISK_27150 but are not
+	  necessarily a fix for the issue.
+
+	  Change-Id: I55f8687ba4ffc0f69578fd850af006a56cbc9a57
+
+2017-08-02 18:44 +0000 [1cf2c79f37]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_outbound_registration.c: Re-REGISTER on transport shutdown.
+
+	  The fix for the issue is broken up into three parts.
+
+	  This is part three which handles the client side of REGISTER requests.
+	  The registered contact may no longer be valid on the server when the
+	  transport used is reliable and the connection is broken.
+
+	  * Re-REGISTER our contact if the reliable transport is broken after
+	  registration completes.  We attempt to re-REGISTER immediately to minimize
+	  the time we are unreachable.  Time may have already passed between the
+	  connection being broken and the loss being detected.
+
+	  * Reorder sip_outbound_registration_state_alloc() so the STATSD_GUAGE's
+	  are still correct if an allocation failure happens.
+
+	  ASTERISK-27147
+
+	  Change-Id: I3668405b1ee75dfefb07c0d637826176f741ce83
+
+2017-07-31 14:21 +0000 [07d026b4cd]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip: Remove ephemeral registered contacts on transport shutdown.
+
+	  The fix for the issue is broken up into three parts.
+
+	  This is part two which handles the server side of REGISTER requests when
+	  rewrite_contact is enabled.  Any registered reliable transport contact
+	  becomes invalid when the transport connection becomes disconnected.
+
+	  * Monitor the rewrite_contact's reliable transport REGISTER contact for
+	  shutdown.  If it is shutdown then the contact must be removed because it
+	  is no longer valid.  Otherwise, when the client attempts to re-REGISTER it
+	  may be blocked because the invalid contact is there.  Also if we try to
+	  send a call to the endpoint using the invalid contact then the endpoint is
+	  not likely to see the request.  The endpoint either won't be listening on
+	  that port for new connections or a NAT/firewall will block it.
+
+	  * Prune any rewrite_contact's registered reliable transport contacts on
+	  boot.  The reliable transport no longer exists so the contact is invalid.
+
+	  * Websockets always rewrite the REGISTER contact address and the transport
+	  needs to be monitored for shutdown.
+
+	  * Made the websocket transport set a unique name since that is what we use
+	  as the ao2 container key.  Otherwise, we would not know which transport we
+	  find when one of them shuts down.  The names are also used for PJPROJECT
+	  debug logging.
+
+	  * Made the websocket transport post the PJSIP_TP_STATE_CONNECTED state
+	  event.  Now the global keep_alive_interval option, initially idle shutdown
+	  timer, and the server REGISTER contact monitor can work on wetsocket
+	  transports.
+
+	  * Made the websocket transport set the PJSIP_TP_DIR_INCOMING direction.
+	  Now initially idle websockets will automatically shutdown.
+
+	  ASTERISK-27147
+
+	  Change-Id: I397a5e7d18476830f7ffe1726adf9ee6c15964f4
+
+2017-07-28 18:26 +0000 [ca261d4b70]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip: PJSIP Transport state monitor refactor.
+
+	  The fix for the issue is broken up into three parts.
+
+	  This is part one which refactors the transport state monitor code to allow
+	  more modules to be able to monitor transports.
+
+	  * Pull the management of PJPROJECT's transport state callback code from
+	  res_pjsip_transport_management.c into res_pjsip.  Now other modules can
+	  dynamically add and remove themselves from transport monitoring without
+	  worrying about breaking PJPROJECT's callback chain.
+
+	  * Add the ability for other modules to get a callback whenever a specific
+	  transport is shutdown.
+
+	  ASTERISK-27147
+
+	  Change-Id: I7d9a31371eb1487c9b7050cf82a9af5180a57912
+
+2017-07-27 15:36 +0000 [162f6ab845]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_transport_management.c: Rename some variables.
+
+	  * Use monitored instead of the misleading keepalive name.
+
+	  Change-Id: I9e5bcbb4ab2b82d49bcd0f06dfe85d15e0b552b6
+
+2017-08-10 09:09 +0000 [22575b6342]  Scott Griepentrog <scott at griepentrog.com>
+
+	* res_pjsip_messaging: IPv6 receive address needs brackets
+
+	  When handling an incoming SIP MESSAGE, PJSIP
+	  attaches the IP address that the message was
+	  received from to the message in the variable
+	  PJSIP_RECVADDR.  When the IP address is IPv6
+	  the :PORT appended results in an unparseable
+	  mess. By using an additional bit flag on the
+	  pj_sockaddr_print call, the conventional use
+	  of brackets around the address is achieved.
+
+	  ASTERISK-27193 #close
+
+	  Change-Id: I12342521f2ce87a5b6e4883d480a3fd957aa9fd9
+
+2017-08-09 08:01 +0000 [363d61ef58]  George Joseph <gjoseph at digium.com>
+
+	* configure:  Add --with-download-cache option
+
+	  To make building without an internet connection easier, a new
+	  ./configure option '--with-download-cache' was added that sets
+	  the cache for externals (like pjproject, the codecs and the DPMA),
+	  AND the sounds files.  It can also be specified as an environment
+	  variable named "AST_DOWNLOAD_CACHE".  The existing
+	  '--with-sounds-cache' option / SOUNDS_CACHE_DIR env variable and
+	  '--with-externals-cache' option / EXTERNALS_CACHE_DIR env variable
+	  remain and if specified, will override '--with-downloads-cache'.
+
+	  Change-Id: I5c3cf15ee61e8fe191b52732303e969854f8d861
+
+2017-07-26 09:17 +0000 [3608f96ea3]  Torrey Searle <torrey at voxbone.com>
+
+	* res_rtp_asterisk: enable rtcp & QOS stats on native bridge
+
+	  Asterisk wasn't generating or forwarding RTCP packets when native
+	  bridge was activated.  Also the stats weren't available via
+	  CHANNEL(qos). Now the RTCP stats are always calculated.
+
+	  ASTERISK-27158 #close
+
+	  Change-Id: I46fb8f61c95e836b9d2dda6054b0cf205c16037b
+
+2017-07-26 11:39 +0000 [0de7312fac]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip_session: Release media resources on session end quicker.
+
+	  A change was made long ago where the session was kept around
+	  until the underlying INVITE session had been destroyed. This
+	  had the side effect of also keeping the underlying media resources
+	  around for this time as well.
+
+	  This change ensures that when we know the session is ending we
+	  release the media resources immediately.
+
+	  ASTERISK-27110
+
+	  Change-Id: I643e431d5c3bf05cda220c1d39e824a505a29b82
+
+2017-08-02 16:08 +0000 [905c4ca3dc]  Corey Farrell <git at cfware.com>
+
+	* app_privacy: remove unused header asterisk/image.h
+
+	  Change-Id: I56ed530633a642633b18383821069e806c92ae82
+
+2017-08-03 13:13 +0000 [38dbc708e7]  Tzafrir Cohen <tzafrir.cohen at xorcom.com>
+
+	* Support GMIME 3.0
+
+	  Support building the Asterisk httpd with version 3.0 of gmime as
+	  well as earlier versions of that library.
+
+	  ASTERISK-27173
+
+	  Change-Id: I7e13dd05a3083ccb0df2dabf83110223f6a9fa8f
+
+2017-07-28 07:53 +0000 [c4f201cd73]  Torrey Searle <torrey at voxbone.com>
+
+	* res_rtp_asterisk:  Make P2P bridge Asymmetric codec aware
+
+	  Introduce a new property to rtp-engine to make it aware of
+	  the desire for assymetric codecs or not.  If asymmetric codecs
+	  is not allowed, the bridge will compare read/write formats
+	  and shut down the p2p bridge if needed
+
+	  ASTERISK-26745 #close
+
+	  Change-Id: I0d9c83e5356df81661e58d40a8db565833501a6f
+
+2017-08-03 21:30 +0000 [84b6a5efd7]  Corey Farrell <git at cfware.com>
+
+	* Correct some leaks in unit tests.
+
+	  * chan_sip: channel in test_sip_rtpqos_1.
+	  * test_config: config hook, config info and global config holder.
+	  * test_core_format: format in format_attribute_set_without_interface.
+	  * test_stream: unneeded frame duplication.
+	  * test_taskprocessor: task_data.
+
+	  Change-Id: I94d364d195cf3b3b5de2bf3ad565343275c7ad31
+
+2017-07-26 17:49 +0000 [f9a823e9dc]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_transport_websocket.c: Fix serializer ref leak.
+
+	  Change-Id: Ib5a19bfd597f63d9021baeb645fc11153b3afa57
+
+2017-08-02 18:41 +0000 [631180a0c3]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_outbound_registration.c: Misc fixes.
+
+	  * Remove unnecessary CMP_STOP.
+
+	  * In handle_client_registration() use DEBUG_ATLEAST() to only do work
+	  needed for the debug log message when the debug log message is needed.
+
+	  * In sip_outbound_registration_state_destroy() check state->registration
+	  for NULL.
+
+	  Change-Id: I656d0fa11dda0b00048103efb1558e67a426fd80
+
+2017-07-31 20:20 +0000 [7b84c6693e]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_nat.c: Remove unnecessary CMP_STOP.
+
+	  Change-Id: I6279b0d723bc3b75b8d65e81e02da9ea9bc0c3da
+
+2017-07-31 14:20 +0000 [a32614a2a8]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_registrar.c: Remove unnecessary CMP_STOP.
+
+	  Most uses of CMP_STOP are superfluous and are only respected when
+	  OBJ_MULTIPLE is used to search the container.
+
+	  Change-Id: I20571a202ec0aa1098bb2749eeba18de7ca110b8
+
+2017-08-03 11:30 +0000 [d066758a4c]  Corey Farrell <git at cfware.com>
+
+	* Fix compile error for old versions of GCC.
+
+	  Use -Wno-format-truncation only if supported by compiler.
+
+	  ASTERISK-27171 #close
+
+	  Change-Id: Iac0aed7a5bcaa16c21b7d62c4e4678d244c4ccb6
+
+2017-08-01 15:57 +0000 [ed1bce956e]  George Joseph <gjoseph at digium.com>
+
+	* Revert "res_pjsip_session: Release media resources on session end quicker."
+
+	  This reverts commit 98709642d640b490f327d220fdcdea6d45fd65d7.
+
+	  See the 15 branch review.
+
+	  Change-Id: I8476b3cdacaad5157fa36b6247d0e4cdf1e8d5c6
+
+2017-06-29 03:47 +0000 [9a09f7dd5d]  Niklas Larsson <niklas at tese.se>
+
+	* app_queue: Add priority to AMI QueueStatus
+
+	  Add priority to callers in AMI QueueStatus response
+
+	  ASTERISK-27092 #close
+
+	  Change-Id: I8d1f737a72c7c38f4cfe1a4ee3ecc0a4f85bd199
+
+2017-07-26 11:39 +0000 [3418d8d145]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip_session: Release media resources on session end quicker.
+
+	  A change was made long ago where the session was kept around
+	  until the underlying INVITE session had been destroyed. This
+	  had the side effect of also keeping the underlying media resources
+	  around for this time as well.
+
+	  This change ensures that when we know the session is ending we
+	  release the media resources immediately.
+
+	  ASTERISK-27110
+
+	  Change-Id: I3c6a82fe7d2c50b9dc9197cb12ef22f20d337501
+
+2017-07-26 08:48 +0000 [4d318cac68]  Sean Bright <sean.bright at gmail.com>
+
+	* res_pjsip_pidf_eyebeam_body_supplement: Correct status presentation
+
+	  This change fixes PIDF content generation when the underlying device
+	  state is considered in use. Previously it was incorrectly marked
+	  as closed meaning they were offline/unavailable. The code now
+	  correctly marks them as open.
+
+	  Additionally:
+
+	    * Generate an XML element for our activity instead of a using a text
+	      node.
+
+	    * Consider every extension state other than "unavailable" to be 'open'
+	      status.
+
+	    * Update the XML namespaces and structure to reflect those
+	      documented in RFC 4480
+
+	    * Use 'on-the-phone' (defined in RFC 4880) instead of 'busy' as the
+	      "in use" activity. This change results in eyeBeam using the
+	      appropriate icon for the watched user.
+
+	  This was tested on eyeBeam 1.5.20.2 build 59030 on Windows.
+
+	  ASTERISK-26659 #close
+	  Reported by: Abraham Liebsch
+	  patches:
+	    ASTERISK-26659.diff submitted by snuffy (license 5024)
+
+	  Change-Id: I6e5ad450f91106029fb30517b8c0ea0c2058c810
+
+2017-07-23 18:34 +0000 [114602f434]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip: Add support for dnsmgr to external_media_address.
+
+	  The "external_media_address" option on transports is now
+	  resolved using dnsmgr. This allows it to be automatically
+	  refreshed regularly if refreshes are enabled in dnsmgr.
+	  If the system is using a dynamic IP address a dynamic DNS
+	  hostname can be provided to keep the IP address up to
+	  date.
+
+	  Change-Id: Ia54771720dff0105bde55d5bbb81a3ba437e05b2
+
+2017-07-27 20:58 +0000 [0f49e6ee2e]  Corey Farrell <git at cfware.com>
+
+	* Fix compiler warnings on Fedora 26 / GCC 7.
+
+	  GCC 7 has added capability to produce warnings, this fixes most of those
+	  warnings.  The specific warnings are disabled in a few places:
+
+	  * app_voicemail.c: truncation of paths more than 4096 chars in many places.
+	  * chan_mgcp.c: callid truncated to 80 chars.
+	  * cdr.c: two userfields are combined to cdr copy, fix would break ABI.
+	  * tcptls.c: ignore use of deprecated method SSLv3_client_method().
+
+	  ASTERISK-27156 #close
+
+	  Change-Id: I65f280e7d3cfad279d16f41823a4d6fddcbc4c88
+
+2017-07-27 06:35 +0000 [0d58fefa30]  George Joseph <gjoseph at digium.com>
+
+	* bundled_pjproject:  Improve SSL/TLS error handling
+
+	  OpenSSL has 2 levels or error processing.  It's possible for the
+	  top layer to return SSL_ERROR_SYSCALL but the lower layer return
+	  no error, in which case processing should continue.  Only the top
+	  layer was being examined though so connections were being torn
+	  down when they didn't need to be.  This patch adds the examination
+	  of the lower level codes, and if they return no errors, allows
+	  processing to continue.
+
+	  ASTERISK-27001
+	  Reported-by: Ian Gilmour
+	  patches:
+	  	pjproject-2.6.patch submitted by Ian Gilmour (license 6889)
+
+	  Updated-by: George Joseph and Sauw Ming (Teluu)
+
+	  Merged to upstream pjproject on 7/27/2017 (commit 5631)
+
+	  Change-Id: I23844ca0c68ef1ee550f14d46f6dae57d33b7bd2
+
+2017-06-26 07:52 +0000 [423d01cf16]  Torrey Searle <torrey at voxbone.com>
+
+	* chan_pjsip: add a new function PJSIP_DTMF_MODE
+
+	  This function is a replica of SIPDtmfMode, allowing the DTMF mode of a
+	  PJSIP call to be modified on a per-call basis
+
+	  ASTERISK-27085 #close
+
+	  Change-Id: I20eef5da3e5d1d3e58b304416bc79683f87e7612
+
+2017-07-25 15:17 +0000 [c16000f201]  Sean Bright <sean.bright at gmail.com>
+
+	* res_rtp_asterisk: Fix mapping of pjsip's ICE roles to ours
+
+	  Change-Id: Ia578ede1a55b21014581793992a429441903278b
+
+2017-07-20 08:08 +0000 [708cdc0b8e]  Sergej Kasumovic <sergej at bicomsystems.com>
+
+	* res_stasis_device_state: Unsubscribe should remove old subscriptions
+
+	  Case scenario with Applications ARI:
+
+	  * Once you subscribe to deviceState with Applications REST API, it will be
+	  added into subscription pool.
+
+	  * When you unsubscribe it will remove from the device_state_subscription
+	  hash table but not from the subscription pool.
+
+	  * When you subscribe again, it will add it to pool again.
+
+	  * Now you will have two subscriptions and you will receive same event
+	  twice.
+
+	  This fix should now remove deviceState subscription from pool and it
+	  should fix unsubscribe on deviceState.
+
+	  ASTERISK-27130 #close
+
+	  Change-Id: I718b70d770a086e39b4ddba4f69a3c616d4476c4
+
+2017-07-24 13:30 +0000 [24bb5a8908]  Joshua Colp <jcolp at digium.com>
+
+	* core: Add VP9 passthrough support.
+
+	  This change adds VP9 as a known codec and creates a cached
+	  "vp9" media format for use.
+
+	  Change-Id: I025a93ed05cf96153d66f36db1839109cc24c5cc
+
+2017-07-21 15:57 +0000 [07f8e45a90]  Matthew Fredrickson <creslin at digium.com>
+
+	* format.h: Fix a few minor errors in comments.
+
+	  A few minor problems were found in comments in format.h.  This patch fixes them.
+
+	  Change-Id: I07f0bdb47b93359b361c4c3d8ecc87cd3199dd94
+
+2017-07-21 17:04 +0000 [7e9aa74daa]  Rusty Newton <rnewton at digium.com>
+
+	* say.c: Fix file locations for second, seconds, minute, minutes files
+
+	  The seconds and minutes files have always existed in the base language
+	  directory of the Core package. So say.c has always been calling the wrong
+	  location (under digits/) for those two files and in the case of second and
+	  minute they didn't exist in the Core packages at all.
+
+	  The 1.6 sounds release moves the second and minute files into Core from
+	  Extra for the languages that already had them. A future release will include
+	  the second and minute files for languages that didn't already have them.
+
+	  This patch just changes all the target locations for second, seconds,
+	  minute, and minutes that were under the digits subdir to be under the root of
+	  sounds instead. Which is where the sounds will be for some languages after 1.6
+	  sounds and for all languages after a future release.
+
+	  ASTERISK-25810 #close
+
+	  Change-Id: I05d9d4bee6a7237030530a46e7eb3df15f13f702
+	  Reported-by: Nicolas Riendeau
+
+2017-07-19 18:11 +0000 [7ff9d8785d]  Richard Mudgett <rmudgett at digium.com>
+
+	* app_voicemail.c: Allow mailbox entry on authentication retry prompt.
+
+	  The following testsuite voicemail tests were failing to re-enter the
+	  mailbox after the first login attempt.
+
+	  tests/apps/voicemail/authenticate_invalid_mailbox
+	  tests/apps/voicemail/authenticate_invalid_password
+
+	  The tests were noting the start of the vm-incorrect-mailbox prompt and
+	  immediately sending the mailbox for the next login attempt.  Since the
+	  invalid message playback had to complete before the digits were
+	  recognized, the test passed for the wrong reason and added approximately
+	  20 seconds to the test times.
+
+	  * Allow the vm-incorrect-mailbox prompt to get interrupted by the mailbox
+	  digits like the initial vm-login prompt so the tests are able to enter the
+	  intended mailbox.
+
+	  Change-Id: I1dc53fe917bfe03a4587b2c4cd24c94696a69df8
+
+2017-07-21 14:20 +0000 [4f93f75e7e]  Rusty Newton <rnewton at digium.com>
+
+	* Sounds: Update Makefile for Extra sounds 1.5.1 release
+
+	  Incrementing version for the Extra sounds release. 1.5.1 Extra sounds
+	  removes two prompts that were moved into the Core packages in the 1.6 Core
+	  sounds release.
+
+	  ASTERISK-27142 #close
+
+	  Change-Id: I82f017812b0ea9599e19dd4635afd55611f13ee7
+
+2017-07-20 09:57 +0000 [cea4ce246d]  Sean Bright <sean.bright at gmail.com>
+
+	* corosync: Fix corosync library name in configure.ac
+
+	  Also add new corosync packages to install_prereq.
+
+	  Reported by Travis Ryan in #asterisk-dev
+
+	  Change-Id: Ib861c95ba630fed62dc54e56784ad8446ed9d2db
+
+2017-07-18 15:04 +0000 [9a47dd7113]  Benjamin Keith Ford <bford at digium.com>
+
+	* pjsip: Increase maximum packet size.
+
+	  The maximum packet size for PJSIP has been increased to handle the
+	  multiple streams being added for WebRTC.
+
+	  Change-Id: I9ea1e8d02668c544acadcb1c6200e1cc1bd588b3
+
+2017-07-11 04:48 +0000 [1c3e7df26e]  Holger Hans Peter Freyther <holger at moiji-mobile.com>
+
+	* app_playback.c: Use the timezonename parameter
+
+	  In say_date_generic the timezonename parameter is passed but never
+	  used. Fix it by passing it to the ast_localtime function.
+
+	  ASTERISK-27124
+
+	  Change-Id: I6afa98f9163190043244b9f3ba91eb1874d1b586
+
+2017-07-16 12:18 +0000 [51761b759d]  Joshua Colp <jcolp at digium.com>
+
+	* res_rtp_asterisk: Use RTP component for ICE if RTCP-MUX is in use.
+
+	  This change makes it so that if an RTCP packet is being sent
+	  the RTP ICE component is used for sending if RTCP-MUX is in use.
+
+	  ASTERISK-27133
+
+	  Change-Id: I6200f611ede709602ee9b89501720c29545ed68b
+
+2017-07-11 09:55 +0000 [a4c85309f0]  Torrey Searle <torrey at voxbone.com>
+
+	* res/res_stasis_snoop: generate silence when audiohook returns null
+
+	  Currently when rtp is paused, no packets are written to the
+	  recorded audio file, causing the silence to be skipped and recording
+	  not properly time aligned.  The read handler as been adapted to
+	  return a silence frame of the correct size.
+
+	  ASTERISK-27128 #close
+
+	  Change-Id: I2d7f60650457860b9c70907b14426756b058a844
+
+2017-07-14 01:25 +0000 [3858d99b73]  Sergej Kasumovic <sergej at bicomsystems.com>
+
+	* app_confbridge: Make sure name recordings are always removed from the filesystem
+
+	  This commit fixes two possible scenarios:
+
+	  * When recording name and if during recording you hangup, file is never
+	  removed. This is due to the fact file location is nulled.
+	  * When recording name and if you hangup during thank-you prompt, file
+	  is never removed.
+
+	  ASTERISK-27123 #close
+
+	  Change-Id: I39b7271408b4b54ce880c5111a886aa8f28c2625
+
+2017-07-14 01:11 +0000 [cdd6ca488a]  Sergej Kasumovic <sergej at bicomsystems.com>
+
+	* chan_iax2: On reload make sure to check for existing MWI subscription
+
+	  On every reload of chan_iax2 module, MWI subscription was added, which
+	  results in additional taskprocessors being accumulated over time.
+
+	  This commit fixes it by making sure we check for existing subscription
+	  first.
+
+	  This was verified with 'core show taskprocessors' CLI command.
+
+	  ASTERISK-27122 #close
+
+	  Change-Id: Ie2ef528fd5ca01b933eeb88188cc10967899cfb9
+
+2017-07-13 15:43 +0000 [9f66fb7901]  Rusty Newton <rnewton at digium.com>
+
+	* Sounds: Update for core sounds 1.6 release
+
+	  Added necessary lines to make the en_NZ language set selectable and to get
+	  core sounds 1.6 pulled down.
+
+	  ASTERISK-26807 #close
+	  ASTERISK-25816 #close
+	  ASTERISK-26274 #close
+
+	  Change-Id: I84e4dd4696568cc1ba318d12ac4b075461d6eed4
+
+2017-07-10 14:04 +0000 [df49ad2528]  Corey Farrell <git at cfware.com>
+
+	* core: Add PARSE_TIMELEN support to ast_parse_arg and ACO.
+
+	  This adds support for parsing timelen values from config files.  This
+	  includes support for all flags which apply to PARSE_INT32.  Support for
+	  this parser is added to ACO via the OPT_TIMELEN_T option type.
+
+	  Fixes an issue where extra characters provided to ast_app_parse_timelen
+	  were ignored, they now cause an error.
+
+	  Testing is included.
+
+	  ASTERISK-27117 #close
+
+	  Change-Id: I6b333feca7e3f83b4ef5bf2636fc0fd613742554
+
+2017-07-12 15:07 +0000 [6d0ff310c6]  Sean Bright <sean.bright at gmail.com>
+
+	* basic-pbx: Remove res_pjsip_multihomed from sample config
+
+	  ASTERISK-27127 #close
+	  Reported by: HZMI8gkCvPpom0tM
+
+	  Change-Id: I2b0c54570d58156e37166ac536728af3b6c01789
+
+2017-07-11 07:26 +0000 [4e555437dc]  George Joseph <gjoseph at digium.com>
+
+	* res_musiconhold:  Add kill_escalation_delay, kill_method to class
+
+	  By default, when res_musiconhold reloads or unloads, it sends a HUP
+	  signal to custom applications (and all descendants), waits 100ms,
+	  then sends a TERM signal, waits 100ms, then finally sends a KILL
+	  signal.  An application which is interacting with an external
+	  device and/or spawns children of its own may not be able to exit
+	  cleanly in the default times, expecially if sent a KILL signal, or
+	  if it's children are getting signals directly from
+	  res_musiconhoild.
+
+	  * To allow extra time, the 'kill_escalation_delay'
+	    class option can be used to set the number of milliseconds
+	    res_musiconhold waits before escalating kill signals, with the
+	    default being the current 100ms.
+
+	  * To control to whom the signals are sent, the "kill_method" class
+	    option can be set to "process_group" (the default, existing
+	    behavior), which sends signals to the application and its
+	    descendants directly, or "process" which sends signals only to the
+	    application itself.
+
+	  Change-Id: Iff70a1a9405685a9021a68416830c0db5158603b
+
+2017-07-03 07:30 +0000 [4f2f3bfebf]  Tzafrir Cohen <tzafrir.cohen at xorcom.com>
+
+	* Avoid setting maxfiles for a remote asterisk
+
+	  Setting maxfiles (maximum number of open files) has no practical
+	  effect on a remote asterisk (rasterisk, rasterisk -x).
+
+	  It has an ill effect of printing an extra message, which
+	  may be annoying in case of -x.
+
+	  ASTERISK-27105 #close
+
+	  Change-Id: Iaf9eb344e4b4b517df91b736b27ec55f6a6921a2
+
+2017-07-05 15:31 +0000 [32b98ad956]  George Joseph <gjoseph at digium.com>
+
+	* http.c:  Reduce log spam
+
+	  Messages like "fwrite() failed: Connection reset by peer" are no
+	  help whatsoever, especially since they can be caused simply by a
+	  client disconnecting.
+
+	  * Make those WARNINGs DEBUGs.
+	  * Check the return of the headers fprintf.
+
+	  Change-Id: I17bd5f3621514152a7b2b263c801324c5e96568b
+
+2017-07-07 11:19 +0000 [25e18bf514]  Benjamin Keith Ford <bford at digium.com>
+
+	* res_pjsip: Fix crash with from_user containing invalid characters.
+
+	  If the from_user field contains certain characters (like @, {, ^, etc.),
+	  PJSIP will return a null value for the URI when attempting to parse it.
+	  This causes a crash when trying to dial out through a trunk that contains
+	  these invalid characters in its from_user field.
+
+	  This change checks the configuration and ensures that an endpoint will
+	  not be created if the from_user contains an invalid character. It also
+	  adds a null check to the PJSIP URI parsing as a backup.
+
+	  ASTERISK-27036 #close
+	  Reported by: Maxim Vasilev
+
+	  Change-Id: I0396fdb5080604e0bdf1277464d5c8a85db913d0
+
+2017-06-27 19:27 +0000 [8a803f75a0]  Richard Mudgett <rmudgett at digium.com>
+
+	* json.c: Add backtrace log to find 'Invalid UTF-8 string' errors
+
+	  Change-Id: I9020ff9f2b3749904317c0c173f47a1bbed6f929
+
+2017-07-05 13:39 +0000 [aa514f420b]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_rtp_asterisk.c: Fix TURN deadlock by using ICE session group lock.
+
+	  When a message is received on the TURN socket, the code processing the
+	  message needs to call into the ICE/STUN session for further processing.
+	  This code path locks the TURN group lock then the ICE/STUN group lock.  In
+	  another thread an ICE/STUN timer can fire off to send a keep alive message
+	  over the TURN socket.  In this code path, the ICE/STUN group lock is
+	  obtained then the TURN group lock is obtained to send the packet.  A
+	  classic deadlock case if the group locks are not the same.
+
+	  * Made TURN get created using the ICE/STUN session's group lock.
+
+	  NOTE: I was originally concerned that the ICE/STUN session can get
+	  recreated by ice_reset_session() for an event like RTCP multiplexing
+	  causing a change during SDP negotiation.  In this case the TURN group lock
+	  would become different.  However, TURN is also recreated as part of the
+	  ICE/STUN recreation in ice_create() when all known ICE candidates are
+	  added to the new ICE session.  While the ICE/STUN and TURN sessions are
+	  being recreated there is a period where the group locks could be
+	  different.
+
+	  ASTERISK-27023 #close
+	  Patches:
+	      res_rtp_asterisk-turn-deadlock-fix.patch (license #6502)
+	          patch uploaded by Michael Walton (modified)
+
+	  Change-Id: Ic870edb99ce4988a8c8eb6e678ca7f19da1432b9
+
+2017-06-23 11:17 +0000 [22c4c1a0ba]  Richard Mudgett <rmudgett at digium.com>
+
+	* bridge_native_rtp.c: Fix direct media video RTP instance ACL check.
+
+	  The video stream was using the audio stream RTP instance addresses to
+	  check if the video RTP gets directed to an allowed direct media Access
+	  Control List (ACL) address.  There is no guarantee that the video RTP
+	  instance uses the same addresses as the audio RTP instance.
+
+	  This looks like it has been a bug since v11 when direct media ACL was
+	  first added to chan_sip and then faithfully reproduced through a couple
+	  code refactorings into the new bridging architecture.
+
+	  Change-Id: I8ddd56320e0eea769f3ceed3fa5b6bdfb51d681a
+
+2017-06-29 13:58 +0000 [194625c1de]  Sean Bright <sean.bright at gmail.com>
+
+	* app_voicemail: Cleanup ODBC connection handling
+
+	  The primary focus of this patch is adding a missing call to
+	  ast_odbc_release_obj(), but is also a general cleanup of the ODBC
+	  related code in app_voicemail.
+
+	  ASTERISK-27093 #close
+
+	  Change-Id: I8e285142eaeb3146b4287a928276b70db76c902b
+
+2017-06-22 07:47 +0000 [154d2914fa]  Torrey Searle <torrey at voxbone.com>
+
+	* res/res_pjsip_t38  ensure t38 requests get rejected quickly
+
+	  arm the t38 webhook always, so we can correctly reject a
+	  T38 negotiation request when t38 is disabled on a channel
+
+	  Change-Id: Ib1ffe35aee145d4e0fe61dd012580be11aae079d
 
 2017-07-06 11:52 +0000  Asterisk Development Team <asteriskteam at digium.com>
 
diff --git a/README-SERIOUSLY.bestpractices.txt b/README-SERIOUSLY.bestpractices.txt
index 108adce..b170d29 100644
--- a/README-SERIOUSLY.bestpractices.txt
+++ b/README-SERIOUSLY.bestpractices.txt
@@ -94,6 +94,13 @@ your ITSP in a place where you didn't expect to allow it. There are a couple of
 ways in which you can mitigate this impact: stricter pattern matching, or using
 the FILTER() dialplan function.
 
+The CALLERID(num) and CALLERID(name) values are other commonly used values that
+are sources of data potentially supplied by outside sources.  If you use these
+values as parameters to the System(), MixMonitor(), or Monitor() applications
+or the SHELL() dialplan function, you can allow injection of arbitrary operating
+system command execution.  The FILTER() dialplan function is available to remove
+dangerous characters from untrusted strings to block the command injection.
+
 Strict Pattern Matching
 -----------------------
 
diff --git a/UPGRADE.txt b/UPGRADE.txt
index d023f40..334fad3 100644
--- a/UPGRADE.txt
+++ b/UPGRADE.txt
@@ -21,6 +21,12 @@
 === UPGRADE-12.txt  -- Upgrade info for 11 to 12
 ===========================================================
 
+From 13.17.0 to 13.18.0:
+
+Core:
+ - ast_app_parse_timelen now returns an error if it encounters extra characters
+   at the end of the string to be parsed.
+
 From 13.15.0 to 13.16.0:
 
 Core:
diff --git a/addons/cdr_mysql.c b/addons/cdr_mysql.c
index 70dab1a..ebe19ad 100644
--- a/addons/cdr_mysql.c
+++ b/addons/cdr_mysql.c
@@ -265,9 +265,7 @@ db_reconnect:
 			/* Need the type and value to determine if we want the raw value or not */
 			if (entry->staticvalue) {
 				value = ast_strdupa(entry->staticvalue);
-			} else if ((!strcmp(cdrname, "answer") ||
-				 !strcmp(cdrname, "end") ||
-				 !strcmp(cdrname, "disposition") ||
+			} else if ((!strcmp(cdrname, "disposition") ||
 				 !strcmp(cdrname, "amaflags")) &&
 				(strstr(entry->type, "int") ||
 				 strstr(entry->type, "dec") ||
@@ -277,7 +275,8 @@ db_reconnect:
 				 strstr(entry->type, "numeric") ||
 				 strstr(entry->type, "fixed"))) {
 				ast_cdr_format_var(cdr, cdrname, &value, workspace, sizeof(workspace), 1);
-			} else if (!strcmp(cdrname, "start")) {
+			} else if (!strcmp(cdrname, "start") || !strcmp(cdrname, "answer") ||
+				 !strcmp(cdrname, "end")) {
 				struct ast_tm tm;
 				char timestr[128];
 				ast_localtime(&cdr->start, &tm, ast_str_strlen(cdrzone) ? ast_str_buffer(cdrzone) : NULL);
@@ -371,7 +370,7 @@ static void free_strings(void)
 }
 
 static int my_unload_module(int reload)
-{ 
+{
 	struct column *entry;
 
 	ast_cli_unregister_multiple(cdr_mysql_status_cli, sizeof(cdr_mysql_status_cli) / sizeof(struct ast_cli_entry));
@@ -518,7 +517,6 @@ static int my_load_module(int reload)
 	} else {
 		calldate_compat = 0;
 	}
-	ast_free(compat);
 
 	if (res < 0) {
 		if (reload) {
diff --git a/addons/chan_ooh323.c b/addons/chan_ooh323.c
index b64d43c..44f339f 100644
--- a/addons/chan_ooh323.c
+++ b/addons/chan_ooh323.c
@@ -3181,7 +3181,7 @@ int reload_config(int reload)
 
 static char *handle_cli_ooh323_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-	char ip_port[30];
+	char ip_port[64];
 	struct ooh323_peer *prev = NULL, *peer = NULL;
 	
 	switch (cmd) {
@@ -3212,7 +3212,7 @@ static char *handle_cli_ooh323_show_peer(struct ast_cli_entry *e, int cmd, struc
 	}
 
 	if (peer) {
-		sprintf(ip_port, "%s:%d", peer->ip, peer->port);
+		sprintf(ip_port, "%s:%hu", peer->ip, peer->port);
 		ast_cli(a->fd, "%-15.15s%s\n", "Name: ", peer->name);
 		ast_cli(a->fd, "%s:%s,%s\n", "FastStart/H.245 Tunneling", peer->faststart?"yes":"no",
 					peer->h245tunneling?"yes":"no");
@@ -3280,7 +3280,7 @@ static char *handle_cli_ooh323_show_peers(struct ast_cli_entry *e, int cmd, stru
 {
 	struct ooh323_peer *prev = NULL, *peer = NULL;
 	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
-   char ip_port[30];
+   char ip_port[64];
 #define FORMAT  "%-15.15s  %-15.15s  %-23.23s  %-s\n"
 
 	switch (cmd) {
@@ -3303,7 +3303,7 @@ static char *handle_cli_ooh323_show_peers(struct ast_cli_entry *e, int cmd, stru
 	peer = peerl.peers;
 	while (peer) {
 		ast_mutex_lock(&peer->lock);
-		snprintf(ip_port, sizeof(ip_port), "%s:%d", peer->ip, peer->port);
+		snprintf(ip_port, sizeof(ip_port), "%s:%hu", peer->ip, peer->port);
 		ast_cli(a->fd, FORMAT, peer->name, 
 					peer->accountcode,
 					ip_port,
diff --git a/addons/ooh323c/src/ooSocket.c b/addons/ooh323c/src/ooSocket.c
index ee02f52..cbef6be 100644
--- a/addons/ooh323c/src/ooSocket.c
+++ b/addons/ooh323c/src/ooSocket.c
@@ -386,7 +386,7 @@ int ooSocketAccept (OOSOCKET socket, OOSOCKET *pNewSocket,
    if (*pNewSocket <= 0) return ASN_E_INVSOCKET;
 
    if (destAddr != 0) {
-      if ((host = ast_sockaddr_stringify_addr(&addr)) != NULL);
+      if ((host = ast_sockaddr_stringify_addr(&addr)) != NULL)
       	strncpy(destAddr, host, strlen(host));
    }
    if (destPort != 0)
diff --git a/apps/Makefile b/apps/Makefile
index 86e5caf..87e45bb 100644
--- a/apps/Makefile
+++ b/apps/Makefile
@@ -30,6 +30,8 @@ include $(ASTTOPDIR)/Makefile.moddir_rules
 clean::
 	rm -f confbridge/*.o confbridge/*.i confbridge/*.gcda confbridge/*.gcno
 
+app_voicemail.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION)
+
 app_confbridge.so: $(subst .c,.o,$(wildcard confbridge/*.c))
 $(subst .c,.o,$(wildcard confbridge/*.c)): _ASTCFLAGS+=$(call MOD_ASTCFLAGS,app_confbridge)
 
diff --git a/apps/app_chanspy.c b/apps/app_chanspy.c
index 2a472bf..aefe940 100644
--- a/apps/app_chanspy.c
+++ b/apps/app_chanspy.c
@@ -1425,7 +1425,7 @@ static int extenspy_exec(struct ast_channel *chan, const char *data)
 static int dahdiscan_exec(struct ast_channel *chan, const char *data)
 {
 	const char *spec = "DAHDI";
-	struct ast_flags flags;
+	struct ast_flags flags = {0};
 	struct spy_dtmf_options user_options = {
 		.cycle = '#',
 		.volume = '\0',
diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c
index c064660..3ea336a 100644
--- a/apps/app_confbridge.c
+++ b/apps/app_confbridge.c
@@ -2137,6 +2137,7 @@ static int conf_rec_name(struct confbridge_user *user, const char *conf_name)
 	}
 
 	if (res == -1) {
+		ast_filedelete(user->name_rec_location, NULL);
 		user->name_rec_location[0] = '\0';
 		return -1;
 	}
@@ -2228,6 +2229,7 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
 {
 	int res = 0, volume_adjustments[2];
 	int quiet = 0;
+	int async_delete_task_pushed = 0;
 	char *parse;
 	const char *b_profile_name = NULL;
 	const char *u_profile_name = NULL;
@@ -2315,7 +2317,11 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
 	if (!quiet &&
 		(ast_test_flag(&user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE) ||
 		(ast_test_flag(&user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE_REVIEW)))) {
-		conf_rec_name(&user, args.conf_name);
+		if (conf_rec_name(&user, args.conf_name)) {
+			pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
+			res = -1; /* Hangup during name recording */
+			goto confbridge_cleanup;
+		}
 	}
 
 	/* menu name */
@@ -2473,6 +2479,7 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
 		async_play_sound_file(conference,
 			conf_get_sound(CONF_SOUND_HAS_LEFT, conference->b_profile.sounds), NULL);
 		async_delete_name_rec(conference, user.name_rec_location);
+		async_delete_task_pushed = 1;
 	}
 
 	/* play the leave sound */
@@ -2501,6 +2508,9 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
 	}
 
 confbridge_cleanup:
+	if (!async_delete_task_pushed && !ast_strlen_zero(user.name_rec_location)) {
+		ast_filedelete(user.name_rec_location, NULL);
+	}
 	ast_bridge_features_cleanup(&user.features);
 	conf_bridge_profile_destroy(&user.b_profile);
 	return res;
diff --git a/apps/app_directory.c b/apps/app_directory.c
index b75e857..9cfcbf8 100644
--- a/apps/app_directory.c
+++ b/apps/app_directory.c
@@ -511,6 +511,11 @@ static struct ast_config *realtime_directory(char *context)
 		const char *mailbox = ast_variable_retrieve(rtdata, category, "mailbox");
 		const char *ctx = ast_variable_retrieve(rtdata, category, "context");
 
+		if (ast_strlen_zero(mailbox)) {
+			ast_debug(3, "Skipping result with missing or empty mailbox\n");
+			continue;
+		}
+
 		fullname = ast_variable_retrieve(rtdata, category, "fullname");
 		hidefromdir = ast_variable_retrieve(rtdata, category, "hidefromdir");
 		if (ast_true(hidefromdir)) {
@@ -531,7 +536,7 @@ static struct ast_config *realtime_directory(char *context)
 
 		/* Does the context exist within the config file? If not, make one */
 		if (!(cat = ast_category_get(cfg, ctx, NULL))) {
-			if (!(cat = ast_category_new(ctx, "", 99999))) {
+			if (!(cat = ast_category_new_dynamic(ctx))) {
 				ast_log(LOG_WARNING, "Out of memory\n");
 				ast_config_destroy(cfg);
 				if (rtdata) {
diff --git a/apps/app_followme.c b/apps/app_followme.c
index 602806b..5f9e220 100644
--- a/apps/app_followme.c
+++ b/apps/app_followme.c
@@ -1527,7 +1527,7 @@ outrun:
 	}
 	if (!ast_strlen_zero(targs->namerecloc)) {
 		int ret;
-		char fn[PATH_MAX];
+		char fn[PATH_MAX + sizeof(REC_FORMAT)];
 
 		snprintf(fn, sizeof(fn), "%s.%s", targs->namerecloc,
 			     REC_FORMAT);
diff --git a/apps/app_meetme.c b/apps/app_meetme.c
index 2ad1920..2cf252d 100644
--- a/apps/app_meetme.c
+++ b/apps/app_meetme.c
@@ -2254,20 +2254,23 @@ static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_
 			if (trunk_ref->ring_timeout) {
 				snprintf(ring_timeout, sizeof(ring_timeout),
 					"%u", trunk_ref->ring_timeout);
-			} else
+			} else {
 				strcpy(ring_timeout, "(none)");
+			}
 			if (trunk_ref->ring_delay) {
 				snprintf(ring_delay, sizeof(ring_delay),
 					"%u", trunk_ref->ring_delay);
-			} else
+			} else {
 				strcpy(ring_delay, "(none)");
-				ast_cli(a->fd, "===    ==> Trunk Name: %s\n"
-			            "===       ==> State:       %s\n"
-			            "===       ==> RingTimeout: %s\n"
-			            "===       ==> RingDelay:   %s\n",
-			            trunk_ref->trunk->name,
-			            trunkstate2str(trunk_ref->state),
-			            ring_timeout, ring_delay);
+			}
+
+			ast_cli(a->fd, "===    ==> Trunk Name: %s\n"
+	            "===       ==> State:       %s\n"
+	            "===       ==> RingTimeout: %s\n"
+	            "===       ==> RingDelay:   %s\n",
+	            trunk_ref->trunk->name,
+	            trunkstate2str(trunk_ref->state),
+	            ring_timeout, ring_delay);
 		}
 		ast_cli(a->fd, "=== ---------------------------------------------------------\n"
 		            "===\n");
@@ -3202,7 +3205,7 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struc
 	const char *agifiledefault = "conf-background.agi", *tmpvar;
 	char meetmesecs[30] = "";
 	char exitcontext[AST_MAX_CONTEXT] = "";
-	char recordingtmp[AST_MAX_EXTENSION] = "";
+	char recordingtmp[AST_MAX_EXTENSION * 2] = "";
 	char members[10] = "";
 	int dtmf = 0, opt_waitmarked_timeout = 0;
 	time_t timeout = 0;
@@ -4520,7 +4523,7 @@ static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char
 		char currenttime[32] = "";
 		char eatime[32] = "";
 		char bookid[51] = "";
-		char recordingtmp[AST_MAX_EXTENSION] = "";
+		char recordingtmp[AST_MAX_EXTENSION * 2] = "";
 		char useropts[OPTIONS_LEN + 1] = ""; /* Used for RealTime conferences */
 		char adminopts[OPTIONS_LEN + 1] = "";
 		struct ast_tm tm, etm;
diff --git a/apps/app_minivm.c b/apps/app_minivm.c
index 1bfcfbb..6b1e8bb 100644
--- a/apps/app_minivm.c
+++ b/apps/app_minivm.c
@@ -1234,6 +1234,8 @@ static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const cha
  * \brief Send voicemail with audio file as an attachment */
 static int sendmail(struct minivm_template *template, struct minivm_account *vmu, char *cidnum, char *cidname, const char *filename, char *format, int duration, int attach_user_voicemail, enum mvm_messagetype type, const char *counter)
 {
+	RAII_VAR(struct ast_str *, str1, ast_str_create(16), ast_free);
+	RAII_VAR(struct ast_str *, str2, ast_str_create(16), ast_free);
 	FILE *p = NULL;
 	int pfd;
 	char email[256] = "";
@@ -1243,20 +1245,18 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu
 	char fname[PATH_MAX];
 	char dur[PATH_MAX];
 	char tmp[80] = "/tmp/astmail-XXXXXX";
-	char tmp2[PATH_MAX];
-	char newtmp[PATH_MAX]; /* Only used with volgain */
+	char mail_cmd_buffer[PATH_MAX];
+	char sox_gain_tmpdir[PATH_MAX] = ""; /* Only used with volgain */
+	char *file_to_delete = NULL, *dir_to_delete = NULL;
 	struct timeval now;
 	struct ast_tm tm;
 	struct minivm_zone *the_zone = NULL;
-	struct ast_channel *ast;
-	char *finalfilename = "";
-	struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
+	struct ast_channel *chan = NULL;
 	char *fromaddress;
 	char *fromemail;
+	int res;
 
 	if (!str1 || !str2) {
-		ast_free(str1);
-		ast_free(str2);
 		return -1;
 	}
 
@@ -1271,9 +1271,7 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu
 
 	if (ast_strlen_zero(email)) {
 		ast_log(LOG_WARNING, "No address to send message to.\n");
-		ast_free(str1);
-		ast_free(str2);
-		return -1;	
+		return -1;
 	}
 
 	ast_debug(3, "Sending mail to %s@%s - Using template %s\n", vmu->username, vmu->domain, template->name);
@@ -1281,35 +1279,30 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu
 	if (!strcmp(format, "wav49"))
 		format = "WAV";
 
-
 	/* If we have a gain option, process it now with sox */
 	if (type == MVM_MESSAGE_EMAIL && (vmu->volgain < -.001 || vmu->volgain > .001) ) {
-		char tmpcmd[PATH_MAX];
-		int tmpfd;
-
-		ast_copy_string(newtmp, "/tmp/XXXXXX", sizeof(newtmp));
-		ast_debug(3, "newtmp: %s\n", newtmp);
-		tmpfd = mkstemp(newtmp);
-		if (tmpfd < 0) {
-			ast_log(LOG_WARNING, "Failed to create temporary file for volgain: %d\n", errno);
-			ast_free(str1);
-			ast_free(str2);
+		char sox_gain_cmd[PATH_MAX];
+
+		ast_copy_string(sox_gain_tmpdir, "/tmp/minivm-gain-XXXXXX", sizeof(sox_gain_tmpdir));
+		ast_debug(3, "sox_gain_tmpdir: %s\n", sox_gain_tmpdir);
+		if (!mkdtemp(sox_gain_tmpdir)) {
+			ast_log(LOG_WARNING, "Failed to create temporary directory for volgain: %d\n", errno);
 			return -1;
 		}
-		snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, filename, format, newtmp, format);
-		ast_safe_system(tmpcmd);
-		close(tmpfd);
-		finalfilename = newtmp;
+		snprintf(fname, sizeof(fname), "%s/output.%s", sox_gain_tmpdir, format);
+		snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s %s", vmu->volgain, filename, format, fname);
+		ast_safe_system(sox_gain_cmd);
 		ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", filename, format, vmu->volgain, vmu->username);
+
+		/* Mark some things for deletion */
+		file_to_delete = fname;
+		dir_to_delete = sox_gain_tmpdir;
 	} else {
-		finalfilename = ast_strdupa(filename);
+		snprintf(fname, sizeof(fname), "%s.%s", filename, format);
 	}
 
-	/* Create file name */
-	snprintf(fname, sizeof(fname), "%s.%s", finalfilename, format);
-
 	if (template->attachment)
-		ast_debug(1, "Attaching file '%s', format '%s', uservm is '%d'\n", finalfilename, format, attach_user_voicemail);
+		ast_debug(1, "Attaching file '%s', format '%s', uservm is '%d'\n", fname, format, attach_user_voicemail);
 
 	/* Make a temporary file instead of piping directly to sendmail, in case the mail
 	   command hangs */
@@ -1324,16 +1317,12 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu
 	}
 	if (!p) {
 		ast_log(LOG_WARNING, "Unable to open temporary file '%s'\n", tmp);
-		ast_free(str1);
-		ast_free(str2);
-		return -1;
+		goto out;
 	}
 	/* Allocate channel used for chanvar substitution */
-	ast = ast_dummy_channel_alloc();
-	if (!ast) {
-		ast_free(str1);
-		ast_free(str2);
-		return -1;
+	chan = ast_dummy_channel_alloc();
+	if (!chan) {
+		goto out;
 	}
 
 	snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
@@ -1361,9 +1350,8 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu
 	/* Set date format for voicemail mail */
 	ast_strftime(date, sizeof(date), template->dateformat, &tm);
 
-
 	/* Populate channel with channel variables for substitution */
-	prep_email_sub_vars(ast, vmu, cidnum, cidname, dur, date, counter);
+	prep_email_sub_vars(chan, vmu, cidnum, cidname, dur, date, counter);
 
 	/* Find email address to use */
 	/* If there's a server e-mail address in the account, use that, othterwise template */
@@ -1388,7 +1376,7 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu
 		fprintf(p, "From: Asterisk PBX <%s>\n", who);
 	} else {
 		ast_debug(4, "Fromaddress template: %s\n", fromaddress);
-		ast_str_substitute_variables(&str1, 0, ast, fromaddress);
+		ast_str_substitute_variables(&str1, 0, chan, fromaddress);
 		if (check_mime(ast_str_buffer(str1))) {
 			int first_line = 1;
 			char *ptr;
@@ -1431,7 +1419,7 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu
 	}
 
 	if (!ast_strlen_zero(template->subject)) {
-		ast_str_substitute_variables(&str1, 0, ast, template->subject);
+		ast_str_substitute_variables(&str1, 0, chan, template->subject);
 		if (check_mime(ast_str_buffer(str1))) {
 			int first_line = 1;
 			char *ptr;
@@ -1464,7 +1452,7 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu
 	fprintf(p, "--%s\n", bound);
 	fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", template->charset);
 	if (!ast_strlen_zero(template->body)) {
-		ast_str_substitute_variables(&str1, 0, ast, template->body);
+		ast_str_substitute_variables(&str1, 0, chan, template->body);
 		ast_debug(3, "Message now: %s\n-----\n", ast_str_buffer(str1));
 		fprintf(p, "%s\n", ast_str_buffer(str1));
 	} else {
@@ -1491,14 +1479,45 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu
 		fprintf(p, "\n\n--%s--\n.\n", bound);
 	}
 	fclose(p);
-	snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", global_mailcmd, tmp, tmp);
-	ast_safe_system(tmp2);
-	ast_debug(1, "Sent message to %s with command '%s' - %s\n", vmu->email, global_mailcmd, template->attachment ? "(media attachment)" : "");
-	ast_debug(3, "Actual command used: %s\n", tmp2);
-	ast = ast_channel_unref(ast);
-	ast_free(str1);
-	ast_free(str2);
-	return 0;
+
+	chan = ast_channel_unref(chan);
+
+	if (file_to_delete && dir_to_delete) {
+		/* We can't delete these files ourselves because the mail command will execute in
+		   the background and we'll end up deleting them out from under it. */
+		res = snprintf(mail_cmd_buffer, sizeof(mail_cmd_buffer),
+					   "( %s < %s ; rm -f %s %s ; rmdir %s ) &",
+					   global_mailcmd, tmp, tmp, file_to_delete, dir_to_delete);
+	} else {
+		res = snprintf(mail_cmd_buffer, sizeof(mail_cmd_buffer),
+					   "( %s < %s ; rm -f %s ) &",
+					   global_mailcmd, tmp, tmp);
+	}
+
+	if (res < sizeof(mail_cmd_buffer)) {
+		file_to_delete = dir_to_delete = NULL;
+	} else {
+		ast_log(LOG_ERROR, "Could not send message, command line too long\n");
+		res = -1;
+		goto out;
+	}
+
+	ast_safe_system(mail_cmd_buffer);
+	ast_debug(1, "Sent message to %s with command '%s'%s\n", vmu->email, global_mailcmd, template->attachment ? " - (media attachment)" : "");
+	ast_debug(3, "Actual command used: %s\n", mail_cmd_buffer);
+
+	res = 0;
+
+out:
+	if (file_to_delete) {
+		unlink(file_to_delete);
+	}
+
+	if (dir_to_delete) {
+		rmdir(dir_to_delete);
+	}
+
+	return res;
 }
 
 /*!\internal
@@ -1757,21 +1776,35 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
 /*! \brief Run external notification for voicemail message */
 static void run_externnotify(struct ast_channel *chan, struct minivm_account *vmu)
 {
-	char arguments[BUFSIZ];
+	char fquser[AST_MAX_CONTEXT * 2];
+	char *argv[5] = { NULL };
+	struct ast_party_caller *caller;
+	char *cid;
+	int idx;
 
-	if (ast_strlen_zero(vmu->externnotify) && ast_strlen_zero(global_externnotify))
+	if (ast_strlen_zero(vmu->externnotify) && ast_strlen_zero(global_externnotify)) {
 		return;
+	}
 
-	snprintf(arguments, sizeof(arguments), "%s %s@%s %s %s&", 
-		ast_strlen_zero(vmu->externnotify) ? global_externnotify : vmu->externnotify, 
-		vmu->username, vmu->domain,
-		(ast_channel_caller(chan)->id.name.valid && ast_channel_caller(chan)->id.name.str)
-			? ast_channel_caller(chan)->id.name.str : "",
-		(ast_channel_caller(chan)->id.number.valid && ast_channel_caller(chan)->id.number.str)
-			? ast_channel_caller(chan)->id.number.str : "");
+	snprintf(fquser, sizeof(fquser), "%s@%s", vmu->username, vmu->domain);
 
-	ast_debug(1, "Executing: %s\n", arguments);
-	ast_safe_system(arguments);
+	caller = ast_channel_caller(chan);
+	idx = 0;
+	argv[idx++] = ast_strlen_zero(vmu->externnotify) ? global_externnotify : vmu->externnotify;
+	argv[idx++] = fquser;
+	cid = S_COR(caller->id.name.valid, caller->id.name.str, NULL);
+	if (cid) {
+		argv[idx++] = cid;
+	}
+	cid = S_COR(caller->id.number.valid, caller->id.number.str, NULL);
+	if (cid) {
+		argv[idx++] = cid;
+	}
+	argv[idx] = NULL;
+
+	ast_debug(1, "Executing: %s %s %s %s\n",
+		argv[0], argv[1], argv[2] ?: "", argv[3] ?: "");
+	ast_safe_execvp(1, argv[0], argv);
 }
 
 /*!\internal
@@ -2253,7 +2286,7 @@ static int minivm_greet_exec(struct ast_channel *chan, const char *data)
 	char ecodes[16] = "#";
 	char *tmpptr;
 	struct minivm_account *vmu;
-	char *username = argv[0];
+	char *username;
 
 	if (ast_strlen_zero(data))  {
 		ast_log(LOG_ERROR, "Minivm needs at least an account argument \n");
diff --git a/apps/app_mixmonitor.c b/apps/app_mixmonitor.c
index 979bf2d..24ce3b6 100644
--- a/apps/app_mixmonitor.c
+++ b/apps/app_mixmonitor.c
@@ -138,6 +138,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 				<para>Will be executed when the recording is over.</para>
 				<para>Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.</para>
 				<para>All variables will be evaluated at the time MixMonitor is called.</para>
+				<warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
+				or <variable>CALLERID(name)</variable> as part of the command parameters.  You
+				risk a command injection attack executing arbitrary commands if the untrusted
+				strings aren't filtered to remove dangerous characters.  See function
+				<variable>FILTER()</variable>.</para></warning>
 			</parameter>
 		</syntax>
 		<description>
@@ -150,6 +155,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 					<para>Will contain the filename used to record.</para>
 				</variable>
 			</variablelist>
+			<warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
+			or <variable>CALLERID(name)</variable> as part of ANY of the application's
+			parameters.  You risk a command injection attack executing arbitrary commands
+			if the untrusted strings aren't filtered to remove dangerous characters.  See
+			function <variable>FILTER()</variable>.</para></warning>
 		</description>
 		<see-also>
 			<ref type="application">Monitor</ref>
@@ -224,6 +234,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 				<para>Will be executed when the recording is over.
 				Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.
 				All variables will be evaluated at the time MixMonitor is called.</para>
+				<warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
+				or <variable>CALLERID(name)</variable> as part of the command parameters.  You
+				risk a command injection attack executing arbitrary commands if the untrusted
+				strings aren't filtered to remove dangerous characters.  See function
+				<variable>FILTER()</variable>.</para></warning>
 			</parameter>
 		</syntax>
 		<description>
diff --git a/apps/app_originate.c b/apps/app_originate.c
index eb4d634..b46c0f5 100644
--- a/apps/app_originate.c
+++ b/apps/app_originate.c
@@ -110,6 +110,7 @@ static int originate_exec(struct ast_channel *chan, const char *data)
 	char *parse;
 	char *chantech, *chandata;
 	int res = -1;
+	int continue_in_dialplan = 0;
 	int outgoing_status = 0;
 	unsigned int timeout = 30;
 	static const char default_exten[] = "s";
@@ -159,6 +160,12 @@ static int originate_exec(struct ast_channel *chan, const char *data)
 		goto return_cleanup;
 	}
 
+	if (strcasecmp(args.type, "exten") && strcasecmp(args.type, "app")) {
+		ast_log(LOG_ERROR, "Incorrect type, it should be 'exten' or 'app': %s\n",
+				args.type);
+		goto return_cleanup;
+	}
+
 	if (!strcasecmp(args.type, "exten")) {
 		int priority = 1; /* Initialized in case priority not specified */
 		const char *exten = args.arg2;
@@ -177,23 +184,30 @@ static int originate_exec(struct ast_channel *chan, const char *data)
 		ast_debug(1, "Originating call to '%s/%s' and connecting them to extension %s,%s,%d\n",
 				chantech, chandata, args.arg1, exten, priority);
 
-		ast_pbx_outgoing_exten(chantech, cap_slin, chandata,
+		res = ast_pbx_outgoing_exten(chantech, cap_slin, chandata,
 				timeout * 1000, args.arg1, exten, priority, &outgoing_status,
 				AST_OUTGOING_WAIT, NULL, NULL, NULL, NULL, NULL, 0, NULL);
-	} else if (!strcasecmp(args.type, "app")) {
+	} else {
 		ast_debug(1, "Originating call to '%s/%s' and connecting them to %s(%s)\n",
 				chantech, chandata, args.arg1, S_OR(args.arg2, ""));
 
-		ast_pbx_outgoing_app(chantech, cap_slin, chandata,
+		res = ast_pbx_outgoing_app(chantech, cap_slin, chandata,
 				timeout * 1000, args.arg1, args.arg2, &outgoing_status,
 				AST_OUTGOING_WAIT, NULL, NULL, NULL, NULL, NULL, NULL);
-	} else {
-		ast_log(LOG_ERROR, "Incorrect type, it should be 'exten' or 'app': %s\n",
-				args.type);
-		goto return_cleanup;
 	}
 
-	res = 0;
+	/*
+	 * Getting here means that we have passed the various validation checks and
+	 * have at least attempted the dial. If we have a reason (outgoing_status),
+	 * we clear our error indicator so that we ultimately report the right thing
+	 * to the caller.
+	 */
+	if (res && outgoing_status) {
+		res = 0;
+	}
+
+	/* We need to exit cleanly if we've gotten this far */
+	continue_in_dialplan = 1;
 
 return_cleanup:
 	if (res) {
@@ -226,7 +240,7 @@ return_cleanup:
 	ao2_cleanup(cap_slin);
 	ast_autoservice_stop(chan);
 
-	return res;
+	return continue_in_dialplan ? 0 : -1;
 }
 
 static int unload_module(void)
diff --git a/apps/app_playback.c b/apps/app_playback.c
index e5df794..422dd8e 100644
--- a/apps/app_playback.c
+++ b/apps/app_playback.c
@@ -324,7 +324,7 @@ static int say_date_generic(struct ast_channel *chan, time_t t,
 	if (format == NULL)
 		format = "";
 
-	ast_localtime(&when, &tm, NULL);
+	ast_localtime(&when, &tm, timezonename);
 	snprintf(buf, sizeof(buf), "%s:%s:%04d%02d%02d%02d%02d.%02d-%d-%3d",
 		prefix,
 		format,
diff --git a/apps/app_privacy.c b/apps/app_privacy.c
index 0e04df6..a77bcf7 100644
--- a/apps/app_privacy.c
+++ b/apps/app_privacy.c
@@ -40,7 +40,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
 #include "asterisk/translate.h"
-#include "asterisk/image.h"
 #include "asterisk/callerid.h"
 #include "asterisk/app.h"
 #include "asterisk/config.h"
diff --git a/apps/app_queue.c b/apps/app_queue.c
index ac4738f..b7718a1 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -2696,6 +2696,7 @@ static void init_queue(struct call_queue *q)
 	}
 	q->found = 1;
 
+	ast_string_field_set(q, moh, "");
 	ast_string_field_set(q, sound_next, "queue-youarenext");
 	ast_string_field_set(q, sound_thereare, "queue-thereare");
 	ast_string_field_set(q, sound_calls, "queue-callswaiting");
@@ -3975,8 +3976,12 @@ static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
 	/* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
 
 	ao2_lock(qe->parent);
-	oldvalue = qe->parent->holdtime;
-	qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
+	if ((qe->parent->callscompleted + qe->parent->callsabandoned) == 0) {
+		qe->parent->holdtime = newholdtime;
+	} else {
+		oldvalue = qe->parent->holdtime;
+		qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
+	}
 	ao2_unlock(qe->parent);
 }
 
@@ -4720,7 +4725,7 @@ static void update_connected_line_from_peer(struct ast_channel *chan, struct ast
  *
  * \todo eventually all call forward logic should be intergerated into and replaced by ast_call_forward()
  */
-static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed, int ringing)
+static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
 {
 	const char *queue = qe->parent->name;
 	struct callattempt *o, *start = NULL, *prev = NULL;
@@ -5239,16 +5244,6 @@ skip_frame:;
 		}
 	}
 
-	/* Make a position announcement, if enabled */
- 	if (qe->parent->announcefrequency && qe->parent->announce_to_first_user) {
-		say_position(qe, ringing);
-	}
-
- 	/* Make a periodic announcement, if enabled */
- 	if (qe->parent->periodicannouncefrequency && qe->parent->announce_to_first_user) {
- 		say_periodic_announcement(qe, ringing);
- 	}
-
 	if (!*to) {
 		for (o = start; o; o = o->call_next) {
 			if (o->chan) {
@@ -5527,7 +5522,7 @@ static int update_queue(struct call_queue *q, struct member *member, int callcom
 	if (callcompletedinsl) {
 		q->callscompletedinsl++;
 	}
-	if (q->callscompletedinsl == 1) {
+	if (q->callscompleted == 1) {
 		q->talktime = newtalktime;
 	} else {
 		/* Calculate talktime using the same exponential average as holdtime code */
@@ -6481,7 +6476,6 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
 	char oldexten[AST_MAX_EXTENSION]="";
 	char oldcontext[AST_MAX_CONTEXT]="";
 	char queuename[256]="";
-	char interfacevar[256]="";
 	struct ast_channel *peer;
 	struct ast_channel *which;
 	struct callattempt *lpeer;
@@ -6656,7 +6650,7 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
 	ring_one(qe, outgoing, &numbusies);
 	lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies,
 		ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT),
-		forwardsallowed, ringing);
+		forwardsallowed);
 
 	ao2_lock(qe->parent);
 	if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) {
@@ -6682,6 +6676,7 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
 		}
 	} else { /* peer is valid */
 		RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
+		RAII_VAR(struct ast_str *, interfacevar, ast_str_create(325), ast_free);
 		/* Ah ha!  Someone answered within the desired timeframe.  Of course after this
 		   we will always return with -1 so that it is hung up properly after the
 		   conversation.  */
@@ -6795,20 +6790,20 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
 		ao2_lock(qe->parent);
 		/* if setinterfacevar is defined, make member variables available to the channel */
 		/* use  pbx_builtin_setvar to set a load of variables with one call */
-		if (qe->parent->setinterfacevar) {
-			snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
+		if (qe->parent->setinterfacevar && interfacevar) {
+			ast_str_set(&interfacevar, 0, "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
 				member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
-		 	pbx_builtin_setvar_multiple(qe->chan, interfacevar);
-			pbx_builtin_setvar_multiple(peer, interfacevar);
+			pbx_builtin_setvar_multiple(qe->chan, ast_str_buffer(interfacevar));
+			pbx_builtin_setvar_multiple(peer, ast_str_buffer(interfacevar));
 		}
 
 		/* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
 		/* use  pbx_builtin_setvar to set a load of variables with one call */
-		if (qe->parent->setqueueentryvar) {
-			snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
+		if (qe->parent->setqueueentryvar && interfacevar) {
+			ast_str_set(&interfacevar, 0, "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
 				(long) (time(NULL) - qe->start), qe->opos);
-			pbx_builtin_setvar_multiple(qe->chan, interfacevar);
-			pbx_builtin_setvar_multiple(peer, interfacevar);
+			pbx_builtin_setvar_multiple(qe->chan, ast_str_buffer(interfacevar));
+			pbx_builtin_setvar_multiple(peer, ast_str_buffer(interfacevar));
 		}
 
 		ao2_unlock(qe->parent);
@@ -7456,12 +7451,10 @@ static int set_member_value(const char *queuename, const char *interface, int pr
 static int get_member_penalty(char *queuename, char *interface)
 {
 	int foundqueue = 0, penalty;
-	struct call_queue *q, tmpq = {
-		.name = queuename,
-	};
+	struct call_queue *q;
 	struct member *mem;
 
-	if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Search for queue"))) {
+	if ((q = find_load_queue_rt_friendly(queuename))) {
 		foundqueue = 1;
 		ao2_lock(q);
 		if ((mem = interface_exists(q, interface))) {
@@ -8050,7 +8043,7 @@ check_turns:
 		goto stop;
 	}
 
-	makeannouncement = 0;
+	makeannouncement = qe.parent->announce_to_first_user;
 
 	for (;;) {
 		/* This is the wait loop for the head caller*/
@@ -8070,15 +8063,17 @@ check_turns:
 
 		if (makeannouncement) {
 			/* Make a position announcement, if enabled */
-			if (qe.parent->announcefrequency)
-				if ((res = say_position(&qe,ringing)))
+			if (qe.parent->announcefrequency) {
+				if ((res = say_position(&qe, ringing))) {
 					goto stop;
+				}
+			}
 		}
 		makeannouncement = 1;
 
 		/* Make a periodic announcement, if enabled */
 		if (qe.parent->periodicannouncefrequency) {
-			if ((res = say_periodic_announcement(&qe,ringing))) {
+			if ((res = say_periodic_announcement(&qe, ringing))) {
 				goto stop;
 			}
 		}
@@ -8212,10 +8207,7 @@ stop:
 static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 {
 	int res = -1;
-	struct call_queue *q, tmpq = {
-		.name = data,
-	};
-
+	struct call_queue *q;
 	char interfacevar[256] = "";
 	float sl = 0;
 
@@ -8224,7 +8216,7 @@ static int queue_function_var(struct ast_channel *chan, const char *cmd, char *d
 		return -1;
 	}
 
-	if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE() function"))) {
+	if ((q = find_load_queue_rt_friendly(data))) {
 		ao2_lock(q);
 		if (q->setqueuevar) {
 			sl = 0;
@@ -8543,9 +8535,7 @@ static int queue_function_queuewaitingcount(struct ast_channel *chan, const char
 /*! \brief Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue */
 static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 {
-	struct call_queue *q, tmpq = {
-		.name = data,
-	};
+	struct call_queue *q;
 	struct member *m;
 
 	/* Ensure an otherwise empty list doesn't return garbage */
@@ -8556,7 +8546,7 @@ static int queue_function_queuememberlist(struct ast_channel *chan, const char *
 		return -1;
 	}
 
-	if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_MEMBER_LIST()"))) {
+	if ((q = find_load_queue_rt_friendly(data))) {
 		int buflen = 0, count = 0;
 		struct ao2_iterator mem_iter;
 
@@ -9695,6 +9685,7 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
 					"ConnectedLineNum: %s\r\n"
 					"ConnectedLineName: %s\r\n"
 					"Wait: %ld\r\n"
+					"Priority: %d\r\n"
 					"%s"
 					"\r\n",
 					q->name, pos++, ast_channel_name(qe->chan), ast_channel_uniqueid(qe->chan),
@@ -9702,7 +9693,7 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
 					S_COR(ast_channel_caller(qe->chan)->id.name.valid, ast_channel_caller(qe->chan)->id.name.str, "unknown"),
 					S_COR(ast_channel_connected(qe->chan)->id.number.valid, ast_channel_connected(qe->chan)->id.number.str, "unknown"),
 					S_COR(ast_channel_connected(qe->chan)->id.name.valid, ast_channel_connected(qe->chan)->id.name.str, "unknown"),
-					(long) (now - qe->start), idText);
+					(long) (now - qe->start), qe->prio, idText);
 				++q_items;
 			}
 		}
diff --git a/apps/app_record.c b/apps/app_record.c
index 56dc5f4..104daa5 100644
--- a/apps/app_record.c
+++ b/apps/app_record.c
@@ -40,6 +40,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/channel.h"
 #include "asterisk/dsp.h"	/* use dsp routines for silence detection */
 #include "asterisk/format_cache.h"
+#include "asterisk/paths.h"
 
 /*** DOCUMENTATION
 	<application name="Record" language="en_US">
@@ -104,7 +105,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 			If the user hangs up during a recording, all data will be lost and the application will terminate.</para>
 			<variablelist>
 				<variable name="RECORDED_FILE">
-					<para>Will be set to the final filename of the recording.</para>
+					<para>Will be set to the final filename of the recording, without an extension.</para>
 				</variable>
 				<variable name="RECORD_STATUS">
 					<para>This is the final status of the command</para>
@@ -133,10 +134,9 @@ enum {
 	OPTION_STAR_TERMINATE = (1 << 4),
 	OPTION_IGNORE_TERMINATE = (1 << 5),
 	OPTION_KEEP = (1 << 6),
-	FLAG_HAS_PERCENT = (1 << 7),
-	OPTION_ANY_TERMINATE = (1 << 8),
-	OPTION_OPERATOR_EXIT = (1 << 9),
-	OPTION_NO_TRUNCATE = (1 << 10),
+	OPTION_ANY_TERMINATE = (1 << 7),
+	OPTION_OPERATOR_EXIT = (1 << 8),
+	OPTION_NO_TRUNCATE = (1 << 9),
 };
 
 AST_APP_OPTIONS(app_opts,{
@@ -182,14 +182,47 @@ static int record_dtmf_response(struct ast_channel *chan, struct ast_flags *flag
 	return 0;
 }
 
+static int create_destination_directory(const char *path)
+{
+	int res;
+	char directory[PATH_MAX], *file_sep;
+
+	if (!(file_sep = strrchr(path, '/'))) {
+		/* No directory to create */
+		return 0;
+	}
+
+	/* Overwrite temporarily */
+	*file_sep = '\0';
+
+	/* Absolute path? */
+	if (path[0] == '/') {
+		res = ast_mkdir(path, 0777);
+		*file_sep = '/';
+		return res;
+	}
+
+	/* Relative path */
+	res = snprintf(directory, sizeof(directory), "%s/sounds/%s",
+				   ast_config_AST_DATA_DIR, path);
+
+	*file_sep = '/';
+
+	if (res >= sizeof(directory)) {
+		/* We truncated, so we fail */
+		return -1;
+	}
+
+	return ast_mkdir(directory, 0777);
+}
+
 static int record_exec(struct ast_channel *chan, const char *data)
 {
 	int res = 0;
-	int count = 0;
 	char *ext = NULL, *opts[0];
-	char *parse, *dir, *file;
+	char *parse;
 	int i = 0;
-	char tmp[256];
+	char tmp[PATH_MAX];
 
 	struct ast_filestream *s = NULL;
 	struct ast_frame *f = NULL;
@@ -229,8 +262,6 @@ static int record_exec(struct ast_channel *chan, const char *data)
 		ast_app_parse_options(app_opts, &flags, opts, args.options);
 
 	if (!ast_strlen_zero(args.filename)) {
-		if (strstr(args.filename, "%d"))
-			ast_set_flag(&flags, FLAG_HAS_PERCENT);
 		ext = strrchr(args.filename, '.'); /* to support filename with a . in the filename, not format */
 		if (!ext)
 			ext = strchr(args.filename, ':');
@@ -268,38 +299,31 @@ static int record_exec(struct ast_channel *chan, const char *data)
 	if (ast_test_flag(&flags, OPTION_IGNORE_TERMINATE))
 		terminator = '\0';
 
-	/* done parsing */
-
-	/* these are to allow the use of the %d in the config file for a wild card of sort to
-	  create a new file with the inputed name scheme */
-	if (ast_test_flag(&flags, FLAG_HAS_PERCENT)) {
-		AST_DECLARE_APP_ARGS(fname,
-			AST_APP_ARG(piece)[100];
-		);
-		char *tmp2 = ast_strdupa(args.filename);
-		char countstring[15];
-		int idx;
+	/*
+	  If a '%d' is specified as part of the filename, we replace that token with
+	  sequentially incrementing numbers until we find a unique filename.
+	*/
+	if (strchr(args.filename, '%')) {
+		size_t src, dst, count = 0;
+		size_t src_len = strlen(args.filename);
+		size_t dst_len = sizeof(tmp) - 1;
 
-		/* Separate each piece out by the format specifier */
-		AST_NONSTANDARD_APP_ARGS(fname, tmp2, '%');
 		do {
-			int tmplen;
-			/* First piece has no leading percent, so it's copied verbatim */
-			ast_copy_string(tmp, fname.piece[0], sizeof(tmp));
-			tmplen = strlen(tmp);
-			for (idx = 1; idx < fname.argc; idx++) {
-				if (fname.piece[idx][0] == 'd') {
-					/* Substitute the count */
-					snprintf(countstring, sizeof(countstring), "%d", count);
-					ast_copy_string(tmp + tmplen, countstring, sizeof(tmp) - tmplen);
-					tmplen += strlen(countstring);
-				} else if (tmplen + 2 < sizeof(tmp)) {
-					/* Unknown format specifier - just copy it verbatim */
-					tmp[tmplen++] = '%';
-					tmp[tmplen++] = fname.piece[idx][0];
+			for (src = 0, dst = 0; src < src_len && dst < dst_len; src++) {
+				if (!strncmp(&args.filename[src], "%d", 2)) {
+					int s = snprintf(&tmp[dst], PATH_MAX - dst, "%zu", count);
+					if (s >= PATH_MAX - dst) {
+						/* We truncated, so we need to bail */
+						ast_log(LOG_WARNING, "Failed to create unique filename from template: %s\n", args.filename);
+						pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
+						return -1;
+					}
+					dst += s;
+					src++;
+				} else {
+					tmp[dst] = args.filename[src];
+					tmp[++dst] = '\0';
 				}
-				/* Copy the remaining portion of the piece */
-				ast_copy_string(tmp + tmplen, &(fname.piece[idx][1]), sizeof(tmp) - tmplen);
 			}
 			count++;
 		} while (ast_fileexists(tmp, ext, ast_channel_language(chan)) > 0);
@@ -307,7 +331,6 @@ static int record_exec(struct ast_channel *chan, const char *data)
 		ast_copy_string(tmp, args.filename, sizeof(tmp));
 
 	pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
-	/* end of routine mentioned */
 
 	if (ast_channel_state(chan) != AST_STATE_UP) {
 		if (ast_test_flag(&flags, OPTION_SKIP)) {
@@ -356,11 +379,11 @@ static int record_exec(struct ast_channel *chan, const char *data)
 		ast_dsp_set_threshold(sildet, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE));
 	} 
 
-	/* Create the directory if it does not exist. */
-	dir = ast_strdupa(tmp);
-	if ((file = strrchr(dir, '/')))
-		*file++ = '\0';
-	ast_mkdir (dir, 0777);
+	if (create_destination_directory(tmp)) {
+		ast_log(LOG_WARNING, "Could not create directory for file %s\n", args.filename);
+		pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
+		goto out;
+	}
 
 	ioflags = ast_test_flag(&flags, OPTION_APPEND) ? O_CREAT|O_APPEND|O_WRONLY : O_CREAT|O_TRUNC|O_WRONLY;
 	s = ast_writefile(tmp, ext, NULL, ioflags, 0, AST_FILE_MODE);
diff --git a/apps/app_system.c b/apps/app_system.c
index 7fe453d..e868a07 100644
--- a/apps/app_system.c
+++ b/apps/app_system.c
@@ -48,6 +48,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 		<syntax>
 			<parameter name="command" required="true">
 				<para>Command to execute</para>
+				<warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
+				or <variable>CALLERID(name)</variable> as part of the command parameters.  You
+				risk a command injection attack executing arbitrary commands if the untrusted
+				strings aren't filtered to remove dangerous characters.  See function
+				<variable>FILTER()</variable>.</para></warning>
 			</parameter>
 		</syntax>
 		<description>
@@ -73,6 +78,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 		<syntax>
 			<parameter name="command" required="true">
 				<para>Command to execute</para>
+				<warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
+				or <variable>CALLERID(name)</variable> as part of the command parameters.  You
+				risk a command injection attack executing arbitrary commands if the untrusted
+				strings aren't filtered to remove dangerous characters.  See function
+				<variable>FILTER()</variable>.</para></warning>
 			</parameter>
 		</syntax>
 		<description>
diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c
index fe5657d..f954e27 100644
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -3781,12 +3781,12 @@ static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
 	SQLHSTMT stmt;
 
 	res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
-	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+	if (!SQL_SUCCEEDED(res)) {
 		ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
 		return NULL;
 	}
 	res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
-	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+	if (!SQL_SUCCEEDED(res)) {
 		ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
 		SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 		return NULL;
@@ -3828,14 +3828,14 @@ static void odbc_update_msg_id(char *dir, int msg_num, char *msg_id)
  * \brief Retrieves a file from an ODBC data store.
  * \param dir the path to the file to be retrieved.
  * \param msgnum the message number, such as within a mailbox folder.
- * 
+ *
  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
  * The purpose is to get the message from the database store to the local file system, so that the message may be played, or the information file may be read.
  *
  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
  * The output is the message information file with the name msgnum and the extension .txt
  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
- * 
+ *
  * \return 0 on success, -1 on error.
  */
 static int retrieve_file(char *dir, int msgnum)
@@ -3848,7 +3848,7 @@ static int retrieve_file(char *dir, int msgnum)
 	SQLSMALLINT colcount = 0;
 	SQLHSTMT stmt;
 	char sql[PATH_MAX];
-	char fmt[80]="";
+	char fmt[80] = "";
 	char *c;
 	char coltitle[256];
 	SQLSMALLINT collen;
@@ -3864,144 +3864,139 @@ static int retrieve_file(char *dir, int msgnum)
 	char msgnums[80];
 	char *argv[] = { dir, msgnums };
 	struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
-
 	struct odbc_obj *obj;
+
 	obj = ast_odbc_request_obj(odbc_database, 0);
-	if (obj) {
-		ast_copy_string(fmt, vmfmts, sizeof(fmt));
-		c = strchr(fmt, '|');
-		if (c)
-			*c = '\0';
-		if (!strcasecmp(fmt, "wav49"))
-			strcpy(fmt, "WAV");
-		snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
-		if (msgnum > -1)
-			make_file(fn, sizeof(fn), dir, msgnum);
-		else
-			ast_copy_string(fn, dir, sizeof(fn));
+	if (!obj) {
+		ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+		return -1;
+	}
 
-		/* Create the information file */
-		snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
-		
-		if (!(f = fopen(full_fn, "w+"))) {
-			ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
-			goto yuck;
-		}
-		
-		snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
-		snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
-		stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-		if (!stmt) {
-			ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-			ast_odbc_release_obj(obj);
-			goto yuck;
-		}
-		res = SQLFetch(stmt);
-		if (res == SQL_NO_DATA) {
-			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-			ast_odbc_release_obj(obj);
-			goto yuck;
-		} else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+	ast_copy_string(fmt, vmfmts, sizeof(fmt));
+	c = strchr(fmt, '|');
+	if (c)
+		*c = '\0';
+	if (!strcasecmp(fmt, "wav49"))
+		strcpy(fmt, "WAV");
+
+	snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
+	if (msgnum > -1)
+		make_file(fn, sizeof(fn), dir, msgnum);
+	else
+		ast_copy_string(fn, dir, sizeof(fn));
+
+	/* Create the information file */
+	snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+
+	if (!(f = fopen(full_fn, "w+"))) {
+		ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
+		goto bail;
+	}
+
+	snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
+	snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
+
+	stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+	if (!stmt) {
+		ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+		goto bail;
+	}
+
+	res = SQLFetch(stmt);
+	if (!SQL_SUCCEEDED(res)) {
+		if (res != SQL_NO_DATA) {
 			ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-			ast_odbc_release_obj(obj);
-			goto yuck;
 		}
-		fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
-		if (fd < 0) {
-			ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
-			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-			ast_odbc_release_obj(obj);
-			goto yuck;
-		}
-		res = SQLNumResultCols(stmt, &colcount);
-		if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {	
-			ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
-			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-			ast_odbc_release_obj(obj);
-			goto yuck;
-		}
-		if (f) 
-			fprintf(f, "[message]\n");
-		for (x = 0; x < colcount; x++) {
-			rowdata[0] = '\0';
-			colsize = 0;
-			collen = sizeof(coltitle);
-			res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
-						&datatype, &colsize, &decimaldigits, &nullable);
-			if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-				ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
-				SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-				ast_odbc_release_obj(obj);
-				goto yuck;
-			}
-			if (!strcasecmp(coltitle, "recording")) {
-				off_t offset;
-				res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
-				fdlen = colsize2;
-				if (fd > -1) {
-					char tmp[1]="";
-					lseek(fd, fdlen - 1, SEEK_SET);
-					if (write(fd, tmp, 1) != 1) {
-						close(fd);
-						fd = -1;
-						continue;
-					}
-					/* Read out in small chunks */
-					for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
-						if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
-							ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
-							SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-							ast_odbc_release_obj(obj);
-							goto yuck;
-						} else {
-							res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
-							munmap(fdm, CHUNKSIZE);
-							if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-								ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-								unlink(full_fn);
-								SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-								ast_odbc_release_obj(obj);
-								goto yuck;
-							}
-						}
+		goto bail_with_handle;
+	}
+
+	fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
+	if (fd < 0) {
+		ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
+		goto bail_with_handle;
+	}
+
+	res = SQLNumResultCols(stmt, &colcount);
+	if (!SQL_SUCCEEDED(res)) {
+		ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
+		goto bail_with_handle;
+	}
+
+	fprintf(f, "[message]\n");
+	for (x = 0; x < colcount; x++) {
+		rowdata[0] = '\0';
+		colsize = 0;
+		collen = sizeof(coltitle);
+		res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen,
+							&datatype, &colsize, &decimaldigits, &nullable);
+		if (!SQL_SUCCEEDED(res)) {
+			ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
+			goto bail_with_handle;
+		}
+		if (!strcasecmp(coltitle, "recording")) {
+			off_t offset;
+			res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
+			fdlen = colsize2;
+			if (fd > -1) {
+				char tmp[1] = "";
+				lseek(fd, fdlen - 1, SEEK_SET);
+				if (write(fd, tmp, 1) != 1) {
+					close(fd);
+					fd = -1;
+					continue;
+				}
+				/* Read out in small chunks */
+				for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
+					if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
+						ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
+						goto bail_with_handle;
 					}
-					if (truncate(full_fn, fdlen) < 0) {
-						ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
+					res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
+					munmap(fdm, CHUNKSIZE);
+					if (!SQL_SUCCEEDED(res)) {
+						ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+						unlink(full_fn);
+						goto bail_with_handle;
 					}
 				}
-			} else {
-				res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-				if ((res == SQL_NULL_DATA) && (!strcasecmp(coltitle, "msg_id"))) {
-					char msg_id[MSG_ID_LEN];
-					generate_msg_id(msg_id);
-					snprintf(rowdata, sizeof(rowdata), "%s", msg_id);
-					odbc_update_msg_id(dir, msgnum, msg_id);
-				} else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-					ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
-					SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-					ast_odbc_release_obj(obj);
-					goto yuck;
+				if (truncate(full_fn, fdlen) < 0) {
+					ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
 				}
-				if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
-					fprintf(f, "%s=%s\n", coltitle, rowdata);
+			}
+		} else {
+			res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+			if (res == SQL_NULL_DATA && !strcasecmp(coltitle, "msg_id")) {
+				char msg_id[MSG_ID_LEN];
+				generate_msg_id(msg_id);
+				snprintf(rowdata, sizeof(rowdata), "%s", msg_id);
+				odbc_update_msg_id(dir, msgnum, msg_id);
+			} else if (!SQL_SUCCEEDED(res)) {
+				ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
+				goto bail_with_handle;
+			}
+			if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir")) {
+				fprintf(f, "%s=%s\n", coltitle, rowdata);
 			}
 		}
-		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-		ast_odbc_release_obj(obj);
-	} else
-		ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-yuck:
+	}
+
+bail_with_handle:
+	SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+
+bail:
 	if (f)
 		fclose(f);
 	if (fd > -1)
 		close(fd);
+
+	ast_odbc_release_obj(obj);
+
 	return x - 1;
 }
 
 /*!
  * \brief Determines the highest message number in use for a given user and mailbox folder.
- * \param vmu 
+ * \param vmu
  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
  *
  * This method is used when mailboxes are stored in an ODBC back end.
@@ -4012,58 +4007,61 @@ yuck:
  */
 static int last_message_index(struct ast_vm_user *vmu, char *dir)
 {
-	int x = 0;
+	int x = -1;
 	int res;
 	SQLHSTMT stmt;
 	char sql[PATH_MAX];
 	char rowdata[20];
 	char *argv[] = { dir };
 	struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
-
 	struct odbc_obj *obj;
+
 	obj = ast_odbc_request_obj(odbc_database, 0);
-	if (obj) {
-		snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
+	if (!obj) {
+		ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+		return -1;
+	}
 
-		stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-		if (!stmt) {
-			ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-			ast_odbc_release_obj(obj);
-			goto yuck;
+	snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
+
+	stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+	if (!stmt) {
+		ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+		goto bail;
+	}
+
+	res = SQLFetch(stmt);
+	if (!SQL_SUCCEEDED(res)) {
+		if (res == SQL_NO_DATA) {
+			ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
+		} else {
+			ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
 		}
-		res = SQLFetch(stmt);
-		if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-			if (res == SQL_NO_DATA) {
-				ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
-			} else {
-				ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-			}
+		goto bail_with_handle;
+	}
 
-			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-			ast_odbc_release_obj(obj);
-			goto yuck;
-		}
-		res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-		if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-			ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-			ast_odbc_release_obj(obj);
-			goto yuck;
-		}
-		if (sscanf(rowdata, "%30d", &x) != 1)
-			ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
-		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-		ast_odbc_release_obj(obj);
-		return x;
-	} else
-		ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-yuck:
-	return x - 1;
+	res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+	if (!SQL_SUCCEEDED(res)) {
+		ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+		goto bail_with_handle;
+	}
+
+	if (sscanf(rowdata, "%30d", &x) != 1) {
+		ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
+	}
+
+bail_with_handle:
+	SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+
+bail:
+	ast_odbc_release_obj(obj);
+
+	return x;
 }
 
 /*!
  * \brief Determines if the specified message exists.
- * \param dir the folder the mailbox folder to look for messages. 
+ * \param dir the folder the mailbox folder to look for messages.
  * \param msgnum the message index to query for.
  *
  * This method is used when mailboxes are stored in an ODBC back end.
@@ -4080,39 +4078,43 @@ static int message_exists(char *dir, int msgnum)
 	char msgnums[20];
 	char *argv[] = { dir, msgnums };
 	struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
-
 	struct odbc_obj *obj;
+
 	obj = ast_odbc_request_obj(odbc_database, 0);
-	if (obj) {
-		snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
-		snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
-		stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-		if (!stmt) {
-			ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-			ast_odbc_release_obj(obj);
-			goto yuck;
-		}
-		res = SQLFetch(stmt);
-		if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-			ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-			ast_odbc_release_obj(obj);
-			goto yuck;
-		}
-		res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-		if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-			ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-			ast_odbc_release_obj(obj);
-			goto yuck;
-		}
-		if (sscanf(rowdata, "%30d", &x) != 1)
-			ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
-		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-		ast_odbc_release_obj(obj);
-	} else
+	if (!obj) {
 		ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-yuck:
+		return 0;
+	}
+
+	snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
+	snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
+	stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+	if (!stmt) {
+		ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+		goto bail;
+	}
+
+	res = SQLFetch(stmt);
+	if (!SQL_SUCCEEDED(res)) {
+		ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+		goto bail_with_handle;
+	}
+
+	res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+	if (!SQL_SUCCEEDED(res)) {
+		ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+		goto bail_with_handle;
+	}
+
+	if (sscanf(rowdata, "%30d", &x) != 1) {
+		ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
+	}
+
+bail_with_handle:
+	SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+
+bail:
+	ast_odbc_release_obj(obj);
 	return x;
 }
 
@@ -4127,48 +4129,50 @@ yuck:
  */
 static int count_messages(struct ast_vm_user *vmu, char *dir)
 {
-	int x = 0;
+	int x = -1;
 	int res;
 	SQLHSTMT stmt;
 	char sql[PATH_MAX];
 	char rowdata[20];
 	char *argv[] = { dir };
 	struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
-
 	struct odbc_obj *obj;
+
 	obj = ast_odbc_request_obj(odbc_database, 0);
-	if (obj) {
-		snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
-		stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-		if (!stmt) {
-			ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-			ast_odbc_release_obj(obj);
-			goto yuck;
-		}
-		res = SQLFetch(stmt);
-		if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-			ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-			ast_odbc_release_obj(obj);
-			goto yuck;
-		}
-		res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-		if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-			ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-			ast_odbc_release_obj(obj);
-			goto yuck;
-		}
-		if (sscanf(rowdata, "%30d", &x) != 1)
-			ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
-		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-		ast_odbc_release_obj(obj);
-		return x;
-	} else
+	if (!obj) {
 		ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-yuck:
-	return x - 1;
+		return -1;
+	}
+
+	snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
+	stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+	if (!stmt) {
+		ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+		goto bail;
+	}
+
+	res = SQLFetch(stmt);
+	if (!SQL_SUCCEEDED(res)) {
+		ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+		goto bail_with_handle;
+	}
+
+	res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+	if (!SQL_SUCCEEDED(res)) {
+		ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+		goto bail_with_handle;
+	}
 
+	if (sscanf(rowdata, "%30d", &x) != 1) {
+		ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
+	}
+
+bail_with_handle:
+	SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+
+bail:
+	ast_odbc_release_obj(obj);
+	return x;
 }
 
 /*!
@@ -4178,7 +4182,7 @@ yuck:
  *
  * This method is used when mailboxes are stored in an ODBC back end.
  * The specified message is directly deleted from the database 'voicemessages' table.
- * 
+ *
  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
  */
 static void delete_file(const char *sdir, int smsg)
@@ -4190,21 +4194,25 @@ static void delete_file(const char *sdir, int smsg)
 	struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
 	struct odbc_obj *obj;
 
-	argv[0] = ast_strdupa(sdir);
-
 	obj = ast_odbc_request_obj(odbc_database, 0);
-	if (obj) {
-		snprintf(msgnums, sizeof(msgnums), "%d", smsg);
-		snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
-		stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-		if (!stmt)
-			ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-		else
-			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-		ast_odbc_release_obj(obj);
-	} else
+	if (!obj) {
 		ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-	return;	
+		return;
+	}
+
+	argv[0] = ast_strdupa(sdir);
+
+	snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+	snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
+	stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+	if (!stmt) {
+		ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+	} else {
+		SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+	}
+	ast_odbc_release_obj(obj);
+
+	return;
 }
 
 /*!
@@ -4232,19 +4240,22 @@ static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailbox
 	generate_msg_id(msg_id);
 	delete_file(ddir, dmsg);
 	obj = ast_odbc_request_obj(odbc_database, 0);
-	if (obj) {
-		snprintf(msgnums, sizeof(msgnums), "%d", smsg);
-		snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
-		snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, msg_id, context, macrocontext, callerid, origtime, duration, recording, flag, mailboxuser, mailboxcontext) SELECT ?,?,?,context,macrocontext,callerid,origtime,duration,recording,flag,?,? FROM %s WHERE dir=? AND msgnum=?", odbc_table, odbc_table);
-		stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-		if (!stmt)
-			ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
-		else
-			SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-		ast_odbc_release_obj(obj);
-	} else
+	if (!obj) {
 		ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-	return;	
+		return;
+	}
+
+	snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+	snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
+	snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, msg_id, context, macrocontext, callerid, origtime, duration, recording, flag, mailboxuser, mailboxcontext) SELECT ?,?,?,context,macrocontext,callerid,origtime,duration,recording,flag,?,? FROM %s WHERE dir=? AND msgnum=?", odbc_table, odbc_table);
+	stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+	if (!stmt)
+		ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
+	else
+		SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+	ast_odbc_release_obj(obj);
+
+	return;
 }
 
 struct insert_data {
@@ -4273,9 +4284,8 @@ static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
 	SQLHSTMT stmt;
 
 	res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
-	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+	if (!SQL_SUCCEEDED(res)) {
 		ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
-		SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 		return NULL;
 	}
 
@@ -4295,7 +4305,7 @@ static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
 		SQLBindParameter(stmt, 13, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
 	}
 	res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
-	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+	if (!SQL_SUCCEEDED(res)) {
 		ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
 		SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 		return NULL;
@@ -4312,7 +4322,7 @@ static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
  * \param msgnum the message index for the message to be stored.
  *
  * This method is used when mailboxes are stored in an ODBC back end.
- * The message sound file and information file is looked up on the file system. 
+ * The message sound file and information file is looked up on the file system.
  * A SQL query is invoked to store the message into the (MySQL) database.
  *
  * \return the zero on success -1 on error.
@@ -4337,7 +4347,9 @@ static int store_file(const char *dir, const char *mailboxuser, const char *mail
 	struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
 
 	delete_file(dir, msgnum);
-	if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
+
+	obj = ast_odbc_request_obj(odbc_database, 0);
+	if (!obj) {
 		ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
 		return -1;
 	}
@@ -4400,25 +4412,25 @@ static int store_file(const char *dir, const char *mailboxuser, const char *mail
 			ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
 			res = -1;
 			break;
-		} 
+		}
 		idata.data = fdm;
 		idata.datalen = idata.indlen = fdlen;
 
-		if (!ast_strlen_zero(idata.category)) 
-			snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,msg_id,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
+		if (!ast_strlen_zero(idata.category))
+			snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,msg_id,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
 		else
 			snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,msg_id) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
 
 		if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
-			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+			SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 		} else {
 			ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
 			res = -1;
 		}
 	} while (0);
-	if (obj) {
-		ast_odbc_release_obj(obj);
-	}
+
+	ast_odbc_release_obj(obj);
+
 	if (valid_config(cfg))
 		ast_config_destroy(cfg);
 	if (fdm != MAP_FAILED)
@@ -4452,20 +4464,23 @@ static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxco
 	struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
 
 	delete_file(ddir, dmsg);
+
 	obj = ast_odbc_request_obj(odbc_database, 0);
-	if (obj) {
-		snprintf(msgnums, sizeof(msgnums), "%d", smsg);
-		snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
-		snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
-		stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-		if (!stmt)
-			ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-		else
-			SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-		ast_odbc_release_obj(obj);
-	} else
+	if (!obj) {
 		ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-	return;	
+		return;
+	}
+
+	snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+	snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
+	snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
+	stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+	if (!stmt)
+		ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+	else
+		SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+	ast_odbc_release_obj(obj);
+	return;
 }
 
 /*!
@@ -5357,55 +5372,95 @@ plain_message:
 
 static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum)
 {
-	char tmpdir[256], newtmp[256];
-	char fname[256];
-	char tmpcmd[256];
-	int tmpfd = -1;
-	int soxstatus = 0;
+	char fname[PATH_MAX] = "";
+	char *file_to_delete = NULL, *dir_to_delete = NULL;
+	int res;
 
 	/* Eww. We want formats to tell us their own MIME type */
-	char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
+	char *mime_type = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
+
+	/* This 'while' loop will only execute once. We use it so that we can 'break' */
+	while (vmu->volgain < -.001 || vmu->volgain > .001) {
+		char tmpdir[PATH_MAX];
+		char sox_gain_tmpdir[PATH_MAX];
 
-	if (vmu->volgain < -.001 || vmu->volgain > .001) {
 		create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
-		snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
-		tmpfd = mkstemp(newtmp);
-		chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
-		ast_debug(3, "newtmp: %s\n", newtmp);
-		if (tmpfd > -1) {
-			snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
-			if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
-				attach = newtmp;
-				ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
+
+		res = snprintf(sox_gain_tmpdir, sizeof(sox_gain_tmpdir), "%s/vm-gain-XXXXXX", tmpdir);
+		if (res >= sizeof(sox_gain_tmpdir)) {
+			ast_log(LOG_ERROR, "Failed to create temporary directory path %s: Out of buffer space\n", tmpdir);
+			break;
+		}
+
+		if (mkdtemp(sox_gain_tmpdir)) {
+			int soxstatus = 0;
+			char sox_gain_cmd[PATH_MAX];
+
+			ast_debug(3, "sox_gain_tmpdir: %s\n", sox_gain_tmpdir);
+
+			/* Save for later */
+			dir_to_delete = sox_gain_tmpdir;
+
+			res = snprintf(fname, sizeof(fname), "%s/output.%s", sox_gain_tmpdir, format);
+			if (res >= sizeof(fname)) {
+				ast_log(LOG_ERROR, "Failed to create filename buffer for %s/output.%s: Too long\n", sox_gain_tmpdir, format);
+				break;
+			}
+
+			res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s %s",
+						   vmu->volgain, attach, format, fname);
+			if (res >= sizeof(sox_gain_cmd)) {
+				ast_log(LOG_ERROR, "Failed to generate sox command, out of buffer space\n");
+				break;
+			}
+
+			soxstatus = ast_safe_system(sox_gain_cmd);
+			if (!soxstatus) {
+				/* Save for later */
+				file_to_delete = fname;
+				ast_debug(3, "VOLGAIN: Stored at: %s - Level: %.4f - Mailbox: %s\n", fname, vmu->volgain, mailbox);
 			} else {
-				ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
-					soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
+				ast_log(LOG_WARNING, "Sox failed to re-encode %s: %s (have you installed support for all sox file formats?)\n",
+						fname,
+						soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
 				ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
 			}
 		}
+
+		break;
 	}
+
+	if (!file_to_delete) {
+		res = snprintf(fname, sizeof(fname), "%s.%s", attach, format);
+		if (res >= sizeof(fname)) {
+			ast_log(LOG_ERROR, "Failed to create filename buffer for %s.%s: Too long\n", attach, format);
+			return -1;
+		}
+	}
+
 	fprintf(p, "--%s" ENDL, bound);
 	if (msgnum > -1)
-		fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
+		fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, mime_type, format, filename);
 	else
-		fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
+		fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, mime_type, format, greeting_attachment, format);
 	fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
 	fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
 	if (msgnum > -1)
 		fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
 	else
 		fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
-	snprintf(fname, sizeof(fname), "%s.%s", attach, format);
 	base_encode(fname, p);
 	if (last)
 		fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
-	if (tmpfd > -1) {
-		if (soxstatus == 0) {
-			unlink(fname);
-		}
-		close(tmpfd);
-		unlink(newtmp);
+
+	if (file_to_delete) {
+		unlink(file_to_delete);
+	}
+
+	if (dir_to_delete) {
+		rmdir(dir_to_delete);
 	}
+
 	return 0;
 }
 
@@ -5665,17 +5720,48 @@ static void free_zone(struct vm_zone *z)
 }
 
 #ifdef ODBC_STORAGE
-static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
+
+static int count_messages_in_folder(struct odbc_obj *odbc, const char *context, const char *mailbox, const char *folder, int *messages)
 {
-	int x = -1;
 	int res;
-	SQLHSTMT stmt = NULL;
 	char sql[PATH_MAX];
 	char rowdata[20];
+	SQLHSTMT stmt = NULL;
+	struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
+
+	if (!messages) {
+		return 0;
+	}
+
+	snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
+	if (!(stmt = ast_odbc_prepare_and_execute(odbc, generic_prepare, &gps))) {
+		ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+		return 1;
+	}
+	res = SQLFetch(stmt);
+	if (!SQL_SUCCEEDED(res)) {
+		ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+		SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+		return 1;
+	}
+	res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+	if (!SQL_SUCCEEDED(res)) {
+		ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+		SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+		return 1;
+	}
+
+	*messages = atoi(rowdata);
+	SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+
+	return 0;
+}
+
+static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
+{
 	char tmp[PATH_MAX] = "";
-	struct odbc_obj *obj = NULL;
+	struct odbc_obj *obj;
 	char *context;
-	struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
 
 	if (newmsgs)
 		*newmsgs = 0;
@@ -5717,87 +5803,28 @@ static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *
 	} else
 		context = "default";
 
-	if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
-		do {
-			if (newmsgs) {
-				snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
-				if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
-					ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-					break;
-				}
-				res = SQLFetch(stmt);
-				if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-					ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-					break;
-				}
-				res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-				if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-					ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-					break;
-				}
-				*newmsgs = atoi(rowdata);
-				SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-			}
-
-			if (oldmsgs) {
-				snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
-				if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
-					ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-					break;
-				}
-				res = SQLFetch(stmt);
-				if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-					ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-					break;
-				}
-				res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-				if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-					ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-					break;
-				}
-				SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-				*oldmsgs = atoi(rowdata);
-			}
-
-			if (urgentmsgs) {
-				snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
-				if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
-					ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-					break;
-				}
-				res = SQLFetch(stmt);
-				if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-					ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-					break;
-				}
-				res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-				if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-					ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-					break;
-				}
-				*urgentmsgs = atoi(rowdata);
-			}
-
-			x = 0;
-		} while (0);
-	} else {
+	obj = ast_odbc_request_obj(odbc_database, 0);
+	if (!obj) {
 		ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+		return -1;
 	}
 
-	if (stmt) {
-		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+	if (count_messages_in_folder(obj, context, tmp, "INBOX", newmsgs)
+	   || count_messages_in_folder(obj, context, tmp, "Old", oldmsgs)
+	   || count_messages_in_folder(obj, context, tmp, "Urgent", urgentmsgs)) {
+		ast_log(AST_LOG_WARNING, "Failed to obtain message count for mailbox %s@%s\n",
+				tmp, context);
 	}
-	if (obj) {
-		ast_odbc_release_obj(obj);
-	}
-	return x;
+
+	ast_odbc_release_obj(obj);
+	return 0;
 }
 
 /*!
  * \brief Gets the number of messages that exist in a mailbox folder.
  * \param mailbox_id
  * \param folder
- * 
+ *
  * This method is used when ODBC backend is used.
  * \return The number of messages in this mailbox folder (zero or more).
  */
@@ -5824,37 +5851,39 @@ static int messagecount(const char *mailbox_id, const char *folder)
 	}
 
 	obj = ast_odbc_request_obj(odbc_database, 0);
-	if (obj) {
-		if (!strcmp(folder, "INBOX")) {
-			snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/INBOX' OR dir = '%s%s/%s/Urgent'", odbc_table, VM_SPOOL_DIR, context, mailbox, VM_SPOOL_DIR, context, mailbox);
-		} else {
-			snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
-		}
-		stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-		if (!stmt) {
-			ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-			goto yuck;
-		}
-		res = SQLFetch(stmt);
-		if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-			ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-			goto yuck;
-		}
-		res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-		if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-			ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-			SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-			goto yuck;
-		}
-		nummsgs = atoi(rowdata);
-		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-	} else
+	if (!obj) {
 		ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+		return 0;
+	}
+
+	if (!strcmp(folder, "INBOX")) {
+		snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/INBOX' OR dir = '%s%s/%s/Urgent'", odbc_table, VM_SPOOL_DIR, context, mailbox, VM_SPOOL_DIR, context, mailbox);
+	} else {
+		snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
+	}
+
+	stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+	if (!stmt) {
+		ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+		goto bail;
+	}
+	res = SQLFetch(stmt);
+	if (!SQL_SUCCEEDED(res)) {
+		ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+		goto bail_with_handle;
+	}
+	res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+	if (!SQL_SUCCEEDED(res)) {
+		ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+		goto bail_with_handle;
+	}
+	nummsgs = atoi(rowdata);
+
+bail_with_handle:
+	SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 
-yuck:
-	if (obj)
-		ast_odbc_release_obj(obj);
+bail:
+	ast_odbc_release_obj(obj);
 	return nummsgs;
 }
 
@@ -6501,6 +6530,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
 	int ausemacro = 0;
 	int ousemacro = 0;
 	int ouseexten = 0;
+	int greeting_only = 0;
 	char tmpdur[16];
 	char priority[16];
 	char origtime[16];
@@ -6560,6 +6590,13 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
 		ast_free(tmp);
 		return res;
 	}
+
+	/* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
+	if (vmu->maxmsg == 0) {
+		greeting_only = 1;
+		ast_set_flag(options, OPT_SILENT);
+	}
+
 	/* Setup pre-file if appropriate */
 	if (strcmp(vmu->context, "default"))
 		snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
@@ -6684,12 +6721,6 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
 		ast_set_flag(options, OPT_SILENT);
 		res = 0;
 	}
-	/* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
-	if (vmu->maxmsg == 0) {
-		ast_debug(3, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
-		pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
-		goto leave_vm_out;
-	}
 	if (!res && !ast_test_flag(options, OPT_SILENT)) {
 		res = ast_stream_and_wait(chan, INTRO, ecodes);
 		if (res == '#') {
@@ -6745,6 +6776,13 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
 		return res;
 	}
 
+	if (greeting_only) {
+		ast_debug(3, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
+		pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
+		res = 0;
+		goto leave_vm_out;
+	}
+
 	if (res < 0) {
 		free_user(vmu);
 		pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
@@ -7926,7 +7964,7 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu,
 
 				*duration += prepend_duration;
 				msg_cat = ast_category_get(msg_cfg, "message", NULL);
-				snprintf(duration_buf, 11, "%ld", *duration);
+				snprintf(duration_buf, sizeof(duration_buf), "%ld", *duration);
 				if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
 					ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
 				}
@@ -8696,7 +8734,7 @@ static int play_message_duration(struct ast_channel *chan, struct vm_state *vms,
 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
 {
 	int res = 0;
-	char filename[256], *cid;
+	char filename[PATH_MAX], *cid;
 	const char *origtime, *context, *category, *duration, *flag;
 	struct ast_config *msg_cfg;
 	struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
@@ -10998,7 +11036,7 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_
 			int skipuser, int max_logins, int silent)
 {
 	int useadsi = 0, valid = 0, logretries = 0;
-	char password[AST_MAX_EXTENSION]="", *passptr;
+	char password[AST_MAX_EXTENSION], *passptr;
 	struct ast_vm_user vmus, *vmu = NULL;
 
 	/* If ADSI is supported, setup login screen */
@@ -11040,7 +11078,8 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_
 			adsi_password(chan);
 
 		if (!ast_strlen_zero(prefix)) {
-			char fullusername[80] = "";
+			char fullusername[80];
+
 			ast_copy_string(fullusername, prefix, sizeof(fullusername));
 			strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
 			ast_copy_string(mailbox, fullusername, mailbox_size);
@@ -11098,6 +11137,10 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_
 					free_user(vmu);
 					return -1;
 				}
+				if (ast_waitstream(chan, "")) {	/* Channel is hung up */
+					free_user(vmu);
+					return -1;
+				}
 			} else {
 				if (useadsi)
 					adsi_login(chan);
@@ -11107,10 +11150,6 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_
 					return -1;
 				}
 			}
-			if (ast_waitstream(chan, "")) {	/* Channel is hung up */
-				free_user(vmu);
-				return -1;
-			}
 		}
 	}
 	if (!valid && (logretries >= max_logins)) {
diff --git a/apps/app_waitforsilence.c b/apps/app_waitforsilence.c
index 07f2824..e56e49f 100644
--- a/apps/app_waitforsilence.c
+++ b/apps/app_waitforsilence.c
@@ -46,6 +46,7 @@
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
+#include "asterisk/app.h"
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
 #include "asterisk/pbx.h"
@@ -59,7 +60,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 			Waits for a specified amount of silence.
 		</synopsis>
 		<syntax>
-			<parameter name="silencerequired" required="true" />
+			<parameter name="silencerequired">
+				<para>If not specified, defaults to <literal>1000</literal> milliseconds.</para>
+			</parameter>
 			<parameter name="iterations">
 				<para>If not specified, defaults to <literal>1</literal>.</para>
 			</parameter>
@@ -103,7 +106,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 			Waits for a specified amount of noise.
 		</synopsis>
 		<syntax>
-			<parameter name="noiserequired" required="true" />
+			<parameter name="noiserequired">
+				<para>If not specified, defaults to <literal>1000</literal> milliseconds.</para>
+			</parameter>
 			<parameter name="iterations">
 				<para>If not specified, defaults to <literal>1</literal>.</para>
 			</parameter>
@@ -127,17 +132,32 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 static char *app_silence = "WaitForSilence";
 static char *app_noise = "WaitForNoise";
 
-static int do_waiting(struct ast_channel *chan, int timereqd, time_t waitstart, int timeout, int wait_for_silence) {
-	struct ast_frame *f = NULL;
-	int dsptime = 0;
+struct wait_type {
+	const char *name;
+	const char *status;
+	int stop_on_frame_timeout;
+	int (*func)(struct ast_dsp *, struct ast_frame *, int *);
+};
+
+static const struct wait_type wait_for_silence = {
+	.name = "silence",
+	.status = "SILENCE",
+	.stop_on_frame_timeout = 1,
+	.func = ast_dsp_silence,
+};
+
+static const struct wait_type wait_for_noise = {
+	.name = "noise",
+	.status = "NOISE",
+	.stop_on_frame_timeout = 0,
+	.func = ast_dsp_noise,
+};
+
+static int do_waiting(struct ast_channel *chan, int timereqd, time_t waitstart, int timeout, const struct wait_type *wait_for)
+{
 	RAII_VAR(struct ast_format *, rfmt, NULL, ao2_cleanup);
-	int res = 0;
-	struct ast_dsp *sildet;	 /* silence detector dsp */
- 	time_t now;
-
-	/*Either silence or noise calc depending on wait_for_silence flag*/
-	int (*ast_dsp_func)(struct ast_dsp*, struct ast_frame*, int*) =
-				wait_for_silence ? ast_dsp_silence : ast_dsp_noise;
+	int res;
+	struct ast_dsp *sildet;
 
 	rfmt = ao2_bump(ast_channel_readformat(chan));
 	if ((res = ast_set_read_format(chan, ast_format_slin)) < 0) {
@@ -147,15 +167,13 @@ static int do_waiting(struct ast_channel *chan, int timereqd, time_t waitstart,
 
 	/* Create the silence detector */
 	if (!(sildet = ast_dsp_new())) {
-		ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
+		ast_log(LOG_WARNING, "Unable to create silence detector\n");
 		return -1;
 	}
 	ast_dsp_set_threshold(sildet, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE));
 
-	/* Await silence... */
 	for (;;) {
-		/* Start with no silence received */
-		dsptime = 0;
+		int dsptime = 0;
 
 		res = ast_waitfor(chan, timereqd);
 
@@ -164,34 +182,36 @@ static int do_waiting(struct ast_channel *chan, int timereqd, time_t waitstart,
 			pbx_builtin_setvar_helper(chan, "WAITSTATUS", "HANGUP");
 			break;
 		}
-		
+
 		/* We waited and got no frame; sounds like digital silence or a muted digital channel */
 		if (res == 0) {
-			dsptime = timereqd;
+			if (wait_for->stop_on_frame_timeout) {
+				dsptime = timereqd;
+			}
 		} else {
 			/* Looks like we did get a frame, so let's check it out */
-			if (!(f = ast_read(chan))) {
+			struct ast_frame *f = ast_read(chan);
+			if (!f) {
 				pbx_builtin_setvar_helper(chan, "WAITSTATUS", "HANGUP");
 				break;
 			}
 			if (f->frametype == AST_FRAME_VOICE) {
-				ast_dsp_func(sildet, f, &dsptime);
+				wait_for->func(sildet, f, &dsptime);
 			}
 			ast_frfree(f);
 		}
 
-		ast_debug(1, "Got %dms %s < %dms required\n", dsptime, wait_for_silence ? "silence" : "noise", timereqd);
+		ast_debug(1, "Got %dms of %s < %dms required\n", dsptime, wait_for->name, timereqd);
 
 		if (dsptime >= timereqd) {
-			ast_verb(3, "Exiting with %dms %s >= %dms required\n", dsptime, wait_for_silence ? "silence" : "noise", timereqd);
-			/* Ended happily with silence */
+			ast_verb(3, "Exiting with %dms of %s >= %dms required\n", dsptime, wait_for->name, timereqd);
+			pbx_builtin_setvar_helper(chan, "WAITSTATUS", wait_for->status);
+			ast_debug(1, "WAITSTATUS was set to %s\n", wait_for->status);
 			res = 1;
-			pbx_builtin_setvar_helper(chan, "WAITSTATUS", wait_for_silence ? "SILENCE" : "NOISE");
-			ast_debug(1, "WAITSTATUS was set to %s\n", wait_for_silence ? "SILENCE" : "NOISE");
 			break;
 		}
 
-		if (timeout && (difftime(time(&now), waitstart) >= timeout)) {
+		if (timeout && difftime(time(NULL), waitstart) >= timeout) {
 			pbx_builtin_setvar_helper(chan, "WAITSTATUS", "TIMEOUT");
 			ast_debug(1, "WAITSTATUS was set to TIMEOUT\n");
 			res = 0;
@@ -199,61 +219,86 @@ static int do_waiting(struct ast_channel *chan, int timereqd, time_t waitstart,
 		}
 	}
 
-
 	if (rfmt && ast_set_read_format(chan, rfmt)) {
 		ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_format_get_name(rfmt), ast_channel_name(chan));
 	}
+
 	ast_dsp_free(sildet);
 	return res;
 }
 
-static int waitfor_exec(struct ast_channel *chan, const char *data, int wait_for_silence)
+static int waitfor_exec(struct ast_channel *chan, const char *data, const struct wait_type *wait_for)
 {
 	int res = 1;
 	int timereqd = 1000;
 	int timeout = 0;
 	int iterations = 1, i;
 	time_t waitstart;
+	char *parse;
 	struct ast_silence_generator *silgen = NULL;
 
-	if (ast_channel_state(chan) != AST_STATE_UP) {
-		res = ast_answer(chan); /* Answer the channel */
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(timereqd);
+		AST_APP_ARG(iterations);
+		AST_APP_ARG(timeout);
+	);
+
+	parse = ast_strdupa(data);
+	AST_STANDARD_APP_ARGS(args, parse);
+
+	if (!ast_strlen_zero(args.timereqd)) {
+		if (sscanf(args.timereqd, "%30d", &timereqd) != 1 || timereqd < 0) {
+			ast_log(LOG_ERROR, "Argument '%srequired' must be an integer greater than or equal to zero.\n",
+					wait_for->name);
+			return -1;
+		}
 	}
 
-	if (!data || ( (sscanf(data, "%30d,%30d,%30d", &timereqd, &iterations, &timeout) != 3) &&
-		(sscanf(data, "%30d,%30d", &timereqd, &iterations) != 2) &&
-		(sscanf(data, "%30d", &timereqd) != 1) ) ) {
-		ast_log(LOG_WARNING, "Using default value of 1000ms, 1 iteration, no timeout\n");
+	if (!ast_strlen_zero(args.iterations)) {
+		if (sscanf(args.iterations, "%30d", &iterations) != 1 || iterations < 1) {
+			ast_log(LOG_ERROR, "Argument 'iterations' must be an integer greater than 0.\n");
+			return -1;
+		}
+	}
+
+	if (!ast_strlen_zero(args.timeout)) {
+		if (sscanf(args.timeout, "%30d", &timeout) != 1 || timeout < 0) {
+			ast_log(LOG_ERROR, "Argument 'timeout' must be an integer greater than or equal to zero.\n");
+			return -1;
+		}
+	}
+
+	if (ast_channel_state(chan) != AST_STATE_UP) {
+		ast_answer(chan); /* Answer the channel */
 	}
 
-	ast_verb(3, "Waiting %d time(s) for %d ms silence with %d timeout\n", iterations, timereqd, timeout);
+	ast_verb(3, "Waiting %d time(s) for %dms of %s with %ds timeout\n",
+			 iterations, timereqd, wait_for->name, timeout);
 
 	if (ast_opt_transmit_silence) {
 		silgen = ast_channel_start_silence_generator(chan);
 	}
+
 	time(&waitstart);
-	res = 1;
-	for (i=0; (i<iterations) && (res == 1); i++) {
-		res = do_waiting(chan, timereqd, waitstart, timeout, wait_for_silence);
+	for (i = 0; i < iterations && res == 1; i++) {
+		res = do_waiting(chan, timereqd, waitstart, timeout, wait_for);
 	}
+
 	if (silgen) {
 		ast_channel_stop_silence_generator(chan, silgen);
 	}
 
-
-	if (res > 0)
-		res = 0;
-	return res;
+	return res > 0 ? 0 : res;
 }
 
 static int waitforsilence_exec(struct ast_channel *chan, const char *data)
 {
-	return waitfor_exec(chan, data, 1);
+	return waitfor_exec(chan, data, &wait_for_silence);
 }
 
 static int waitfornoise_exec(struct ast_channel *chan, const char *data)
 {
-	return waitfor_exec(chan, data, 0);
+	return waitfor_exec(chan, data, &wait_for_noise);
 }
 
 static int unload_module(void)
@@ -274,6 +319,4 @@ static int load_module(void)
 	return res;
 }
 
-AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Wait For Silence");
-
-
+AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Wait For Silence/Noise");
diff --git a/asterisk-13.17.0-summary.html b/asterisk-13.17.0-summary.html
deleted file mode 100644
index 3b74101..0000000
--- a/asterisk-13.17.0-summary.html
+++ /dev/null
@@ -1,306 +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.17.0</title><h1 align="center"><a name="top">Release Summary</a></h1><h3 align="center">asterisk-13.17.0</h3><h3 align="center">Date: 2017-07-12</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%">17 Sean Bright <sean.bright at gmail.com><br/>12 George Joseph <gjoseph at digium.com><br/>10 Joshua Colp <jcolp at digium.com><br/>9 Alexei Gradinari <alex2grad at gmail.com><br/>5 Richard Mudgett <rmudgett at digium.com><br/>5 Kevin Harwell <kharwell at digium.com><br/>2 Torrey Searle <tsearle at gmail.com><br/>2 Guido Falsi <madpilot at freebsd.org><br/>2 Alexander Traud <pabstraud at compuserve.com><br/>1 Jan Friesse <jfriesse at redhat.com><br/>1 Florian Floimair <f.floimair at comm [...]
-</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: Addons/format_mp3</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-23951">ASTERISK-23951</a>:  Asterisk attempts and fails to build format_mp3 even if mp3lib was not downloaded<br/>Reported by: Tzafrir Cohen<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=97b003f5e2d4a350508fc20173e180a23f8ef525">[97b003f5e2]</a> Sean Bright -- format_mp3: Re-work menuselect/build issues</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=72213c98e3d4d5287ed321f1b4fb67087a7a129c">[72213c98e3]</a> Sean Bright -- format_mp3: Don't try to build format_mp3 if we don't have sources</li>
-</ul><br><h4>Category: Applications/app_confbridge</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27012">ASTERISK-27012</a>: app_confbridge: ConfBridge sometimes does not play user name recording while leaving<br/>Reported by: Robert Mordec<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=f1b32de2c5fb8854183f0c7d8c9df7470ab9c140">[f1b32de2c5]</a> Robert Mordec -- app_confbridge: Race between removing and playing name recording while leaving</li>
-</ul><br><h4>Category: Applications/app_meetme</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27025">ASTERISK-27025</a>: channel / meetme: Fix missing parentheses<br/>Reported by: Joshua Colp<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=dc05183f4b7d728534ec6fa5f3fc21802396aabf">[dc05183f4b]</a> Joshua Colp -- channel / app_meetme: Fix parentheses.</li>
-</ul><br><h4>Category: Applications/app_queue</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25665">ASTERISK-25665</a>: Duplicate logging in queue log for EXITEMPTY events<br/>Reported by: Ove Aursand<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=2c43ca0ac50764ab17d691844a84158bbf590b0e">[2c43ca0ac5]</a> Ivan Poddubny -- app_queue: Fix returning to dialplan when a queue is empty</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27065">ASTERISK-27065</a>: call hangup after leaving app_queue<br/>Reported by: Marek Cervenka<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=2c43ca0ac50764ab17d691844a84158bbf590b0e">[2c43ca0ac5]</a> Ivan Poddubny -- app_queue: Fix returning to dialplan when a queue is empty</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26399">ASTERISK-26399</a>: app_queue: Agent not called when caller is parked<br/>Reported by: wushumasters<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=6bfcb1acc7ae53d50e1b784b4d46c588744aae8b">[6bfcb1acc7]</a> Joshua Colp -- app_queue: Fix members showing as being in call when not.</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26400">ASTERISK-26400</a>: app_queue: Queue member stops being called after AMI "Redirect" action for queues with wrapuptime<br/>Reported by: Etienne Lessard<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=6bfcb1acc7ae53d50e1b784b4d46c588744aae8b">[6bfcb1acc7]</a> Joshua Colp -- app_queue: Fix members showing as being in call when not.</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26715">ASTERISK-26715</a>: app_queue: Member will not receive any new calls after doing a transfer if wrapuptime = greater than 0 and using Local channel<br/>Reported by: David Brillert<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=6bfcb1acc7ae53d50e1b784b4d46c588744aae8b">[6bfcb1acc7]</a> Joshua Colp -- app_queue: Fix members showing as being in call when not.</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26975">ASTERISK-26975</a>: app_queue: Non-zero wrapup time can cause agents not to receive queue calls after transfer queue call<br/>Reported by: Lorne Gaetz<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=6bfcb1acc7ae53d50e1b784b4d46c588744aae8b">[6bfcb1acc7]</a> Joshua Colp -- app_queue: Fix members showing as being in call when not.</li>
-</ul><br><h4>Category: Applications/app_voicemail/IMAP</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-24052">ASTERISK-24052</a>: app_voicemail reloads result in leaked IMAP sockets.<br/>Reported by: Louis Jocelyn Paquet<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=8f356192d196ae146b0c2390f8d62024694e691f">[8f356192d1]</a> Alexei Gradinari -- app_voicemail: IMAP connection control</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=3b6c327c515944d74aa798f385e01768a4bb04c2">[3b6c327c51]</a> Alexei Gradinari -- app_voicemail: IMAP logout on reload/unload</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=08be5e01e8ab72a7e9e80525e20967467a6df99b">[08be5e01e8]</a> Alexei Gradinari -- app_voicemail: IMAP logout on MWI unsubscribe</li>
-</ul><br><h4>Category: Bridges/bridge_simple</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26973">ASTERISK-26973</a>: bridge: Crash when freeing frame and snooping<br/>Reported by: Michel R. Vaillancourt<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=adfb28882bfd2055d8b54705805db573d8ce6c94">[adfb28882b]</a> Kevin Harwell -- channel: ast_write frame wrongly freed after call to audiohooks</li>
-</ul><br><h4>Category: Channels/chan_pjsip</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27039">ASTERISK-27039</a>: chan_pjsip: Device state is idle when channel from endpoint is in early media<br/>Reported by: Joshua Colp<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=1f10c6b3b044f9979c523f65f449670047dcb57f">[1f10c6b3b0]</a> Joshua Colp -- chan_pjsip: Update device state when in early media.</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26996">ASTERISK-26996</a>: chan_pjsip: Flipping between codecs<br/>Reported by: Michael Maier<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=996a4791ff123e80d71d44cb0fd13bb201d197b1">[996a4791ff]</a> Joshua Colp -- pjsip: Extend 'asymmetric_rtp_codec' option to include us changing.</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26281">ASTERISK-26281</a>: chan_pjsip would send INVITE to 'Unreachable' endpoints<br/>Reported by: Jacek Konieczny<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=746c2c574578608a6b48d4794ba33cda5a6dd484">[746c2c5745]</a> Joshua Colp -- res_pjsip: Add support for returning only reachable contacts and use it.</li>
-</ul><br><h4>Category: Channels/chan_sip/General</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27106">ASTERISK-27106</a>: [patch] autodomain (SIP Domain Support): Add only really different domain with TLS.<br/>Reported by: Alexander Traud<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=39d2ebbf56635355432eb96ff850c0c9bf2a5d63">[39d2ebbf56]</a> Alexander Traud -- chan_sip: Only when different, add TCP|TLS in autodomain (SIP Domain Support).</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=9f4b3b966e911fae157a484d8f4a1440130eede6">[9f4b3b966e]</a> Alexander Traud -- chan_sip: Fix a typo for tlsbindaddr in autodomain (SIP Domain Support).</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26982">ASTERISK-26982</a>: chan_sip: rtcp_mux setting may cause ice completion failure/delay if client offers rtcp-mux as negotiable<br/>Reported by: Stefan Engström<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=4479038073e57a67c19c1ec5dc8896fcc8c3a0fb">[4479038073]</a> Sean Bright -- chan_sip: Better ICE handling for RTCP-MUX</li>
-</ul><br><h4>Category: Channels/chan_sip/SRTP</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25101">ASTERISK-25101</a>: DTLS configuration can not be specified in the general section - documentation<br/>Reported by: Ben Langfeld<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=971a401ce95ed0f566b2e90a52d69d0274c63ff8">[971a401ce9]</a> Sean Bright -- sip.conf.sample: Clarify where DTLS settings are permitted</li>
-</ul><br><h4>Category: Codecs/General</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-24858">ASTERISK-24858</a>: [patch]Asterisk 13 PJSIP sends RTP packets in wrong byte order on Intel platform when using slin codec<br/>Reported by: Frankie Chin<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=70e5887906db8d585892409cde89e5e28111549a">[70e5887906]</a> Sean Bright -- format: Reintroduce smoother flags</li>
-</ul><br><h4>Category: Core/Bridging</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27075">ASTERISK-27075</a>: bridge: stuck channel(s) after failed attended transfer<br/>Reported by: Kevin Harwell<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=67664fbf95a00ced30f8791fd1089b4595e29479">[67664fbf95]</a> Kevin Harwell -- bridge: stuck channel(s) after failed attended transfer</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26923">ASTERISK-26923</a>: bridging: T.38 request is lost when channels are added to bridge<br/>Reported by: Torrey Searle<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=e414833f6e77345f4969116972e9cf1ad9b595fd">[e414833f6e]</a> Joshua Colp -- bridge: Add a deferred queue.</li>
-</ul><br><h4>Category: Core/Channels</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27100">ASTERISK-27100</a>: channel: ast_waitfordigit_full fails to clear flag in an error branch.<br/>Reported by: Corey Farrell<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=73520e9f58857049a086fb88106e342cdc25d3a1">[73520e9f58]</a> Corey Farrell -- channel: Clear channel flag in error branch.</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27074">ASTERISK-27074</a>: core_local: local channel data not being properly unref'ed and unlocked<br/>Reported by: Kevin Harwell<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=1f9913f2723cbcbf6d78f4da7ee4dd4decc13c05">[1f9913f272]</a> Kevin Harwell -- core_local: local channel data not being properly unref'ed and unlocked</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26923">ASTERISK-26923</a>: bridging: T.38 request is lost when channels are added to bridge<br/>Reported by: Torrey Searle<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=e414833f6e77345f4969116972e9cf1ad9b595fd">[e414833f6e]</a> Joshua Colp -- bridge: Add a deferred queue.</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27025">ASTERISK-27025</a>: channel / meetme: Fix missing parentheses<br/>Reported by: Joshua Colp<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=dc05183f4b7d728534ec6fa5f3fc21802396aabf">[dc05183f4b]</a> Joshua Colp -- channel / app_meetme: Fix parentheses.</li>
-</ul><br><h4>Category: Core/General</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26789">ASTERISK-26789</a>: Audit manipulation of channel flags without locks<br/>Reported by: Joshua Colp<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=161820396495a549c9a378d32136cbb5f28ef2af">[1618203964]</a> Joshua Colp -- asterisk: Audit locking of channel when manipulating flags.</li>
-</ul><br><h4>Category: Core/PBX</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27041">ASTERISK-27041</a>: Core/PBX: [patch] Deadlock between dialplan execution and application unregistration<br/>Reported by: Frederic LE FOLL<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=dc307af7f2ed653914aeadb0b7e613cb4e239b06">[dc307af7f2]</a> Frederic LE FOLL -- Core/PBX: Deadlock between dialplan execution and application unregistration.</li>
-</ul><br><h4>Category: Core/RTP</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26978">ASTERISK-26978</a>: rtp: Crash in ast_rtp_codecs_payload_code()<br/>Reported by: Ross Beer<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=eb48e99bd4f4556424a6799e2e5f7aebf8911e8d">[eb48e99bd4]</a> George Joseph -- bridge_native_rtp: Keep rtp instance refs on bridge_channel</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-24858">ASTERISK-24858</a>: [patch]Asterisk 13 PJSIP sends RTP packets in wrong byte order on Intel platform when using slin codec<br/>Reported by: Frankie Chin<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=70e5887906db8d585892409cde89e5e28111549a">[70e5887906]</a> Sean Bright -- format: Reintroduce smoother flags</li>
-</ul><br><h4>Category: Core/Sorcery</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27057">ASTERISK-27057</a>: Seg Fault in ast_sorcery_object_get_id at sorcery.c<br/>Reported by: Ryan Smith<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=c2eea791e4178e5f2e4446a5f70d81ac27cf2a0e">[c2eea791e4]</a> George Joseph -- res_pjsip_pubsub:  Fix reference to released endpoint</li>
-</ul><br><h4>Category: Documentation</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-23839">ASTERISK-23839</a>: AGI - RECORD FILE - documentation doesn't describe BEEP argument<br/>Reported by: Rusty Newton<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=3eb7fbba72482b3019a7493c68e533e67d9d8235">[3eb7fbba72]</a> Sean Bright -- res_agi: Clarify 'RECORD FILE' documentation</li>
-</ul><br><h4>Category: General</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27108">ASTERISK-27108</a>: Crash using 'data get' CLI command<br/>Reported by: Sean Bright<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=6258de458b2e6ba02e91ed67bbd2801f0984526a">[6258de458b]</a> Sean Bright -- core: Fix segfault when invoking 'data get' CLI command</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27060">ASTERISK-27060</a>: Comment typo format_g729.c<br/>Reported by: Matthew Fredrickson<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=0a40073750b46ae28ddf1041d5ed3ab57151298e">[0a40073750]</a> Matthew Fredrickson -- formats/format_g729: Fix typo in comment</li>
-</ul><br><h4>Category: PBX/pbx_realtime</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-19291">ASTERISK-19291</a>: Background in realtime<br/>Reported by: Andrew Nowrot<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=283cc59af746896a2b2bc23899fc86118895f7c0">[283cc59af7]</a> Sean Bright -- pbx_builtin: Properly handle hangup during Background</li>
-</ul><br><h4>Category: Resources/res_agi</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-23839">ASTERISK-23839</a>: AGI - RECORD FILE - documentation doesn't describe BEEP argument<br/>Reported by: Rusty Newton<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=3eb7fbba72482b3019a7493c68e533e67d9d8235">[3eb7fbba72]</a> Sean Bright -- res_agi: Clarify 'RECORD FILE' documentation</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-22432">ASTERISK-22432</a>: Async AGI crashes Asterisk when issuing "set variable" command without args<br/>Reported by: Antoine Pitrou<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=f306e451f6f905a2bb74c15cb844735c244a7610">[f306e451f6]</a> Sean Bright -- res_agi: Prevent crash when SET VARIABLE called without arguments</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25662">ASTERISK-25662</a>: Malformed AGI 520 Usage response<br/>Reported by: Tony Mountifield<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=a007e438c36960d4179e2f188767e7ae14a204d1">[a007e438c3]</a> Sean Bright -- res_agi: Fix malformed AGI usage response</li>
-</ul><br><h4>Category: Resources/res_ari</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27026">ASTERISK-27026</a>: res_ari: Crash when no ari.conf configuration file exists<br/>Reported by: Ronald Raikes<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=7901b9853e8f60e1d2dce44ce81dec6f7f866ccc">[7901b9853e]</a> George Joseph -- res_ari:  Add "module loaded" check to ari stubs</li>
-</ul><br><h4>Category: Resources/res_ari_recordings</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27021">ASTERISK-27021</a>: GET /recordings/stored returns 500 Internal Server Error<br/>Reported by: Tim Morgan<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=cf6cf59646f52dc3de12dac16c3c3824ce9ae927">[cf6cf59646]</a> Sean Bright -- stasis_recording: Correct ast_asprintf error checking</li>
-</ul><br><h4>Category: Resources/res_format_attr_h264</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27008">ASTERISK-27008</a>: res_format_attr_h264: SDP parse fails if fmtp optional parameters have a space<br/>Reported by: John Harris<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=700ef6861ab966008ca16e5f23c64eb68b047c08">[700ef6861a]</a> Sean Bright -- res_format_attr_h26x: Trim blanks in fmtp attributes</li>
-</ul><br><h4>Category: Resources/res_parking</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26399">ASTERISK-26399</a>: app_queue: Agent not called when caller is parked<br/>Reported by: wushumasters<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=6bfcb1acc7ae53d50e1b784b4d46c588744aae8b">[6bfcb1acc7]</a> Joshua Colp -- app_queue: Fix members showing as being in call when not.</li>
-</ul><br><h4>Category: Resources/res_pjsip</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27090">ASTERISK-27090</a>: PJSIP: Deadlock using TCP transport<br/>Reported by: Richard Mudgett<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=0d64cbde5756eaa1c7ee62116e112b7ebd198bbe">[0d64cbde57]</a> Richard Mudgett -- pjsip_distributor.c: Fix deadlock with TCP type transports.</li>
-</ul><br><h4>Category: Resources/res_pjsip/Bundling</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27052">ASTERISK-27052</a>: Asterisk build process fails with flag --with-pjproject-bundled with curl download command and slow network<br/>Reported by: alex<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=0bde568669ac26735c1058115ae96223a7e69a6b">[0bde568669]</a> George Joseph -- pjproject_bundled:  Use the asterisk github mirror for download</li>
-</ul><br><h4>Category: Resources/res_pjsip_refer</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27053">ASTERISK-27053</a>: res_pjsip_refer/session: Calls dropped during transfer<br/>Reported by: Kevin Harwell<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=6cdf3191d3538b2e9a1aec31580db1e01d73d5ef">[6cdf3191d3]</a> Kevin Harwell -- res_pjsip_refer/session: Calls dropped during transfer</li>
-</ul><br><h4>Category: Resources/res_pjsip_session</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27024">ASTERISK-27024</a>: nat/external_media settings ignored in 14.4.1<br/>Reported by: Christopher van de Sande<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=2dee95cc7a280d0ab84c778bf44a76aa62ac758b">[2dee95cc7a]</a> Florian Floimair -- res_pjsip_session:  Correct inverted test in session_outgoing_nat_hook</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27053">ASTERISK-27053</a>: res_pjsip_refer/session: Calls dropped during transfer<br/>Reported by: Kevin Harwell<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=6cdf3191d3538b2e9a1aec31580db1e01d73d5ef">[6cdf3191d3]</a> Kevin Harwell -- res_pjsip_refer/session: Calls dropped during transfer</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26964">ASTERISK-26964</a>: res_pjsip_session: Wrong From on reinvite when request and To URI differ<br/>Reported by: Yasin CANER<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=36628cc9c474b52b134a415803b14f87e420dce6">[36628cc9c4]</a> Yasin CANER -- res_pjsip_session : fixed wrong From Header number On Re-invite</li>
-</ul><br><h4>Category: Resources/res_pjsip_transport_websocket</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27046">ASTERISK-27046</a>: res_pjsip_transport_websocket: segfault in get_write_timeout<br/>Reported by: Jørgen H<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=e16a669c70c5a93bb9a38c218a5348cd62bd780a">[e16a669c70]</a> Jørgen H -- res_pjsip_transport_websocket: Add NULL check in get_write_timeout</li>
-</ul><br><h4>Category: Resources/res_rtp_asterisk</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27022">ASTERISK-27022</a>: res_rtp_asterisk: Incorrect SSRC change for RTCP component<br/>Reported by: Michael Walton<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=7dafe82751fd512d58bb3843601daff013958dd2">[7dafe82751]</a> George Joseph -- res_rtp_asterisk:  Fix ssrc change for rtcp srtp</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-24858">ASTERISK-24858</a>: [patch]Asterisk 13 PJSIP sends RTP packets in wrong byte order on Intel platform when using slin codec<br/>Reported by: Frankie Chin<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=70e5887906db8d585892409cde89e5e28111549a">[70e5887906]</a> Sean Bright -- format: Reintroduce smoother flags</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25101">ASTERISK-25101</a>: DTLS configuration can not be specified in the general section - documentation<br/>Reported by: Ben Langfeld<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=971a401ce95ed0f566b2e90a52d69d0274c63ff8">[971a401ce9]</a> Sean Bright -- sip.conf.sample: Clarify where DTLS settings are permitted</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26979">ASTERISK-26979</a>: res_rtp_asterisk: SRTP unprotect failed with authentication failure 10 or 110<br/>Reported by: Javier Riveros <ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=e91efef2bb35cd0b03f45ad1b1ba43203948368d">[e91efef2bb]</a> Kevin Harwell -- res_rtp_asterisk: rtcp mux using the wrong srtp unprotecting algorithm</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26982">ASTERISK-26982</a>: chan_sip: rtcp_mux setting may cause ice completion failure/delay if client offers rtcp-mux as negotiable<br/>Reported by: Stefan Engström<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=4479038073e57a67c19c1ec5dc8896fcc8c3a0fb">[4479038073]</a> Sean Bright -- chan_sip: Better ICE handling for RTCP-MUX</li>
-</ul><br><h4>Category: Resources/res_srtp</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25294">ASTERISK-25294</a>: srtp's crypto_get_random deprecated<br/>Reported by: Tzafrir Cohen<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=5e9cd1f20d86de1c25b7a9accffb7d3e2601878b">[5e9cd1f20d]</a> Sean Bright -- res_srtp: Add support for libsrtp2</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25101">ASTERISK-25101</a>: DTLS configuration can not be specified in the general section - documentation<br/>Reported by: Ben Langfeld<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=971a401ce95ed0f566b2e90a52d69d0274c63ff8">[971a401ce9]</a> Sean Bright -- sip.conf.sample: Clarify where DTLS settings are permitted</li>
-</ul><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26979">ASTERISK-26979</a>: res_rtp_asterisk: SRTP unprotect failed with authentication failure 10 or 110<br/>Reported by: Javier Riveros <ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=e91efef2bb35cd0b03f45ad1b1ba43203948368d">[e91efef2bb]</a> Kevin Harwell -- res_rtp_asterisk: rtcp mux using the wrong srtp unprotecting algorithm</li>
-</ul><br><h4>Category: Resources/res_stasis_snoop</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26973">ASTERISK-26973</a>: bridge: Crash when freeing frame and snooping<br/>Reported by: Michel R. Vaillancourt<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=adfb28882bfd2055d8b54705805db573d8ce6c94">[adfb28882b]</a> Kevin Harwell -- channel: ast_write frame wrongly freed after call to audiohooks</li>
-</ul><br><h4>Category: pjproject/pjsip</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26333">ASTERISK-26333</a>: Problems with Blind Transfer, PJSIP (Aastra 6869i)<br/>Reported by: Matthias Binder<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=6af2dd34afc2c20bdabd07bc3836821690db4c86">[6af2dd34af]</a> Alexei Gradinari -- res_pjsip: New endpoint option "refer_blind_progress"</li>
-</ul><br><h3>Improvement</h3><h4>Category: Core/BuildSystem</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27043">ASTERISK-27043</a>: Core/BuildSystem: Add defines to fix build with LibreSSL<br/>Reported by: Guido Falsi<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=6a64f65fe6fee96702668bdd3344233f19232850">[6a64f65fe6]</a> Guido Falsi -- BuildSystem: Add patches to allow building with recent LibreSSL</li>
-</ul><br><h4>Category: Core/Channels</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26419">ASTERISK-26419</a>: audiohooks: Remove redundant codec translations when using audiohooks<br/>Reported by: Michael Walton<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=adfb28882bfd2055d8b54705805db573d8ce6c94">[adfb28882b]</a> Kevin Harwell -- channel: ast_write frame wrongly freed after call to audiohooks</li>
-</ul><br><h4>Category: Core/General</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26419">ASTERISK-26419</a>: audiohooks: Remove redundant codec translations when using audiohooks<br/>Reported by: Michael Walton<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=adfb28882bfd2055d8b54705805db573d8ce6c94">[adfb28882b]</a> Kevin Harwell -- channel: ast_write frame wrongly freed after call to audiohooks</li>
-</ul><br><h4>Category: Core/Portability</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27042">ASTERISK-27042</a>: Unpatched asterisk sources fail to build on FreeBSD due to missing crypt.h file<br/>Reported by: Guido Falsi<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=44cee2f4a15db911d2c9bdd6f845d17a1e6c6c17">[44cee2f4a1]</a> Guido Falsi -- BuildSystem: Fix build on FreeBSD due to missing crypt.h</li>
-</ul><br><h4>Category: Resources/res_agi</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26124">ASTERISK-26124</a>: res_agi: Set audio format for EAGI audio stream<br/>Reported by: John Fawcett<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=90237dca11d0adf129198cef4a6a0716a52618b5">[90237dca11]</a> Sean Bright -- res_agi: Allow configuration of audio format of EAGI pipe</li>
-</ul><br><h4>Category: Resources/res_pjsip_mwi</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26230">ASTERISK-26230</a>: [patch] res_pjsip_mwi: unsolicited mwi could block PJSIP taskprocessor on startup<br/>Reported by: Alexei Gradinari<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=0f6a9617eb44a8d59b5828cd860d3852cc824ce9">[0f6a9617eb]</a> Alexei Gradinari -- res_pjsip_mwi: update unsolicited MWI subscriptions on updating contact</li>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=59c9bbe6961a5677ddb13eed2a130d16b6ffc0ee">[59c9bbe696]</a> Alexei Gradinari -- res_pjsip_mwi: don't create mwi subscriptions if initial unsolicited disabled</li>
-</ul><br><h4>Category: Resources/res_rtp_asterisk</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26976">ASTERISK-26976</a>: libsrtp-2.x.x support<br/>Reported by: Alex<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=5e9cd1f20d86de1c25b7a9accffb7d3e2601878b">[5e9cd1f20d]</a> Sean Bright -- res_srtp: Add support for libsrtp2</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: Bridges/bridge_simple</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26469">ASTERISK-26469</a>: Infinite loop after a dual Redirect<br/>Reported by: Etienne Allovon<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=b07b2162359ccc9a3f84324fabce18b6ad63eee3">[b07b216235]</a> Joshua Colp -- manager: Clear the flag on the other channel.</li>
-</ul><br><h4>Category: Channels/chan_pjsip</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27095">ASTERISK-27095</a>: chan_pjsip: When connected_line_method is set to invite, we're not trying UPDATE<br/>Reported by: George Joseph<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=6bd7c0f37cb7b513d1333717ece0118bd8875546">[6bd7c0f37c]</a> George Joseph -- chan_pjsip:  Fix ability to send UPDATE on COLP</li>
-</ul><br><h4>Category: Core/Bridging</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27016">ASTERISK-27016</a>: Crash occurs when a channel in a 'mixing,dtmf_events' bridge is muted multiple times.<br/>Reported by: Chris Howard<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=4910a3bf402baddf8ed72badfaed7ae64da48686">[4910a3bf40]</a> Joshua Colp -- channel: Fix reference counting in ast_channel_suppress.</li>
-</ul><br><h4>Category: General</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27088">ASTERISK-27088</a>: res_rtp_asterisk: Better handle ICE renegotiation and unidirectional negotiation<br/>Reported by: Joshua Colp<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=0426b1d88ab97c4fc1b2b27f8da93b28096f2dfc">[0426b1d88a]</a> Joshua Colp -- res_rtp_asterisk: Fix issues with ICE renegotiation.</li>
-</ul><br><h4>Category: Resources/res_corosync</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25370">ASTERISK-25370</a>: res_corosync segfaults at startup with corosync version > 2.x<br/>Reported by: mdu113<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=005a4afa6b0e710e11b47b11cfc152b028c596fc">[005a4afa6b]</a> Jan Friesse -- res_corosync: Change thread stack size</li>
-</ul><br><h4>Category: Resources/res_pjsip_dialog_info_body_generator</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-26919">ASTERISK-26919</a>: res_pjsip_dialog_info_body_generator: Ringing&&InUse behavior difference between chan_sip and res_pjsip<br/>Reported by: Zach R<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=a6e4899612ca71bc3c9180dadea0c0117e8ae462">[a6e4899612]</a> Alexei Gradinari -- res_pjsip: New endpoint option "notify_early_inuse_ringing"</li>
-</ul><br><h4>Category: Resources/res_pjsip_mwi</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27051">ASTERISK-27051</a>: res_pjsip_mwi: unsolicited MWI has to be unsubscribed on deleting the endpoint's last contact<br/>Reported by: Alexei Gradinari<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=8e749c8f51c20fb13bfe93e969cf02d7e74cdb27">[8e749c8f51]</a> Alexei Gradinari -- res_pjsip_mwi: unsubscribe unsolicited MWI on deleting endpoint last contact</li>
-</ul><br><h4>Category: Resources/res_stasis</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27059">ASTERISK-27059</a>: res_stasis: Stolen channel references are leaking<br/>Reported by: George Joseph<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=edfdb4dff5d8438bdb1dfb526c57618944ea6bf3">[edfdb4dff5]</a> George Joseph -- res_stasis:  Plug reference leak on stolen channels</li>
-</ul><br><h4>Category: Third-Party/pjproject</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27097">ASTERISK-27097</a>: pjproject_bundled:  We don't pass options needed for cross-compile to pjproject configure<br/>Reported by: George Joseph<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=bbe68f139db525b2d922f63d8452d9732fb5f1b9">[bbe68f139d]</a> George Joseph -- pjproject_bundled:  Allow passing configure options to bundled</li>
-</ul><br><h3>Improvement</h3><h4>Category: Applications/app_voicemail/IMAP</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27068">ASTERISK-27068</a>: app_voicemail: Add global option "imap_poll_logout" to specify post-polling disconnect<br/>Reported by: Alexei Gradinari<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=8f356192d196ae146b0c2390f8d62024694e691f">[8f356192d1]</a> Alexei Gradinari -- app_voicemail: IMAP connection control</li>
-</ul><br><h4>Category: Channels/chan_pjsip</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27066">ASTERISK-27066</a>: res_pjsip: Add DTMF INFO Failback mode<br/>Reported by: Torrey Searle<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=9fbc34d2bd5393d93d8b3b3a8c6daa895c2e9633">[9fbc34d2bd]</a> Torrey Searle -- res_pjsip:  Add DTMF INFO Failback mode</li>
-</ul><br><h4>Category: Resources/res_pjsip</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27066">ASTERISK-27066</a>: res_pjsip: Add DTMF INFO Failback mode<br/>Reported by: Torrey Searle<ul>
-<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=9fbc34d2bd5393d93d8b3b3a8c6daa895c2e9633">[9fbc34d2bd]</a> Torrey Searle -- res_pjsip:  Add DTMF INFO Failback mode</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=0c00ee754b436ca926b92b469ce259e8fdc8732e">0c00ee754b</a></td><td>George Joseph</td><td>Update for 13.17.0-rc1</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=379fe658312e11699ff8c8e8a463e31b3c277237">379fe65831</a></td><td>George Joseph</td><td>Fix alembic branches</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=905d18e8bf52ea7657acaaf2ec0cbe58531fb625">905d18e8bf</a></td><td>Richard Mudgett</td><td>pjsip_distributor.c: Fix unidentified_requests hash functions.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=1f59d08924bc676970cabc6f3e291c7d1d2f2707">1f59d08924</a></td><td>Torrey Searle</td><td>res/res_pjsip_t38: fix incorrect increment of media_count</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=764d04fa8705d9e5c2e7aee8a6f1c774d7d28595">764d04fa87</a></td><td>Richard Mudgett</td><td>res_pjsip_mwi.c: Eliminate RAII_VAR in contact delete observer</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=cecf6540dc4779598289f711340bb966bbfcc6aa">cecf6540dc</a></td><td>Rodrigo Ramírez Norambuena</td><td>cdr: fix mistake spelling of a word for Unanswered.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=b9a4ab8c8c00c8d53584d6f7e31729b5027c8dd6">b9a4ab8c8c</a></td><td>Richard Mudgett</td><td>chan_pjsip: Fix PJSIP_MEDIA_OFFER dialplan function read.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=f1a209d5ac8f8b7fe96e54d6aba55dbf0dbb1403">f1a209d5ac</a></td><td>Richard Mudgett</td><td>app_voicemail.c: Fix compile error when IMAP enabled.</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=68de35a6a01e2a1fe732e156b73f800bb672a421">68de35a6a0</a></td><td>David M. Lee</td><td>CFLAGS for BIND8 support</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=da3312457e6cf1c0d7bc8cb2a4aba57877fb5afc">da3312457e</a></td><td>Sean Bright</td><td>codecs.conf.sample: Fix max_bandwidth speling error</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=590ffcaf0b03bbe3d25730ad750a2075a46c7208">590ffcaf0b</a></td><td>Sean Bright</td><td>eventfd: Disable during cross compilation</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=5520b6c201875133a73db5a2c88b5fc5b78864bb">5520b6c201</a></td><td>Alexei Gradinari</td><td>CHANGES: correct version for a new option 'refer_blind_progress'</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=c093bf8072ff65bf29d290c1330291c460cd7fdf">c093bf8072</a></td><td>Sean Bright</td><td>res_rtp_multicast: Use consistent timestamps when possible</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=c10341646d353922b4ee92c77fc4e5560d263c73">c10341646d</a></td><td>George Joseph</td><td>test_json:  Fix test names with reserved words</td></tr>
-<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=65898c3af82e2d780a48d9d50d3b1c952c208a89">65898c3af8</a></td><td>George Joseph</td><td>unittests:  Add a unit test that causes a SEGV and...</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.16.0-summary.html                                                                   |  405 ---
-asterisk-13.16.0-summary.txt                                                                    |  952 ---------
-b/.version                                                                                      |    2
-b/CHANGES                                                                                       |   54
-b/ChangeLog                                                                                     | 1045 +++++++++-
-b/Makefile                                                                                      |    3
-b/addons/Makefile                                                                               |   10
-b/apps/app_chanspy.c                                                                            |   16
-b/apps/app_confbridge.c                                                                         |   79
-b/apps/app_dial.c                                                                               |    6
-b/apps/app_disa.c                                                                               |   10
-b/apps/app_dumpchan.c                                                                           |    4
-b/apps/app_externalivr.c                                                                        |    6
-b/apps/app_meetme.c                                                                             |    2
-b/apps/app_queue.c                                                                              |  109 -
-b/apps/app_voicemail.c                                                                          |   80
-b/asterisk-13.17.0-rc1-summary.html                                                             |  311 ++
-b/asterisk-13.17.0-rc1-summary.txt                                                              |  832 +++++++
-b/autoconf/ast_ext_lib.m4                                                                       |   36
-b/bridges/bridge_native_rtp.c                                                                   |  677 +++++-
-b/bridges/bridge_simple.c                                                                       |   32
-b/channels/chan_pjsip.c                                                                         |   68
-b/channels/chan_sip.c                                                                           |    8
-b/channels/pjsip/dialplan_functions.c                                                           |   37
-b/configs/samples/cdr.conf.sample                                                               |    2
-b/configs/samples/codecs.conf.sample                                                            |    6
-b/configs/samples/pjsip.conf.sample                                                             |   20
-b/configs/samples/sip.conf.sample                                                               |    3
-b/configs/samples/voicemail.conf.sample                                                         |    3
-b/configure                                                                                     |  434 +++-
-b/configure.ac                                                                                  |  100
-b/contrib/ast-db-manage/config/versions/164abbd708c_add_auto_info_to_endpoint_dtmf_mode.py      |   58
-b/contrib/ast-db-manage/config/versions/86bb1efa278d_add_ps_endpoints_refer_blind_progress.py   |   30
-b/contrib/ast-db-manage/config/versions/d7983954dd96_add_ps_endpoints_notify_early_inuse_.py    |   30
-b/contrib/realtime/mssql/mssql_config.sql                                                       |   46
-b/contrib/realtime/mysql/mysql_config.sql                                                       |   18
-b/contrib/realtime/oracle/oracle_config.sql                                                     |   46
-b/contrib/realtime/postgresql/postgresql_config.sql                                             |   22
-b/formats/format_g729.c                                                                         |    2
-b/include/asterisk/ari.h                                                                        |   10
-b/include/asterisk/autoconfig.h.in                                                              |    3
-b/include/asterisk/bridge_channel.h                                                             |    2
-b/include/asterisk/bridge_channel_internal.h                                                    |   11
-b/include/asterisk/bridge_technology.h                                                          |    3
-b/include/asterisk/channel.h                                                                    |   25
-b/include/asterisk/codec.h                                                                      |    3
-b/include/asterisk/core_local.h                                                                 |   37
-b/include/asterisk/format.h                                                                     |   11
-b/include/asterisk/res_pjsip.h                                                                  |   74
-b/include/asterisk/res_pjsip_presence_xml.h                                                     |    3
-b/include/asterisk/res_pjsip_session.h                                                          |   11
-b/include/asterisk/rtp_engine.h                                                                 |    9
-b/include/asterisk/smoother.h                                                                   |    1
-b/include/asterisk/test.h                                                                       |    8
-b/main/autoservice.c                                                                            |    2
-b/main/bridge.c                                                                                 |   10
-b/main/bridge_after.c                                                                           |    2
-b/main/bridge_channel.c                                                                         |   38
-b/main/channel.c                                                                                |   90
-b/main/codec_builtin.c                                                                          |   19
-b/main/core_local.c                                                                             |   54
-b/main/crypt.c                                                                                  |    2
-b/main/data.c                                                                                   |    4
-b/main/file.c                                                                                   |   20
-b/main/format.c                                                                                 |    8
-b/main/libasteriskssl.c                                                                         |    4
-b/main/manager.c                                                                                |    8
-b/main/pbx.c                                                                                    |    4
-b/main/pbx_app.c                                                                                |    7
-b/main/pbx_builtins.c                                                                           |    8
-b/main/tcptls.c                                                                                 |    4
-b/main/test.c                                                                                   |    4
-b/makeopts.in                                                                                   |    2
-b/res/res_agi.c                                                                                 |   73
-b/res/res_ari_applications.c                                                                    |    4
-b/res/res_ari_asterisk.c                                                                        |    4
-b/res/res_ari_bridges.c                                                                         |    4
-b/res/res_ari_channels.c                                                                        |    4
-b/res/res_ari_device_states.c                                                                   |    4
-b/res/res_ari_endpoints.c                                                                       |    4
-b/res/res_ari_events.c                                                                          |   33
-b/res/res_ari_mailboxes.c                                                                       |    4
-b/res/res_ari_playbacks.c                                                                       |    4
-b/res/res_ari_recordings.c                                                                      |    4
-b/res/res_ari_sounds.c                                                                          |    4
-b/res/res_corosync.c                                                                            |   29
-b/res/res_format_attr_h263.c                                                                    |    2
-b/res/res_format_attr_h264.c                                                                    |    2
-b/res/res_musiconhold.c                                                                         |    4
-b/res/res_pjsip.c                                                                               |   31
-b/res/res_pjsip/location.c                                                                      |   53
-b/res/res_pjsip/pjsip_configuration.c                                                           |    9
-b/res/res_pjsip/pjsip_distributor.c                                                             |  242 +-
-b/res/res_pjsip/presence_xml.c                                                                  |    9
-b/res/res_pjsip_dialog_info_body_generator.c                                                    |   10
-b/res/res_pjsip_mwi.c                                                                           |   87
-b/res/res_pjsip_pidf_body_generator.c                                                           |    2
-b/res/res_pjsip_pidf_eyebeam_body_supplement.c                                                  |    2
-b/res/res_pjsip_pubsub.c                                                                        |    8
-b/res/res_pjsip_refer.c                                                                         |   28
-b/res/res_pjsip_sdp_rtp.c                                                                       |   38
-b/res/res_pjsip_session.c                                                                       |   37
-b/res/res_pjsip_session.exports.in                                                              |    1
-b/res/res_pjsip_t38.c                                                                           |    2
-b/res/res_pjsip_transport_websocket.c                                                           |    4
-b/res/res_pjsip_xpidf_body_generator.c                                                          |    2
-b/res/res_rtp_asterisk.c                                                                        |   41
-b/res/res_rtp_multicast.c                                                                       |  139 +
-b/res/res_srtp.c                                                                                |   15
-b/res/res_stasis.c                                                                              |   20
-b/res/srtp/srtp_compat.h                                                                        |   29
-b/res/stasis_recording/stored.c                                                                 |    4
-b/rest-api-templates/res_ari_resource.c.mustache                                                |   35
-b/tests/test_bridging.c                                                                         |  292 ++
-b/tests/test_json.c                                                                             |   16
-b/tests/test_pbx.c                                                                              |   22
-b/third-party/configure.m4                                                                      |    5
-b/third-party/pjproject/Makefile                                                                |    2
-b/third-party/pjproject/Makefile.rules                                                          |    7
-b/third-party/pjproject/configure.m4                                                            |   24
-b/third-party/pjproject/patches/0070-Set-PJSIP_INV_SUPPORT_UPDATE-correctly-in-pjsip_inv_.patch |   16
-121 files changed, 5477 insertions(+), 2043 deletions(-)</pre><br></html>
\ No newline at end of file
diff --git a/asterisk-13.17.0-summary.txt b/asterisk-13.17.0-summary.txt
deleted file mode 100644
index f828bc4..0000000
--- a/asterisk-13.17.0-summary.txt
+++ /dev/null
@@ -1,814 +0,0 @@
-                                Release Summary
-
-                                asterisk-13.17.0
-
-                                Date: 2017-07-12
-
-                           <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.16.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                  
-   17 Sean Bright                                  4 Alexei Gradinari         
-   12 George Joseph                                4 Joshua Colp              
-   10 Joshua Colp                                  3 Kevin Harwell            
-   9 Alexei Gradinari                              3 Louis Jocelyn Paquet     
-   5 Richard Mudgett                               3 Tzafrir Cohen            
-   5 Kevin Harwell                                 3 George Joseph            
-   2 Torrey Searle                                 2 Guido Falsi              
-   2 Guido Falsi                                   2 Alexander Traud          
-   2 Alexander Traud                               2 Michael Walton           
-   1 Jan Friesse                                   2 Torrey Searle            
-   1 Florian Floimair                              1 Rusty Newton             
-   1 Ivan Poddubny                                 1 Matthew Fredrickson      
-   1 Matthew Fredrickson                           1 Jacek Konieczny          
-   1 Yasin CANER                                   1 Tim Morgan               
-   1 David M. Lee                                  1 Etienne Allovon          
-   1 Robert Mordec                                 1 alex                     
-   1 JA,rgen H                                     1 Kinsey Moore             
-   1 Rodrigo Ramirez Norambuena                    1 John Harris              
-   1 Frederic LE FOLL                              1 Javier Riveros           
-   1 Corey Farrell                                 1 Sean Bright              
-                                                   1 Robert Mordec            
-                                                   1 Ross Beer                
-                                                   1 Chris Howard             
-                                                   1 mdu113                   
-                                                   1 Andrew Nowrot            
-                                                   1 'alex'                   
-                                                   1 Lorne Gaetz              
-                                                   1 Ben Langfeld             
-                                                   1 John Fawcett             
-                                                   1 Corey Farrell            
-                                                   1 Frankie Chin             
-                                                   1 Zach R                   
-                                                   1 Matthias Binder          
-                                                   1 Christopher van de Sande 
-                                                   1 Stefan EngstrAP:m        
-                                                   1 Antoine Pitrou           
-                                                   1 Alex                     
-                                                   1 Etienne Lessard          
-                                                   1 Ryan Smith               
-                                                   1 Michael Maier            
-                                                   1 OpenBSD ports            
-                                                   1 Marek Cervenka           
-                                                   1 Ronald Raikes            
-                                                   1 Ove Aursand              
-                                                   1 Richard Mudgett          
-                                                   1 Frederic LE FOLL         
-                                                   1 wushumasters             
-                                                   1 Tony Mountifield         
-                                                   1 JA,rgen H                
-                                                   1 Michel R. Vaillancourt   
-                                                   1 David Brillert           
-                                                   1 Yasin CANER              
-
-     ----------------------------------------------------------------------
-
-                                 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: Addons/format_mp3
-
-   ASTERISK-23951: Asterisk attempts and fails to build format_mp3 even if
-   mp3lib was not downloaded
-   Reported by: Tzafrir Cohen
-     * [97b003f5e2] Sean Bright -- format_mp3: Re-work menuselect/build
-       issues
-     * [72213c98e3] Sean Bright -- format_mp3: Don't try to build format_mp3
-       if we don't have sources
-
-    Category: Applications/app_confbridge
-
-   ASTERISK-27012: app_confbridge: ConfBridge sometimes does not play user
-   name recording while leaving
-   Reported by: Robert Mordec
-     * [f1b32de2c5] Robert Mordec -- app_confbridge: Race between removing
-       and playing name recording while leaving
-
-    Category: Applications/app_meetme
-
-   ASTERISK-27025: channel / meetme: Fix missing parentheses
-   Reported by: Joshua Colp
-     * [dc05183f4b] Joshua Colp -- channel / app_meetme: Fix parentheses.
-
-    Category: Applications/app_queue
-
-   ASTERISK-25665: Duplicate logging in queue log for EXITEMPTY events
-   Reported by: Ove Aursand
-     * [2c43ca0ac5] Ivan Poddubny -- app_queue: Fix returning to dialplan
-       when a queue is empty
-   ASTERISK-27065: call hangup after leaving app_queue
-   Reported by: Marek Cervenka
-     * [2c43ca0ac5] Ivan Poddubny -- app_queue: Fix returning to dialplan
-       when a queue is empty
-   ASTERISK-26399: app_queue: Agent not called when caller is parked
-   Reported by: wushumasters
-     * [6bfcb1acc7] Joshua Colp -- app_queue: Fix members showing as being in
-       call when not.
-   ASTERISK-26400: app_queue: Queue member stops being called after AMI
-   "Redirect" action for queues with wrapuptime
-   Reported by: Etienne Lessard
-     * [6bfcb1acc7] Joshua Colp -- app_queue: Fix members showing as being in
-       call when not.
-   ASTERISK-26715: app_queue: Member will not receive any new calls after
-   doing a transfer if wrapuptime = greater than 0 and using Local channel
-   Reported by: David Brillert
-     * [6bfcb1acc7] Joshua Colp -- app_queue: Fix members showing as being in
-       call when not.
-   ASTERISK-26975: app_queue: Non-zero wrapup time can cause agents not to
-   receive queue calls after transfer queue call
-   Reported by: Lorne Gaetz
-     * [6bfcb1acc7] Joshua Colp -- app_queue: Fix members showing as being in
-       call when not.
-
-    Category: Applications/app_voicemail/IMAP
-
-   ASTERISK-24052: app_voicemail reloads result in leaked IMAP sockets.
-   Reported by: Louis Jocelyn Paquet
-     * [8f356192d1] Alexei Gradinari -- app_voicemail: IMAP connection
-       control
-     * [3b6c327c51] Alexei Gradinari -- app_voicemail: IMAP logout on
-       reload/unload
-     * [08be5e01e8] Alexei Gradinari -- app_voicemail: IMAP logout on MWI
-       unsubscribe
-
-    Category: Bridges/bridge_simple
-
-   ASTERISK-26973: bridge: Crash when freeing frame and snooping
-   Reported by: Michel R. Vaillancourt
-     * [adfb28882b] Kevin Harwell -- channel: ast_write frame wrongly freed
-       after call to audiohooks
-
-    Category: Channels/chan_pjsip
-
-   ASTERISK-27039: chan_pjsip: Device state is idle when channel from
-   endpoint is in early media
-   Reported by: Joshua Colp
-     * [1f10c6b3b0] Joshua Colp -- chan_pjsip: Update device state when in
-       early media.
-   ASTERISK-26996: chan_pjsip: Flipping between codecs
-   Reported by: Michael Maier
-     * [996a4791ff] Joshua Colp -- pjsip: Extend 'asymmetric_rtp_codec'
-       option to include us changing.
-   ASTERISK-26281: chan_pjsip would send INVITE to 'Unreachable' endpoints
-   Reported by: Jacek Konieczny
-     * [746c2c5745] Joshua Colp -- res_pjsip: Add support for returning only
-       reachable contacts and use it.
-
-    Category: Channels/chan_sip/General
-
-   ASTERISK-27106: [patch] autodomain (SIP Domain Support): Add only really
-   different domain with TLS.
-   Reported by: Alexander Traud
-     * [39d2ebbf56] Alexander Traud -- chan_sip: Only when different, add
-       TCP|TLS in autodomain (SIP Domain Support).
-     * [9f4b3b966e] Alexander Traud -- chan_sip: Fix a typo for tlsbindaddr
-       in autodomain (SIP Domain Support).
-   ASTERISK-26982: chan_sip: rtcp_mux setting may cause ice completion
-   failure/delay if client offers rtcp-mux as negotiable
-   Reported by: Stefan EngstrAP:m
-     * [4479038073] Sean Bright -- chan_sip: Better ICE handling for RTCP-MUX
-
-    Category: Channels/chan_sip/SRTP
-
-   ASTERISK-25101: DTLS configuration can not be specified in the general
-   section - documentation
-   Reported by: Ben Langfeld
-     * [971a401ce9] Sean Bright -- sip.conf.sample: Clarify where DTLS
-       settings are permitted
-
-    Category: Codecs/General
-
-   ASTERISK-24858: [patch]Asterisk 13 PJSIP sends RTP packets in wrong byte
-   order on Intel platform when using slin codec
-   Reported by: Frankie Chin
-     * [70e5887906] Sean Bright -- format: Reintroduce smoother flags
-
-    Category: Core/Bridging
-
-   ASTERISK-27075: bridge: stuck channel(s) after failed attended transfer
-   Reported by: Kevin Harwell
-     * [67664fbf95] Kevin Harwell -- bridge: stuck channel(s) after failed
-       attended transfer
-   ASTERISK-26923: bridging: T.38 request is lost when channels are added to
-   bridge
-   Reported by: Torrey Searle
-     * [e414833f6e] Joshua Colp -- bridge: Add a deferred queue.
-
-    Category: Core/Channels
-
-   ASTERISK-27100: channel: ast_waitfordigit_full fails to clear flag in an
-   error branch.
-   Reported by: Corey Farrell
-     * [73520e9f58] Corey Farrell -- channel: Clear channel flag in error
-       branch.
-   ASTERISK-27074: core_local: local channel data not being properly unref'ed
-   and unlocked
-   Reported by: Kevin Harwell
-     * [1f9913f272] Kevin Harwell -- core_local: local channel data not being
-       properly unref'ed and unlocked
-   ASTERISK-26923: bridging: T.38 request is lost when channels are added to
-   bridge
-   Reported by: Torrey Searle
-     * [e414833f6e] Joshua Colp -- bridge: Add a deferred queue.
-   ASTERISK-27025: channel / meetme: Fix missing parentheses
-   Reported by: Joshua Colp
-     * [dc05183f4b] Joshua Colp -- channel / app_meetme: Fix parentheses.
-
-    Category: Core/General
-
-   ASTERISK-26789: Audit manipulation of channel flags without locks
-   Reported by: Joshua Colp
-     * [1618203964] Joshua Colp -- asterisk: Audit locking of channel when
-       manipulating flags.
-
-    Category: Core/PBX
-
-   ASTERISK-27041: Core/PBX: [patch] Deadlock between dialplan execution and
-   application unregistration
-   Reported by: Frederic LE FOLL
-     * [dc307af7f2] Frederic LE FOLL -- Core/PBX: Deadlock between dialplan
-       execution and application unregistration.
-
-    Category: Core/RTP
-
-   ASTERISK-26978: rtp: Crash in ast_rtp_codecs_payload_code()
-   Reported by: Ross Beer
-     * [eb48e99bd4] George Joseph -- bridge_native_rtp: Keep rtp instance
-       refs on bridge_channel
-   ASTERISK-24858: [patch]Asterisk 13 PJSIP sends RTP packets in wrong byte
-   order on Intel platform when using slin codec
-   Reported by: Frankie Chin
-     * [70e5887906] Sean Bright -- format: Reintroduce smoother flags
-
-    Category: Core/Sorcery
-
-   ASTERISK-27057: Seg Fault in ast_sorcery_object_get_id at sorcery.c
-   Reported by: Ryan Smith
-     * [c2eea791e4] George Joseph -- res_pjsip_pubsub: Fix reference to
-       released endpoint
-
-    Category: Documentation
-
-   ASTERISK-23839: AGI - RECORD FILE - documentation doesn't describe BEEP
-   argument
-   Reported by: Rusty Newton
-     * [3eb7fbba72] Sean Bright -- res_agi: Clarify 'RECORD FILE'
-       documentation
-
-    Category: General
-
-   ASTERISK-27108: Crash using 'data get' CLI command
-   Reported by: Sean Bright
-     * [6258de458b] Sean Bright -- core: Fix segfault when invoking 'data
-       get' CLI command
-   ASTERISK-27060: Comment typo format_g729.c
-   Reported by: Matthew Fredrickson
-     * [0a40073750] Matthew Fredrickson -- formats/format_g729: Fix typo in
-       comment
-
-    Category: PBX/pbx_realtime
-
-   ASTERISK-19291: Background in realtime
-   Reported by: Andrew Nowrot
-     * [283cc59af7] Sean Bright -- pbx_builtin: Properly handle hangup during
-       Background
-
-    Category: Resources/res_agi
-
-   ASTERISK-23839: AGI - RECORD FILE - documentation doesn't describe BEEP
-   argument
-   Reported by: Rusty Newton
-     * [3eb7fbba72] Sean Bright -- res_agi: Clarify 'RECORD FILE'
-       documentation
-   ASTERISK-22432: Async AGI crashes Asterisk when issuing "set variable"
-   command without args
-   Reported by: Antoine Pitrou
-     * [f306e451f6] Sean Bright -- res_agi: Prevent crash when SET VARIABLE
-       called without arguments
-   ASTERISK-25662: Malformed AGI 520 Usage response
-   Reported by: Tony Mountifield
-     * [a007e438c3] Sean Bright -- res_agi: Fix malformed AGI usage response
-
-    Category: Resources/res_ari
-
-   ASTERISK-27026: res_ari: Crash when no ari.conf configuration file exists
-   Reported by: Ronald Raikes
-     * [7901b9853e] George Joseph -- res_ari: Add "module loaded" check to
-       ari stubs
-
-    Category: Resources/res_ari_recordings
-
-   ASTERISK-27021: GET /recordings/stored returns 500 Internal Server Error
-   Reported by: Tim Morgan
-     * [cf6cf59646] Sean Bright -- stasis_recording: Correct ast_asprintf
-       error checking
-
-    Category: Resources/res_format_attr_h264
-
-   ASTERISK-27008: res_format_attr_h264: SDP parse fails if fmtp optional
-   parameters have a space
-   Reported by: John Harris
-     * [700ef6861a] Sean Bright -- res_format_attr_h26x: Trim blanks in fmtp
-       attributes
-
-    Category: Resources/res_parking
-
-   ASTERISK-26399: app_queue: Agent not called when caller is parked
-   Reported by: wushumasters
-     * [6bfcb1acc7] Joshua Colp -- app_queue: Fix members showing as being in
-       call when not.
-
-    Category: Resources/res_pjsip
-
-   ASTERISK-27090: PJSIP: Deadlock using TCP transport
-   Reported by: Richard Mudgett
-     * [0d64cbde57] Richard Mudgett -- pjsip_distributor.c: Fix deadlock with
-       TCP type transports.
-
-    Category: Resources/res_pjsip/Bundling
-
-   ASTERISK-27052: Asterisk build process fails with flag
-   --with-pjproject-bundled with curl download command and slow network
-   Reported by: alex
-     * [0bde568669] George Joseph -- pjproject_bundled: Use the asterisk
-       github mirror for download
-
-    Category: Resources/res_pjsip_refer
-
-   ASTERISK-27053: res_pjsip_refer/session: Calls dropped during transfer
-   Reported by: Kevin Harwell
-     * [6cdf3191d3] Kevin Harwell -- res_pjsip_refer/session: Calls dropped
-       during transfer
-
-    Category: Resources/res_pjsip_session
-
-   ASTERISK-27024: nat/external_media settings ignored in 14.4.1
-   Reported by: Christopher van de Sande
-     * [2dee95cc7a] Florian Floimair -- res_pjsip_session: Correct inverted
-       test in session_outgoing_nat_hook
-   ASTERISK-27053: res_pjsip_refer/session: Calls dropped during transfer
-   Reported by: Kevin Harwell
-     * [6cdf3191d3] Kevin Harwell -- res_pjsip_refer/session: Calls dropped
-       during transfer
-   ASTERISK-26964: res_pjsip_session: Wrong From on reinvite when request and
-   To URI differ
-   Reported by: Yasin CANER
-     * [36628cc9c4] Yasin CANER -- res_pjsip_session : fixed wrong From
-       Header number On Re-invite
-
-    Category: Resources/res_pjsip_transport_websocket
-
-   ASTERISK-27046: res_pjsip_transport_websocket: segfault in
-   get_write_timeout
-   Reported by: JA,rgen H
-     * [e16a669c70] JA,rgen H -- res_pjsip_transport_websocket: Add NULL
-       check in get_write_timeout
-
-    Category: Resources/res_rtp_asterisk
-
-   ASTERISK-27022: res_rtp_asterisk: Incorrect SSRC change for RTCP component
-   Reported by: Michael Walton
-     * [7dafe82751] George Joseph -- res_rtp_asterisk: Fix ssrc change for
-       rtcp srtp
-   ASTERISK-24858: [patch]Asterisk 13 PJSIP sends RTP packets in wrong byte
-   order on Intel platform when using slin codec
-   Reported by: Frankie Chin
-     * [70e5887906] Sean Bright -- format: Reintroduce smoother flags
-   ASTERISK-25101: DTLS configuration can not be specified in the general
-   section - documentation
-   Reported by: Ben Langfeld
-     * [971a401ce9] Sean Bright -- sip.conf.sample: Clarify where DTLS
-       settings are permitted
-   ASTERISK-26979: res_rtp_asterisk: SRTP unprotect failed with
-   authentication failure 10 or 110
-   Reported by: Javier Riveros
-     * [e91efef2bb] Kevin Harwell -- res_rtp_asterisk: rtcp mux using the
-       wrong srtp unprotecting algorithm
-   ASTERISK-26982: chan_sip: rtcp_mux setting may cause ice completion
-   failure/delay if client offers rtcp-mux as negotiable
-   Reported by: Stefan EngstrAP:m
-     * [4479038073] Sean Bright -- chan_sip: Better ICE handling for RTCP-MUX
-
-    Category: Resources/res_srtp
-
-   ASTERISK-25294: srtp's crypto_get_random deprecated
-   Reported by: Tzafrir Cohen
-     * [5e9cd1f20d] Sean Bright -- res_srtp: Add support for libsrtp2
-   ASTERISK-25101: DTLS configuration can not be specified in the general
-   section - documentation
-   Reported by: Ben Langfeld
-     * [971a401ce9] Sean Bright -- sip.conf.sample: Clarify where DTLS
-       settings are permitted
-   ASTERISK-26979: res_rtp_asterisk: SRTP unprotect failed with
-   authentication failure 10 or 110
-   Reported by: Javier Riveros
-     * [e91efef2bb] Kevin Harwell -- res_rtp_asterisk: rtcp mux using the
-       wrong srtp unprotecting algorithm
-
-    Category: Resources/res_stasis_snoop
-
-   ASTERISK-26973: bridge: Crash when freeing frame and snooping
-   Reported by: Michel R. Vaillancourt
-     * [adfb28882b] Kevin Harwell -- channel: ast_write frame wrongly freed
-       after call to audiohooks
-
-    Category: pjproject/pjsip
-
-   ASTERISK-26333: Problems with Blind Transfer, PJSIP (Aastra 6869i)
-   Reported by: Matthias Binder
-     * [6af2dd34af] Alexei Gradinari -- res_pjsip: New endpoint option
-       "refer_blind_progress"
-
-  Improvement
-
-    Category: Core/BuildSystem
-
-   ASTERISK-27043: Core/BuildSystem: Add defines to fix build with LibreSSL
-   Reported by: Guido Falsi
-     * [6a64f65fe6] Guido Falsi -- BuildSystem: Add patches to allow building
-       with recent LibreSSL
-
-    Category: Core/Channels
-
-   ASTERISK-26419: audiohooks: Remove redundant codec translations when using
-   audiohooks
-   Reported by: Michael Walton
-     * [adfb28882b] Kevin Harwell -- channel: ast_write frame wrongly freed
-       after call to audiohooks
-
-    Category: Core/General
-
-   ASTERISK-26419: audiohooks: Remove redundant codec translations when using
-   audiohooks
-   Reported by: Michael Walton
-     * [adfb28882b] Kevin Harwell -- channel: ast_write frame wrongly freed
-       after call to audiohooks
-
-    Category: Core/Portability
-
-   ASTERISK-27042: Unpatched asterisk sources fail to build on FreeBSD due to
-   missing crypt.h file
-   Reported by: Guido Falsi
-     * [44cee2f4a1] Guido Falsi -- BuildSystem: Fix build on FreeBSD due to
-       missing crypt.h
-
-    Category: Resources/res_agi
-
-   ASTERISK-26124: res_agi: Set audio format for EAGI audio stream
-   Reported by: John Fawcett
-     * [90237dca11] Sean Bright -- res_agi: Allow configuration of audio
-       format of EAGI pipe
-
-    Category: Resources/res_pjsip_mwi
-
-   ASTERISK-26230: [patch] res_pjsip_mwi: unsolicited mwi could block PJSIP
-   taskprocessor on startup
-   Reported by: Alexei Gradinari
-     * [0f6a9617eb] Alexei Gradinari -- res_pjsip_mwi: update unsolicited MWI
-       subscriptions on updating contact
-     * [59c9bbe696] Alexei Gradinari -- res_pjsip_mwi: don't create mwi
-       subscriptions if initial unsolicited disabled
-
-    Category: Resources/res_rtp_asterisk
-
-   ASTERISK-26976: libsrtp-2.x.x support
-   Reported by: Alex
-     * [5e9cd1f20d] Sean Bright -- res_srtp: Add support for libsrtp2
-
-     ----------------------------------------------------------------------
-
-                                  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: Bridges/bridge_simple
-
-   ASTERISK-26469: Infinite loop after a dual Redirect
-   Reported by: Etienne Allovon
-     * [b07b216235] Joshua Colp -- manager: Clear the flag on the other
-       channel.
-
-    Category: Channels/chan_pjsip
-
-   ASTERISK-27095: chan_pjsip: When connected_line_method is set to invite,
-   we're not trying UPDATE
-   Reported by: George Joseph
-     * [6bd7c0f37c] George Joseph -- chan_pjsip: Fix ability to send UPDATE
-       on COLP
-
-    Category: Core/Bridging
-
-   ASTERISK-27016: Crash occurs when a channel in a 'mixing,dtmf_events'
-   bridge is muted multiple times.
-   Reported by: Chris Howard
-     * [4910a3bf40] Joshua Colp -- channel: Fix reference counting in
-       ast_channel_suppress.
-
-    Category: General
-
-   ASTERISK-27088: res_rtp_asterisk: Better handle ICE renegotiation and
-   unidirectional negotiation
-   Reported by: Joshua Colp
-     * [0426b1d88a] Joshua Colp -- res_rtp_asterisk: Fix issues with ICE
-       renegotiation.
-
-    Category: Resources/res_corosync
-
-   ASTERISK-25370: res_corosync segfaults at startup with corosync version >
-   2.x
-   Reported by: mdu113
-     * [005a4afa6b] Jan Friesse -- res_corosync: Change thread stack size
-
-    Category: Resources/res_pjsip_dialog_info_body_generator
-
-   ASTERISK-26919: res_pjsip_dialog_info_body_generator: Ringing&&InUse
-   behavior difference between chan_sip and res_pjsip
-   Reported by: Zach R
-     * [a6e4899612] Alexei Gradinari -- res_pjsip: New endpoint option
-       "notify_early_inuse_ringing"
-
-    Category: Resources/res_pjsip_mwi
-
-   ASTERISK-27051: res_pjsip_mwi: unsolicited MWI has to be unsubscribed on
-   deleting the endpoint's last contact
-   Reported by: Alexei Gradinari
-     * [8e749c8f51] Alexei Gradinari -- res_pjsip_mwi: unsubscribe
-       unsolicited MWI on deleting endpoint last contact
-
-    Category: Resources/res_stasis
-
-   ASTERISK-27059: res_stasis: Stolen channel references are leaking
-   Reported by: George Joseph
-     * [edfdb4dff5] George Joseph -- res_stasis: Plug reference leak on
-       stolen channels
-
-    Category: Third-Party/pjproject
-
-   ASTERISK-27097: pjproject_bundled: We don't pass options needed for
-   cross-compile to pjproject configure
-   Reported by: George Joseph
-     * [bbe68f139d] George Joseph -- pjproject_bundled: Allow passing
-       configure options to bundled
-
-  Improvement
-
-    Category: Applications/app_voicemail/IMAP
-
-   ASTERISK-27068: app_voicemail: Add global option "imap_poll_logout" to
-   specify post-polling disconnect
-   Reported by: Alexei Gradinari
-     * [8f356192d1] Alexei Gradinari -- app_voicemail: IMAP connection
-       control
-
-    Category: Channels/chan_pjsip
-
-   ASTERISK-27066: res_pjsip: Add DTMF INFO Failback mode
-   Reported by: Torrey Searle
-     * [9fbc34d2bd] Torrey Searle -- res_pjsip: Add DTMF INFO Failback mode
-
-    Category: Resources/res_pjsip
-
-   ASTERISK-27066: res_pjsip: Add DTMF INFO Failback mode
-   Reported by: Torrey Searle
-     * [9fbc34d2bd] Torrey Searle -- res_pjsip: Add DTMF INFO Failback mode
-
-     ----------------------------------------------------------------------
-
-                      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                                |
-   |------------+------------------+----------------------------------------|
-   | 0c00ee754b | George Joseph    | Update for 13.17.0-rc1                 |
-   |------------+------------------+----------------------------------------|
-   | 379fe65831 | George Joseph    | Fix alembic branches                   |
-   |------------+------------------+----------------------------------------|
-   | 905d18e8bf | Richard Mudgett  | pjsip_distributor.c: Fix               |
-   |            |                  | unidentified_requests hash functions.  |
-   |------------+------------------+----------------------------------------|
-   | 1f59d08924 | Torrey Searle    | res/res_pjsip_t38: fix incorrect       |
-   |            |                  | increment of media_count               |
-   |------------+------------------+----------------------------------------|
-   | 764d04fa87 | Richard Mudgett  | res_pjsip_mwi.c: Eliminate RAII_VAR in |
-   |            |                  | contact delete observer                |
-   |------------+------------------+----------------------------------------|
-   | cecf6540dc | Rodrigo RamArez  | cdr: fix mistake spelling of a word    |
-   |            | Norambuena       | for Unanswered.                        |
-   |------------+------------------+----------------------------------------|
-   | b9a4ab8c8c | Richard Mudgett  | chan_pjsip: Fix PJSIP_MEDIA_OFFER      |
-   |            |                  | dialplan function read.                |
-   |------------+------------------+----------------------------------------|
-   | f1a209d5ac | Richard Mudgett  | app_voicemail.c: Fix compile error     |
-   |            |                  | when IMAP enabled.                     |
-   |------------+------------------+----------------------------------------|
-   | 68de35a6a0 | David M. Lee     | CFLAGS for BIND8 support               |
-   |------------+------------------+----------------------------------------|
-   | da3312457e | Sean Bright      | codecs.conf.sample: Fix max_bandwidth  |
-   |            |                  | speling error                          |
-   |------------+------------------+----------------------------------------|
-   | 590ffcaf0b | Sean Bright      | eventfd: Disable during cross          |
-   |            |                  | compilation                            |
-   |------------+------------------+----------------------------------------|
-   | 5520b6c201 | Alexei Gradinari | CHANGES: correct version for a new     |
-   |            |                  | option 'refer_blind_progress'          |
-   |------------+------------------+----------------------------------------|
-   | c093bf8072 | Sean Bright      | res_rtp_multicast: Use consistent      |
-   |            |                  | timestamps when possible               |
-   |------------+------------------+----------------------------------------|
-   | c10341646d | George Joseph    | test_json: Fix test names with         |
-   |            |                  | reserved words                         |
-   |------------+------------------+----------------------------------------|
-   | 65898c3af8 | George Joseph    | unittests: Add a unit test that causes |
-   |            |                  | a SEGV and...                          |
-   +------------------------------------------------------------------------+
-
-     ----------------------------------------------------------------------
-
-                                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.16.0-summary.html                                                                   |  405 ---
- asterisk-13.16.0-summary.txt                                                                    |  952 ---------
- b/.version                                                                                      |    2
- b/CHANGES                                                                                       |   54
- b/ChangeLog                                                                                     | 1045 +++++++++-
- b/Makefile                                                                                      |    3
- b/addons/Makefile                                                                               |   10
- b/apps/app_chanspy.c                                                                            |   16
- b/apps/app_confbridge.c                                                                         |   79
- b/apps/app_dial.c                                                                               |    6
- b/apps/app_disa.c                                                                               |   10
- b/apps/app_dumpchan.c                                                                           |    4
- b/apps/app_externalivr.c                                                                        |    6
- b/apps/app_meetme.c                                                                             |    2
- b/apps/app_queue.c                                                                              |  109 -
- b/apps/app_voicemail.c                                                                          |   80
- b/asterisk-13.17.0-rc1-summary.html                                                             |  311 ++
- b/asterisk-13.17.0-rc1-summary.txt                                                              |  832 +++++++
- b/autoconf/ast_ext_lib.m4                                                                       |   36
- b/bridges/bridge_native_rtp.c                                                                   |  677 +++++-
- b/bridges/bridge_simple.c                                                                       |   32
- b/channels/chan_pjsip.c                                                                         |   68
- b/channels/chan_sip.c                                                                           |    8
- b/channels/pjsip/dialplan_functions.c                                                           |   37
- b/configs/samples/cdr.conf.sample                                                               |    2
- b/configs/samples/codecs.conf.sample                                                            |    6
- b/configs/samples/pjsip.conf.sample                                                             |   20
- b/configs/samples/sip.conf.sample                                                               |    3
- b/configs/samples/voicemail.conf.sample                                                         |    3
- b/configure                                                                                     |  434 +++-
- b/configure.ac                                                                                  |  100
- b/contrib/ast-db-manage/config/versions/164abbd708c_add_auto_info_to_endpoint_dtmf_mode.py      |   58
- b/contrib/ast-db-manage/config/versions/86bb1efa278d_add_ps_endpoints_refer_blind_progress.py   |   30
- b/contrib/ast-db-manage/config/versions/d7983954dd96_add_ps_endpoints_notify_early_inuse_.py    |   30
- b/contrib/realtime/mssql/mssql_config.sql                                                       |   46
- b/contrib/realtime/mysql/mysql_config.sql                                                       |   18
- b/contrib/realtime/oracle/oracle_config.sql                                                     |   46
- b/contrib/realtime/postgresql/postgresql_config.sql                                             |   22
- b/formats/format_g729.c                                                                         |    2
- b/include/asterisk/ari.h                                                                        |   10
- b/include/asterisk/autoconfig.h.in                                                              |    3
- b/include/asterisk/bridge_channel.h                                                             |    2
- b/include/asterisk/bridge_channel_internal.h                                                    |   11
- b/include/asterisk/bridge_technology.h                                                          |    3
- b/include/asterisk/channel.h                                                                    |   25
- b/include/asterisk/codec.h                                                                      |    3
- b/include/asterisk/core_local.h                                                                 |   37
- b/include/asterisk/format.h                                                                     |   11
- b/include/asterisk/res_pjsip.h                                                                  |   74
- b/include/asterisk/res_pjsip_presence_xml.h                                                     |    3
- b/include/asterisk/res_pjsip_session.h                                                          |   11
- b/include/asterisk/rtp_engine.h                                                                 |    9
- b/include/asterisk/smoother.h                                                                   |    1
- b/include/asterisk/test.h                                                                       |    8
- b/main/autoservice.c                                                                            |    2
- b/main/bridge.c                                                                                 |   10
- b/main/bridge_after.c                                                                           |    2
- b/main/bridge_channel.c                                                                         |   38
- b/main/channel.c                                                                                |   90
- b/main/codec_builtin.c                                                                          |   19
- b/main/core_local.c                                                                             |   54
- b/main/crypt.c                                                                                  |    2
- b/main/data.c                                                                                   |    4
- b/main/file.c                                                                                   |   20
- b/main/format.c                                                                                 |    8
- b/main/libasteriskssl.c                                                                         |    4
- b/main/manager.c                                                                                |    8
- b/main/pbx.c                                                                                    |    4
- b/main/pbx_app.c                                                                                |    7
- b/main/pbx_builtins.c                                                                           |    8
- b/main/tcptls.c                                                                                 |    4
- b/main/test.c                                                                                   |    4
- b/makeopts.in                                                                                   |    2
- b/res/res_agi.c                                                                                 |   73
- b/res/res_ari_applications.c                                                                    |    4
- b/res/res_ari_asterisk.c                                                                        |    4
- b/res/res_ari_bridges.c                                                                         |    4
- b/res/res_ari_channels.c                                                                        |    4
- b/res/res_ari_device_states.c                                                                   |    4
- b/res/res_ari_endpoints.c                                                                       |    4
- b/res/res_ari_events.c                                                                          |   33
- b/res/res_ari_mailboxes.c                                                                       |    4
- b/res/res_ari_playbacks.c                                                                       |    4
- b/res/res_ari_recordings.c                                                                      |    4
- b/res/res_ari_sounds.c                                                                          |    4
- b/res/res_corosync.c                                                                            |   29
- b/res/res_format_attr_h263.c                                                                    |    2
- b/res/res_format_attr_h264.c                                                                    |    2
- b/res/res_musiconhold.c                                                                         |    4
- b/res/res_pjsip.c                                                                               |   31
- b/res/res_pjsip/location.c                                                                      |   53
- b/res/res_pjsip/pjsip_configuration.c                                                           |    9
- b/res/res_pjsip/pjsip_distributor.c                                                             |  242 +-
- b/res/res_pjsip/presence_xml.c                                                                  |    9
- b/res/res_pjsip_dialog_info_body_generator.c                                                    |   10
- b/res/res_pjsip_mwi.c                                                                           |   87
- b/res/res_pjsip_pidf_body_generator.c                                                           |    2
- b/res/res_pjsip_pidf_eyebeam_body_supplement.c                                                  |    2
- b/res/res_pjsip_pubsub.c                                                                        |    8
- b/res/res_pjsip_refer.c                                                                         |   28
- b/res/res_pjsip_sdp_rtp.c                                                                       |   38
- b/res/res_pjsip_session.c                                                                       |   37
- b/res/res_pjsip_session.exports.in                                                              |    1
- b/res/res_pjsip_t38.c                                                                           |    2
- b/res/res_pjsip_transport_websocket.c                                                           |    4
- b/res/res_pjsip_xpidf_body_generator.c                                                          |    2
- b/res/res_rtp_asterisk.c                                                                        |   41
- b/res/res_rtp_multicast.c                                                                       |  139 +
- b/res/res_srtp.c                                                                                |   15
- b/res/res_stasis.c                                                                              |   20
- b/res/srtp/srtp_compat.h                                                                        |   29
- b/res/stasis_recording/stored.c                                                                 |    4
- b/rest-api-templates/res_ari_resource.c.mustache                                                |   35
- b/tests/test_bridging.c                                                                         |  292 ++
- b/tests/test_json.c                                                                             |   16
- b/tests/test_pbx.c                                                                              |   22
- b/third-party/configure.m4                                                                      |    5
- b/third-party/pjproject/Makefile                                                                |    2
- b/third-party/pjproject/Makefile.rules                                                          |    7
- b/third-party/pjproject/configure.m4                                                            |   24
- b/third-party/pjproject/patches/0070-Set-PJSIP_INV_SUPPORT_UPDATE-correctly-in-pjsip_inv_.patch |   16
- 121 files changed, 5477 insertions(+), 2043 deletions(-)
diff --git a/asterisk-13.18.5-summary.html b/asterisk-13.18.5-summary.html
new file mode 100644
index 0000000..46c1013
--- /dev/null
+++ b/asterisk-13.18.5-summary.html
@@ -0,0 +1,11 @@
+<!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.18.5</title><h1 align="center"><a name="top">Release Summary</a></h1><h3 align="center">asterisk-13.18.5</h3><h3 align="center">Date: 2017-12-22</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="#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%">1 Kevin Harwell <kharwell at digium.com><br/></td><td width="33%"><td width="33%">1 Ross Beer <ross.beer at voicehost.co.uk><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: Channels/chan_pjsip</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-27480">ASTERISK-27480</a>: Security: Authenticated SUBSCRIBE without Contact crashes asterisk<br/>Reported by: Ross Beer<ul>
+<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=5b5cb3dfe8725afba13c70b32ae8d028eedf90bc">[5b5cb3dfe8]</a> Kevin Harwell -- AST-2017-014: res_pjsip - Missing contact header can cause crash</li>
+</ul><br><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>0 files changed</pre><br></html>
\ No newline at end of file
diff --git a/asterisk-13.18.5-summary.txt b/asterisk-13.18.5-summary.txt
new file mode 100644
index 0000000..e2c39d8
--- /dev/null
+++ b/asterisk-13.18.5-summary.txt
@@ -0,0 +1,82 @@
+                                Release Summary
+
+                                asterisk-13.18.5
+
+                                Date: 2017-12-22
+
+                           <asteriskteam at digium.com>
+
+     ----------------------------------------------------------------------
+
+                               Table of Contents
+
+    1. Summary
+    2. Contributors
+    3. Closed Issues
+    4. 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.18.4.
+
+     ----------------------------------------------------------------------
+
+                                  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                
+   1 Kevin Harwell                                   1 Ross Beer              
+
+     ----------------------------------------------------------------------
+
+                                 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: Channels/chan_pjsip
+
+   ASTERISK-27480: Security: Authenticated SUBSCRIBE without Contact crashes
+   asterisk
+   Reported by: Ross Beer
+     * [5b5cb3dfe8] Kevin Harwell -- AST-2017-014: res_pjsip - Missing
+       contact header can cause crash
+
+     ----------------------------------------------------------------------
+
+                                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.
+
+ 0 files changed
diff --git a/bridges/bridge_native_rtp.c b/bridges/bridge_native_rtp.c
index 4e55a3e..122c132 100644
--- a/bridges/bridge_native_rtp.c
+++ b/bridges/bridge_native_rtp.c
@@ -184,10 +184,10 @@ static int rtp_glue_data_get(struct ast_channel *c0, struct rtp_glue_data *glue0
 		}
 	}
 	if (glue0->video.result == glue1->video.result && glue1->video.result == AST_RTP_GLUE_RESULT_REMOTE) {
-		if (glue0->cb->allow_vrtp_remote && !glue0->cb->allow_vrtp_remote(c0, glue1->audio.instance)) {
-			/* if the allow_vrtp_remote indicates that remote isn't allowed, revert to local bridge */
+		if (glue0->cb->allow_vrtp_remote && !glue0->cb->allow_vrtp_remote(c0, glue1->video.instance)) {
+			/* If the allow_vrtp_remote indicates that remote isn't allowed, revert to local bridge */
 			glue0->video.result = glue1->video.result = AST_RTP_GLUE_RESULT_LOCAL;
-		} else if (glue1->cb->allow_vrtp_remote && !glue1->cb->allow_vrtp_remote(c1, glue0->audio.instance)) {
+		} else if (glue1->cb->allow_vrtp_remote && !glue1->cb->allow_vrtp_remote(c1, glue0->video.instance)) {
 			glue0->video.result = glue1->video.result = AST_RTP_GLUE_RESULT_LOCAL;
 		}
 	}
@@ -541,10 +541,12 @@ static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel
 static struct ast_frame *native_rtp_framehook(struct ast_channel *chan,
 	struct ast_frame *f, enum ast_framehook_event event, void *data)
 {
-	RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
+	struct ast_bridge *bridge;
 	struct native_rtp_framehook_data *native_data = data;
 
-	if (!f || (event != AST_FRAMEHOOK_EVENT_WRITE)) {
+	if (!f
+		|| f->frametype != AST_FRAME_CONTROL
+		|| event != AST_FRAMEHOOK_EVENT_WRITE) {
 		return f;
 	}
 
@@ -563,14 +565,20 @@ static struct ast_frame *native_rtp_framehook(struct ast_channel *chan,
 		ast_channel_unlock(chan);
 		ast_bridge_lock(bridge);
 		if (!native_data->detached) {
-			if (f->subclass.integer == AST_CONTROL_HOLD) {
+			switch (f->subclass.integer) {
+			case AST_CONTROL_HOLD:
 				native_rtp_bridge_stop(bridge, chan);
-			} else if ((f->subclass.integer == AST_CONTROL_UNHOLD) ||
-				(f->subclass.integer == AST_CONTROL_UPDATE_RTP_PEER)) {
+				break;
+			case AST_CONTROL_UNHOLD:
+			case AST_CONTROL_UPDATE_RTP_PEER:
 				native_rtp_bridge_start(bridge, chan);
+				break;
+			default:
+				break;
 			}
 		}
 		ast_bridge_unlock(bridge);
+		ao2_ref(bridge, -1);
 		ast_channel_lock(chan);
 	}
 
@@ -592,7 +600,8 @@ static int native_rtp_framehook_consume(void *data, enum ast_frame_type type)
  */
 static int native_rtp_bridge_capable(struct ast_channel *chan)
 {
-	return !ast_channel_has_hook_requiring_audio(chan);
+	return !ast_channel_has_hook_requiring_audio(chan)
+			&& ast_channel_state(chan) == AST_STATE_UP;
 }
 
 /*!
diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c
index 486330a..f82f350 100644
--- a/bridges/bridge_softmix.c
+++ b/bridges/bridge_softmix.c
@@ -727,14 +727,10 @@ static int softmix_bridge_write(struct ast_bridge *bridge, struct ast_bridge_cha
 		res = ast_bridge_queue_everyone_else(bridge, bridge_channel, frame);
 		break;
 	case AST_FRAME_VOICE:
-		if (bridge_channel) {
-			softmix_bridge_write_voice(bridge, bridge_channel, frame);
-		}
+		softmix_bridge_write_voice(bridge, bridge_channel, frame);
 		break;
 	case AST_FRAME_VIDEO:
-		if (bridge_channel) {
-			softmix_bridge_write_video(bridge, bridge_channel, frame);
-		}
+		softmix_bridge_write_video(bridge, bridge_channel, frame);
 		break;
 	case AST_FRAME_CONTROL:
 		res = softmix_bridge_write_control(bridge, bridge_channel, frame);
diff --git a/build_tools/download_externals b/build_tools/download_externals
index b0a414e..efeb6c5 100755
--- a/build_tools/download_externals
+++ b/build_tools/download_externals
@@ -5,7 +5,24 @@ if [[ ( ${BASH_VERSINFO[0]} == 4 && ${BASH_VERSINFO[1]} > 1 ) || ${BASH_VERSINFO
 fi
 set -e
 
+
 ASTTOPDIR=${ASTTOPDIR:-.}
+export make=`sed -n -r -e "s/^MAKE\s*=\s*//gp" ${ASTTOPDIR}/makeopts`
+
+getvar() {
+	$make --quiet --no-print-directory -f- <<EOF
+include ${ASTTOPDIR}/makeopts
+all:
+	@echo "\$($1)"
+EOF
+}
+
+XMLSTARLET=`getvar XMLSTARLET`
+ASTMODDIR=`getvar ASTMODDIR`
+cache_dir=`getvar EXTERNALS_CACHE_DIR`
+DOWNLOAD_TO_STDOUT=`getvar DOWNLOAD_TO_STDOUT`
+HOST_CPU=`getvar HOST_CPU`
+INSTALL=`getvar INSTALL`
 
 module_name=${1%%-*}
 variant=${1##*-}
@@ -26,20 +43,16 @@ if [[ -z "${tmpdir}" ]] ; then
 fi
 trap "rm -rf ${tmpdir}" EXIT
 
-sed -r -e "s/^([^ =]+)\s*=\s*(.*)$/\1=\"\2\"/g" ${ASTTOPDIR}/makeopts >${tmpdir}/makeopts
-source ${tmpdir}/makeopts
 if [[ -z "${ASTMODDIR}" ]] ; then
 	echo "${module_name}: Unable to parse ${ASTTOPDIR}/makeopts."
 	exit 1
 fi
 
-XMLSTARLET=${XMLSTARLET:-xmlstarlet}
 if [[ "${XMLSTARLET}" = ":" ]] ; then
 	echo "${module_name}: The externals downloader requires xmlstarlet to be installed."
 	exit 1
 fi
 
-cache_dir="${EXTERNALS_CACHE_DIR}"
 if [[ -z ${cache_dir} ]] ; then
 	cache_dir=${tmpdir}
 fi
@@ -187,7 +200,7 @@ if [[ -f ${cache_dir}/${full_name}.manifest.xml ]] ; then
 fi
 
 if [[ ${need_download} = 1 ]] ; then
-	echo "${full_name}: Downloading ${remote_url}/${tarball}"
+	echo "${full_name}: Downloading ${remote_url}/${tarball} to ${cache_dir}/${tarball}"
 	${DOWNLOAD_TO_STDOUT} ${remote_url}/${tarball} > ${cache_dir}/${tarball} || {
 		echo "${full_name}: Unable to fetch ${remote_url}/${tarball}"
 		exit 1
diff --git a/build_tools/list_valid_installed_externals b/build_tools/list_valid_installed_externals
index 12aff3f..ed36274 100755
--- a/build_tools/list_valid_installed_externals
+++ b/build_tools/list_valid_installed_externals
@@ -6,6 +6,23 @@ fi
 set -e
 
 ASTTOPDIR=${ASTTOPDIR:-.}
+export make=`sed -n -r -e "s/^MAKE\s*=\s*//gp" ${ASTTOPDIR}/makeopts`
+
+getvar() {
+	$make --quiet --no-print-directory -f- <<EOF
+include ${ASTTOPDIR}/makeopts
+all:
+	@echo "\$($1)"
+EOF
+}
+
+
+XMLSTARLET=`getvar XMLSTARLET`
+ASTMODDIR=`getvar ASTMODDIR`
+cache_dir=`getvar EXTERNALS_CACHE_DIR`
+DOWNLOAD_TO_STDOUT=`getvar DOWNLOAD_TO_STDOUT`
+HOST_CPU=`getvar HOST_CPU`
+INSTALL=`getvar INSTALL`
 
 tmpdir=$(mktemp -d)
 if [[ -z "${tmpdir}" ]] ; then
@@ -14,14 +31,11 @@ if [[ -z "${tmpdir}" ]] ; then
 fi
 trap "rm -rf ${tmpdir}" EXIT
 
-sed -r -e "s/^([^ =]+)\s*=\s*(.*)$/\1=\"\2\"/g" ${ASTTOPDIR}/makeopts >${tmpdir}/makeopts
-source ${tmpdir}/makeopts
 if [[ -z "${ASTMODDIR}" ]] ; then
 	echo "${module_name}: Unable to parse ${ASTTOPDIR}/makeopts."
 	exit 1
 fi
 
-XMLSTARLET=${XMLSTARLET:-xmlstarlet}
 if [[ "${XMLSTARLET}" = ":" ]] ; then
 	echo "${module_name}: The externals downloader requires xmlstarlet to be installed."
 	exit 1
diff --git a/channels/Makefile b/channels/Makefile
index cacfde1..fdc1390 100644
--- a/channels/Makefile
+++ b/channels/Makefile
@@ -49,6 +49,8 @@ CHAN_DAHDI_OBJS= \
 chan_dahdi.so: $(CHAN_DAHDI_OBJS)
 $(CHAN_DAHDI_OBJS): _ASTCFLAGS+=$(call MOD_ASTCFLAGS,chan_dahdi)
 
+chan_mgcp.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION)
+
 chan_misdn.o: _ASTCFLAGS+=-Imisdn
 
 misdn_config.o: _ASTCFLAGS+=-Imisdn
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index 62a86f0..18352d3 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -64,6 +64,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #endif
 #include <sys/stat.h>
 #include <math.h>
+#include <sys/sysmacros.h>
 
 #include "sig_analog.h"
 /* Analog signaling is currently still present in chan_dahdi for use with
@@ -14320,7 +14321,7 @@ static char *handle_pri_service_generic(struct ast_cli_entry *e, int cmd, struct
 	int trunkgroup;
 	int x, y, fd = a->fd;
 	int interfaceid = 0;
-	char db_chan_name[20], db_answer[5];
+	char db_chan_name[20], db_answer[15];
 	struct dahdi_pvt *tmp;
 	struct dahdi_pri *pri;
 
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index 67552ce..f422aae 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -8571,7 +8571,7 @@ static int try_transfer(struct chan_iax2_pvt *pvt, struct iax_ies *ies)
 {
 	int newcall = 0;
 	struct iax_ie_data ied;
-	struct ast_sockaddr new;
+	struct ast_sockaddr new = { {0,} };
 
 	memset(&ied, 0, sizeof(ied));
 	if (!ast_sockaddr_isnull(&ies->apparent_addr)) {
@@ -13084,7 +13084,7 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
 		ast_free_acl_list(oldacl);
 	}
 
-	if (!ast_strlen_zero(peer->mailbox)) {
+	if (!ast_strlen_zero(peer->mailbox) && !peer->mwi_event_sub) {
 		struct stasis_topic *mailbox_specific_topic;
 
 		mailbox_specific_topic = ast_mwi_topic(peer->mailbox);
diff --git a/channels/chan_motif.c b/channels/chan_motif.c
index 4bb84c9..3141037 100644
--- a/channels/chan_motif.c
+++ b/channels/chan_motif.c
@@ -1907,7 +1907,7 @@ static struct ast_channel *jingle_request(const char *type, struct ast_format_ca
 {
 	RAII_VAR(struct jingle_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
 	RAII_VAR(struct jingle_endpoint *, endpoint, NULL, ao2_cleanup);
-	char *dialed, target[200] = "";
+	char *dialed, target[1024] = "";
 	struct ast_xmpp_buddy *buddy;
 	struct jingle_session *session;
 	struct ast_channel *chan;
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index e2fd13c..75dcd76 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -727,14 +727,11 @@ static struct ast_frame *chan_pjsip_read(struct ast_channel *ast)
 
 	session = channel->session;
 
-	if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
-		ast_debug(1, "Oooh, got a frame with format of %s on channel '%s' when it has not been negotiated\n",
-			ast_format_get_name(f->subclass.format), ast_channel_name(ast));
-
-		ast_frfree(f);
-		return &ast_null_frame;
-	}
-
+	/*
+	 * Asymmetric RTP only has one native format set at a time.
+	 * Therefore we need to update the native format to the current
+	 * raw read format BEFORE the native format check
+	 */
 	if (!session->endpoint->asymmetric_rtp_codec &&
 		ast_format_cmp(ast_channel_rawwriteformat(ast), f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
 		struct ast_format_cap *caps;
@@ -761,6 +758,14 @@ static struct ast_frame *chan_pjsip_read(struct ast_channel *ast)
 		}
 	}
 
+	if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
+		ast_debug(1, "Oooh, got a frame with format of %s on channel '%s' when it has not been negotiated\n",
+			ast_format_get_name(f->subclass.format), ast_channel_name(ast));
+
+		ast_frfree(f);
+		return &ast_null_frame;
+	}
+
 	if (session->dsp) {
 		int dsp_features;
 
@@ -840,6 +845,8 @@ static int chan_pjsip_write(struct ast_channel *ast, struct ast_frame *frame)
 		break;
 	case AST_FRAME_MODEM:
 		break;
+	case AST_FRAME_CNG:
+		break;
 	default:
 		ast_log(LOG_WARNING, "Can't send %u type frames with PJSIP\n", frame->frametype);
 		break;
@@ -1376,7 +1383,8 @@ static int chan_pjsip_indicate(struct ast_channel *ast, int condition, const voi
 			/* FIXME: Only use this for VP8. Additional work would have to be done to
 			 * fully support other video codecs */
 
-			if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), ast_format_vp8) != AST_FORMAT_CMP_NOT_EQUAL) {
+			if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), ast_format_vp8) != AST_FORMAT_CMP_NOT_EQUAL ||
+				ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), ast_format_vp9) != AST_FORMAT_CMP_NOT_EQUAL) {
 				/* FIXME Fake RTP write, this will be sent as an RTCP packet. Ideally the
 				 * RTP engine would provide a way to externally write/schedule RTCP
 				 * packets */
@@ -1700,7 +1708,7 @@ static int chan_pjsip_digit_begin(struct ast_channel *chan, char digit)
 	struct ast_sip_session_media *media = pvt->media[SIP_MEDIA_AUDIO];
 	int res = 0;
 
-	switch (channel->session->endpoint->dtmf) {
+	switch (channel->session->dtmf) {
 	case AST_SIP_DTMF_RFC_4733:
 		if (!media || !media->rtp) {
 			return -1;
@@ -1820,7 +1828,7 @@ static int chan_pjsip_digit_end(struct ast_channel *ast, char digit, unsigned in
 	struct ast_sip_session_media *media = pvt->media[SIP_MEDIA_AUDIO];
 	int res = 0;
 
-	switch (channel->session->endpoint->dtmf) {
+	switch (channel->session->dtmf) {
 	case AST_SIP_DTMF_AUTO_INFO:
 	{
 		if (!media || !media->rtp) {
@@ -2632,6 +2640,12 @@ static struct ast_custom_function media_offer_function = {
 	.write = pjsip_acf_media_offer_write
 };
 
+static struct ast_custom_function dtmf_mode_function = {
+	.name = "PJSIP_DTMF_MODE",
+	.read = pjsip_acf_dtmf_mode_read,
+	.write = pjsip_acf_dtmf_mode_write
+};
+
 static struct ast_custom_function session_refresh_function = {
 	.name = "PJSIP_SEND_SESSION_REFRESH",
 	.write = pjsip_acf_session_refresh_write,
@@ -2676,6 +2690,11 @@ static int load_module(void)
 		goto end;
 	}
 
+	if (ast_custom_function_register(&dtmf_mode_function)) {
+		ast_log(LOG_WARNING, "Unable to register PJSIP_DTMF_MODE dialplan function\n");
+		goto end;
+	}
+
 	if (ast_custom_function_register(&session_refresh_function)) {
 		ast_log(LOG_WARNING, "Unable to register PJSIP_SEND_SESSION_REFRESH dialplan function\n");
 		goto end;
@@ -2735,6 +2754,7 @@ static int load_module(void)
 end:
 	ao2_cleanup(pjsip_uids_onhold);
 	pjsip_uids_onhold = NULL;
+	ast_custom_function_unregister(&dtmf_mode_function);
 	ast_custom_function_unregister(&media_offer_function);
 	ast_custom_function_unregister(&chan_pjsip_dial_contacts_function);
 	ast_custom_function_unregister(&session_refresh_function);
@@ -2757,6 +2777,7 @@ static int unload_module(void)
 	ast_sip_session_unregister_supplement(&chan_pjsip_ack_supplement);
 	ast_sip_session_unregister_supplement(&call_pickup_supplement);
 
+	ast_custom_function_unregister(&dtmf_mode_function);
 	ast_custom_function_unregister(&media_offer_function);
 	ast_custom_function_unregister(&chan_pjsip_dial_contacts_function);
 	ast_custom_function_unregister(&session_refresh_function);
diff --git a/channels/chan_rtp.c b/channels/chan_rtp.c
index d671706..2ab8414 100644
--- a/channels/chan_rtp.c
+++ b/channels/chan_rtp.c
@@ -119,6 +119,22 @@ static int rtp_hangup(struct ast_channel *ast)
 	return 0;
 }
 
+static struct ast_format *derive_format_from_cap(struct ast_format_cap *cap)
+{
+	struct ast_format *fmt = ast_format_cap_get_format(cap, 0);
+
+	if (ast_format_cap_count(cap) == 1 && fmt == ast_format_slin) {
+		/*
+		 * Because we have no SDP, we must use one of the static RTP payload
+		 * assignments. Signed linear @ 8kHz does not map, so if that is our
+		 * only capability, we force μ-law instead.
+		 */
+		fmt = ast_format_ulaw;
+	}
+
+	return fmt;
+}
+
 /*! \brief Function called when we should prepare to call the multicast destination */
 static struct ast_channel *multicast_rtp_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause)
 {
@@ -173,7 +189,7 @@ static struct ast_channel *multicast_rtp_request(const char *type, struct ast_fo
 
 	fmt = ast_multicast_rtp_options_get_format(mcast_options);
 	if (!fmt) {
-		fmt = ast_format_cap_get_format(cap, 0);
+		fmt = derive_format_from_cap(cap);
 	}
 	if (!fmt) {
 		ast_log(LOG_ERROR, "No codec available for sending RTP to '%s'\n",
@@ -300,7 +316,7 @@ static struct ast_channel *unicast_rtp_request(const char *type, struct ast_form
 			goto failure;
 		}
 	} else {
-		fmt = ast_format_cap_get_format(cap, 0);
+		fmt = derive_format_from_cap(cap);
 		if (!fmt) {
 			ast_log(LOG_ERROR, "No codec available for sending RTP to '%s'\n",
 				args.destination);
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 488fbf4..724021e 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -2080,7 +2080,7 @@ static int sip_cc_monitor_request_cc(struct ast_cc_monitor *monitor, int *availa
 static int construct_pidf_body(enum sip_cc_publish_state state, char *pidf_body, size_t size, const char *presentity)
 {
 	struct ast_str *body = ast_str_alloca(size);
-	char tuple_id[32];
+	char tuple_id[64];
 
 	generate_random_string(tuple_id, sizeof(tuple_id));
 
@@ -13512,12 +13512,13 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
 
 	get_our_media_address(p, needvideo, needtext, &addr, &vaddr, &taddr, &dest, &vdest, &tdest);
 
+	/* We don't use dest here but p->ourip because address in o= field must not change in reINVITE */
 	snprintf(owner, sizeof(owner), "o=%s %d %d IN %s %s\r\n",
 		 ast_strlen_zero(global_sdpowner) ? "-" : global_sdpowner,
 		 p->sessionid, p->sessionversion,
-		 (ast_sockaddr_is_ipv6(&dest) && !ast_sockaddr_is_ipv4_mapped(&dest)) ?
+		 (ast_sockaddr_is_ipv6(&p->ourip) && !ast_sockaddr_is_ipv4_mapped(&p->ourip)) ?
 			"IP6" : "IP4",
-		 ast_sockaddr_stringify_addr_remote(&dest));
+		 ast_sockaddr_stringify_addr_remote(&p->ourip));
 
 	snprintf(connection, sizeof(connection), "c=IN %s %s\r\n",
 		 (ast_sockaddr_is_ipv6(&dest) && !ast_sockaddr_is_ipv4_mapped(&dest)) ?
@@ -15319,7 +15320,7 @@ static int transmit_cc_notify(struct ast_cc_agent *agent, struct sip_pvt *subscr
 {
 	struct sip_request req;
 	struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
-	char uri[SIPBUFSIZE];
+	char uri[SIPBUFSIZE + sizeof("cc-URI: \r\n") - 1];
 	char state_str[64];
 	char subscription_state_hdr[64];
 
@@ -15336,7 +15337,7 @@ static int transmit_cc_notify(struct ast_cc_agent *agent, struct sip_pvt *subscr
 	add_header(&req, "Subscription-State", subscription_state_hdr);
 	if (state == CC_READY) {
 		generate_uri(subscription, agent_pvt->notify_uri, sizeof(agent_pvt->notify_uri));
-		snprintf(uri, sizeof(uri) - 1, "cc-URI: %s\r\n", agent_pvt->notify_uri);
+		snprintf(uri, sizeof(uri), "cc-URI: %s\r\n", agent_pvt->notify_uri);
 	}
 	add_content(&req, state_str);
 	if (state == CC_READY) {
@@ -18025,7 +18026,7 @@ static int get_pai(struct sip_pvt *p, struct sip_request *req)
 	}
 
 	ast_copy_string(privacy, sip_get_header(req, "Privacy"), sizeof(privacy));
-	if (!ast_strlen_zero(privacy) && !strncmp(privacy, "id", 2)) {
+	if (!ast_strlen_zero(privacy) && strcasecmp(privacy, "none")) {
 		callingpres = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
 	}
 	if (!cid_name) {
@@ -18568,6 +18569,11 @@ static int get_sip_pvt_from_replaces(const char *callid, const char *totag,
 		}
 	}
 
+	if (!sip_pvt_ptr) {
+		/* return error if sip_pvt was not found */
+		return -1;
+	}
+
 	/* If we're here sip_pvt_ptr has been copied to *out_pvt, prevent RAII_VAR cleanup */
 	sip_pvt_ptr = NULL;
 
diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c
index ad7351d..ccf6a94 100644
--- a/channels/chan_skinny.c
+++ b/channels/chan_skinny.c
@@ -7429,6 +7429,11 @@ static void destroy_session(struct skinnysession *s)
 	}
 	ast_mutex_unlock(&s->lock);
 	ast_mutex_destroy(&s->lock);
+
+	if (s->t != AST_PTHREADT_NULL) {
+		pthread_detach(s->t);
+	}
+
 	ast_free(s);
 }
 
@@ -7515,11 +7520,6 @@ static void *skinny_session(void *data)
 	int eventmessage = 0;
 	struct pollfd fds[1];
 
-	if (!s) {
-		ast_log(LOG_WARNING, "Bad Skinny Session\n");
-		return 0;
-	}
-
 	ast_log(LOG_NOTICE, "Starting Skinny session from %s\n", ast_inet_ntoa(s->sin.sin_addr));
 
 	pthread_cleanup_push(skinny_session_cleanup, s);
@@ -7685,6 +7685,7 @@ static void *accept_thread(void *ignore)
 		s->keepalive_timeout_sched = -1;
 
 		if (ast_pthread_create(&s->t, NULL, skinny_session, s)) {
+			s->t = AST_PTHREADT_NULL;
 			destroy_session(s);
 		}
 	}
diff --git a/channels/chan_unistim.c b/channels/chan_unistim.c
index 029ce91..c3baa8a 100644
--- a/channels/chan_unistim.c
+++ b/channels/chan_unistim.c
@@ -372,7 +372,7 @@ struct unistim_subchannel {
 struct unistim_line {
 	ast_mutex_t lock;
 	char name[80]; /*! Like 200 */
-	char fullname[80]; /*! Like USTM/200\@black */
+	char fullname[101]; /*! Like USTM/200\@black */
 	char exten[AST_MAX_EXTENSION]; /*! Extension where to start */
 	char cid_num[AST_MAX_EXTENSION]; /*! CallerID Number */
 	char mailbox[AST_MAX_EXTENSION]; /*! Mailbox for MWI */
@@ -3699,7 +3699,7 @@ static void key_select_option(struct unistimsession *pte, char keycode)
 #define SELECTCODEC_MSG "Codec number : .."
 static void handle_select_codec(struct unistimsession *pte)
 {
-	char buf[30], buf2[5];
+	char buf[30], buf2[6];
 
 	pte->state = STATE_SELECTCODEC;
 	ast_copy_string(buf, ustmtext("Using codec", pte), sizeof(buf));
diff --git a/channels/chan_vpb.cc b/channels/chan_vpb.cc
index 0f050a8..da02ff3 100644
--- a/channels/chan_vpb.cc
+++ b/channels/chan_vpb.cc
@@ -1785,7 +1785,7 @@ static int vpb_digit_end(struct ast_channel *ast, char digit, unsigned int durat
 	ast_verb(4, "%s: vpb_digit: asked to play digit[%s]\n", p->dev, s);
 
 	ast_mutex_lock(&p->play_dtmf_lock);
-	strncat(p->play_dtmf, s, sizeof(*p->play_dtmf) - strlen(p->play_dtmf) - 1);
+	strncat(p->play_dtmf, s, sizeof(p->play_dtmf) - strlen(p->play_dtmf) - 1);
 	ast_mutex_unlock(&p->play_dtmf_lock);
 
 	ast_mutex_unlock(&p->lock);
diff --git a/channels/iax2/firmware.c b/channels/iax2/firmware.c
index a1ee435..0286132 100644
--- a/channels/iax2/firmware.c
+++ b/channels/iax2/firmware.c
@@ -46,6 +46,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "include/firmware.h"
 
+#define IAX_FIRMWARE_SUBDIR "/firmware/iax"
+
 struct iax_firmware {
 	AST_LIST_ENTRY(iax_firmware) list;
 	int fd;
@@ -208,7 +210,7 @@ void iax_firmware_reload(void)
 	struct iax_firmware *cur = NULL;
 	DIR *fwd;
 	struct dirent *de;
-	char dir[256], fn[256];
+	char fn[PATH_MAX + sizeof(IAX_FIRMWARE_SUBDIR) + sizeof(de->d_name)];
 
 	AST_LIST_LOCK(&firmwares);
 
@@ -218,12 +220,13 @@ void iax_firmware_reload(void)
 	}
 
 	/* Now that we have marked them dead... load new ones */
-	snprintf(dir, sizeof(dir), "%s/firmware/iax", ast_config_AST_DATA_DIR);
-	fwd = opendir(dir);
+	snprintf(fn, sizeof(fn), "%s%s", ast_config_AST_DATA_DIR, IAX_FIRMWARE_SUBDIR);
+	fwd = opendir(fn);
 	if (fwd) {
 		while((de = readdir(fwd))) {
 			if (de->d_name[0] != '.') {
-				snprintf(fn, sizeof(fn), "%s/%s", dir, de->d_name);
+				snprintf(fn, sizeof(fn), "%s%s/%s",
+					ast_config_AST_DATA_DIR, IAX_FIRMWARE_SUBDIR, de->d_name);
 				if (!try_firmware(fn)) {
 					ast_verb(2, "Loaded firmware '%s'\n", de->d_name);
 				}
@@ -231,7 +234,7 @@ void iax_firmware_reload(void)
 		}
 		closedir(fwd);
 	} else {
-		ast_log(LOG_WARNING, "Error opening firmware directory '%s': %s\n", dir, strerror(errno));
+		ast_log(LOG_WARNING, "Error opening firmware directory '%s': %s\n", fn, strerror(errno));
 	}
 
 	/* Clean up leftovers */
diff --git a/channels/pjsip/dialplan_functions.c b/channels/pjsip/dialplan_functions.c
index 22d7738..ae1c265 100644
--- a/channels/pjsip/dialplan_functions.c
+++ b/channels/pjsip/dialplan_functions.c
@@ -68,6 +68,18 @@
 		<ref type="function">PJSIP_SEND_SESSION_REFRESH</ref>
 	</see-also>
 </function>
+<function name="PJSIP_DTMF_MODE" language="en_US">
+	<synopsis>
+		Get or change the DTMF mode for a SIP call.
+	</synopsis>
+	<syntax>
+	</syntax>
+	<description>
+		<para>When read, returns the current DTMF mode</para>
+		<para>When written, sets the current DTMF mode</para>
+		<para>This function uses the same DTMF mode naming as the dtmf_mode configuration option</para>
+	</description>
+</function>
 <function name="PJSIP_SEND_SESSION_REFRESH" language="en_US">
 	<synopsis>
 		W/O: Initiate a session refresh via an UPDATE or re-INVITE on an established media session
@@ -381,9 +393,15 @@
 					<enum name="local_uri">
 						<para>The local URI.</para>
 					</enum>
+					<enum name="local_tag">
+						<para>Tag in From header</para>
+					</enum>
 					<enum name="remote_uri">
 						<para>The remote URI.</para>
 					</enum>
+					<enum name="remote_tag">
+						<para>Tag in To header</para>
+					</enum>
 					<enum name="t38state">
 						<para>The current state of any T.38 fax on this channel.</para>
 						<enumlist>
@@ -440,6 +458,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/app.h"
 #include "asterisk/channel.h"
 #include "asterisk/format.h"
+#include "asterisk/dsp.h"
 #include "asterisk/pbx.h"
 #include "asterisk/res_pjsip.h"
 #include "asterisk/res_pjsip_session.h"
@@ -678,10 +697,18 @@ static int channel_read_pjsip(struct ast_channel *chan, const char *type, const
 		pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, dlg->local.info->uri, buf, buflen);
 		buf_copy = ast_strdupa(buf);
 		ast_escape_quoted(buf_copy, buf, buflen);
+	} else if (!strcmp(type, "local_tag")) {
+		ast_copy_pj_str(buf, &dlg->local.info->tag, buflen);
+		buf_copy = ast_strdupa(buf);
+		ast_escape_quoted(buf_copy, buf, buflen);
 	} else if (!strcmp(type, "remote_uri")) {
 		pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, dlg->remote.info->uri, buf, buflen);
 		buf_copy = ast_strdupa(buf);
 		ast_escape_quoted(buf_copy, buf, buflen);
+	} else if (!strcmp(type, "remote_tag")) {
+		ast_copy_pj_str(buf, &dlg->remote.info->tag, buflen);
+		buf_copy = ast_strdupa(buf);
+		ast_escape_quoted(buf_copy, buf, buflen);
 	} else if (!strcmp(type, "t38state")) {
 		ast_copy_string(buf, t38state_to_string[channel->session->t38state], buflen);
 	} else if (!strcmp(type, "local_addr")) {
@@ -1039,6 +1066,34 @@ int pjsip_acf_media_offer_write(struct ast_channel *chan, const char *cmd, char
 	return ast_sip_push_task_synchronous(channel->session->serializer, media_offer_write_av, &mdata);
 }
 
+int pjsip_acf_dtmf_mode_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+	struct ast_sip_channel_pvt *channel;
+
+	if (!chan) {
+		ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
+		return -1;
+	}
+
+	ast_channel_lock(chan);
+	if (strcmp(ast_channel_tech(chan)->type, "PJSIP")) {
+		ast_log(LOG_WARNING, "Cannot call %s on a non-PJSIP channel\n", cmd);
+		ast_channel_unlock(chan);
+		return -1;
+	}
+
+	channel = ast_channel_tech_pvt(chan);
+
+	if (ast_sip_dtmf_to_str(channel->session->dtmf, buf, len) < 0) {
+		ast_log(LOG_WARNING, "Unknown DTMF mode %d on PJSIP channel %s\n", channel->session->dtmf, ast_channel_name(chan));
+		ast_channel_unlock(chan);
+		return -1;
+	}
+
+	ast_channel_unlock(chan);
+	return 0;
+}
+
 struct refresh_data {
 	struct ast_sip_session *session;
 	enum ast_sip_session_refresh_method method;
@@ -1067,6 +1122,120 @@ static int sip_session_response_cb(struct ast_sip_session *session, pjsip_rx_dat
 	return 0;
 }
 
+static int dtmf_mode_refresh_cb(void *obj)
+{
+	struct refresh_data *data = obj;
+
+	if (data->session->inv_session->state == PJSIP_INV_STATE_CONFIRMED) {
+		ast_debug(3, "Changing DTMF mode on channel %s after OFFER/ANSWER completion. Sending session refresh\n", ast_channel_name(data->session->channel));
+
+		ast_sip_session_refresh(data->session, NULL, NULL,
+			sip_session_response_cb, data->method, 1);
+	} else if (data->session->inv_session->state == PJSIP_INV_STATE_INCOMING) {
+		ast_debug(3, "Changing DTMF mode on channel %s during OFFER/ANSWER exchange. Updating SDP answer\n", ast_channel_name(data->session->channel));
+		ast_sip_session_regenerate_answer(data->session, NULL);
+	}
+
+	return 0;
+}
+
+int pjsip_acf_dtmf_mode_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+	struct ast_sip_channel_pvt *channel;
+	struct chan_pjsip_pvt *pjsip_pvt;
+	int dsp_features = 0;
+	int dtmf = -1;
+	struct refresh_data rdata = {
+			.method = AST_SIP_SESSION_REFRESH_METHOD_INVITE,
+		};
+
+	if (!chan) {
+		ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
+		return -1;
+	}
+
+	ast_channel_lock(chan);
+	if (strcmp(ast_channel_tech(chan)->type, "PJSIP")) {
+		ast_log(LOG_WARNING, "Cannot call %s on a non-PJSIP channel\n", cmd);
+		ast_channel_unlock(chan);
+		return -1;
+	}
+
+	channel = ast_channel_tech_pvt(chan);
+	rdata.session = channel->session;
+
+	dtmf = ast_sip_str_to_dtmf(value);
+
+	if (dtmf == -1) {
+		ast_log(LOG_WARNING, "Cannot set DTMF mode to '%s' on channel '%s' as value is invalid.\n", value,
+			ast_channel_name(chan));
+		ast_channel_unlock(chan);
+		return -1;
+	}
+
+	if (channel->session->dtmf == dtmf) {
+		/* DTMF mode unchanged, nothing to do! */
+		ast_channel_unlock(chan);
+		return 0;
+	}
+
+	channel->session->dtmf = dtmf;
+
+	pjsip_pvt = channel->pvt;
+	if (pjsip_pvt->media[SIP_MEDIA_AUDIO]  && (pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp) {
+		if (channel->session->dtmf == AST_SIP_DTMF_RFC_4733) {
+			ast_rtp_instance_set_prop((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_PROPERTY_DTMF, 1);
+			ast_rtp_instance_dtmf_mode_set((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_DTMF_MODE_RFC2833);
+		} else if (channel->session->dtmf == AST_SIP_DTMF_INFO) {
+			ast_rtp_instance_set_prop((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_PROPERTY_DTMF, 0);
+			ast_rtp_instance_dtmf_mode_set((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_DTMF_MODE_NONE);
+		} else if (channel->session->dtmf == AST_SIP_DTMF_INBAND) {
+			ast_rtp_instance_set_prop((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_PROPERTY_DTMF, 0);
+			ast_rtp_instance_dtmf_mode_set((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_DTMF_MODE_INBAND);
+		} else if (channel->session->dtmf == AST_SIP_DTMF_NONE) {
+			ast_rtp_instance_set_prop((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_PROPERTY_DTMF, 0);
+			ast_rtp_instance_dtmf_mode_set((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_DTMF_MODE_NONE);
+		} else if (channel->session->dtmf == AST_SIP_DTMF_AUTO) {
+			if (ast_rtp_instance_dtmf_mode_get((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp) != AST_RTP_DTMF_MODE_RFC2833) {
+				/* no RFC4733 negotiated, enable inband */
+				ast_rtp_instance_dtmf_mode_set((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_DTMF_MODE_INBAND);
+			}
+		} else if (channel->session->dtmf == AST_SIP_DTMF_AUTO_INFO) {
+			ast_rtp_instance_set_prop((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_PROPERTY_DTMF, 0);
+			if (ast_rtp_instance_dtmf_mode_get((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp) == AST_RTP_DTMF_MODE_INBAND) {
+				/* if inband, switch to INFO */
+				ast_rtp_instance_dtmf_mode_set((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_DTMF_MODE_NONE);
+			}
+		}
+	}
+
+	if (channel->session->dsp) {
+		dsp_features = ast_dsp_get_features(channel->session->dsp);
+	}
+	if (channel->session->dtmf == AST_SIP_DTMF_INBAND ||
+		channel->session->dtmf == AST_SIP_DTMF_AUTO) {
+		dsp_features |= DSP_FEATURE_DIGIT_DETECT;
+	} else {
+		dsp_features &= ~DSP_FEATURE_DIGIT_DETECT;
+	}
+	if (dsp_features) {
+		if (!channel->session->dsp) {
+			if (!(channel->session->dsp = ast_dsp_new())) {
+				ast_channel_unlock(chan);
+				return 0;
+			}
+		}
+		ast_dsp_set_features(channel->session->dsp, dsp_features);
+	} else if (channel->session->dsp) {
+		ast_dsp_free(channel->session->dsp);
+		channel->session->dsp = NULL;
+	}
+
+	ast_channel_unlock(chan);
+
+	return ast_sip_push_task_synchronous(channel->session->serializer, dtmf_mode_refresh_cb, &rdata);
+}
+
 static int refresh_write_cb(void *obj)
 {
 	struct refresh_data *data = obj;
diff --git a/channels/pjsip/include/dialplan_functions.h b/channels/pjsip/include/dialplan_functions.h
index 8b80bfa..731e91d 100644
--- a/channels/pjsip/include/dialplan_functions.h
+++ b/channels/pjsip/include/dialplan_functions.h
@@ -48,6 +48,31 @@ int pjsip_acf_channel_read(struct ast_channel *chan, const char *cmd, char *data
 int pjsip_acf_media_offer_write(struct ast_channel *chan, const char *cmd, char *data, const char *value);
 
 /*!
+ * \brief PJSIP_DTMF_MODE function read callback
+ * \param chan The channel the function is called on
+ * \param cmd The name of the function
+ * \param data Arguments passed to the function
+ * \param buf Out buffer that should be populated with the data
+ * \param len Size of the buffer
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int pjsip_acf_dtmf_mode_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
+
+/*!
+ * \brief PJSIP_DTMF_MODE function write callback
+ * \param chan The channel the function is called on
+ * \param cmd The name of the function
+ * \param data Arguments passed to the function
+ * \param value Value to be set by the function
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int pjsip_acf_dtmf_mode_write(struct ast_channel *chan, const char *cmd, char *data, const char *value);
+
+/*!
  * \brief PJSIP_MEDIA_OFFER function read callback
  * \param chan The channel the function is called on
  * \param cmd The name of the function
diff --git a/channels/sig_pri.c b/channels/sig_pri.c
index d6b8507..f371fbf 100644
--- a/channels/sig_pri.c
+++ b/channels/sig_pri.c
@@ -2040,7 +2040,7 @@ static void *do_idle_thread(void *v_pvt)
 	struct sig_pri_chan *pvt = v_pvt;
 	struct ast_channel *chan = pvt->owner;
 	struct ast_frame *f;
-	char ex[80];
+	char ex[128];
 	/* Wait up to 30 seconds for an answer */
 	int timeout_ms = 30000;
 	int ms;
@@ -2281,7 +2281,7 @@ static void sig_pri_party_name_convert(struct ast_party_name *ast_name, const st
  */
 static void sig_pri_party_number_convert(struct ast_party_number *ast_number, const struct pri_party_number *pri_number, struct sig_pri_span *pri)
 {
-	char number[AST_MAX_EXTENSION];
+	char number[AST_MAX_EXTENSION * 2];
 
 	apply_plan_to_existing_number(number, sizeof(number), pri, pri_number->str,
 		pri_number->plan);
@@ -6245,7 +6245,7 @@ static void *pri_dchannel(void *vpri)
 	struct timeval lastidle = { 0, 0 };
 	pthread_t p;
 	struct ast_channel *idle;
-	char idlen[80];
+	char idlen[128];
 	int nextidle = -1;
 	int haveidles;
 	int activeidles;
@@ -6651,7 +6651,7 @@ static void *pri_dchannel(void *vpri)
 						e->service_ack.changestatus, PRI_SPAN(e->service_ack.channel), PRI_CHANNEL(e->service_ack.channel), pri->span);
 				} else {
 					char db_chan_name[20];
-					char db_answer[5];
+					char db_answer[15];
 					int ch;
 					unsigned *why;
 
diff --git a/channels/sig_pri.h b/channels/sig_pri.h
index d3e5350..8c9babd 100644
--- a/channels/sig_pri.h
+++ b/channels/sig_pri.h
@@ -300,7 +300,7 @@ struct sig_pri_chan {
 	char cid_name[AST_MAX_EXTENSION];
 	char cid_ani[AST_MAX_EXTENSION];
 	/*! \brief User tag for party id's sent from this device driver. */
-	char user_tag[AST_MAX_EXTENSION];
+	char user_tag[AST_MAX_EXTENSION * 2];
 	char exten[AST_MAX_EXTENSION];
 
 	/* Internal variables -- Don't touch */
diff --git a/channels/sip/dialplan_functions.c b/channels/sip/dialplan_functions.c
index b859c60..7a1a443 100644
--- a/channels/sip/dialplan_functions.c
+++ b/channels/sip/dialplan_functions.c
@@ -41,6 +41,9 @@
 		<enum name="uri">
 			<para>R/O Get the URI from the Contact: header.</para>
 		</enum>
+		<enum name="ruri">
+			<para>R/O Get the Request-URI from the INVITE header.</para>
+		</enum>
 		<enum name="useragent">
 			<para>R/O Get the useragent.</para>
 		</enum>
@@ -164,6 +167,9 @@ int sip_acf_channel_read(struct ast_channel *chan, const char *funcname, char *p
 		ast_copy_string(buf, p->from, buflen);
 	} else if (!strcasecmp(args.param, "uri")) {
 		ast_copy_string(buf, p->uri, buflen);
+	} else if (!strcasecmp(args.param, "ruri")) {
+		char *tmpruri = REQ_OFFSET_TO_STR(&p->initreq, rlpart2);
+		ast_copy_string(buf, tmpruri, buflen);
 	} else if (!strcasecmp(args.param, "useragent")) {
 		ast_copy_string(buf, p->useragent, buflen);
 	} else if (!strcasecmp(args.param, "peername")) {
@@ -486,6 +492,9 @@ done:
 		dialog_unlink_all(p);
 		dialog_unref(p, "Destroy test object");
 	}
+	if (chan) {
+		ast_channel_unref(chan);
+	}
 	ast_rtp_engine_unregister(&test_engine);
 	return res;
 }
diff --git a/configs/basic-pbx/modules.conf b/configs/basic-pbx/modules.conf
index 7b60125..05fcc14 100644
--- a/configs/basic-pbx/modules.conf
+++ b/configs/basic-pbx/modules.conf
@@ -78,7 +78,6 @@ load = res_pjsip_exten_state.so
 load = res_pjsip_header_funcs.so
 load = res_pjsip_logger.so
 load = res_pjsip_messaging.so
-load = res_pjsip_multihomed.so
 load = res_pjsip_mwi_body_generator.so
 load = res_pjsip_mwi.so
 load = res_pjsip_nat.so
diff --git a/configs/samples/config_test.conf.sample b/configs/samples/config_test.conf.sample
index 2fff45e..b7cb212 100644
--- a/configs/samples/config_test.conf.sample
+++ b/configs/samples/config_test.conf.sample
@@ -6,6 +6,10 @@
 [global]
 intopt=-1
 uintopt=1
+timelenopt1=1ms
+timelenopt2=1s
+timelenopt3=1m
+timelenopt4=1h
 doubleopt=0.1
 sockaddropt=1.2.3.4:1234
 boolopt=true
@@ -23,6 +27,10 @@ customopt=yes
 [item]
 intopt=-1
 uintopt=1
+timelenopt1=1
+timelenopt2=1
+timelenopt3=1
+timelenopt4=1
 doubleopt=0.1
 sockaddropt=1.2.3.4:1234
 boolopt=true
diff --git a/configs/samples/minivm.conf.sample b/configs/samples/minivm.conf.sample
index 2df3449..79fdbb0 100644
--- a/configs/samples/minivm.conf.sample
+++ b/configs/samples/minivm.conf.sample
@@ -51,7 +51,7 @@ silencethreshold=128
 ; If you need to have an external program, i.e. /usr/bin/myapp called when a
 ; voicemail is received by the server. The arguments are
 ;
-; 	<app> <username at domain> <callerid-number> <callerid-name>
+; 	<app> <username at domain> <callerid-name> <callerid-number>
 ;
 ;externnotify=/usr/bin/myapp
 ; The character set for voicemail messages can be specified here
diff --git a/configs/samples/musiconhold.conf.sample b/configs/samples/musiconhold.conf.sample
index 8b2202d..67570ee 100644
--- a/configs/samples/musiconhold.conf.sample
+++ b/configs/samples/musiconhold.conf.sample
@@ -91,3 +91,26 @@ directory=moh
 ;mode=custom
 ;directory=/var/lib/asterisk/mohmp3
 ;application=/site/sw/bin/madplay -Q -o raw:- --mono -R 8000 -a -12
+
+; By default, when res_musiconhold reloads or unloads, it sends a HUP signal
+; to custom applications (and all descendants), waits 100ms, then sends a
+; TERM signal, waits 100ms, then finally sends a KILL signal.  An application
+; which is interacting with an external device and/or spawns children of its
+; own may not be able to exit cleanly in the default times, expecially if sent
+; a KILL signal, or if it's children are getting signals directly from
+; res_musiconhoild.  To allow extra time, the 'kill_escalation_delay'
+; class option can be used to set the number of milliseconds res_musiconhold
+; waits before escalating kill signals, with the default being the current
+; 100ms.  To control to whom the signals are sent, the "kill_method"
+; class option can be set to "process_group" (the default, existing behavior),
+; which sends signals to the application and its descendants directly, or
+; "process" which sends signals only to the application itself.
+
+;[sox_from_device]
+;mode=custom
+;directory=/var/lib/asterisk/mohmp3
+;application=/usr/bin/sox -q -t alsa -c 2 -r 48000 hw:1 -c 1 -r 8000 -t raw -s -
+; Wait 500ms before escalating kill signals
+;kill_escalation_delay=500
+; Send signals to just the child process instead of all descendants
+;kill_method=process
diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index f983a87..ba7d932 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -780,6 +780,12 @@
                               ; The value "yes" is useful for some SIP phones
                               ; (Cisco SPA) to be able to indicate and pick up
                               ; ringing devices.
+;incoming_mwi_mailbox = ; Mailbox name to use when incoming MWI NOTIFYs are
+                        ; received.
+                        ; If an MWI NOTIFY is received FROM this endpoint,
+                        ; this mailbox will be used when notifying other modules
+                        ; of MWI status changes.  If not set, incoming MWI
+                        ; NOTIFYs are ignored.
 
 ;==========================AUTH SECTION OPTIONS=========================
 ;[auth]
@@ -888,7 +894,13 @@
 ;max_contacts=0 ; Maximum number of contacts that can bind to an AoR (default:
                 ; "0")
 ;minimum_expiration=60  ; Minimum keep alive time for an AoR (default: "60")
-;remove_existing=no     ; Determines whether new contacts replace existing ones
+;remove_existing=no     ; Allow a registration to succeed by displacing any existing
+                        ; contacts that now exceed the max_contacts count.  Any
+                        ; removed contacts are the next to expire.  The behaviour is
+                        ; beneficial when rewrite_contact is enabled and max_contacts
+                        ; is greater than one.  The removed contact is likely the old
+                        ; contact created by rewrite_contact that the device is
+                        ; refreshing.
                         ; (default: "no")
 ;type=  ; Must be of type aor (default: "")
 ;qualify_frequency=0    ; Interval at which to qualify an AoR (default: "0")
@@ -1117,7 +1129,7 @@
 
 
 ; MODULE PROVIDING BELOW SECTION(S): res_pjsip_outbound_publish
-;======================OUTBOUND_PUBLISHEN SECTION OPTIONS=====================
+;======================OUTBOUND_PUBLISH SECTION OPTIONS=====================
 ; See https://wiki.asterisk.org/wiki/display/AST/Publishing+Extension+State
 ; for more information.
 ;[outbound-publish]
@@ -1127,7 +1139,7 @@
 
 ;outbound_auth=            ; Authentication object(s) to be used for outbound
                            ; publishes.
-                           ; This is a comma-delimited list of auth	sections
+                           ; This is a comma-delimited list of auth sections
                            ; defined in pjsip.conf used to respond to outbound
                            ; authentication challenges.
                            ; Using the same auth section for inbound and
diff --git a/configs/samples/res_config_sqlite.conf.sample b/configs/samples/res_config_sqlite.conf.sample
index 04e6ae2..2d14d46 100644
--- a/configs/samples/res_config_sqlite.conf.sample
+++ b/configs/samples/res_config_sqlite.conf.sample
@@ -8,4 +8,4 @@ dbfile => /var/lib/asterisk/sqlite.db
 ; extconfig.conf, the value given here is used. If cdr_table is omitted, CDR
 ; support is simply disabled.
 config_table => ast_config
-cdr_table => ast_cdr
+; cdr_table => ast_cdr
diff --git a/configs/samples/xmpp.conf.sample b/configs/samples/xmpp.conf.sample
index dad0f79..e3a4be1 100644
--- a/configs/samples/xmpp.conf.sample
+++ b/configs/samples/xmpp.conf.sample
@@ -18,6 +18,29 @@
 ;pubsub_node=pubsub.astjab.org		; Node to use for publishing events via PubSub
 ;username=asterisk at astjab.org/asterisk	; Username with optional resource.
 ;secret=blah				; Password
+;refresh_token=TOKEN_VALUE		; Refresh token issued by Google OAuth 2.0 protocol.
+					; `secret` must NOT be set if you use OAuth.
+					; See https://developers.google.com/identity/protocols/OAuth2WebServer
+					; for more details.
+					; For test reasons you can obtain one on the page
+					; https://developers.google.com/oauthplayground/
+					; 1. Click on Settings icon, check "Use your own OAuth credentials"
+					;    and enter your Client ID and Client Secret (see below).
+					; 2. Input the scope https://www.googleapis.com/auth/googletalk
+					;    and push "Authorize APIs" button.
+					; 3. Approve permissions.
+					; 4. On section "Step 2" push "Exchange authorization code for tokens"
+					;    and get your Refresh token.
+;oauth_clientid=OAUTH_CLIENT_ID_VALUE	; The application's client id to authorize using Google OAuth 2.0 protocol.
+;oauth_secret=OAUTH_SECRET_VALUE	; The application's client secret to authorize using Google OAuth 2.0 protocol.
+					; 1. Create new Project on the page:
+					;    https://console.cloud.google.com/apis/credentials/oauthclient
+					; 2. Create new Application ID on the same page with type Web-application.
+					;    In section "Allowed URI redirections" put the path to the corresponding
+					;    script on your site or https://developers.google.com/oauthplayground
+					;    if you would like to obtain refresh_token from users by hand
+					;    (for example, for test reasons).
+					; 3. Client ID and Client Secret will be shown and available on the same page.
 ;priority=1				; Resource priority
 ;port=5222				; Port to use defaults to 5222
 ;usetls=yes				; Use tls or not
diff --git a/configure b/configure
index c566357..8308b9e 100755
--- a/configure
+++ b/configure
@@ -701,6 +701,7 @@ BIND8_CFLAGS
 AST_RPATH
 AST_NATIVE_ARCH
 AST_SHADOW_WARNINGS
+AST_NO_FORMAT_TRUNCATION
 AST_NO_STRICT_OVERFLOW
 AST_FORTIFY_SOURCE
 AST_TRAMPOLINES
@@ -1199,6 +1200,7 @@ AST_NESTED_FUNCTIONS
 AST_CODE_COVERAGE
 EXTERNALS_CACHE_DIR
 SOUNDS_CACHE_DIR
+AST_DOWNLOAD_CACHE
 AST_DEVMODE_STRICT
 AST_DEVMODE
 NOISY_BUILD
@@ -1352,6 +1354,7 @@ ac_user_opts='
 enable_option_checking
 with_gnu_ld
 enable_dev_mode
+with_download_cache
 with_sounds_cache
 with_externals_cache
 enable_coverage
@@ -2095,6 +2098,9 @@ Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
   --with-gnu-ld           assume the C compiler uses GNU ld [default=no]
+  --with-download-cache=PATH
+                          use cached sound AND external module tarfiles in
+                          PATH
   --with-sounds-cache=PATH
                           use cached sound tarfiles in PATH
   --with-externals-cache=PATH
@@ -9015,6 +9021,30 @@ fi
 
 
 
+# Check whether --with-download-cache was given.
+if test "${with_download_cache+set}" = set; then :
+  withval=$with_download_cache;
+	case ${withval} in
+	n|no)
+		unset AST_DOWNLOAD_CACHE
+		;;
+	*)
+		if test "x${withval}" = "x"; then
+			:
+		else
+			AST_DOWNLOAD_CACHE="${withval}"
+		fi
+		;;
+	esac
+
+else
+  :
+fi
+
+
+
+
+
 # Check whether --with-sounds-cache was given.
 if test "${with_sounds_cache+set}" = set; then :
   withval=$with_sounds_cache;
@@ -9277,11 +9307,11 @@ $as_echo "configuring" >&6; }
 		PJPROJECT_CONFIGURE_OPTS+=" --host=$host"
 	fi
 
-	export TAR PATCH SED NM EXTERNALS_CACHE_DIR DOWNLOAD_TO_STDOUT DOWNLOAD_TIMEOUT DOWNLOAD MD5 CAT
+	export TAR PATCH SED NM EXTERNALS_CACHE_DIR AST_DOWNLOAD_CACHE DOWNLOAD_TO_STDOUT DOWNLOAD_TIMEOUT DOWNLOAD MD5 CAT
 	export NOISY_BUILD
 	${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} \
 		PJPROJECT_CONFIGURE_OPTS="$PJPROJECT_CONFIGURE_OPTS" \
-		EXTERNALS_CACHE_DIR="${EXTERNALS_CACHE_DIR}" \
+		EXTERNALS_CACHE_DIR="${EXTERNALS_CACHE_DIR:-${AST_DOWNLOAD_CACHE}}" \
 		configure
 	if test $? -ne 0 ; then
 		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
@@ -9294,7 +9324,7 @@ $as_echo "$as_me: Unable to configure ${PJPROJECT_DIR}" >&6;}
 	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for bundled pjproject" >&5
 $as_echo_n "checking for bundled pjproject... " >&6; }
 
-	PJPROJECT_INCLUDE=$(${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} PJPROJECT_CONFIGURE_OPTS="$PJPROJECT_CONFIGURE_OPTS" EXTERNALS_CACHE_DIR="${EXTERNALS_CACHE_DIR}" echo_cflags)
+	PJPROJECT_INCLUDE=$(${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} PJPROJECT_CONFIGURE_OPTS="$PJPROJECT_CONFIGURE_OPTS" EXTERNALS_CACHE_DIR="${EXTERNALS_CACHE_DIR:-${AST_DOWNLOAD_CACHE}}" echo_cflags)
 	PJPROJECT_CFLAGS="$PJPROJECT_INCLUDE"
 	PBX_PJPROJECT=1
 
@@ -18830,6 +18860,19 @@ $as_echo "no" >&6; }
 fi
 
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -Wno-format-truncation" >&5
+$as_echo_n "checking for -Wno-format-truncation... " >&6; }
+if $(${CC} -O2 -Wno-format-truncation -S -o /dev/null -xc /dev/null > /dev/null 2>&1); then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+	AST_NO_FORMAT_TRUNCATION=-Wno-format-truncation
+else
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+	AST_NO_FORMAT_TRUNCATION=
+fi
+
+
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -Wshadow" >&5
 $as_echo_n "checking for -Wshadow... " >&6; }
 if $(${CC} -Wshadow -S -o /dev/null -xc /dev/null > /dev/null 2>&1); then
@@ -31098,7 +31141,7 @@ if eval \${$as_ac_Lib+:} false; then :
   $as_echo_n "(cached) " >&6
 else
   ac_check_lib_save_LIBS=$LIBS
-LIBS="-lcpg ${pbxlibdir} -lcfg $LIBS"
+LIBS="-lcpg ${pbxlibdir} -lcpg $LIBS"
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -31140,7 +31183,7 @@ fi
 
    # now check for the header.
    if test "${AST_COROSYNC_FOUND}" = "yes"; then
-      COROSYNC_LIB="${pbxlibdir} -lcpg -lcfg"
+      COROSYNC_LIB="${pbxlibdir} -lcpg -lcpg"
       # if --with-COROSYNC=DIR has been specified, use it.
       if test "x${COROSYNC_DIR}" != "x"; then
          COROSYNC_INCLUDE="-I${COROSYNC_DIR}/include"
@@ -33238,7 +33281,7 @@ fi
     fi
 fi
 
-for ver in 2.0 2.2 2.4 2.6; do
+for ver in 2.0 2.2 2.4 2.6 3.0; do
 
    if test "x${PBX_GMIME}" != "x1" -a "${USE_GMIME}" != "no"; then
 
diff --git a/configure.ac b/configure.ac
index de4bce2..6c5f4e3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -408,6 +408,7 @@ AC_SUBST(NOISY_BUILD)
 AC_SUBST(AST_DEVMODE)
 AC_SUBST(AST_DEVMODE_STRICT)
 
+AST_OPTION_ONLY([download-cache], [AST_DOWNLOAD_CACHE], [cached sound AND external module tarfiles], [])
 AST_OPTION_ONLY([sounds-cache], [SOUNDS_CACHE_DIR], [cached sound tarfiles], [])
 AST_OPTION_ONLY([externals-cache], [EXTERNALS_CACHE_DIR], [cached external module tarfiles], [])
 
@@ -1225,6 +1226,16 @@ else
 fi
 AC_SUBST(AST_NO_STRICT_OVERFLOW)
 
+AC_MSG_CHECKING(for -Wno-format-truncation)
+if $(${CC} -O2 -Wno-format-truncation -S -o /dev/null -xc /dev/null > /dev/null 2>&1); then
+	AC_MSG_RESULT(yes)
+	AST_NO_FORMAT_TRUNCATION=-Wno-format-truncation
+else
+	AC_MSG_RESULT(no)
+	AST_NO_FORMAT_TRUNCATION=
+fi
+AC_SUBST(AST_NO_FORMAT_TRUNCATION)
+
 AC_MSG_CHECKING(for -Wshadow)
 if $(${CC} -Wshadow -S -o /dev/null -xc /dev/null > /dev/null 2>&1); then
 	AC_MSG_RESULT(yes)
@@ -2363,7 +2374,7 @@ else
 	AST_EXT_LIB_CHECK([RADIUS], [radiusclient-ng], [rc_read_config], [radiusclient-ng.h])
 fi
 
-AST_EXT_LIB_CHECK([COROSYNC], [cpg], [cpg_join], [corosync/cpg.h], [-lcfg])
+AST_EXT_LIB_CHECK([COROSYNC], [cpg], [cpg_join], [corosync/cpg.h], [-lcpg])
 AST_EXT_LIB_CHECK([COROSYNC_CFG_STATE_TRACK], [cfg], [corosync_cfg_state_track], [corosync/cfg.h], [-lcfg])
 
 AST_EXT_LIB_CHECK([SPEEX], [speex], [speex_encode], [speex/speex.h], [-lm])
@@ -2492,7 +2503,7 @@ then
     fi
 fi
 
-for ver in 2.0 2.2 2.4 2.6; do
+for ver in 2.0 2.2 2.4 2.6 3.0; do
 	AST_PKG_CONFIG_CHECK([GMIME], gmime-$ver)
 	if test "$PBX_GMIME" = 1; then
 		break;
diff --git a/contrib/ast-db-manage/config/versions/15db7b91a97a_add_rtcp_mux.py b/contrib/ast-db-manage/config/versions/15db7b91a97a_add_rtcp_mux.py
index 8b0214a..de9e10a 100644
--- a/contrib/ast-db-manage/config/versions/15db7b91a97a_add_rtcp_mux.py
+++ b/contrib/ast-db-manage/config/versions/15db7b91a97a_add_rtcp_mux.py
@@ -28,4 +28,6 @@ def upgrade():
 
 
 def downgrade():
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_endpoints_rtcp_mux_yesno_values','ps_endpoints')
     op.drop_column('ps_endpoints', 'rtcp_mux')
diff --git a/contrib/ast-db-manage/config/versions/164abbd708c_add_auto_info_to_endpoint_dtmf_mode.py b/contrib/ast-db-manage/config/versions/164abbd708c_add_auto_info_to_endpoint_dtmf_mode.py
index 932773f..d7d111d 100644
--- a/contrib/ast-db-manage/config/versions/164abbd708c_add_auto_info_to_endpoint_dtmf_mode.py
+++ b/contrib/ast-db-manage/config/versions/164abbd708c_add_auto_info_to_endpoint_dtmf_mode.py
@@ -52,7 +52,7 @@ def downgrade():
         enum.create(op.get_bind(), checkfirst=False)
 
         op.execute('ALTER TABLE ps_endpoints ALTER COLUMN dtmf_mode TYPE'
-                   ' pjsip_dtmf_mode_values USING'
+                   ' pjsip_dtmf_mode_values_v2 USING'
                    ' dtmf_mode::text::pjsip_dtmf_mode_values_v2')
 
         ENUM(name="pjsip_dtmf_mode_values_v3").drop(op.get_bind(), checkfirst=False)
diff --git a/contrib/ast-db-manage/config/versions/23530d604b96_add_rpid_immediate.py b/contrib/ast-db-manage/config/versions/23530d604b96_add_rpid_immediate.py
index dc0c01c..b3ffaee 100644
--- a/contrib/ast-db-manage/config/versions/23530d604b96_add_rpid_immediate.py
+++ b/contrib/ast-db-manage/config/versions/23530d604b96_add_rpid_immediate.py
@@ -45,4 +45,6 @@ def upgrade():
     op.add_column('ps_endpoints', sa.Column('rpid_immediate', yesno_values))
 
 def downgrade():
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_endpoints_rpid_immediate_yesno_values','ps_endpoints')
     op.drop_column('ps_endpoints', 'rpid_immediate')
diff --git a/contrib/ast-db-manage/config/versions/26d7f3bf0fa5_add_bind_rtp_to_media_address_to_pjsip.py b/contrib/ast-db-manage/config/versions/26d7f3bf0fa5_add_bind_rtp_to_media_address_to_pjsip.py
index e7c11da..ce2ac1e 100644
--- a/contrib/ast-db-manage/config/versions/26d7f3bf0fa5_add_bind_rtp_to_media_address_to_pjsip.py
+++ b/contrib/ast-db-manage/config/versions/26d7f3bf0fa5_add_bind_rtp_to_media_address_to_pjsip.py
@@ -28,4 +28,6 @@ def upgrade():
 
 
 def downgrade():
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_endpoints_bind_rtp_to_media_address_yesno_values','ps_endpoints')
     op.drop_column('ps_endpoints', 'bind_rtp_to_media_address')
diff --git a/contrib/ast-db-manage/config/versions/28ab27a7826d_add_srv_lookups_to_identify.py b/contrib/ast-db-manage/config/versions/28ab27a7826d_add_srv_lookups_to_identify.py
index 8831e20..9325da3 100644
--- a/contrib/ast-db-manage/config/versions/28ab27a7826d_add_srv_lookups_to_identify.py
+++ b/contrib/ast-db-manage/config/versions/28ab27a7826d_add_srv_lookups_to_identify.py
@@ -28,4 +28,6 @@ def upgrade():
 
 
 def downgrade():
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_endpoint_id_ips_srv_lookups_yesno_values','ps_endpoint_id_ips')
     op.drop_column('ps_endpoint_id_ips', 'srv_lookups')
diff --git a/contrib/ast-db-manage/config/versions/28b8e71e541f_add_g726_non_standard.py b/contrib/ast-db-manage/config/versions/28b8e71e541f_add_g726_non_standard.py
index ad36bd9..08e2e3f 100644
--- a/contrib/ast-db-manage/config/versions/28b8e71e541f_add_g726_non_standard.py
+++ b/contrib/ast-db-manage/config/versions/28b8e71e541f_add_g726_non_standard.py
@@ -27,4 +27,6 @@ def upgrade():
 
 
 def downgrade():
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_endpoints_g726_non_standard_yesno_values','ps_endpoints')
     op.drop_column('ps_endpoints', 'g726_non_standard')
diff --git a/contrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py b/contrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py
index d39ddb4..3ca16fe 100644
--- a/contrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py
+++ b/contrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py
@@ -142,6 +142,8 @@ def upgrade():
 def downgrade():
     ########################## drop columns ##########################
 
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_aors_support_path_yesno_values', 'ps_aors')
     op.drop_column('ps_aors', 'support_path')
     op.drop_column('ps_aors', 'outbound_proxy')
     op.drop_column('ps_aors', 'maximum_expiration')
@@ -153,6 +155,8 @@ def downgrade():
         new_column_name='mwi_fromuser', existing_type=sa.String(40))
 
     op.drop_column('ps_endpoints', 'set_var')
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_endpoints_redirect_method_pjsip_redirect_method_values', 'ps_endpoints')
     op.drop_column('ps_endpoints', 'redirect_method')
     op.drop_column('ps_endpoints', 'media_address')
 
diff --git a/contrib/ast-db-manage/config/versions/371a3bf4143e_add_user_eq_phone_option_to_pjsip.py b/contrib/ast-db-manage/config/versions/371a3bf4143e_add_user_eq_phone_option_to_pjsip.py
index ea2b291..62e9668 100644
--- a/contrib/ast-db-manage/config/versions/371a3bf4143e_add_user_eq_phone_option_to_pjsip.py
+++ b/contrib/ast-db-manage/config/versions/371a3bf4143e_add_user_eq_phone_option_to_pjsip.py
@@ -27,4 +27,6 @@ def upgrade():
     op.add_column('ps_endpoints', sa.Column('user_eq_phone', yesno_values))
 
 def downgrade():
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_endpoints_user_eq_phone_yesno_values','ps_endpoints')
     op.drop_column('ps_endpoints', 'user_eq_phone')
diff --git a/contrib/ast-db-manage/config/versions/3772f8f828da_update_identify_by.py b/contrib/ast-db-manage/config/versions/3772f8f828da_update_identify_by.py
index 92695b0..3c80f48 100644
--- a/contrib/ast-db-manage/config/versions/3772f8f828da_update_identify_by.py
+++ b/contrib/ast-db-manage/config/versions/3772f8f828da_update_identify_by.py
@@ -16,6 +16,8 @@ import sqlalchemy as sa
 
 def enum_update(table_name, column_name, enum_name, enum_values):
     if op.get_context().bind.dialect.name != 'postgresql':
+        if op.get_context().bind.dialect.name == 'mssql':
+            op.drop_constraint('ck_ps_endpoints_identify_by_pjsip_identify_by_values', 'ps_endpoints')
         op.alter_column(table_name, column_name,
                         type_=sa.Enum(*enum_values, name=enum_name))
         return
diff --git a/contrib/ast-db-manage/config/versions/3bcc0b5bc2c9_add_allow_reload_to_ps_transports.py b/contrib/ast-db-manage/config/versions/3bcc0b5bc2c9_add_allow_reload_to_ps_transports.py
index 0becc1e..093a5d4 100644
--- a/contrib/ast-db-manage/config/versions/3bcc0b5bc2c9_add_allow_reload_to_ps_transports.py
+++ b/contrib/ast-db-manage/config/versions/3bcc0b5bc2c9_add_allow_reload_to_ps_transports.py
@@ -22,4 +22,6 @@ def upgrade():
     op.add_column('ps_transports', sa.Column('allow_reload', yesno_values))
 
 def downgrade():
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_transports_allow_reload_yesno_values','ps_transports')
     op.drop_column('ps_transports', 'allow_reload')
diff --git a/contrib/ast-db-manage/config/versions/4468b4a91372_add_pjsip_asymmetric_rtp_codec.py b/contrib/ast-db-manage/config/versions/4468b4a91372_add_pjsip_asymmetric_rtp_codec.py
index c121495..1a8ede6 100644
--- a/contrib/ast-db-manage/config/versions/4468b4a91372_add_pjsip_asymmetric_rtp_codec.py
+++ b/contrib/ast-db-manage/config/versions/4468b4a91372_add_pjsip_asymmetric_rtp_codec.py
@@ -28,4 +28,6 @@ def upgrade():
 
 
 def downgrade():
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_endpoints_asymmetric_rtp_codec_yesno_values','ps_endpoints')
     op.drop_column('ps_endpoints', 'asymmetric_rtp_codec')
diff --git a/contrib/ast-db-manage/config/versions/4c573e7135bd_fix_tos_field_types.py b/contrib/ast-db-manage/config/versions/4c573e7135bd_fix_tos_field_types.py
index 27b498f..f99e2ea 100644
--- a/contrib/ast-db-manage/config/versions/4c573e7135bd_fix_tos_field_types.py
+++ b/contrib/ast-db-manage/config/versions/4c573e7135bd_fix_tos_field_types.py
@@ -29,7 +29,7 @@ def upgrade():
     op.alter_column('ps_transports', 'tos', type_=sa.String(10))
 
     # Can't cast YENO_VALUES to Integers, so dropping and adding is required
-    op.drop_column('ps_transports', 'cos')
+    op.drop_column('ps_transports', 'cos', schema=None, mssql_drop_check=True)
     op.add_column('ps_transports', sa.Column('cos', sa.Integer))
 
 def downgrade():
@@ -46,6 +46,8 @@ def downgrade():
     op.add_column('ps_endpoints', sa.Column('cos_audio', yesno_values))
     op.add_column('ps_endpoints', sa.Column('cos_video', yesno_values))
 
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_transports_tos_yesno_values', 'ps_transports')
     op.drop_column('ps_transports', 'tos')
     op.add_column('ps_transports', sa.Column('tos', yesno_values))
     # Can't cast integers to YESNO_VALUES, so dropping and adding is required
diff --git a/contrib/ast-db-manage/config/versions/5139253c0423_make_q_member_uniqueid_autoinc.py b/contrib/ast-db-manage/config/versions/5139253c0423_make_q_member_uniqueid_autoinc.py
index 01d4985..3b329b5 100644
--- a/contrib/ast-db-manage/config/versions/5139253c0423_make_q_member_uniqueid_autoinc.py
+++ b/contrib/ast-db-manage/config/versions/5139253c0423_make_q_member_uniqueid_autoinc.py
@@ -39,7 +39,7 @@ def upgrade():
     op.drop_column('queue_members', 'uniqueid')
     op.add_column('queue_members', sa.Column(name='uniqueid', type_=sa.Integer,
                                              nullable=False, unique=True))
-    # The postgres backend does not like the autoincrement needed for
+    # The postgres and mssql backends do not like the autoincrement needed for
     # mysql here.  It is just the backend that is giving a warning and
     # not the database itself.
     op.alter_column(table_name='queue_members', column_name='uniqueid',
@@ -50,5 +50,7 @@ def upgrade():
 def downgrade():
     # Was unable to find a way to use op.alter_column() to remove the
     # unique index property.
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('uq_queue_members_uniqueid', 'queue_members')
     op.drop_column('queue_members', 'uniqueid')
     op.add_column('queue_members', sa.Column(name='uniqueid', type_=sa.String(80), nullable=False))
diff --git a/contrib/ast-db-manage/config/versions/51f8cb66540e_add_further_dtls_options.py b/contrib/ast-db-manage/config/versions/51f8cb66540e_add_further_dtls_options.py
index c2dacda..2eea713 100644
--- a/contrib/ast-db-manage/config/versions/51f8cb66540e_add_further_dtls_options.py
+++ b/contrib/ast-db-manage/config/versions/51f8cb66540e_add_further_dtls_options.py
@@ -28,5 +28,8 @@ def upgrade():
     op.add_column('ps_endpoints', sa.Column('media_use_received_transport', yesno_values))
 
 def downgrade():
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_endpoints_force_avp_yesno_values', 'ps_endpoints')
+        op.drop_constraint('ck_ps_endpoints_media_use_received_transport_yesno_values', 'ps_endpoints')
     op.drop_column('ps_endpoints', 'force_avp')
     op.drop_column('ps_endpoints', 'media_use_received_transport')
diff --git a/contrib/ast-db-manage/config/versions/5950038a6ead_fix_pjsip_verifiy_typo.py b/contrib/ast-db-manage/config/versions/5950038a6ead_fix_pjsip_verifiy_typo.py
index 28ebc8b..a1154a3 100644
--- a/contrib/ast-db-manage/config/versions/5950038a6ead_fix_pjsip_verifiy_typo.py
+++ b/contrib/ast-db-manage/config/versions/5950038a6ead_fix_pjsip_verifiy_typo.py
@@ -11,19 +11,32 @@ revision = '5950038a6ead'
 down_revision = 'd39508cb8d8'
 
 from alembic import op
+import sqlalchemy as sa
 from sqlalchemy.dialects.postgresql import ENUM
 
 YESNO_NAME = 'yesno_values'
 YESNO_VALUES = ['yes', 'no']
 
-
 def upgrade():
     yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
-    op.alter_column('ps_transports', 'verifiy_server', type_=yesno_values,
-                    new_column_name='verify_server')
+
+    if op.get_context().bind.dialect.name != 'mssql':
+        op.alter_column('ps_transports', 'verifiy_server', type_=yesno_values,
+                        new_column_name='verify_server')
+    else:
+        op.alter_column('ps_transports', 'verifiy_server', existing_type=yesno_values, type_=sa.String(3),
+                        new_column_name='verify_server')
+        yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=True)
+        op.alter_column('ps_transports', 'verify_server', existing_type=sa.String(3), type_=yesno_values)
 
 
 def downgrade():
     yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
-    op.alter_column('ps_transports', 'verify_server', type_=yesno_values,
-                    new_column_name='verifiy_server')
+    if op.get_context().bind.dialect.name != 'mssql':
+        op.alter_column('ps_transports', 'verify_server', type_=yesno_values,
+                        new_column_name='verifiy_server')
+    else:
+        op.alter_column('ps_transports', 'verify_server', existing_type=yesno_values, type_=sa.String(3),
+                        new_column_name='verifiy_server')
+        yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=True)
+        op.alter_column('ps_transports', 'verifiy_server', existing_type=sa.String(3), type_=yesno_values)
diff --git a/contrib/ast-db-manage/config/versions/837aa67461fb_ps_contacts_add_authenticate_qualify.py b/contrib/ast-db-manage/config/versions/837aa67461fb_ps_contacts_add_authenticate_qualify.py
index 76faf39..033a999 100644
--- a/contrib/ast-db-manage/config/versions/837aa67461fb_ps_contacts_add_authenticate_qualify.py
+++ b/contrib/ast-db-manage/config/versions/837aa67461fb_ps_contacts_add_authenticate_qualify.py
@@ -28,5 +28,7 @@ def upgrade():
 
 
 def downgrade():
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_contacts_authenticate_qualify_yesno_values','ps_contacts')
     op.drop_column('ps_contacts', 'authenticate_qualify')
 
diff --git a/contrib/ast-db-manage/config/versions/86bb1efa278d_add_ps_endpoints_refer_blind_progress.py b/contrib/ast-db-manage/config/versions/86bb1efa278d_add_ps_endpoints_refer_blind_progress.py
index 9b0f6d4..a46ed0c 100644
--- a/contrib/ast-db-manage/config/versions/86bb1efa278d_add_ps_endpoints_refer_blind_progress.py
+++ b/contrib/ast-db-manage/config/versions/86bb1efa278d_add_ps_endpoints_refer_blind_progress.py
@@ -27,4 +27,6 @@ def upgrade():
     op.add_column('ps_endpoints', sa.Column('refer_blind_progress', yesno_values))
 
 def downgrade():
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_endpoints_refer_blind_progress_yesno_values','ps_endpoints')
     op.drop_column('ps_endpoints', 'refer_blind_progress')
diff --git a/contrib/ast-db-manage/config/versions/8d478ab86e29_pjsip_add_disable_multi_domain.py b/contrib/ast-db-manage/config/versions/8d478ab86e29_pjsip_add_disable_multi_domain.py
index a782685..a8d7b48 100644
--- a/contrib/ast-db-manage/config/versions/8d478ab86e29_pjsip_add_disable_multi_domain.py
+++ b/contrib/ast-db-manage/config/versions/8d478ab86e29_pjsip_add_disable_multi_domain.py
@@ -28,4 +28,6 @@ def upgrade():
 
 
 def downgrade():
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_globals_disable_multi_domain_yesno_values','ps_globals')
     op.drop_column('ps_globals', 'disable_multi_domain')
diff --git a/contrib/ast-db-manage/config/versions/8fce4c573e15_add_pjsip_allow_overlap.py b/contrib/ast-db-manage/config/versions/8fce4c573e15_add_pjsip_allow_overlap.py
index 24057ec..539944e 100644
--- a/contrib/ast-db-manage/config/versions/8fce4c573e15_add_pjsip_allow_overlap.py
+++ b/contrib/ast-db-manage/config/versions/8fce4c573e15_add_pjsip_allow_overlap.py
@@ -28,4 +28,6 @@ def upgrade():
 
 
 def downgrade():
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_endpoints_allow_overlap_yesno_values','ps_endpoints')
     op.drop_column('ps_endpoints', 'allow_overlap')
diff --git a/contrib/ast-db-manage/config/versions/a1698e8bb9c5_add_incoming_mwi_mailbox.py b/contrib/ast-db-manage/config/versions/a1698e8bb9c5_add_incoming_mwi_mailbox.py
new file mode 100644
index 0000000..86c307e
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/a1698e8bb9c5_add_incoming_mwi_mailbox.py
@@ -0,0 +1,21 @@
+"""add_incoming_mwi_mailbox
+
+Revision ID: a1698e8bb9c5
+Revises: b83645976fdd
+Create Date: 2017-09-08 13:45:18.937571
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'a1698e8bb9c5'
+down_revision = 'b83645976fdd'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.add_column('ps_endpoints', sa.Column('incoming_mwi_mailbox', sa.String(40)))
+
+def downgrade():
+    op.drop_column('ps_endpoints', 'incoming_mwi_mailbox')
diff --git a/contrib/ast-db-manage/config/versions/a6ef36f1309_ps_globals_add_ignore_uri_user_options.py b/contrib/ast-db-manage/config/versions/a6ef36f1309_ps_globals_add_ignore_uri_user_options.py
index 2ce40a0..4daa422 100644
--- a/contrib/ast-db-manage/config/versions/a6ef36f1309_ps_globals_add_ignore_uri_user_options.py
+++ b/contrib/ast-db-manage/config/versions/a6ef36f1309_ps_globals_add_ignore_uri_user_options.py
@@ -28,5 +28,7 @@ def upgrade():
 
 
 def downgrade():
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_globals_ignore_uri_user_options_yesno_values','ps_globals')
     op.drop_column('ps_globals', 'ignore_uri_user_options')
 
diff --git a/contrib/ast-db-manage/config/versions/b83645976fdd_add_dtls_fingerprint_to_ps_endpoints.py b/contrib/ast-db-manage/config/versions/b83645976fdd_add_dtls_fingerprint_to_ps_endpoints.py
new file mode 100644
index 0000000..d957104
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/b83645976fdd_add_dtls_fingerprint_to_ps_endpoints.py
@@ -0,0 +1,40 @@
+"""add dtls_fingerprint to ps_endpoints
+
+Revision ID: b83645976fdd
+Revises: f3d1c5d38b56
+Create Date: 2017-08-03 09:01:49.558111
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'b83645976fdd'
+down_revision = 'f3d1c5d38b56'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
+
+SHA_HASH_NAME = 'sha_hash_values'
+SHA_HASH_VALUES = ['SHA-1', 'SHA-256']
+
+def upgrade():
+    context = op.get_context()
+
+    if context.bind.dialect.name == 'postgresql':
+        enum = ENUM(*SHA_HASH_VALUES, name=SHA_HASH_NAME)
+        enum.create(op.get_bind(), checkfirst=False)
+
+    op.add_column('ps_endpoints',
+             sa.Column('dtls_fingerprint', ENUM(*SHA_HASH_VALUES,
+                 name=SHA_HASH_NAME, create_type=False)))
+
+def downgrade():
+    context = op.get_context()
+
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_endpoints_dtls_fingerprint_sha_hash_values', 'ps_endpoints')
+    op.drop_column('ps_endpoints', 'dtls_fingerprint')
+
+    if context.bind.dialect.name == 'postgresql':
+        enum = ENUM(*SHA_HASH_VALUES, name=SHA_HASH_NAME)
+        enum.drop(op.get_bind(), checkfirst=False)
diff --git a/contrib/ast-db-manage/config/versions/c7a44a5a0851_pjsip_add_global_mwi_options.py b/contrib/ast-db-manage/config/versions/c7a44a5a0851_pjsip_add_global_mwi_options.py
index d3efa22..c762fc4 100644
--- a/contrib/ast-db-manage/config/versions/c7a44a5a0851_pjsip_add_global_mwi_options.py
+++ b/contrib/ast-db-manage/config/versions/c7a44a5a0851_pjsip_add_global_mwi_options.py
@@ -32,4 +32,6 @@ def upgrade():
 def downgrade():
     op.drop_column('ps_globals', 'mwi_tps_queue_high')
     op.drop_column('ps_globals', 'mwi_tps_queue_low')
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_globals_mwi_disable_initial_unsolicited_yesno_values','ps_globals')
     op.drop_column('ps_globals', 'mwi_disable_initial_unsolicited')
diff --git a/contrib/ast-db-manage/config/versions/d7983954dd96_add_ps_endpoints_notify_early_inuse_.py b/contrib/ast-db-manage/config/versions/d7983954dd96_add_ps_endpoints_notify_early_inuse_.py
index e1dcdd1..a5419a1 100644
--- a/contrib/ast-db-manage/config/versions/d7983954dd96_add_ps_endpoints_notify_early_inuse_.py
+++ b/contrib/ast-db-manage/config/versions/d7983954dd96_add_ps_endpoints_notify_early_inuse_.py
@@ -27,4 +27,6 @@ def upgrade():
     op.add_column('ps_endpoints', sa.Column('notify_early_inuse_ringing', yesno_values))
 
 def downgrade():
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_endpoints_notify_early_inuse_ringing_yesno_values', 'ps_endpoints')
     op.drop_column('ps_endpoints', 'notify_early_inuse_ringing')
diff --git a/contrib/ast-db-manage/config/versions/dbc44d5a908_add_missing_columns_to_sys_and_reg.py b/contrib/ast-db-manage/config/versions/dbc44d5a908_add_missing_columns_to_sys_and_reg.py
index 8aa16f1..9acd4a0 100644
--- a/contrib/ast-db-manage/config/versions/dbc44d5a908_add_missing_columns_to_sys_and_reg.py
+++ b/contrib/ast-db-manage/config/versions/dbc44d5a908_add_missing_columns_to_sys_and_reg.py
@@ -29,6 +29,9 @@ def upgrade():
     op.add_column('ps_registrations', sa.Column('endpoint', sa.String(40)))
 
 def downgrade():
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_systems_disable_tcp_switch_yesno_values','ps_systems')
+        op.drop_constraint('ck_ps_registrations_line_yesno_values','ps_registrations')
     op.drop_column('ps_systems', 'disable_tcp_switch')
     op.drop_column('ps_registrations', 'line')
     op.drop_column('ps_registrations', 'endpoint')
diff --git a/contrib/ast-db-manage/config/versions/e96a0b8071c_increase_pjsip_column_size.py b/contrib/ast-db-manage/config/versions/e96a0b8071c_increase_pjsip_column_size.py
index 9c00c01..da103b1 100644
--- a/contrib/ast-db-manage/config/versions/e96a0b8071c_increase_pjsip_column_size.py
+++ b/contrib/ast-db-manage/config/versions/e96a0b8071c_increase_pjsip_column_size.py
@@ -28,7 +28,13 @@ def upgrade():
 def downgrade():
     op.alter_column('ps_globals', 'user_agent', type_=sa.String(40))
 
-    op.alter_column('ps_contacts', 'id', type_=sa.String(40))
+    if op.get_context().bind.dialect.name != 'mssql':
+        op.alter_column('ps_contacts', 'id', type_=sa.String(40))
+    else:
+        op.drop_constraint('uq_ps_contacts_id', 'ps_contacts')
+        op.drop_index('ps_contacts_id', 'ps_contacts')
+        op.alter_column('ps_contacts', 'id', type_=sa.String(40))
+        op.create_index('ps_contacts_id', 'ps_contacts', ['id'])
     op.alter_column('ps_contacts', 'uri', type_=sa.String(40))
     op.alter_column('ps_contacts', 'user_agent', type_=sa.String(40))
 
diff --git a/contrib/ast-db-manage/config/versions/eb88a14f2a_add_media_encryption_optimistic_to_pjsip.py b/contrib/ast-db-manage/config/versions/eb88a14f2a_add_media_encryption_optimistic_to_pjsip.py
index 2d96b37..6653aa6 100644
--- a/contrib/ast-db-manage/config/versions/eb88a14f2a_add_media_encryption_optimistic_to_pjsip.py
+++ b/contrib/ast-db-manage/config/versions/eb88a14f2a_add_media_encryption_optimistic_to_pjsip.py
@@ -28,4 +28,6 @@ def upgrade():
 
 
 def downgrade():
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_endpoints_media_encryption_optimistic_yesno_values','ps_endpoints')
     op.drop_column('ps_endpoints', 'media_encryption_optimistic')
diff --git a/contrib/ast-db-manage/config/versions/ef7efc2d3964_ps_contacts_add_endpoint_and_modify_.py b/contrib/ast-db-manage/config/versions/ef7efc2d3964_ps_contacts_add_endpoint_and_modify_.py
index 43d4028..0ac63c8 100644
--- a/contrib/ast-db-manage/config/versions/ef7efc2d3964_ps_contacts_add_endpoint_and_modify_.py
+++ b/contrib/ast-db-manage/config/versions/ef7efc2d3964_ps_contacts_add_endpoint_and_modify_.py
@@ -27,7 +27,11 @@ def upgrade():
     op.create_index('ps_contacts_qualifyfreq_exp', 'ps_contacts', ['qualify_frequency', 'expiration_time'])
     op.create_index('ps_aors_qualifyfreq_contact', 'ps_aors', ['qualify_frequency', 'contact'])
 def downgrade():
-    op.drop_index('ps_aors_qualifyfreq_contact')
-    op.drop_index('ps_contacts_qualifyfreq_exp')
+    if op.get_context().bind.dialect.name != 'mssql':
+        op.drop_index('ps_aors_qualifyfreq_contact')
+        op.drop_index('ps_contacts_qualifyfreq_exp')
+    else:
+        op.drop_index('ps_aors_qualifyfreq_contact', table_name='ps_aors')
+        op.drop_index('ps_contacts_qualifyfreq_exp', table_name='ps_contacts')
     op.drop_column('ps_contacts', 'endpoint')
     op.alter_column('ps_contacts', 'expiration_time', type_=sa.String(40))
diff --git a/contrib/ast-db-manage/config/versions/28ab27a7826d_add_srv_lookups_to_identify.py b/contrib/ast-db-manage/config/versions/f3d1c5d38b56_add_prune_on_boot.py
similarity index 55%
copy from contrib/ast-db-manage/config/versions/28ab27a7826d_add_srv_lookups_to_identify.py
copy to contrib/ast-db-manage/config/versions/f3d1c5d38b56_add_prune_on_boot.py
index 8831e20..83baf5b 100644
--- a/contrib/ast-db-manage/config/versions/28ab27a7826d_add_srv_lookups_to_identify.py
+++ b/contrib/ast-db-manage/config/versions/f3d1c5d38b56_add_prune_on_boot.py
@@ -1,14 +1,14 @@
-"""add srv_lookups to identify
+"""add_prune_on_boot
 
-Revision ID: 28ab27a7826d
-Revises: 4468b4a91372
-Create Date: 2017-01-06 14:53:38.829655
+Revision ID: f3d1c5d38b56
+Revises: 164abbd708c
+Create Date: 2017-08-04 17:31:23.124767
 
 """
 
 # revision identifiers, used by Alembic.
-revision = '28ab27a7826d'
-down_revision = '4468b4a91372'
+revision = 'f3d1c5d38b56'
+down_revision = '164abbd708c'
 
 from alembic import op
 import sqlalchemy as sa
@@ -24,8 +24,10 @@ def upgrade():
     # type to get around "already created" issue - works okay with mysql
     yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
 
-    op.add_column('ps_endpoint_id_ips', sa.Column('srv_lookups', yesno_values))
+    op.add_column('ps_contacts', sa.Column('prune_on_boot', yesno_values))
 
 
 def downgrade():
-    op.drop_column('ps_endpoint_id_ips', 'srv_lookups')
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_contacts_prune_on_boot_yesno_values', 'ps_contacts')
+    op.drop_column('ps_contacts', 'prune_on_boot')
diff --git a/contrib/ast-db-manage/config/versions/f638dbe2eb23_symmetric_transport.py b/contrib/ast-db-manage/config/versions/f638dbe2eb23_symmetric_transport.py
index 51b5066..0585aa5 100644
--- a/contrib/ast-db-manage/config/versions/f638dbe2eb23_symmetric_transport.py
+++ b/contrib/ast-db-manage/config/versions/f638dbe2eb23_symmetric_transport.py
@@ -29,4 +29,6 @@ def upgrade():
 
 def downgrade():
     op.drop_column('ps_subscription_persistence', 'contact_uri')
+    if op.get_context().bind.dialect.name == 'mssql':
+        op.drop_constraint('ck_ps_transports_symmetric_transport_yesno_values','ps_transports')
     op.drop_column('ps_transports', 'symmetric_transport')
diff --git a/contrib/ast-db-manage/env.py b/contrib/ast-db-manage/env.py
index a903451..1864239 100644
--- a/contrib/ast-db-manage/env.py
+++ b/contrib/ast-db-manage/env.py
@@ -2,7 +2,8 @@ from __future__ import with_statement
 from alembic import context
 from alembic.script import ScriptDirectory
 from alembic.operations import Operations
-from sqlalchemy import engine_from_config, pool
+from sqlalchemy import engine_from_config, pool, MetaData
+from sqlalchemy.ext.declarative import declarative_base
 from logging.config import fileConfig
 import logging
 
@@ -17,12 +18,24 @@ try:
 except:
     pass
 
+## below block is needed for mssql
+meta = MetaData(naming_convention = {
+	"ix": 'ix_%(column_0_label)s',
+	"uq": "uq_%(table_name)s_%(column_0_name)s",
+	"ck": "ck_%(table_name)s_%(column_0_name)s_%(constraint_name)s",
+	"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
+	"pk": "pk_%(table_name)s"
+})
+Base = declarative_base(metadata=meta)
+
 logger = logging.getLogger('alembic.runtime.setup')
 # add your model's MetaData object here
 # for 'autogenerate' support
 # from myapp import mymodel
 # target_metadata = mymodel.Base.metadata
 target_metadata = None
+#Comment above line and uncomment below line for mssql
+#target_metadata = Base.metadata
 
 # other values from the config, defined by the needs of env.py,
 # can be acquired:
@@ -42,7 +55,7 @@ def run_migrations_offline():
 
     """
     url = config.get_main_option("sqlalchemy.url")
-    context.configure(url=url)
+    context.configure(url=url,target_metadata=target_metadata)
 
     with context.begin_transaction():
         context.run_migrations()
diff --git a/contrib/realtime/mssql/mssql_config.sql b/contrib/realtime/mssql/mssql_config.sql
index 12f6aaf..21b28a3 100644
--- a/contrib/realtime/mssql/mssql_config.sql
+++ b/contrib/realtime/mssql/mssql_config.sql
@@ -847,6 +847,14 @@ ALTER TABLE ps_transports ALTER COLUMN tos VARCHAR(10);
 
 GO
 
+declare @const_name varchar(256)
+select @const_name = [name] from sys.check_constraints
+where parent_object_id = object_id('ps_transports')
+and col_name(parent_object_id, parent_column_id) = 'cos'
+exec('alter table ps_transports drop constraint ' + @const_name);
+
+GO
+
 ALTER TABLE ps_transports DROP COLUMN cos;
 
 GO
@@ -1007,6 +1015,10 @@ GO
 
 -- Running upgrade d39508cb8d8 -> 5950038a6ead
 
+ALTER TABLE ps_transports DROP CONSTRAINT yesno_values;
+
+GO
+
 ALTER TABLE ps_transports ALTER COLUMN verifiy_server VARCHAR(3);
 
 GO
@@ -1015,7 +1027,11 @@ EXEC sp_rename 'ps_transports.verifiy_server', verify_server, 'COLUMN';
 
 GO
 
-ALTER TABLE ps_transports ADD CONSTRAINT yesno_values CHECK (verifiy_server IN ('yes', 'no'));
+ALTER TABLE ps_transports ALTER COLUMN verify_server VARCHAR(3);
+
+GO
+
+ALTER TABLE ps_transports ADD CONSTRAINT yesno_values CHECK (verify_server IN ('yes', 'no'));
 
 GO
 
@@ -1501,6 +1517,10 @@ GO
 
 -- Running upgrade c7a44a5a0851 -> 3772f8f828da
 
+ALTER TABLE ps_endpoints DROP CONSTRAINT ck_ps_endpoints_identify_by_pjsip_identify_by_values;
+
+GO
+
 ALTER TABLE ps_endpoints ALTER COLUMN identify_by VARCHAR(13);
 
 GO
@@ -1753,6 +1773,44 @@ UPDATE alembic_version SET version_num='164abbd708c' WHERE alembic_version.versi
 
 GO
 
+-- Running upgrade 164abbd708c -> f3d1c5d38b56
+
+ALTER TABLE ps_contacts ADD prune_on_boot VARCHAR(3) NULL;
+
+GO
+
+ALTER TABLE ps_contacts ADD CONSTRAINT yesno_values CHECK (prune_on_boot IN ('yes', 'no'));
+
+GO
+
+UPDATE alembic_version SET version_num='f3d1c5d38b56' WHERE alembic_version.version_num = '164abbd708c';
+
+GO
+
+-- Running upgrade f3d1c5d38b56 -> b83645976fdd
+
+ALTER TABLE ps_endpoints ADD dtls_fingerprint VARCHAR(7) NULL;
+
+GO
+
+ALTER TABLE ps_endpoints ADD CONSTRAINT sha_hash_values CHECK (dtls_fingerprint IN ('SHA-1', 'SHA-256'));
+
+GO
+
+UPDATE alembic_version SET version_num='b83645976fdd' WHERE alembic_version.version_num = 'f3d1c5d38b56';
+
+GO
+
+-- Running upgrade b83645976fdd -> a1698e8bb9c5
+
+ALTER TABLE ps_endpoints ADD incoming_mwi_mailbox VARCHAR(40) NULL;
+
+GO
+
+UPDATE alembic_version SET version_num='a1698e8bb9c5' WHERE alembic_version.version_num = 'b83645976fdd';
+
+GO
+
 COMMIT;
 
 GO
diff --git a/contrib/realtime/mysql/mysql_config.sql b/contrib/realtime/mysql/mysql_config.sql
index 896990d..e484c7b 100644
--- a/contrib/realtime/mysql/mysql_config.sql
+++ b/contrib/realtime/mysql/mysql_config.sql
@@ -1068,3 +1068,21 @@ ALTER TABLE ps_endpoints MODIFY dtmf_mode ENUM('rfc4733','inband','info','auto',
 
 UPDATE alembic_version SET version_num='164abbd708c' WHERE alembic_version.version_num = 'd7983954dd96';
 
+-- Running upgrade 164abbd708c -> f3d1c5d38b56
+
+ALTER TABLE ps_contacts ADD COLUMN prune_on_boot ENUM('yes','no');
+
+UPDATE alembic_version SET version_num='f3d1c5d38b56' WHERE alembic_version.version_num = '164abbd708c';
+
+-- Running upgrade f3d1c5d38b56 -> b83645976fdd
+
+ALTER TABLE ps_endpoints ADD COLUMN dtls_fingerprint ENUM('SHA-1','SHA-256');
+
+UPDATE alembic_version SET version_num='b83645976fdd' WHERE alembic_version.version_num = 'f3d1c5d38b56';
+
+-- Running upgrade b83645976fdd -> a1698e8bb9c5
+
+ALTER TABLE ps_endpoints ADD COLUMN incoming_mwi_mailbox VARCHAR(40);
+
+UPDATE alembic_version SET version_num='a1698e8bb9c5' WHERE alembic_version.version_num = 'b83645976fdd';
+
diff --git a/contrib/realtime/oracle/oracle_config.sql b/contrib/realtime/oracle/oracle_config.sql
index 6d734b3..4b3e06a 100644
--- a/contrib/realtime/oracle/oracle_config.sql
+++ b/contrib/realtime/oracle/oracle_config.sql
@@ -1751,3 +1751,41 @@ UPDATE alembic_version SET version_num='164abbd708c' WHERE alembic_version.versi
 
 /
 
+-- Running upgrade 164abbd708c -> f3d1c5d38b56
+
+ALTER TABLE ps_contacts ADD prune_on_boot VARCHAR(3 CHAR)
+
+/
+
+ALTER TABLE ps_contacts ADD CONSTRAINT yesno_values CHECK (prune_on_boot IN ('yes', 'no'))
+
+/
+
+UPDATE alembic_version SET version_num='f3d1c5d38b56' WHERE alembic_version.version_num = '164abbd708c'
+
+/
+
+-- Running upgrade f3d1c5d38b56 -> b83645976fdd
+
+ALTER TABLE ps_endpoints ADD dtls_fingerprint VARCHAR(7 CHAR)
+
+/
+
+ALTER TABLE ps_endpoints ADD CONSTRAINT sha_hash_values CHECK (dtls_fingerprint IN ('SHA-1', 'SHA-256'))
+
+/
+
+UPDATE alembic_version SET version_num='b83645976fdd' WHERE alembic_version.version_num = 'f3d1c5d38b56'
+
+/
+
+-- Running upgrade b83645976fdd -> a1698e8bb9c5
+
+ALTER TABLE ps_endpoints ADD incoming_mwi_mailbox VARCHAR2(40 CHAR)
+
+/
+
+UPDATE alembic_version SET version_num='a1698e8bb9c5' WHERE alembic_version.version_num = 'b83645976fdd'
+
+/
+
diff --git a/contrib/realtime/postgresql/postgresql_config.sql b/contrib/realtime/postgresql/postgresql_config.sql
index eaa5704..f757059 100644
--- a/contrib/realtime/postgresql/postgresql_config.sql
+++ b/contrib/realtime/postgresql/postgresql_config.sql
@@ -1148,5 +1148,25 @@ DROP TYPE pjsip_dtmf_mode_values_v2;
 
 UPDATE alembic_version SET version_num='164abbd708c' WHERE alembic_version.version_num = 'd7983954dd96';
 
+-- Running upgrade 164abbd708c -> f3d1c5d38b56
+
+ALTER TABLE ps_contacts ADD COLUMN prune_on_boot yesno_values;
+
+UPDATE alembic_version SET version_num='f3d1c5d38b56' WHERE alembic_version.version_num = '164abbd708c';
+
+-- Running upgrade f3d1c5d38b56 -> b83645976fdd
+
+CREATE TYPE sha_hash_values AS ENUM ('SHA-1', 'SHA-256');
+
+ALTER TABLE ps_endpoints ADD COLUMN dtls_fingerprint sha_hash_values;
+
+UPDATE alembic_version SET version_num='b83645976fdd' WHERE alembic_version.version_num = 'f3d1c5d38b56';
+
+-- Running upgrade b83645976fdd -> a1698e8bb9c5
+
+ALTER TABLE ps_endpoints ADD COLUMN incoming_mwi_mailbox VARCHAR(40);
+
+UPDATE alembic_version SET version_num='a1698e8bb9c5' WHERE alembic_version.version_num = 'b83645976fdd';
+
 COMMIT;
 
diff --git a/contrib/scripts/install_prereq b/contrib/scripts/install_prereq
index fb24089..d69f552 100755
--- a/contrib/scripts/install_prereq
+++ b/contrib/scripts/install_prereq
@@ -26,7 +26,7 @@ PACKAGES_DEBIAN="$PACKAGES_DEBIAN libncurses-dev libz-dev libssl-dev libxml2-dev
 PACKAGES_DEBIAN="$PACKAGES_DEBIAN libcurl-dev libspeex-dev libspeexdsp-dev libogg-dev libvorbis-dev libasound2-dev portaudio19-dev libcurl4-openssl-dev"
 PACKAGES_DEBIAN="$PACKAGES_DEBIAN libpq-dev unixodbc-dev libsqlite0-dev libmysqlclient15-dev libneon27-dev libgmime-dev libusb-dev liblua5.1-0-dev lua5.1"
 PACKAGES_DEBIAN="$PACKAGES_DEBIAN libopenh323-dev libvpb-dev libgtk2.0-dev libmysqlclient-dev libbluetooth-dev libradiusclient-ng-dev freetds-dev"
-PACKAGES_DEBIAN="$PACKAGES_DEBIAN libsnmp-dev libiksemel-dev libcorosync-dev libnewt-dev libpopt-dev libical-dev libspandsp-dev libjack-dev"
+PACKAGES_DEBIAN="$PACKAGES_DEBIAN libsnmp-dev libiksemel-dev libcorosync-dev libcpg-dev libcfg-dev libnewt-dev libpopt-dev libical-dev libspandsp-dev libjack-dev"
 PACKAGES_DEBIAN="$PACKAGES_DEBIAN libresample-dev libc-client-dev binutils-dev libsrtp-dev libgsm1-dev libedit-dev doxygen libjansson-dev libldap-dev"
 PACKAGES_DEBIAN="$PACKAGES_DEBIAN subversion git libxslt1-dev automake libsrtp-dev libncurses5-dev python-dev"
 PACKAGES_RH="automake bzip2 gcc gcc-c++ patch ncurses-devel openssl-devel libxml2-devel unixODBC-devel libcurl-devel libogg-devel libvorbis-devel speex-devel"
diff --git a/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py b/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py
index 98a5e95..eb3aab3 100755
--- a/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py
+++ b/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py
@@ -116,6 +116,27 @@ def set_dtmfmode(key, val, section, pjsip, nmapped):
         set_value(key, 'none', section, pjsip, nmapped)
 
 
+def setup_udptl(section, pjsip, nmapped):
+    """Sets values from udptl into the appropriate pjsip.conf options."""
+    try:
+        val = sip.get(section, 't38pt_udptl')[0]
+    except LookupError:
+        try:
+             val = sip.get('general', 't38pt_udptl')[0]
+        except LookupError:
+	     return
+
+    ec = 'none'
+    if 'yes' in val:
+        set_value('t38_udptl', 'yes', section, pjsip, nmapped)
+    if 'no' in val:
+        set_value('t38_udptl', 'no', section, pjsip, nmapped)
+    if 'redundancy' in val:
+        ec = 'redundancy'
+    if 'fec' in val:
+        ec = 'fec'
+    set_value('t38_udptl_ec', ec, section, pjsip, nmapped)
+
 def from_nat(key, val, section, pjsip, nmapped):
     """Sets values from nat into the appropriate pjsip.conf options."""
     # nat from sip.conf can be comma separated list of values:
@@ -387,6 +408,7 @@ peer_map = [
     ['allow',              merge_value],
     ['nat',                from_nat],            # rtp_symmetric, force_rport,
                                                  # rewrite_contact
+    ['rtptimeout',         set_value('rtp_timeout')],
     ['icesupport',         set_value('ice_support')],
     ['autoframing',        set_value('use_ptime')],
     ['outboundproxy',      set_value('outbound_proxy')],
@@ -1068,6 +1090,7 @@ def map_peer(sip, section, pjsip, nmapped):
         except LookupError:
             pass  # key not found in sip.conf
 
+    setup_udptl(section, pjsip, nmapped)
 
 def find_non_mapped(sections, nmapped):
     """
@@ -1101,6 +1124,13 @@ def map_system(sip, pjsip, nmapped):
     except LookupError:
         pass
 
+
+    try:
+        sipdebug = sip.get('general', 'sipdebug')[0]
+        set_value('debug', sipdebug, 'global', pjsip, nmapped, 'global')
+    except LookupError:
+        pass
+
     try:
         useroption_parsing = sip.get('general', 'legacy_useroption_parsing')[0]
         set_value('ignore_uri_user_options', useroption_parsing, 'global', pjsip, nmapped, 'global')
diff --git a/formats/format_g719.c b/formats/format_g719.c
index 6678585..e27822d 100644
--- a/formats/format_g719.c
+++ b/formats/format_g719.c
@@ -42,20 +42,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 static struct ast_frame *g719read(struct ast_filestream *s, int *whennext)
 {
-	int res;
-	/* Send a frame from the file to the appropriate channel */
+	size_t res;
 
+	/* Send a frame from the file to the appropriate channel */
 	AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE);
 	if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) != s->fr.datalen) {
-		if (feof(s->f)) {
-			if (res) {
-				ast_debug(3, "Incomplete frame data at end of %s file "
-						  "(expected %d bytes, read %d)\n",
-						  ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res);
-			}
-		} else {
-			ast_log(LOG_ERROR, "Error while reading %s file: %s\n",
-					ast_format_get_name(s->fr.subclass.format), strerror(errno));
+		if (res) {
+			ast_log(LOG_WARNING, "Short read of %s data (expected %d bytes, read %zu): %s\n",
+					ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res,
+					strerror(errno));
 		}
 		return NULL;
 	}
diff --git a/formats/format_g723.c b/formats/format_g723.c
index 1182124..9b77033 100644
--- a/formats/format_g723.c
+++ b/formats/format_g723.c
@@ -42,7 +42,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 static struct ast_frame *g723_read(struct ast_filestream *s, int *whennext)
 {
 	unsigned short size;
-	int res;
+	size_t res;
 	int delay;
 	/* Read the delay for the next packet, and schedule again if necessary */
 	/* XXX is this ignored ? */
@@ -67,15 +67,10 @@ static struct ast_frame *g723_read(struct ast_filestream *s, int *whennext)
 	/* Read the data into the buffer */
 	AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, size);
 	if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) != s->fr.datalen) {
-		if (feof(s->f)) {
-			if (res) {
-				ast_debug(3, "Incomplete frame data at end of %s file "
-						  "(expected %d bytes, read %d)\n",
-						  ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res);
-			}
-		} else {
-			ast_log(LOG_ERROR, "Error while reading %s file: %s\n",
-					ast_format_get_name(s->fr.subclass.format), strerror(errno));
+		if (res) {
+			ast_log(LOG_WARNING, "Short read of %s data (expected %d bytes, read %zu): %s\n",
+					ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res,
+					strerror(errno));
 		}
 		return NULL;
 	}
diff --git a/formats/format_g726.c b/formats/format_g726.c
index 94623f4..50b558e 100644
--- a/formats/format_g726.c
+++ b/formats/format_g726.c
@@ -119,22 +119,17 @@ static int g726_16_rewrite(struct ast_filestream *s, const char *comment)
 
 static struct ast_frame *g726_read(struct ast_filestream *s, int *whennext)
 {
-	int res;
+	size_t res;
 	struct g726_desc *fs = (struct g726_desc *)s->_private;
 
 	/* Send a frame from the file to the appropriate channel */
 	AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, frame_size[fs->rate]);
 	s->fr.samples = 8 * FRAME_TIME;
 	if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) != s->fr.datalen) {
-		if (feof(s->f)) {
-			if (res) {
-				ast_debug(3, "Incomplete frame data at end of %s file "
-						  "(expected %d bytes, read %d)\n",
-						  ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res);
-			}
-		} else {
-			ast_log(LOG_ERROR, "Error while reading %s file: %s\n",
-					ast_format_get_name(s->fr.subclass.format), strerror(errno));
+		if (res) {
+			ast_log(LOG_WARNING, "Short read of %s data (expected %d bytes, read %zu): %s\n",
+					ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res,
+					strerror(errno));
 		}
 		return NULL;
 	}
diff --git a/formats/format_g729.c b/formats/format_g729.c
index 8436446..35c68bd 100644
--- a/formats/format_g729.c
+++ b/formats/format_g729.c
@@ -48,20 +48,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 static struct ast_frame *g729_read(struct ast_filestream *s, int *whennext)
 {
-	int res;
+	size_t res;
+
 	/* Send a frame from the file to the appropriate channel */
 	s->fr.samples = G729A_SAMPLES;
 	AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE);
 	if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) != s->fr.datalen) {
-		if (feof(s->f)) {
-			if (res) {
-				ast_debug(3, "Incomplete frame data at end of %s file "
-						  "(expected %d bytes, read %d)\n",
-						  ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res);
-			}
-		} else {
-			ast_log(LOG_ERROR, "Error while reading %s file: %s\n",
-					ast_format_get_name(s->fr.subclass.format), strerror(errno));
+		if (res && res != 10) /* XXX what for ? */ {
+			ast_log(LOG_WARNING, "Short read of %s data (expected %d bytes, read %zu): %s\n",
+					ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res,
+					strerror(errno));
 		}
 		return NULL;
 	}
diff --git a/formats/format_gsm.c b/formats/format_gsm.c
index cfc9452..783d955 100644
--- a/formats/format_gsm.c
+++ b/formats/format_gsm.c
@@ -55,19 +55,14 @@ static const char gsm_silence[] = /* 33 */
 
 static struct ast_frame *gsm_read(struct ast_filestream *s, int *whennext)
 {
-	int res;
+	size_t res;
 
 	AST_FRAME_SET_BUFFER(&(s->fr), s->buf, AST_FRIENDLY_OFFSET, GSM_FRAME_SIZE);
 	if ((res = fread(s->fr.data.ptr, 1, GSM_FRAME_SIZE, s->f)) != GSM_FRAME_SIZE) {
-		if (feof(s->f)) {
-			if (res) {
-				ast_debug(3, "Incomplete frame data at end of %s file "
-						  "(expected %d bytes, read %d)\n",
-						  ast_format_get_name(s->fr.subclass.format), GSM_FRAME_SIZE, res);
-			}
-		} else {
-			ast_log(LOG_ERROR, "Error while reading %s file: %s\n",
-					ast_format_get_name(s->fr.subclass.format), strerror(errno));
+		if (res) {
+			ast_log(LOG_WARNING, "Short read of %s data (expected %d bytes, read %zu): %s\n",
+					ast_format_get_name(s->fr.subclass.format), GSM_FRAME_SIZE, res,
+					strerror(errno));
 		}
 		return NULL;
 	}
diff --git a/formats/format_h263.c b/formats/format_h263.c
index 5d59972..be8e1df 100644
--- a/formats/format_h263.c
+++ b/formats/format_h263.c
@@ -69,7 +69,7 @@ static int h263_open(struct ast_filestream *s)
 
 static struct ast_frame *h263_read(struct ast_filestream *s, int *whennext)
 {
-	int res;
+	size_t res;
 	uint32_t mark;
 	unsigned short len;
 	unsigned int ts;
@@ -87,15 +87,10 @@ static struct ast_frame *h263_read(struct ast_filestream *s, int *whennext)
 	}
 	AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, len);
 	if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) != s->fr.datalen) {
-		if (feof(s->f)) {
-			if (res) {
-				ast_debug(3, "Incomplete frame data at end of %s file "
-						  "(expected %d bytes, read %d)\n",
-						  ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res);
-			}
-		} else {
-			ast_log(LOG_ERROR, "Error while reading %s file: %s\n",
-					ast_format_get_name(s->fr.subclass.format), strerror(errno));
+		if (res) {
+			ast_log(LOG_WARNING, "Short read of %s data (expected %d bytes, read %zu): %s\n",
+					ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res,
+					strerror(errno));
 		}
 		return NULL;
 	}
diff --git a/formats/format_h264.c b/formats/format_h264.c
index f8906f4..3060400 100644
--- a/formats/format_h264.c
+++ b/formats/format_h264.c
@@ -61,7 +61,7 @@ static int h264_open(struct ast_filestream *s)
 
 static struct ast_frame *h264_read(struct ast_filestream *s, int *whennext)
 {
-	int res;
+	size_t res;
 	int mark = 0;
 	unsigned short len;
 	unsigned int ts;
@@ -79,15 +79,10 @@ static struct ast_frame *h264_read(struct ast_filestream *s, int *whennext)
 	}
 	AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, len);
 	if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) != s->fr.datalen) {
-		if (feof(s->f)) {
-			if (res) {
-				ast_debug(3, "Incomplete frame data at end of %s file "
-						  "(expected %d bytes, read %d)\n",
-						  ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res);
-			}
-		} else {
-			ast_log(LOG_ERROR, "Error while reading %s file: %s\n",
-					ast_format_get_name(s->fr.subclass.format), strerror(errno));
+		if (res) {
+			ast_log(LOG_WARNING, "Short read of %s data (expected %d bytes, read %zu): %s\n",
+					ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res,
+					strerror(errno));
 		}
 		return NULL;
 	}
diff --git a/formats/format_ilbc.c b/formats/format_ilbc.c
index 6e06ef3..d4fbe96 100644
--- a/formats/format_ilbc.c
+++ b/formats/format_ilbc.c
@@ -47,19 +47,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 static struct ast_frame *ilbc_read(struct ast_filestream *s, int *whennext)
 {
-	int res;
+	size_t res;
+
 	/* Send a frame from the file to the appropriate channel */
 	AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, ILBC_BUF_SIZE);
 	if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) != s->fr.datalen) {
-		if (feof(s->f)) {
-			if (res) {
-				ast_debug(3, "Incomplete frame data at end of %s file "
-						  "(expected %d bytes, read %d)\n",
-						  ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res);
-			}
-		} else {
-			ast_log(LOG_ERROR, "Error while reading %s file: %s\n",
-					ast_format_get_name(s->fr.subclass.format), strerror(errno));
+		if (res) {
+			ast_log(LOG_WARNING, "Short read of %s data (expected %d bytes, read %zu): %s\n",
+					ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res,
+					strerror(errno));
 		}
 		return NULL;
 	}
diff --git a/formats/format_pcm.c b/formats/format_pcm.c
index bd7cf77..7b06482 100644
--- a/formats/format_pcm.c
+++ b/formats/format_pcm.c
@@ -80,21 +80,15 @@ static int pcma_rewrite(struct ast_filestream *s, const char *comment)
 
 static struct ast_frame *pcm_read(struct ast_filestream *s, int *whennext)
 {
-	int res;
-	
-	/* Send a frame from the file to the appropriate channel */
+	size_t res;
 
+	/* Send a frame from the file to the appropriate channel */
 	AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE);
-	if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) != s->fr.datalen) {
-		if (feof(s->f)) {
-			if (res) {
-				ast_debug(3, "Incomplete frame data at end of %s file "
-						  "(expected %d bytes, read %d)\n",
-						  ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res);
-			}
-		} else {
-			ast_log(LOG_ERROR, "Error while reading %s file: %s\n",
-					ast_format_get_name(s->fr.subclass.format), strerror(errno));
+	if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) < 1) {
+		if (res) {
+			ast_log(LOG_WARNING, "Short read of %s data (expected %d bytes, read %zu): %s\n",
+					ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res,
+					strerror(errno));
 		}
 		return NULL;
 	}
diff --git a/formats/format_siren14.c b/formats/format_siren14.c
index 5aaa1f1..3e42bef 100644
--- a/formats/format_siren14.c
+++ b/formats/format_siren14.c
@@ -42,20 +42,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 static struct ast_frame *siren14read(struct ast_filestream *s, int *whennext)
 {
-	int res;
-	/* Send a frame from the file to the appropriate channel */
+	size_t res;
 
+	/* Send a frame from the file to the appropriate channel */
 	AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE);
 	if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) != s->fr.datalen) {
-		if (feof(s->f)) {
-			if (res) {
-				ast_debug(3, "Incomplete frame data at end of %s file "
-						  "(expected %d bytes, read %d)\n",
-						  ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res);
-			}
-		} else {
-			ast_log(LOG_ERROR, "Error while reading %s file: %s\n",
-					ast_format_get_name(s->fr.subclass.format), strerror(errno));
+		if (res) {
+			ast_log(LOG_WARNING, "Short read of %s data (expected %d bytes, read %zu): %s\n",
+					ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res,
+					strerror(errno));
 		}
 		return NULL;
 	}
diff --git a/formats/format_siren7.c b/formats/format_siren7.c
index 87e1372..f1bde00 100644
--- a/formats/format_siren7.c
+++ b/formats/format_siren7.c
@@ -42,20 +42,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 static struct ast_frame *siren7read(struct ast_filestream *s, int *whennext)
 {
-	int res;
-	/* Send a frame from the file to the appropriate channel */
+	size_t res;
 
+	/* Send a frame from the file to the appropriate channel */
 	AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE);
 	if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) != s->fr.datalen) {
-		if (feof(s->f)) {
-			if (res) {
-				ast_debug(3, "Incomplete frame data at end of %s file "
-						  "(expected %d bytes, read %d)\n",
-						  ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res);
-			}
-		} else {
-			ast_log(LOG_ERROR, "Error while reading %s file: %s\n",
-					ast_format_get_name(s->fr.subclass.format), strerror(errno));
+		if (res) {
+			ast_log(LOG_WARNING, "Short read of %s data (expected %d bytes, read %zu): %s\n",
+					ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res,
+					strerror(errno));
 		}
 		return NULL;
 	}
diff --git a/formats/format_sln.c b/formats/format_sln.c
index 2f4cc57..48bad8a 100644
--- a/formats/format_sln.c
+++ b/formats/format_sln.c
@@ -36,20 +36,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 static struct ast_frame *generic_read(struct ast_filestream *s, int *whennext, unsigned int buf_size)
 {
-	int res;
-	/* Send a frame from the file to the appropriate channel */
+	size_t res;
 
+	/* Send a frame from the file to the appropriate channel */
 	AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, buf_size);
-	if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) != s->fr.datalen) {
-		if (feof(s->f)) {
-			if (res) {
-				ast_debug(3, "Incomplete frame data at end of %s file "
-						  "(expected %d bytes, read %d)\n",
-						  ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res);
-			}
-		} else {
-			ast_log(LOG_ERROR, "Error while reading %s file: %s\n",
-					ast_format_get_name(s->fr.subclass.format), strerror(errno));
+	if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) < 1) {
+		if (res) {
+			ast_log(LOG_WARNING, "Short read of %s data (expected %d bytes, read %zu): %s\n",
+					ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res,
+					strerror(errno));
 		}
 		return NULL;
 	}
diff --git a/formats/format_vox.c b/formats/format_vox.c
index 26d4169..813dabf 100644
--- a/formats/format_vox.c
+++ b/formats/format_vox.c
@@ -42,20 +42,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 static struct ast_frame *vox_read(struct ast_filestream *s, int *whennext)
 {
-	int res;
+	size_t res;
 
 	/* Send a frame from the file to the appropriate channel */
 	AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE);
-	if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) != s->fr.datalen) {
-		if (feof(s->f)) {
-			if (res) {
-				ast_debug(3, "Incomplete frame data at end of %s file "
-						  "(expected %d bytes, read %d)\n",
-						  ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res);
-			}
-		} else {
-			ast_log(LOG_ERROR, "Error while reading %s file: %s\n",
-					ast_format_get_name(s->fr.subclass.format), strerror(errno));
+	if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) < 1) {
+		if (res) {
+			ast_log(LOG_WARNING, "Short read of %s data (expected %d bytes, read %zu): %s\n",
+					ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res,
+					strerror(errno));
 		}
 		return NULL;
 	}
diff --git a/formats/format_wav.c b/formats/format_wav.c
index 2903992..cead61c 100644
--- a/formats/format_wav.c
+++ b/formats/format_wav.c
@@ -371,7 +371,7 @@ static void wav_close(struct ast_filestream *s)
 
 static struct ast_frame *wav_read(struct ast_filestream *s, int *whennext)
 {
-	int res;
+	size_t res;
 	int samples;	/* actual samples read */
 #if __BYTE_ORDER == __BIG_ENDIAN
 	int x;
@@ -393,16 +393,11 @@ static struct ast_frame *wav_read(struct ast_filestream *s, int *whennext)
 /* 	ast_debug(1, "here: %d, maxlen: %d, bytes: %d\n", here, s->maxlen, bytes); */
 	AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, bytes);
 
-	if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) != s->fr.datalen) {
-		if (feof(s->f)) {
-			if (res) {
-				ast_debug(3, "Incomplete frame data at end of %s file "
-						  "(expected %d bytes, read %d)\n",
-						  ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res);
-			}
-		} else {
-			ast_log(LOG_ERROR, "Error while reading %s file: %s\n",
-					ast_format_get_name(s->fr.subclass.format), strerror(errno));
+	if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) == 0) {
+		if (res) {
+			ast_log(LOG_WARNING, "Short read of %s data (expected %d bytes, read %zu): %s\n",
+					ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res,
+					strerror(errno));
 		}
 		return NULL;
 	}
diff --git a/formats/format_wav_gsm.c b/formats/format_wav_gsm.c
index 2f80a9a..423dfe4 100644
--- a/formats/format_wav_gsm.c
+++ b/formats/format_wav_gsm.c
@@ -421,18 +421,13 @@ static struct ast_frame *wav_read(struct ast_filestream *s, int *whennext)
 	} else {
 		/* read and convert */
 		unsigned char msdata[MSGSM_FRAME_SIZE];
-		int res;
-		
+		size_t res;
+
 		if ((res = fread(msdata, 1, MSGSM_FRAME_SIZE, s->f)) != MSGSM_FRAME_SIZE) {
-			if (feof(s->f)) {
-				if (res) {
-					ast_debug(3, "Incomplete frame data at end of %s file "
-							  "(expected %d bytes, read %d)\n",
-							  ast_format_get_name(s->fr.subclass.format), MSGSM_FRAME_SIZE, res);
-				}
-			} else {
-				ast_log(LOG_ERROR, "Error while reading %s file: %s\n",
-						ast_format_get_name(s->fr.subclass.format), strerror(errno));
+			if (res && res != 1) {
+				ast_log(LOG_WARNING, "Short read of %s data (expected %d bytes, read %zu): %s\n",
+						ast_format_get_name(s->fr.subclass.format), MSGSM_FRAME_SIZE, res,
+						strerror(errno));
 			}
 			return NULL;
 		}
diff --git a/funcs/func_cdr.c b/funcs/func_cdr.c
index 76d468b..f704857 100644
--- a/funcs/func_cdr.c
+++ b/funcs/func_cdr.c
@@ -282,7 +282,7 @@ static void cdr_read_callback(void *data, struct stasis_subscription *sub, struc
 
 	if (ast_strlen_zero(ast_channel_name(payload->chan))) {
 		/* Format request on a dummy channel */
-		ast_cdr_format_var(ast_channel_cdr(payload->chan), args.variable, &value, tempbuf, sizeof(tempbuf), 0);
+		ast_cdr_format_var(ast_channel_cdr(payload->chan), args.variable, &value, tempbuf, sizeof(tempbuf), ast_test_flag(&flags, OPT_UNPARSED));
 		if (ast_strlen_zero(value)) {
 			return;
 		}
diff --git a/funcs/func_shell.c b/funcs/func_shell.c
index e403efc..79b7f99 100644
--- a/funcs/func_shell.c
+++ b/funcs/func_shell.c
@@ -84,6 +84,11 @@ static int shell_helper(struct ast_channel *chan, const char *cmd, char *data,
 		<syntax>
 			<parameter name="command" required="true">
 				<para>The command that the shell should execute.</para>
+				<warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
+				or <variable>CALLERID(name)</variable> as part of the command parameters.  You
+				risk a command injection attack executing arbitrary commands if the untrusted
+				strings aren't filtered to remove dangerous characters.  See function
+				<variable>FILTER()</variable>.</para></warning>
 			</parameter>
 		</syntax>
 		<description>
diff --git a/include/asterisk/app.h b/include/asterisk/app.h
index 86336e3..5b10b1c 100644
--- a/include/asterisk/app.h
+++ b/include/asterisk/app.h
@@ -871,9 +871,34 @@ int ast_vm_test_destroy_user(const char *context, const char *mailbox);
 int ast_vm_test_create_user(const char *context, const char *mailbox);
 #endif
 
-/*! \brief Safely spawn an external program while closing file descriptors
-	\note This replaces the \b system call in all Asterisk modules
-*/
+/*!
+ * \brief Safely spawn an external program while closing file descriptors
+ *
+ * \note This replaces the \b execvp call in all Asterisk modules
+ *
+ * \param dualfork Non-zero to simulate running the program in the
+ * background by forking twice.  The option provides similar
+ * functionality to the '&' in the OS shell command "cmd &".  The
+ * option allows Asterisk to run a reaper loop to watch the first fork
+ * which immediately exits after spaning the second fork.  The actual
+ * program is run in the second fork.
+ * \param file execvp(file, argv) file parameter
+ * \param argv execvp(file, argv) argv parameter
+ */
+int ast_safe_execvp(int dualfork, const char *file, char *const argv[]);
+
+/*!
+ * \brief Safely spawn an OS shell command while closing file descriptors
+ *
+ * \note This replaces the \b system call in all Asterisk modules
+ *
+ * \param s - OS shell command string to execute.
+ *
+ * \warning Command injection can happen using this call if the passed
+ * in string is created using untrusted data from an external source.
+ * It is best not to use untrusted data.  However, the caller could
+ * filter out dangerous characters to avoid command injection.
+ */
 int ast_safe_system(const char *s);
 
 /*!
diff --git a/include/asterisk/bridge_after.h b/include/asterisk/bridge_after.h
index 53f30b9..0451685 100644
--- a/include/asterisk/bridge_after.h
+++ b/include/asterisk/bridge_after.h
@@ -45,6 +45,8 @@ enum ast_bridge_after_cb_reason {
 	AST_BRIDGE_AFTER_CB_REASON_DEPART,
 	/*! Was explicitly removed by external code. */
 	AST_BRIDGE_AFTER_CB_REASON_REMOVED,
+	/*! The channel failed to enter the bridge. */
+	AST_BRIDGE_AFTER_CB_REASON_IMPART_FAILED,
 };
 
 /*!
diff --git a/include/asterisk/bridge_technology.h b/include/asterisk/bridge_technology.h
index 5add455..e4091e0 100644
--- a/include/asterisk/bridge_technology.h
+++ b/include/asterisk/bridge_technology.h
@@ -108,11 +108,13 @@ struct ast_bridge_technology {
 	 *
 	 * \note On entry, bridge is already locked.
 	 *
-	 * \note The bridge technology must tollerate a failed to join channel
+	 * \note The bridge technology must tolerate a failed to join channel
 	 * until it can be kicked from the bridge.
 	 *
 	 * \note A channel may be in a suspended state already when joining a bridge
 	 * technology. The technology must handle this case.
+	 *
+	 * \note A channel may not be answered when joining a bridge technology.
 	 */
 	int (*join)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
 	/*!
diff --git a/include/asterisk/calendar.h b/include/asterisk/calendar.h
index da4af01..57140ba 100644
--- a/include/asterisk/calendar.h
+++ b/include/asterisk/calendar.h
@@ -133,7 +133,7 @@ struct ast_calendar {
 	pthread_t thread;    /*!< The thread that the calendar is loaded/updated in */
 	ast_cond_t unload;
 	int unloading:1;
-	int pending_deletion:1;
+	int pending_deletion:1; /*!< No longer used */
 	struct ao2_container *events;  /*!< The events that are known at this time */
 };
 
diff --git a/include/asterisk/cdr.h b/include/asterisk/cdr.h
index 5654c38..91d4c01 100644
--- a/include/asterisk/cdr.h
+++ b/include/asterisk/cdr.h
@@ -217,19 +217,19 @@
 
 /*! \brief CDR engine settings */
 enum ast_cdr_settings {
-	CDR_ENABLED = 1 << 0,               /*< Enable CDRs */
-	CDR_BATCHMODE = 1 << 1,             /*< Whether or not we should dispatch CDRs in batches */
-	CDR_UNANSWERED = 1 << 2,            /*< Log unanswered CDRs */
-	CDR_CONGESTION = 1 << 3,            /*< Treat congestion as if it were a failed call */
-	CDR_END_BEFORE_H_EXTEN = 1 << 4,    /*< End the CDR before the 'h' extension runs */
-	CDR_INITIATED_SECONDS = 1 << 5,     /*< Include microseconds into the billing time */
-	CDR_DEBUG = 1 << 6,                 /*< Enables extra debug statements */
+	CDR_ENABLED = 1 << 0,               /*!< Enable CDRs */
+	CDR_BATCHMODE = 1 << 1,             /*!< Whether or not we should dispatch CDRs in batches */
+	CDR_UNANSWERED = 1 << 2,            /*!< Log unanswered CDRs */
+	CDR_CONGESTION = 1 << 3,            /*!< Treat congestion as if it were a failed call */
+	CDR_END_BEFORE_H_EXTEN = 1 << 4,    /*!< End the CDR before the 'h' extension runs */
+	CDR_INITIATED_SECONDS = 1 << 5,     /*!< Include microseconds into the billing time */
+	CDR_DEBUG = 1 << 6,                 /*!< Enables extra debug statements */
 };
 
 /*! \brief CDR Batch Mode settings */
 enum ast_cdr_batch_mode_settings {
-	BATCH_MODE_SCHEDULER_ONLY = 1 << 0, /*< Don't spawn a thread to handle the batches - do it on the scheduler */
-	BATCH_MODE_SAFE_SHUTDOWN = 1 << 1,  /*< During safe shutdown, submit the batched CDRs */
+	BATCH_MODE_SCHEDULER_ONLY = 1 << 0, /*!< Don't spawn a thread to handle the batches - do it on the scheduler */
+	BATCH_MODE_SAFE_SHUTDOWN = 1 << 1,  /*!< During safe shutdown, submit the batched CDRs */
 };
 
 /*!
@@ -237,14 +237,14 @@ enum ast_cdr_batch_mode_settings {
  * state of a CDR object based on these flags.
  */
 enum ast_cdr_options {
-	AST_CDR_FLAG_KEEP_VARS = (1 << 0),   /*< Copy variables during the operation */
-	AST_CDR_FLAG_DISABLE = (1 << 1),     /*< Disable the current CDR */
-	AST_CDR_FLAG_DISABLE_ALL = (3 << 1), /*< Disable the CDR and all future CDRs */
-	AST_CDR_FLAG_PARTY_A = (1 << 3),     /*< Set the channel as party A */
-	AST_CDR_FLAG_FINALIZE = (1 << 4),    /*< Finalize the current CDRs */
-	AST_CDR_FLAG_SET_ANSWER = (1 << 5),  /*< If the channel is answered, set the answer time to now */
-	AST_CDR_FLAG_RESET = (1 << 6),       /*< If set, set the start and answer time to now */
-	AST_CDR_LOCK_APP = (1 << 7),         /*< Prevent any further changes to the application field/data field for this CDR */
+	AST_CDR_FLAG_KEEP_VARS = (1 << 0),   /*!< Copy variables during the operation */
+	AST_CDR_FLAG_DISABLE = (1 << 1),     /*!< Disable the current CDR */
+	AST_CDR_FLAG_DISABLE_ALL = (3 << 1), /*!< Disable the CDR and all future CDRs */
+	AST_CDR_FLAG_PARTY_A = (1 << 3),     /*!< Set the channel as party A */
+	AST_CDR_FLAG_FINALIZE = (1 << 4),    /*!< Finalize the current CDRs */
+	AST_CDR_FLAG_SET_ANSWER = (1 << 5),  /*!< If the channel is answered, set the answer time to now */
+	AST_CDR_FLAG_RESET = (1 << 6),       /*!< If set, set the start and answer time to now */
+	AST_CDR_LOCK_APP = (1 << 7),         /*!< Prevent any further changes to the application field/data field for this CDR */
 };
 
 /*!
@@ -262,11 +262,11 @@ enum ast_cdr_disposition {
 
 /*! \brief The global options available for CDRs */
 struct ast_cdr_config {
-	struct ast_flags settings;			/*< CDR settings */
+	struct ast_flags settings;			/*!< CDR settings */
 	struct batch_settings {
-		unsigned int time;				/*< Time between batches */
-		unsigned int size;				/*< Size to trigger a batch */
-		struct ast_flags settings;		/*< Settings for batches */
+		unsigned int time;				/*!< Time between batches */
+		unsigned int size;				/*!< Size to trigger a batch */
+		struct ast_flags settings;		/*!< Settings for batches */
 	} batch_settings;
 };
 
@@ -312,7 +312,7 @@ struct ast_cdr {
 	unsigned int flags;
 	/*! Unique Channel Identifier */
 	char uniqueid[AST_MAX_UNIQUEID];
-	/* Linked group Identifier */
+	/*! Linked group Identifier */
 	char linkedid[AST_MAX_UNIQUEID];
 	/*! User field */
 	char userfield[AST_MAX_USER_FIELD];
diff --git a/include/asterisk/config.h b/include/asterisk/config.h
index 4dc473c..c61cee5 100644
--- a/include/asterisk/config.h
+++ b/include/asterisk/config.h
@@ -1086,6 +1086,11 @@ enum ast_parse_flags {
 	PARSE_UINT16	= 	0x0005,
 #endif
 
+	/* Returns an int processed by ast_app_parse_timelen.
+	 * The first argument is an enum ast_timelen value (required).
+	 */
+	PARSE_TIMELEN	=	0x0006,
+
 	/* Returns a struct ast_sockaddr, with optional default value
 	 * (passed by reference) and port handling (accept, ignore,
 	 * require, forbid). The format is 'ipaddress[:port]'. IPv6 address
@@ -1152,6 +1157,12 @@ enum ast_parse_flags {
  * returns 1, b unchanged
  *    ast_parse_arg("12", PARSE_UINT32|PARSE_IN_RANGE|PARSE_RANGE_DEFAULTS, &a, 1, 10);
  * returns 1, a = 10
+ *     ast_parse_arg("223", PARSE_TIMELEN|PARSE_IN_RANGE, &a, TIMELEN_SECONDS, -1000, 1000);
+ * returns 0, a = 1000
+ *     ast_parse_arg("223", PARSE_TIMELEN|PARSE_IN_RANGE, &a, TIMELEN_SECONDS, -1000, 250000);
+ * returns 0, a = 223000
+ *     ast_parse_arg("223", PARSE_TIMELEN|PARSE_IN_RANGE|PARSE_DEFAULT, &a, TIMELEN_SECONDS, 9999, -1000, 250000);
+ * returns 0, a = 9999
  *    ast_parse_arg("www.foo.biz:44", PARSE_INADDR, &sa);
  * returns 0, sa contains address and port
  *    ast_parse_arg("www.foo.biz", PARSE_INADDR|PARSE_PORT_REQUIRE, &sa);
diff --git a/include/asterisk/config_options.h b/include/asterisk/config_options.h
index 30d0c91..f4c3db1 100644
--- a/include/asterisk/config_options.h
+++ b/include/asterisk/config_options.h
@@ -467,6 +467,31 @@ enum aco_option_type {
 	 * {endcode}
 	 */
 	OPT_YESNO_T,
+
+	/*! \brief Type for default option handler for time length signed integers
+	 *
+	 * \note aco_option_register flags:
+	 *   See flags available for use with the PARSE_TIMELEN type for the ast_parse_arg function
+	 * aco_option_register varargs:
+	 *   FLDSET macro with the field of type int
+	 *   The remaining varargs for should be arguments compatible with the varargs for the
+	 *   ast_parse_arg function with the PARSE_TIMELEN type and the flags passed in the
+	 *   aco_option_register flags parameter.
+	 *
+	 * \note In most situations, it is preferable to not pass the PARSE_DEFAULT flag. If a config
+	 * contains an invalid value, it is better to let the config loading fail with warnings so that
+	 * the problem is fixed by the administrator.
+	 *
+	 * Example:
+	 * struct test_item {
+	 *     int timelen;
+	 * };
+	 * {code}
+	 * aco_option_register(&cfg_info, "timelen", ACO_EXACT, my_types, "3", OPT_TIMELEN_T, PARSE_IN_RANGE, FLDSET(struct test_item, intopt), TIMELEN_MILLISECONDS, -10, 10);
+	 * {endcode}
+	 */
+	OPT_TIMELEN_T,
+
 };
 
 /*! \brief A callback function for handling a particular option
diff --git a/include/asterisk/features_config.h b/include/asterisk/features_config.h
index baaff18..1bce50b 100644
--- a/include/asterisk/features_config.h
+++ b/include/asterisk/features_config.h
@@ -117,6 +117,21 @@ struct ast_features_xfer_config *ast_get_chan_features_xfer_config(struct ast_ch
 char *ast_get_chan_features_xferfailsound(struct ast_channel *chan);
 
 /*!
+ * \brief Get the transfer configuration option atxferabort
+ *
+ * \note The channel should be locked before calling this function.
+ * \note The returned value has to be freed.
+ *
+ * If no channel is provided, then option is pulled from the global
+ * transfer configuration.
+ *
+ * \param chan The channel to get configuration options for
+ * \retval NULL Failed to get configuration
+ * \retval non-NULL The atxferabort
+ */
+char *ast_get_chan_features_atxferabort(struct ast_channel *chan);
+
+/*!
  * \brief Configuration relating to call pickup
  */
 struct ast_features_pickup_config {
diff --git a/include/asterisk/format.h b/include/asterisk/format.h
index 368e410..3b48af8 100644
--- a/include/asterisk/format.h
+++ b/include/asterisk/format.h
@@ -32,7 +32,7 @@ struct ast_format;
 
 /*! \brief Format comparison results */
 enum ast_format_cmp_res {
-	/*! Both formats are equivalent to eachother */
+	/*! Both formats are equivalent to each other */
 	AST_FORMAT_CMP_EQUAL = 0,
 	/*! Both formats are completely different and not the same in any way */
 	AST_FORMAT_CMP_NOT_EQUAL,
@@ -110,7 +110,7 @@ struct ast_format_interface {
 	struct ast_format *(* const format_parse_sdp_fmtp)(const struct ast_format *format, const char *attributes);
 
 	/*!
-	 * \brief Generate SDP attribute information from an ast_format_attr structure.
+	 * \brief Generate SDP attribute information from an ast_format structure.
 	 *
 	 * \param format The format containing attributes
 	 * \param payload The payload number to place into the fmtp line
diff --git a/include/asterisk/format_cache.h b/include/asterisk/format_cache.h
index ff03bb4..d716cea 100644
--- a/include/asterisk/format_cache.h
+++ b/include/asterisk/format_cache.h
@@ -184,6 +184,11 @@ extern struct ast_format *ast_format_mp4;
 extern struct ast_format *ast_format_vp8;
 
 /*!
+ * \brief Built-in cached vp9 format.
+ */
+extern struct ast_format *ast_format_vp9;
+
+/*!
  * \brief Built-in cached jpeg format.
  */
 extern struct ast_format *ast_format_jpeg;
diff --git a/include/asterisk/logger.h b/include/asterisk/logger.h
index 1a2bf90..e19ba94 100644
--- a/include/asterisk/logger.h
+++ b/include/asterisk/logger.h
@@ -447,7 +447,9 @@ void ast_callid_strnprint(char *buffer, size_t buffer_size, struct ast_callid *c
 
 #define DEBUG_ATLEAST(level) \
 	(option_debug >= (level) \
-		|| (ast_opt_dbg_module && (int)ast_debug_get_by_module(AST_MODULE) >= (level)))
+		|| (ast_opt_dbg_module \
+        	&& ((int)ast_debug_get_by_module(AST_MODULE) >= (level) \
+				|| (int)ast_debug_get_by_module(__FILE__) >= (level))))
 
 /*!
  * \brief Log a DEBUG message
diff --git a/include/asterisk/manager.h b/include/asterisk/manager.h
index afd9ca1..27944ad 100644
--- a/include/asterisk/manager.h
+++ b/include/asterisk/manager.h
@@ -54,7 +54,7 @@
 - \ref manager.c Main manager code file
  */
 
-#define AMI_VERSION                     "2.10.0"
+#define AMI_VERSION                     "2.10.2"
 #define DEFAULT_MANAGER_PORT 5038	/* Default port for Asterisk management via TCP */
 #define DEFAULT_MANAGER_TLS_PORT 5039	/* Default port for Asterisk management via TCP */
 
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index b5a0288..ac1267d 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -98,22 +98,41 @@ struct ast_sip_transport_state {
 	 */
 	pj_ssl_cipher ciphers[SIP_TLS_MAX_CIPHERS];
 	/*!
-	 * Optional local network information, used for NAT purposes
+	 * Optional local network information, used for NAT purposes.
+	 * "deny" (set) means that it's in the local network. Use the
+	 * ast_sip_transport_is_nonlocal and ast_sip_transport_is_local
+	 * macro's.
 	 * \since 13.8.0
 	 */
 	struct ast_ha *localnet;
 	/*!
-	 * DNS manager for refreshing the external address
+	 * DNS manager for refreshing the external signaling address
 	 * \since 13.8.0
 	 */
-	struct ast_dnsmgr_entry *external_address_refresher;
+	struct ast_dnsmgr_entry *external_signaling_address_refresher;
 	/*!
-	 * Optional external address information
+	 * Optional external signaling address information
 	 * \since 13.8.0
 	 */
-	struct ast_sockaddr external_address;
+	struct ast_sockaddr external_signaling_address;
+	/*!
+	 * DNS manager for refreshing the external media address
+	 * \since 13.18.0
+	 */
+	struct ast_dnsmgr_entry *external_media_address_refresher;
+	/*!
+	 * Optional external signaling address information
+	 * \since 13.18.0
+	 */
+	struct ast_sockaddr external_media_address;
 };
 
+#define ast_sip_transport_is_nonlocal(transport_state, addr) \
+	(!transport_state->localnet || ast_apply_ha(transport_state->localnet, addr) == AST_SENSE_ALLOW)
+
+#define ast_sip_transport_is_local(transport_state, addr) \
+	(transport_state->localnet && ast_apply_ha(transport_state->localnet, addr) != AST_SENSE_ALLOW)
+
 /*
  * \brief Transport to bind to
  */
@@ -260,6 +279,8 @@ struct ast_sip_contact {
 	AST_STRING_FIELD_EXTENDED(call_id);
 	/*! The name of the endpoint that added the contact */
 	AST_STRING_FIELD_EXTENDED(endpoint_name);
+	/*! If true delete the contact on Asterisk restart/boot */
+	int prune_on_boot;
 };
 
 #define CONTACT_STATUS "contact_status"
@@ -767,6 +788,8 @@ struct ast_sip_endpoint {
 	unsigned int refer_blind_progress;
 	/*! Whether to notifies dialog-info 'early' on INUSE && RINGING state */
 	unsigned int notify_early_inuse_ringing;
+	/*! If set, we'll push incoming MWI NOTIFYs to stasis using this mailbox */
+	AST_STRING_FIELD_EXTENDED(incoming_mwi_mailbox);
 };
 
 /*! URI parameter for symmetric transport */
@@ -1201,6 +1224,9 @@ struct ast_sip_contact *ast_sip_location_retrieve_contact(const char *contact_na
  * \param expiration_time Optional expiration time of the contact
  * \param path_info Path information
  * \param user_agent User-Agent header from REGISTER request
+ * \param via_addr
+ * \param via_port
+ * \param call_id
  * \param endpoint The endpoint that resulted in the contact being added
  *
  * \retval -1 failure
@@ -1224,6 +1250,9 @@ int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
  * \param expiration_time Optional expiration time of the contact
  * \param path_info Path information
  * \param user_agent User-Agent header from REGISTER request
+ * \param via_addr
+ * \param via_port
+ * \param call_id
  * \param endpoint The endpoint that resulted in the contact being added
  *
  * \retval -1 failure
@@ -1238,6 +1267,31 @@ int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri
 	struct ast_sip_endpoint *endpoint);
 
 /*!
+ * \brief Create a new contact for an AOR without locking the AOR
+ * \since 13.18.0
+ *
+ * \param aor Pointer to the AOR
+ * \param uri Full contact URI
+ * \param expiration_time Optional expiration time of the contact
+ * \param path_info Path information
+ * \param user_agent User-Agent header from REGISTER request
+ * \param via_addr
+ * \param via_port
+ * \param call_id
+ * \param prune_on_boot Non-zero if the contact cannot survive a restart/boot.
+ * \param endpoint The endpoint that resulted in the contact being added
+ *
+ * \return The created contact or NULL on failure.
+ *
+ * \warning
+ * This function should only be called if you already hold a named write lock on the aor.
+ */
+struct ast_sip_contact *ast_sip_location_create_contact(struct ast_sip_aor *aor,
+	const char *uri, struct timeval expiration_time, const char *path_info,
+	const char *user_agent, const char *via_addr, int via_port, const char *call_id,
+	int prune_on_boot, struct ast_sip_endpoint *endpoint);
+
+/*!
  * \brief Update a contact
  *
  * \param contact New contact object with details
@@ -1258,6 +1312,12 @@ int ast_sip_location_update_contact(struct ast_sip_contact *contact);
 int ast_sip_location_delete_contact(struct ast_sip_contact *contact);
 
 /*!
+ * \brief Prune the prune_on_boot contacts
+ * \since 13.18.0
+ */
+void ast_sip_location_prune_boot_contacts(void);
+
+/*!
  * \brief Callback called when an outbound request with authentication credentials is to be sent in dialog
  *
  * This callback will have the created request on it. The callback's purpose is to do any extra
@@ -2906,4 +2966,118 @@ int ast_sip_set_tpselector_from_ep_or_uri(const struct ast_sip_endpoint *endpoin
 int ast_sip_dlg_set_transport(const struct ast_sip_endpoint *endpoint, pjsip_dialog *dlg,
 	pjsip_tpselector *selector);
 
+/*!
+ * \brief Convert the DTMF mode enum value into a string
+ * \since 13.18.0
+ *
+ * \param dtmf the dtmf mode
+ * \param buf Buffer to receive dtmf mode string
+ * \param buf_len Buffer length
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ *
+ */
+int ast_sip_dtmf_to_str(const enum ast_sip_dtmf_mode dtmf,
+	char *buf, size_t buf_len);
+
+/*!
+ * \brief Convert the DTMF mode name into an enum
+ * \since 13.18.0
+ *
+ * \param dtmf_mode dtmf mode as a string
+ *
+ * \retval  >= 0 The enum value
+ * \retval -1 Failure
+ *
+ */
+int ast_sip_str_to_dtmf(const char *dtmf_mode);
+
+/*!
+ * \brief Transport shutdown monitor callback.
+ * \since 13.18.0
+ *
+ * \param data User data to know what to do when transport shuts down.
+ *
+ * \note The callback does not need to care that data is an ao2 object.
+ *
+ * \return Nothing
+ */
+typedef void (*ast_transport_monitor_shutdown_cb)(void *data);
+
+enum ast_transport_monitor_reg {
+	/*! \brief Successfully registered the transport monitor */
+	AST_TRANSPORT_MONITOR_REG_SUCCESS,
+	/*! \brief Replaced the already existing transport monitor with new one. */
+	AST_TRANSPORT_MONITOR_REG_REPLACED,
+	/*!
+	 * \brief Transport not found to monitor.
+	 * \note Transport is either already shutdown or is not reliable.
+	 */
+	AST_TRANSPORT_MONITOR_REG_NOT_FOUND,
+	/*! \brief Error while registering transport monitor. */
+	AST_TRANSPORT_MONITOR_REG_FAILED,
+};
+
+/*!
+ * \brief Register a reliable transport shutdown monitor callback.
+ * \since 13.18.0
+ *
+ * \param transport Transport to monitor for shutdown.
+ * \param cb Who to call when transport is shutdown.
+ * \param ao2_data Data to pass with the callback.
+ *
+ * \return enum ast_transport_monitor_reg
+ */
+enum ast_transport_monitor_reg ast_sip_transport_monitor_register(pjsip_transport *transport,
+	ast_transport_monitor_shutdown_cb cb, void *ao2_data);
+
+/*!
+ * \brief Unregister a reliable transport shutdown monitor callback.
+ * \since 13.18.0
+ *
+ * \param transport Transport to monitor for shutdown.
+ * \param cb Who to call when transport is shutdown.
+ *
+ * \return Nothing
+ */
+void ast_sip_transport_monitor_unregister(pjsip_transport *transport, ast_transport_monitor_shutdown_cb cb);
+
+/*!
+ * \brief Unregister monitor callback from all reliable transports.
+ * \since 13.18.0
+ *
+ * \param cb Who to call when a transport is shutdown.
+ *
+ * \return Nothing
+ */
+void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb);
+
+/*! Transport state notification registration element.  */
+struct ast_sip_tpmgr_state_callback {
+	/*! PJPROJECT transport state notification callback */
+	pjsip_tp_state_callback cb;
+	AST_LIST_ENTRY(ast_sip_tpmgr_state_callback) node;
+};
+
+/*!
+ * \brief Register a transport state notification callback element.
+ * \since 13.18.0
+ *
+ * \param element What we are registering.
+ *
+ * \return Nothing
+ */
+void ast_sip_transport_state_register(struct ast_sip_tpmgr_state_callback *element);
+
+/*!
+ * \brief Unregister a transport state notification callback element.
+ * \since 13.18.0
+ *
+ * \param element What we are unregistering.
+ *
+ * \return Nothing
+ */
+void ast_sip_transport_state_unregister(struct ast_sip_tpmgr_state_callback *element);
+
 #endif /* _RES_PJSIP_H */
diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h
index 457edd8..073cd2e 100644
--- a/include/asterisk/res_pjsip_session.h
+++ b/include/asterisk/res_pjsip_session.h
@@ -157,6 +157,8 @@ struct ast_sip_session {
 	unsigned int defer_end:1;
 	/*! Session end (remote hangup) requested while termination deferred */
 	unsigned int ended_while_deferred:1;
+	/*! DTMF mode to use with this session, from endpoint but can change */
+	enum ast_sip_dtmf_mode dtmf;
 };
 
 typedef int (*ast_sip_session_request_creation_cb)(struct ast_sip_session *session, pjsip_tx_data *tdata);
@@ -540,6 +542,13 @@ int ast_sip_session_register_supplement(struct ast_sip_session_supplement *suppl
 void ast_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement);
 
 /*!
+ * \brief Add supplements to a SIP session
+ *
+ * \param session The session to initialize
+ */
+int ast_sip_session_add_supplements(struct ast_sip_session *session);
+
+/*!
  * \brief Alternative for ast_datastore_alloc()
  *
  * There are two major differences between this and ast_datastore_alloc()
@@ -622,6 +631,23 @@ int ast_sip_session_refresh(struct ast_sip_session *session,
 		int generate_new_sdp);
 
 /*!
+ * \brief Regenerate SDP Answer
+ *
+ * This method is used when an SDP offer has been received but an SDP answer
+ * has not been sent yet. It requests that a new local SDP be created and
+ * set as the SDP answer. As with any outgoing request in res_pjsip_session,
+ * this will call into registered supplements in case they wish to add anything.
+ *
+ * \param session The session on which the answer will be updated
+ * \param on_sdp_creation Callback called when SDP is created
+ * \param generate_new_sdp Boolean to indicate if a new SDP should be created
+ * \retval 0 Successfully updated the SDP answer
+ * \retval -1 Failure to updated the SDP answer
+ */
+int ast_sip_session_regenerate_answer(struct ast_sip_session *session,
+		ast_sip_session_sdp_creation_cb on_sdp_creation);
+
+/*!
  * \brief Send a SIP response
  *
  * This will send the SIP response specified in tdata and
diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h
index f9bdc50..0b29f34 100644
--- a/include/asterisk/rtp_engine.h
+++ b/include/asterisk/rtp_engine.h
@@ -114,6 +114,8 @@ enum ast_rtp_property {
 	AST_RTP_PROPERTY_STUN,
 	/*! Enable RTCP support */
 	AST_RTP_PROPERTY_RTCP,
+	/*! Enable Asymmetric RTP Codecs */
+	AST_RTP_PROPERTY_ASYMMETRIC_CODEC,
 
 	/*!
 	 * \brief Maximum number of RTP properties supported
diff --git a/include/asterisk/strings.h b/include/asterisk/strings.h
index 1200eb9..963519d 100644
--- a/include/asterisk/strings.h
+++ b/include/asterisk/strings.h
@@ -1383,4 +1383,24 @@ char *ast_generate_random_string(char *buf, size_t size);
  */
 int ast_strings_match(const char *left, const char *op, const char *right);
 
+/*!
+ * \brief Read lines from a string buffer
+ * \since 13.18.0
+ *
+ * \param buffer [IN/OUT] A pointer to a char * string with either Unix or Windows line endings
+ *
+ * \return The "next" line
+ *
+ * \warning The original string and *buffer will be modified.
+ *
+ * \details
+ * Both '\n' and '\r\n' are treated as single delimiters but consecutive occurrances of
+ * the delimiters are NOT considered to be a single delimiter.  This preserves blank
+ * lines in the input.
+ *
+ * MacOS line endings ('\r') are not supported at this time.
+ *
+ */
+char *ast_read_line_from_buffer(char **buffer);
+
 #endif /* _ASTERISK_STRINGS_H */
diff --git a/include/asterisk/vector.h b/include/asterisk/vector.h
index 2073bae..8270e25 100644
--- a/include/asterisk/vector.h
+++ b/include/asterisk/vector.h
@@ -304,27 +304,31 @@ AST_VECTOR(ast_vector_int, int);
  * \brief Add an element into a sorted vector
  *
  * \param vec Sorted vector to add to.
- * \param elem Element to insert.
+ * \param elem Element to insert. Must not be an array type.
  * \param cmp A strcmp compatible compare function.
  *
  * \return 0 on success.
  * \return Non-zero on failure.
  *
  * \warning Use of this macro on an unsorted vector will produce unpredictable results
+ * \warning 'elem' must not be an array type so passing 'x' where 'x' is defined as
+ *          'char x[4]' will fail to compile. However casting 'x' as 'char *' does
+ *          result in a value that CAN be used.
  */
 #define AST_VECTOR_ADD_SORTED(vec, elem, cmp) ({ \
 	int res = 0; \
 	size_t __idx = (vec)->current; \
+	typeof(elem) __elem = (elem); \
 	do { \
 		if (__make_room((vec)->current, vec) != 0) { \
 			res = -1; \
 			break; \
 		} \
-		while (__idx > 0 && (cmp((vec)->elems[__idx - 1], elem) > 0)) { \
+		while (__idx > 0 && (cmp((vec)->elems[__idx - 1], __elem) > 0)) { \
 			(vec)->elems[__idx] = (vec)->elems[__idx - 1]; \
 			__idx--; \
 		} \
-		(vec)->elems[__idx] = elem; \
+		(vec)->elems[__idx] = __elem; \
 		(vec)->current++; \
 	} while (0); \
 	res; \
@@ -544,6 +548,14 @@ AST_VECTOR(ast_vector_int, int);
 #define AST_VECTOR_SIZE(vec) (vec)->current
 
 /*!
+ * \brief Get the maximum number of elements the vector can currently hold.
+ *
+ * \param vec Vector to query.
+ * \return Maximum number of elements the vector can currently hold.
+ */
+#define AST_VECTOR_MAX_SIZE(vec) (vec)->max
+
+/*!
  * \brief Reset vector.
  *
  * \param vec Vector to reset.
diff --git a/main/Makefile b/main/Makefile
index bbddb03..18f1025 100644
--- a/main/Makefile
+++ b/main/Makefile
@@ -140,6 +140,7 @@ endif
 	$(CMD_PREFIX) rm $@.fix
 
 ast_expr2f.o: _ASTCFLAGS+=-Wno-unused
+cdr.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION)
 
 testexpr2: ast_expr2f.c ast_expr2.c ast_expr2.h
 	$(CC) -g -c -Iinclude -DSTANDALONE ast_expr2f.c
@@ -314,7 +315,7 @@ endif
 
 endif
 
-tcptls.o: _ASTCFLAGS+=$(OPENSSL_INCLUDE)
+tcptls.o: _ASTCFLAGS+=$(OPENSSL_INCLUDE) -Wno-deprecated-declarations
 
 $(MAIN_TGT): $(OBJS) $(ASTSSL_LIB) $(ASTPJ_LIB) $(LIBEDIT_OBJ)
 	@$(CC) -c -o buildinfo.o $(_ASTCFLAGS) buildinfo.c $(ASTCFLAGS)
diff --git a/main/acl.c b/main/acl.c
index 9820e8b..94a242a 100644
--- a/main/acl.c
+++ b/main/acl.c
@@ -739,8 +739,8 @@ enum ast_acl_sense ast_apply_ha(const struct ast_ha *ha, const struct ast_sockad
 		char iabuf[INET_ADDRSTRLEN];
 		char iabuf2[INET_ADDRSTRLEN];
 		/* DEBUG */
-		ast_copy_string(iabuf, ast_inet_ntoa(sin->sin_addr), sizeof(iabuf));
-		ast_copy_string(iabuf2, ast_inet_ntoa(ha->netaddr), sizeof(iabuf2));
+		ast_copy_string(iabuf, ast_sockaddr_stringify(addr), sizeof(iabuf));
+		ast_copy_string(iabuf2, ast_sockaddr_stringify(&current_ha->addr), sizeof(iabuf2));
 		ast_debug(1, "##### Testing %s with %s\n", iabuf, iabuf2);
 #endif
 		if (ast_sockaddr_is_ipv4(&current_ha->addr)) {
diff --git a/main/app.c b/main/app.c
index ee7cef2..8ea6f82 100644
--- a/main/app.c
+++ b/main/app.c
@@ -1113,6 +1113,8 @@ static int control_streamfile(struct ast_channel *chan,
 		if (!strcasecmp(end, ":end")) {
 			*end = '\0';
 			end++;
+		} else {
+			end = NULL;
 		}
 	}
 
@@ -3069,19 +3071,32 @@ int ast_app_parse_timelen(const char *timestr, int *result, enum ast_timelen uni
 		case 'h':
 		case 'H':
 			unit = TIMELEN_HOURS;
+			if (u[1] != '\0') {
+				return -1;
+			}
 			break;
 		case 's':
 		case 'S':
 			unit = TIMELEN_SECONDS;
+			if (u[1] != '\0') {
+				return -1;
+			}
 			break;
 		case 'm':
 		case 'M':
 			if (toupper(u[1]) == 'S') {
 				unit = TIMELEN_MILLISECONDS;
+				if (u[2] != '\0') {
+					return -1;
+				}
 			} else if (u[1] == '\0') {
 				unit = TIMELEN_MINUTES;
+			} else {
+				return -1;
 			}
 			break;
+		default:
+			return -1;
 		}
 	}
 
diff --git a/main/ast_expr2.c b/main/ast_expr2.c
index a3c715a..1b866fa 100644
--- a/main/ast_expr2.c
+++ b/main/ast_expr2.c
@@ -2637,13 +2637,11 @@ to_string (struct val *vp)
 	if (vp->type == AST_EXPR_string || vp->type == AST_EXPR_numeric_string)
 		return;
 
-	tmp = malloc ((size_t)25);
-	if (tmp == NULL) {
-		ast_log(LOG_WARNING,"malloc() failed\n");
+	if (asprintf(&tmp, FP___PRINTF, vp->u.i) == -1) {
+		ast_log(LOG_WARNING, "asprintf() failed\n");
 		return;
 	}
 
-	sprintf(tmp, FP___PRINTF, vp->u.i);
 	vp->type = AST_EXPR_string;
 	vp->u.s  = tmp;
 }
diff --git a/main/ast_expr2.y b/main/ast_expr2.y
index 4f60877..7163a71 100644
--- a/main/ast_expr2.y
+++ b/main/ast_expr2.y
@@ -630,13 +630,11 @@ to_string (struct val *vp)
 	if (vp->type == AST_EXPR_string || vp->type == AST_EXPR_numeric_string)
 		return;
 
-	tmp = malloc ((size_t)25);
-	if (tmp == NULL) {
-		ast_log(LOG_WARNING,"malloc() failed\n");
+	if (asprintf(&tmp, FP___PRINTF, vp->u.i) == -1) {
+		ast_log(LOG_WARNING, "asprintf() failed\n");
 		return;
 	}
 
-	sprintf(tmp, FP___PRINTF, vp->u.i);
 	vp->type = AST_EXPR_string;
 	vp->u.s  = tmp;
 }
diff --git a/main/asterisk.c b/main/asterisk.c
index e256276..0818cfb 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -386,6 +386,9 @@ static void ast_el_write_default_histfile(void);
 
 static void asterisk_daemon(int isroot, const char *runuser, const char *rungroup);
 
+#define DEFAULT_MONITOR_DIR DEFAULT_SPOOL_DIR "/monitor"
+#define DEFAULT_RECORDING_DIR DEFAULT_SPOOL_DIR "/recording"
+
 struct _cfg_paths {
 	char config_dir[PATH_MAX];
 	char module_dir[PATH_MAX];
@@ -1283,11 +1286,10 @@ void ast_unreplace_sigchld(void)
 	ast_mutex_unlock(&safe_system_lock);
 }
 
-int ast_safe_system(const char *s)
+/*! \brief fork and perform other preparations for spawning applications */
+static pid_t safe_exec_prep(int dualfork)
 {
 	pid_t pid;
-	int res;
-	int status;
 
 #if defined(HAVE_WORKING_FORK) || defined(HAVE_WORKING_VFORK)
 	ast_replace_sigchld();
@@ -1309,35 +1311,101 @@ int ast_safe_system(const char *s)
 		cap_free(cap);
 #endif
 #ifdef HAVE_WORKING_FORK
-		if (ast_opt_high_priority)
+		if (ast_opt_high_priority) {
 			ast_set_priority(0);
+		}
 		/* Close file descriptors and launch system command */
 		ast_close_fds_above_n(STDERR_FILENO);
 #endif
-		execl("/bin/sh", "/bin/sh", "-c", s, (char *) NULL);
-		_exit(1);
-	} else if (pid > 0) {
+		if (dualfork) {
+#ifdef HAVE_WORKING_FORK
+			pid = fork();
+#else
+			pid = vfork();
+#endif
+			if (pid < 0) {
+				/* Second fork failed. */
+				/* No logger available. */
+				_exit(1);
+			}
+
+			if (pid > 0) {
+				/* This is the first fork, exit so the reaper finishes right away. */
+				_exit(0);
+			}
+
+			/* This is the second fork.  The first fork will exit immediately so
+			 * Asterisk doesn't have to wait for completion.
+			 * ast_safe_system("cmd &") would run in the background, but the '&'
+			 * cannot be added with ast_safe_execvp, so we have to double fork.
+			 */
+		}
+	}
+
+	if (pid < 0) {
+		ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
+	}
+#else
+	ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(ENOTSUP));
+	pid = -1;
+#endif
+
+	return pid;
+}
+
+/*! \brief wait for spawned application to complete and unreplace sigchld */
+static int safe_exec_wait(pid_t pid)
+{
+	int res = -1;
+
+#if defined(HAVE_WORKING_FORK) || defined(HAVE_WORKING_VFORK)
+	if (pid > 0) {
 		for (;;) {
+			int status;
+
 			res = waitpid(pid, &status, 0);
 			if (res > -1) {
 				res = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
 				break;
-			} else if (errno != EINTR)
+			}
+			if (errno != EINTR) {
 				break;
+			}
 		}
-	} else {
-		ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
-		res = -1;
 	}
 
 	ast_unreplace_sigchld();
-#else /* !defined(HAVE_WORKING_FORK) && !defined(HAVE_WORKING_VFORK) */
-	res = -1;
 #endif
 
 	return res;
 }
 
+int ast_safe_execvp(int dualfork, const char *file, char *const argv[])
+{
+	pid_t pid = safe_exec_prep(dualfork);
+
+	if (pid == 0) {
+		execvp(file, argv);
+		_exit(1);
+		/* noreturn from _exit */
+	}
+
+	return safe_exec_wait(pid);
+}
+
+int ast_safe_system(const char *s)
+{
+	pid_t pid = safe_exec_prep(0);
+
+	if (pid == 0) {
+		execl("/bin/sh", "/bin/sh", "-c", s, (char *) NULL);
+		_exit(1);
+		/* noreturn from _exit */
+	}
+
+	return safe_exec_wait(pid);
+}
+
 /*!
  * \brief enable or disable a logging level to a specified console
  */
@@ -3654,8 +3722,8 @@ static void ast_readconfig(void)
 	ast_copy_string(cfg_paths.config_dir, DEFAULT_CONFIG_DIR, sizeof(cfg_paths.config_dir));
 	ast_copy_string(cfg_paths.spool_dir, DEFAULT_SPOOL_DIR, sizeof(cfg_paths.spool_dir));
 	ast_copy_string(cfg_paths.module_dir, DEFAULT_MODULE_DIR, sizeof(cfg_paths.module_dir));
-	snprintf(cfg_paths.monitor_dir, sizeof(cfg_paths.monitor_dir), "%s/monitor", cfg_paths.spool_dir);
-	snprintf(cfg_paths.recording_dir, sizeof(cfg_paths.recording_dir), "%s/recording", cfg_paths.spool_dir);
+	ast_copy_string(cfg_paths.monitor_dir, DEFAULT_MONITOR_DIR, sizeof(cfg_paths.monitor_dir));
+	ast_copy_string(cfg_paths.recording_dir, DEFAULT_RECORDING_DIR, sizeof(cfg_paths.recording_dir));
 	ast_copy_string(cfg_paths.var_dir, DEFAULT_VAR_DIR, sizeof(cfg_paths.var_dir));
 	ast_copy_string(cfg_paths.data_dir, DEFAULT_DATA_DIR, sizeof(cfg_paths.data_dir));
 	ast_copy_string(cfg_paths.log_dir, DEFAULT_LOG_DIR, sizeof(cfg_paths.log_dir));
@@ -3811,7 +3879,9 @@ static void ast_readconfig(void)
 		/* Set the maximum amount of open files */
 		} else if (!strcasecmp(v->name, "maxfiles")) {
 			ast_option_maxfiles = atoi(v->value);
-			set_ulimit(ast_option_maxfiles);
+			if (!ast_opt_remote) {
+				set_ulimit(ast_option_maxfiles);
+			}
 		/* What user to run as */
 		} else if (!strcasecmp(v->name, "runuser")) {
 			ast_copy_string(cfg_paths.run_user, v->value, sizeof(cfg_paths.run_user));
diff --git a/main/bridge.c b/main/bridge.c
index ece3daa..f689b29 100644
--- a/main/bridge.c
+++ b/main/bridge.c
@@ -1742,12 +1742,13 @@ join_exit:;
 static void *bridge_channel_depart_thread(void *data)
 {
 	struct ast_bridge_channel *bridge_channel = data;
+	int res = 0;
 
 	if (bridge_channel->callid) {
 		ast_callid_threadassoc_add(bridge_channel->callid);
 	}
 
-	bridge_channel_internal_join(bridge_channel);
+	res = bridge_channel_internal_join(bridge_channel);
 
 	/*
 	 * cleanup
@@ -1759,7 +1760,8 @@ static void *bridge_channel_depart_thread(void *data)
 	ast_bridge_features_destroy(bridge_channel->features);
 	bridge_channel->features = NULL;
 
-	ast_bridge_discard_after_callback(bridge_channel->chan, AST_BRIDGE_AFTER_CB_REASON_DEPART);
+	ast_bridge_discard_after_callback(bridge_channel->chan,
+		res ? AST_BRIDGE_AFTER_CB_REASON_IMPART_FAILED : AST_BRIDGE_AFTER_CB_REASON_DEPART);
 	/* If join failed there will be impart threads waiting. */
 	bridge_channel_impart_signal(bridge_channel->chan);
 	ast_bridge_discard_after_goto(bridge_channel->chan);
diff --git a/main/bridge_after.c b/main/bridge_after.c
index 1208b57..8135105 100644
--- a/main/bridge_after.c
+++ b/main/bridge_after.c
@@ -295,23 +295,23 @@ int ast_bridge_set_after_callback(struct ast_channel *chan, ast_bridge_after_cb
 	return 0;
 }
 
-const char *reason_strings[] = {
-	[AST_BRIDGE_AFTER_CB_REASON_DESTROY] = "Channel destroyed (hungup)",
-	[AST_BRIDGE_AFTER_CB_REASON_REPLACED] = "Callback was replaced",
-	[AST_BRIDGE_AFTER_CB_REASON_MASQUERADE] = "Channel masqueraded",
-	[AST_BRIDGE_AFTER_CB_REASON_DEPART] = "Channel was departed from bridge",
-	[AST_BRIDGE_AFTER_CB_REASON_REMOVED] = "Callback was removed",
-};
-
 const char *ast_bridge_after_cb_reason_string(enum ast_bridge_after_cb_reason reason)
 {
-	if (reason < AST_BRIDGE_AFTER_CB_REASON_DESTROY
-		|| AST_BRIDGE_AFTER_CB_REASON_REMOVED < reason
-		|| !reason_strings[reason]) {
-		return "Unknown";
-	}
-
-	return reason_strings[reason];
+	switch (reason) {
+	case AST_BRIDGE_AFTER_CB_REASON_DESTROY:
+		return "Channel destroyed (hungup)";
+	case AST_BRIDGE_AFTER_CB_REASON_REPLACED:
+		return "Callback was replaced";
+	case AST_BRIDGE_AFTER_CB_REASON_MASQUERADE:
+		return "Channel masqueraded";
+	case AST_BRIDGE_AFTER_CB_REASON_DEPART:
+		return "Channel was departed from bridge";
+	case AST_BRIDGE_AFTER_CB_REASON_REMOVED:
+		return "Callback was removed";
+	case AST_BRIDGE_AFTER_CB_REASON_IMPART_FAILED:
+		return "Channel failed joining the bridge";
+	}
+	return "Unknown";
 }
 
 struct after_bridge_goto_ds {
diff --git a/main/bridge_channel.c b/main/bridge_channel.c
index 0af688a..08aea5b 100644
--- a/main/bridge_channel.c
+++ b/main/bridge_channel.c
@@ -2258,6 +2258,10 @@ static void bridge_channel_handle_control(struct ast_bridge_channel *bridge_chan
 	case AST_CONTROL_ANSWER:
 		if (ast_channel_state(chan) != AST_STATE_UP) {
 			ast_answer(chan);
+			ast_bridge_channel_lock_bridge(bridge_channel);
+			bridge_channel->bridge->reconfigured = 1;
+			bridge_reconfigured(bridge_channel->bridge, 0);
+			ast_bridge_unlock(bridge_channel->bridge);
 		} else {
 			ast_indicate(chan, -1);
 		}
diff --git a/main/ccss.c b/main/ccss.c
index 002b9a3..5067751 100644
--- a/main/ccss.c
+++ b/main/ccss.c
@@ -3552,7 +3552,7 @@ struct ast_cc_monitor *ast_cc_get_monitor_by_recall_core_id(const int core_id, c
  */
 static void cc_unique_append(struct ast_str **str, const char *dialstring)
 {
-	char dialstring_search[AST_CHANNEL_NAME];
+	char dialstring_search[AST_CHANNEL_NAME + 1];
 
 	if (ast_strlen_zero(dialstring)) {
 		/* No dialstring to append. */
diff --git a/main/cdr.c b/main/cdr.c
index e09efe2..5fd2a75 100644
--- a/main/cdr.c
+++ b/main/cdr.c
@@ -223,7 +223,7 @@ static int cdr_toggle_runtime_options(void);
 
 /*! \brief The configuration settings for this module */
 struct module_config {
-	struct ast_cdr_config *general;		/*< CDR global settings */
+	struct ast_cdr_config *general;		/*!< CDR global settings */
 };
 
 /*! \brief The container for the module configuration */
@@ -751,11 +751,7 @@ static void free_variables(struct varshead *headp)
  */
 static void cdr_object_snapshot_copy(struct cdr_object_snapshot *dst, struct cdr_object_snapshot *src)
 {
-	if (dst->snapshot) {
-		ao2_t_ref(dst->snapshot, -1, "release old snapshot during copy");
-	}
-	dst->snapshot = src->snapshot;
-	ao2_t_ref(dst->snapshot, +1, "bump new snapshot during copy");
+	ao2_t_replace(dst->snapshot, src->snapshot, "CDR snapshot copy");
 	strcpy(dst->userfield, src->userfield);
 	dst->flags = src->flags;
 	copy_variables(&dst->variables, &src->variables);
@@ -785,11 +781,11 @@ static int cdr_object_channel_hash_fn(const void *obj, const int flags)
 	const struct cdr_object *cdr;
 	const char *key;
 
-	switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
-	case OBJ_KEY:
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_KEY:
 		key = obj;
 		break;
-	case OBJ_POINTER:
+	case OBJ_SEARCH_OBJECT:
 		cdr = obj;
 		key = cdr->uniqueid;
 		break;
@@ -810,14 +806,14 @@ static int cdr_object_channel_cmp_fn(void *obj, void *arg, int flags)
     const char *right_key = arg;
     int cmp;
 
-    switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
-    case OBJ_POINTER:
+    switch (flags & OBJ_SEARCH_MASK) {
+    case OBJ_SEARCH_OBJECT:
         right_key = right->uniqueid;
         /* Fall through */
-    case OBJ_KEY:
+    case OBJ_SEARCH_KEY:
         cmp = strcmp(left->uniqueid, right_key);
         break;
-    case OBJ_PARTIAL_KEY:
+    case OBJ_SEARCH_PARTIAL_KEY:
         /*
          * We could also use a partial key struct containing a length
          * so strlen() does not get called for every comparison instead.
@@ -1358,11 +1354,7 @@ static void cdr_object_swap_snapshot(struct cdr_object_snapshot *old_snapshot,
 		struct ast_channel_snapshot *new_snapshot)
 {
 	cdr_object_update_cid(old_snapshot, new_snapshot);
-	if (old_snapshot->snapshot) {
-		ao2_t_ref(old_snapshot->snapshot, -1, "Drop ref for swap");
-	}
-	ao2_t_ref(new_snapshot, +1, "Bump ref for swap");
-	old_snapshot->snapshot = new_snapshot;
+	ao2_t_replace(old_snapshot->snapshot, new_snapshot, "Swap CDR shapshot");
 }
 
 /* BASE METHOD IMPLEMENTATIONS */
@@ -1462,7 +1454,8 @@ static int base_process_parked_channel(struct cdr_object *cdr, struct ast_parked
 
 /* SINGLE STATE */
 
-static void single_state_init_function(struct cdr_object *cdr) {
+static void single_state_init_function(struct cdr_object *cdr)
+{
 	cdr->start = ast_tvnow();
 	cdr_object_check_party_a_answer(cdr);
 }
@@ -1575,11 +1568,10 @@ static enum process_bridge_enter_results single_state_process_bridge_enter(struc
 	for (it_cdrs = ao2_iterator_init(bridge->channels, 0);
 		!success && (channel_id = ao2_iterator_next(&it_cdrs));
 		ao2_ref(channel_id, -1)) {
-		RAII_VAR(struct cdr_object *, cand_cdr_master,
-			ao2_find(active_cdrs_by_channel, channel_id, OBJ_KEY),
-			ao2_cleanup);
+		struct cdr_object *cand_cdr_master;
 		struct cdr_object *cand_cdr;
 
+		cand_cdr_master = ao2_find(active_cdrs_by_channel, channel_id, OBJ_SEARCH_KEY);
 		if (!cand_cdr_master) {
 			continue;
 		}
@@ -1601,6 +1593,7 @@ static enum process_bridge_enter_results single_state_process_bridge_enter(struc
 			break;
 		}
 		ao2_unlock(cand_cdr_master);
+		ao2_cleanup(cand_cdr_master);
 	}
 	ao2_iterator_destroy(&it_cdrs);
 
@@ -1627,11 +1620,9 @@ static int single_state_process_parking_bridge_enter(struct cdr_object *cdr, str
 static void dial_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
 {
 	ast_assert(snapshot != NULL);
+	ast_assert(cdr->party_b.snapshot
+		&& !strcasecmp(cdr->party_b.snapshot->name, snapshot->name));
 
-	if (!cdr->party_b.snapshot
-		|| strcasecmp(cdr->party_b.snapshot->name, snapshot->name)) {
-		return;
-	}
 	cdr_object_swap_snapshot(&cdr->party_b, snapshot);
 
 	/* If party B hangs up, finalize this CDR */
@@ -1725,11 +1716,10 @@ static enum process_bridge_enter_results dial_state_process_bridge_enter(struct
 	for (it_cdrs = ao2_iterator_init(bridge->channels, 0);
 		!success && (channel_id = ao2_iterator_next(&it_cdrs));
 		ao2_ref(channel_id, -1)) {
-		RAII_VAR(struct cdr_object *, cand_cdr_master,
-			ao2_find(active_cdrs_by_channel, channel_id, OBJ_KEY),
-			ao2_cleanup);
+		struct cdr_object *cand_cdr_master;
 		struct cdr_object *cand_cdr;
 
+		cand_cdr_master = ao2_find(active_cdrs_by_channel, channel_id, OBJ_SEARCH_KEY);
 		if (!cand_cdr_master) {
 			continue;
 		}
@@ -1764,6 +1754,7 @@ static enum process_bridge_enter_results dial_state_process_bridge_enter(struct
 			break;
 		}
 		ao2_unlock(cand_cdr_master);
+		ao2_cleanup(cand_cdr_master);
 	}
 	ao2_iterator_destroy(&it_cdrs);
 
@@ -1827,10 +1818,9 @@ static int dialed_pending_state_process_dial_begin(struct cdr_object *cdr, struc
 
 static void bridge_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
 {
-	if (!cdr->party_b.snapshot
-		|| strcasecmp(cdr->party_b.snapshot->name, snapshot->name)) {
-		return;
-	}
+	ast_assert(cdr->party_b.snapshot
+		&& !strcasecmp(cdr->party_b.snapshot->name, snapshot->name));
+
 	cdr_object_swap_snapshot(&cdr->party_b, snapshot);
 
 	/* If party B hangs up, finalize this CDR */
@@ -1928,7 +1918,7 @@ static int filter_channel_cache_message(struct ast_channel_snapshot *old_snapsho
 static void handle_dial_message(void *data, struct stasis_subscription *sub, struct stasis_message *message)
 {
 	RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
-	RAII_VAR(struct cdr_object *, cdr, NULL, ao2_cleanup);
+	struct cdr_object *cdr;
 	struct ast_multi_channel_blob *payload = stasis_message_data(message);
 	struct ast_channel_snapshot *caller;
 	struct ast_channel_snapshot *peer;
@@ -1942,6 +1932,10 @@ static void handle_dial_message(void *data, struct stasis_subscription *sub, str
 	if (!peer && !caller) {
 		return;
 	}
+	if (filter_channel_snapshot(peer) || (caller && filter_channel_snapshot(caller))) {
+		return;
+	}
+
 	dial_status_blob = ast_json_object_get(ast_multi_channel_blob_get_json(payload), "dialstatus");
 	if (dial_status_blob) {
 		dial_status = ast_json_string_get(dial_status_blob);
@@ -1954,17 +1948,12 @@ static void handle_dial_message(void *data, struct stasis_subscription *sub, str
 			(unsigned int)stasis_message_timestamp(message)->tv_sec,
 			(unsigned int)stasis_message_timestamp(message)->tv_usec);
 
-	if (filter_channel_snapshot(peer) || (caller && filter_channel_snapshot(caller))) {
-		return;
-	}
-
 	/* Figure out who is running this show */
 	if (caller) {
-		cdr = ao2_find(active_cdrs_by_channel, caller->uniqueid, OBJ_KEY);
+		cdr = ao2_find(active_cdrs_by_channel, caller->uniqueid, OBJ_SEARCH_KEY);
 	} else {
-		cdr = ao2_find(active_cdrs_by_channel, peer->uniqueid, OBJ_KEY);
+		cdr = ao2_find(active_cdrs_by_channel, peer->uniqueid, OBJ_SEARCH_KEY);
 	}
-
 	if (!cdr) {
 		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", caller ? caller->name : peer->name);
 		ast_assert(0);
@@ -2004,15 +1993,12 @@ static void handle_dial_message(void *data, struct stasis_subscription *sub, str
 		struct cdr_object *new_cdr;
 
 		new_cdr = cdr_object_create_and_append(cdr);
-		if (!new_cdr) {
-			ao2_unlock(cdr);
-			return;
+		if (new_cdr) {
+			new_cdr->fn_table->process_dial_begin(new_cdr, caller, peer);
 		}
-		new_cdr->fn_table->process_dial_begin(new_cdr,
-				caller,
-				peer);
 	}
 	ao2_unlock(cdr);
+	ao2_cleanup(cdr);
 }
 
 static int cdr_object_finalize_party_b(void *obj, void *arg, int flags)
@@ -2020,6 +2006,7 @@ static int cdr_object_finalize_party_b(void *obj, void *arg, int flags)
 	struct cdr_object *cdr = obj;
 	struct ast_channel_snapshot *party_b = arg;
 	struct cdr_object *it_cdr;
+
 	for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
 		if (it_cdr->party_b.snapshot
 			&& !strcasecmp(it_cdr->party_b.snapshot->name, party_b->name)) {
@@ -2037,6 +2024,7 @@ static int cdr_object_update_party_b(void *obj, void *arg, int flags)
 	struct cdr_object *cdr = obj;
 	struct ast_channel_snapshot *party_b = arg;
 	struct cdr_object *it_cdr;
+
 	for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
 		if (!it_cdr->fn_table->process_party_b) {
 			continue;
@@ -2084,13 +2072,11 @@ static int check_new_cdr_needed(struct ast_channel_snapshot *old_snapshot,
  */
 static void handle_channel_cache_message(void *data, struct stasis_subscription *sub, struct stasis_message *message)
 {
-	RAII_VAR(struct cdr_object *, cdr, NULL, ao2_cleanup);
+	struct cdr_object *cdr;
 	RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
 	struct stasis_cache_update *update = stasis_message_data(message);
 	struct ast_channel_snapshot *old_snapshot;
 	struct ast_channel_snapshot *new_snapshot;
-	const char *uniqueid;
-	const char *name;
 	struct cdr_object *it_cdr;
 
 	ast_assert(update != NULL);
@@ -2098,8 +2084,6 @@ static void handle_channel_cache_message(void *data, struct stasis_subscription
 
 	old_snapshot = stasis_message_data(update->old_snapshot);
 	new_snapshot = stasis_message_data(update->new_snapshot);
-	uniqueid = new_snapshot ? new_snapshot->uniqueid : old_snapshot->uniqueid;
-	name = new_snapshot ? new_snapshot->name : old_snapshot->name;
 
 	if (filter_channel_cache_message(old_snapshot, new_snapshot)) {
 		return;
@@ -2112,19 +2096,25 @@ static void handle_channel_cache_message(void *data, struct stasis_subscription
 		}
 		cdr->is_root = 1;
 		ao2_link(active_cdrs_by_channel, cdr);
+	} else {
+		const char *uniqueid;
+
+		uniqueid = new_snapshot ? new_snapshot->uniqueid : old_snapshot->uniqueid;
+		cdr = ao2_find(active_cdrs_by_channel, uniqueid, OBJ_SEARCH_KEY);
 	}
 
 	/* Handle Party A */
 	if (!cdr) {
-		cdr = ao2_find(active_cdrs_by_channel, uniqueid, OBJ_KEY);
-	}
-	if (!cdr) {
+		const char *name;
+
+		name = new_snapshot ? new_snapshot->name : old_snapshot->name;
 		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", name);
 		ast_assert(0);
 	} else {
 		ao2_lock(cdr);
 		if (new_snapshot) {
 			int all_reject = 1;
+
 			for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
 				if (!it_cdr->fn_table->process_party_a) {
 					continue;
@@ -2134,6 +2124,7 @@ static void handle_channel_cache_message(void *data, struct stasis_subscription
 			if (all_reject && check_new_cdr_needed(old_snapshot, new_snapshot)) {
 				/* We're not hung up and we have a new snapshot - we need a new CDR */
 				struct cdr_object *new_cdr;
+
 				new_cdr = cdr_object_create_and_append(cdr);
 				if (new_cdr) {
 					new_cdr->fn_table->process_party_a(new_cdr, new_snapshot);
@@ -2159,6 +2150,7 @@ static void handle_channel_cache_message(void *data, struct stasis_subscription
 			old_snapshot);
 	}
 
+	ao2_cleanup(cdr);
 }
 
 struct bridge_leave_data {
@@ -2221,9 +2213,7 @@ static void handle_bridge_leave_message(void *data, struct stasis_subscription *
 	struct ast_channel_snapshot *channel = update->channel;
 	RAII_VAR(struct module_config *, mod_cfg,
 			ao2_global_obj_ref(module_configs), ao2_cleanup);
-	RAII_VAR(struct cdr_object *, cdr,
-			ao2_find(active_cdrs_by_channel, channel->uniqueid, OBJ_KEY),
-			ao2_cleanup);
+	struct cdr_object *cdr;
 	struct cdr_object *it_cdr;
 	struct bridge_leave_data leave_data = {
 		.bridge = bridge,
@@ -2244,6 +2234,7 @@ static void handle_bridge_leave_message(void *data, struct stasis_subscription *
 			(unsigned int)stasis_message_timestamp(message)->tv_sec,
 			(unsigned int)stasis_message_timestamp(message)->tv_usec);
 
+	cdr = ao2_find(active_cdrs_by_channel, channel->uniqueid, OBJ_SEARCH_KEY);
 	if (!cdr) {
 		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name);
 		ast_assert(0);
@@ -2264,16 +2255,16 @@ static void handle_bridge_leave_message(void *data, struct stasis_subscription *
 		}
 	}
 	ao2_unlock(cdr);
-	if (!left_bridge) {
-		return;
-	}
 
-	if (strcmp(bridge->subclass, "parking")) {
-		/* Party B */
+	/* Party B */
+	if (left_bridge
+		&& strcmp(bridge->subclass, "parking")) {
 		ao2_callback(active_cdrs_by_channel, OBJ_NODATA,
-				cdr_object_party_b_left_bridge_cb,
-				&leave_data);
+			cdr_object_party_b_left_bridge_cb,
+			&leave_data);
 	}
+
+	ao2_cleanup(cdr);
 }
 
 /*!
@@ -2378,17 +2369,14 @@ static void handle_bridge_pairings(struct cdr_object *cdr, struct ast_bridge_sna
 
 	it_channels = ao2_iterator_init(bridge->channels, 0);
 	while ((channel_id = ao2_iterator_next(&it_channels))) {
-		RAII_VAR(struct cdr_object *, cand_cdr,
-			ao2_find(active_cdrs_by_channel, channel_id, OBJ_KEY),
-			ao2_cleanup);
+		struct cdr_object *cand_cdr;
 
-		if (!cand_cdr) {
-			ao2_ref(channel_id, -1);
-			continue;
+		cand_cdr = ao2_find(active_cdrs_by_channel, channel_id, OBJ_SEARCH_KEY);
+		if (cand_cdr) {
+			bridge_candidate_process(cdr, cand_cdr);
+			ao2_ref(cand_cdr, -1);
 		}
 
-		bridge_candidate_process(cdr, cand_cdr);
-
 		ao2_ref(channel_id, -1);
 	}
 	ao2_iterator_destroy(&it_channels);
@@ -2524,9 +2512,7 @@ static void handle_bridge_enter_message(void *data, struct stasis_subscription *
 	struct ast_bridge_blob *update = stasis_message_data(message);
 	struct ast_bridge_snapshot *bridge = update->bridge;
 	struct ast_channel_snapshot *channel = update->channel;
-	RAII_VAR(struct cdr_object *, cdr,
-			ao2_find(active_cdrs_by_channel, channel->uniqueid, OBJ_KEY),
-			ao2_cleanup);
+	struct cdr_object *cdr;
 	RAII_VAR(struct module_config *, mod_cfg,
 			ao2_global_obj_ref(module_configs), ao2_cleanup);
 
@@ -2543,6 +2529,7 @@ static void handle_bridge_enter_message(void *data, struct stasis_subscription *
 			(unsigned int)stasis_message_timestamp(message)->tv_sec,
 			(unsigned int)stasis_message_timestamp(message)->tv_usec);
 
+	cdr = ao2_find(active_cdrs_by_channel, channel->uniqueid, OBJ_SEARCH_KEY);
 	if (!cdr) {
 		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name);
 		ast_assert(0);
@@ -2554,6 +2541,7 @@ static void handle_bridge_enter_message(void *data, struct stasis_subscription *
 	} else {
 		handle_standard_bridge_enter_message(cdr, bridge, channel);
 	}
+	ao2_cleanup(cdr);
 }
 
 /*!
@@ -2568,7 +2556,7 @@ static void handle_parked_call_message(void *data, struct stasis_subscription *s
 {
 	struct ast_parked_call_payload *payload = stasis_message_data(message);
 	struct ast_channel_snapshot *channel = payload->parkee;
-	RAII_VAR(struct cdr_object *, cdr, NULL, ao2_cleanup);
+	struct cdr_object *cdr;
 	RAII_VAR(struct module_config *, mod_cfg,
 			ao2_global_obj_ref(module_configs), ao2_cleanup);
 	int unhandled = 1;
@@ -2593,7 +2581,7 @@ static void handle_parked_call_message(void *data, struct stasis_subscription *s
 			(unsigned int)stasis_message_timestamp(message)->tv_sec,
 			(unsigned int)stasis_message_timestamp(message)->tv_usec);
 
-	cdr = ao2_find(active_cdrs_by_channel, channel->uniqueid, OBJ_KEY);
+	cdr = ao2_find(active_cdrs_by_channel, channel->uniqueid, OBJ_SEARCH_KEY);
 	if (!cdr) {
 		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name);
 		ast_assert(0);
@@ -2610,7 +2598,9 @@ static void handle_parked_call_message(void *data, struct stasis_subscription *s
 
 	if (unhandled) {
 		/* Nothing handled the messgae - we need a new one! */
-		struct cdr_object *new_cdr = cdr_object_create_and_append(cdr);
+		struct cdr_object *new_cdr;
+
+		new_cdr = cdr_object_create_and_append(cdr);
 		if (new_cdr) {
 			/* As the new CDR is created in the single state, it is guaranteed
 			 * to have a function for the parked call message and will handle
@@ -2621,6 +2611,7 @@ static void handle_parked_call_message(void *data, struct stasis_subscription *s
 
 	ao2_unlock(cdr);
 
+	ao2_cleanup(cdr);
 }
 
 /*!
@@ -2889,7 +2880,7 @@ void ast_cdr_format_var(struct ast_cdr *cdr, const char *name, char **ret, char
 	}
 }
 
-/*
+/*!
  * \internal
  * \brief Callback that finds all CDRs that reference a particular channel by name
  */
@@ -2905,7 +2896,7 @@ static int cdr_object_select_all_by_name_cb(void *obj, void *arg, int flags)
 	return 0;
 }
 
-/*
+/*!
  * \internal
  * \brief Callback that finds a CDR by channel name
  */
@@ -3093,15 +3084,16 @@ static struct cdr_object *cdr_object_get_by_name(const char *name)
 
 int ast_cdr_getvar(const char *channel_name, const char *name, char *value, size_t length)
 {
-	RAII_VAR(struct cdr_object *, cdr, cdr_object_get_by_name(channel_name), ao2_cleanup);
+	struct cdr_object *cdr;
 	struct cdr_object *cdr_obj;
 
-	if (!cdr) {
-		ast_log(AST_LOG_ERROR, "Unable to find CDR for channel %s\n", channel_name);
+	if (ast_strlen_zero(name)) {
 		return 1;
 	}
 
-	if (ast_strlen_zero(name)) {
+	cdr = cdr_object_get_by_name(channel_name);
+	if (!cdr) {
+		ast_log(AST_LOG_ERROR, "Unable to find CDR for channel %s\n", channel_name);
 		return 1;
 	}
 
@@ -3115,18 +3107,20 @@ int ast_cdr_getvar(const char *channel_name, const char *name, char *value, size
 
 	ao2_unlock(cdr);
 
+	ao2_cleanup(cdr);
 	return 0;
 }
 
 int ast_cdr_serialize_variables(const char *channel_name, struct ast_str **buf, char delim, char sep)
 {
-	RAII_VAR(struct cdr_object *, cdr, cdr_object_get_by_name(channel_name), ao2_cleanup);
+	struct cdr_object *cdr;
 	struct cdr_object *it_cdr;
 	struct ast_var_t *variable;
 	const char *var;
 	char workspace[256];
 	int total = 0, x = 0, i;
 
+	cdr = cdr_object_get_by_name(channel_name);
 	if (!cdr) {
 		RAII_VAR(struct module_config *, mod_cfg,
 			 ao2_global_obj_ref(module_configs), ao2_cleanup);
@@ -3142,8 +3136,9 @@ int ast_cdr_serialize_variables(const char *channel_name, struct ast_str **buf,
 
 	ao2_lock(cdr);
 	for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
-		if (++x > 1)
+		if (++x > 1) {
 			ast_str_append(buf, 0, "\n");
+		}
 
 		AST_LIST_TRAVERSE(&it_cdr->party_a.variables, variable, entries) {
 			if (!(var = ast_var_name(variable))) {
@@ -3174,6 +3169,7 @@ int ast_cdr_serialize_variables(const char *channel_name, struct ast_str **buf,
 		}
 	}
 	ao2_unlock(cdr);
+	ao2_cleanup(cdr);
 	return total;
 }
 
@@ -3226,13 +3222,15 @@ static int cdr_object_update_party_b_userfield_cb(void *obj, void *arg, int flag
 	struct cdr_object *cdr = obj;
 	struct party_b_userfield_update *info = arg;
 	struct cdr_object *it_cdr;
+
 	for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
 		if (it_cdr->fn_table == &finalized_state_fn_table && it_cdr->next != NULL) {
 			continue;
 		}
 		if (it_cdr->party_b.snapshot
 			&& !strcasecmp(it_cdr->party_b.snapshot->name, info->channel_name)) {
-			strcpy(it_cdr->party_b.userfield, info->userfield);
+			ast_copy_string(it_cdr->party_b.userfield, info->userfield,
+				sizeof(it_cdr->party_b.userfield));
 		}
 	}
 	return 0;
@@ -3240,7 +3238,7 @@ static int cdr_object_update_party_b_userfield_cb(void *obj, void *arg, int flag
 
 void ast_cdr_setuserfield(const char *channel_name, const char *userfield)
 {
-	RAII_VAR(struct cdr_object *, cdr, cdr_object_get_by_name(channel_name), ao2_cleanup);
+	struct cdr_object *cdr;
 	struct party_b_userfield_update party_b_info = {
 			.channel_name = channel_name,
 			.userfield = userfield,
@@ -3248,13 +3246,15 @@ void ast_cdr_setuserfield(const char *channel_name, const char *userfield)
 	struct cdr_object *it_cdr;
 
 	/* Handle Party A */
+	cdr = cdr_object_get_by_name(channel_name);
 	if (cdr) {
 		ao2_lock(cdr);
 		for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
 			if (it_cdr->fn_table == &finalized_state_fn_table && it_cdr->next != NULL) {
 				continue;
 			}
-			ast_copy_string(it_cdr->party_a.userfield, userfield, AST_MAX_USER_FIELD);
+			ast_copy_string(it_cdr->party_a.userfield, userfield,
+				sizeof(it_cdr->party_a.userfield));
 		}
 		ao2_unlock(cdr);
 	}
@@ -3264,6 +3264,7 @@ void ast_cdr_setuserfield(const char *channel_name, const char *userfield)
 			cdr_object_update_party_b_userfield_cb,
 			&party_b_info);
 
+	ao2_cleanup(cdr);
 }
 
 static void post_cdr(struct ast_cdr *cdr)
@@ -3295,9 +3296,10 @@ static void post_cdr(struct ast_cdr *cdr)
 
 int ast_cdr_set_property(const char *channel_name, enum ast_cdr_options option)
 {
-	RAII_VAR(struct cdr_object *, cdr, cdr_object_get_by_name(channel_name), ao2_cleanup);
+	struct cdr_object *cdr;
 	struct cdr_object *it_cdr;
 
+	cdr = cdr_object_get_by_name(channel_name);
 	if (!cdr) {
 		return -1;
 	}
@@ -3315,14 +3317,16 @@ int ast_cdr_set_property(const char *channel_name, enum ast_cdr_options option)
 	}
 	ao2_unlock(cdr);
 
+	ao2_cleanup(cdr);
 	return 0;
 }
 
 int ast_cdr_clear_property(const char *channel_name, enum ast_cdr_options option)
 {
-	RAII_VAR(struct cdr_object *, cdr, cdr_object_get_by_name(channel_name), ao2_cleanup);
+	struct cdr_object *cdr;
 	struct cdr_object *it_cdr;
 
+	cdr = cdr_object_get_by_name(channel_name);
 	if (!cdr) {
 		return -1;
 	}
@@ -3336,15 +3340,17 @@ int ast_cdr_clear_property(const char *channel_name, enum ast_cdr_options option
 	}
 	ao2_unlock(cdr);
 
+	ao2_cleanup(cdr);
 	return 0;
 }
 
 int ast_cdr_reset(const char *channel_name, int keep_variables)
 {
-	RAII_VAR(struct cdr_object *, cdr, cdr_object_get_by_name(channel_name), ao2_cleanup);
+	struct cdr_object *cdr;
 	struct ast_var_t *vardata;
 	struct cdr_object *it_cdr;
 
+	cdr = cdr_object_get_by_name(channel_name);
 	if (!cdr) {
 		return -1;
 	}
@@ -3372,6 +3378,7 @@ int ast_cdr_reset(const char *channel_name, int keep_variables)
 	}
 	ao2_unlock(cdr);
 
+	ao2_cleanup(cdr);
 	return 0;
 }
 
@@ -3781,7 +3788,7 @@ static void cli_show_channel(struct ast_cli_args *a)
 	char answer_time_buffer[64];
 	char end_time_buffer[64];
 	const char *channel_name = a->argv[3];
-	RAII_VAR(struct cdr_object *, cdr, NULL, ao2_cleanup);
+	struct cdr_object *cdr;
 
 #define TITLE_STRING "%-10.10s %-20.20s %-25.25s %-15.15s %-15.15s %-8.8s %-8.8s %-8.8s %-8.8s %-8.8s\n"
 #define FORMAT_STRING "%-10.10s %-20.20s %-25.25s %-15.15s %-15.15s %-8.8s %-8.8s %-8.8s %-8.8ld %-8.8ld\n"
@@ -3800,6 +3807,7 @@ static void cli_show_channel(struct ast_cli_args *a)
 	ao2_lock(cdr);
 	for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
 		struct timeval end;
+
 		if (snapshot_is_dialed(it_cdr->party_a.snapshot)) {
 			continue;
 		}
@@ -3825,6 +3833,9 @@ static void cli_show_channel(struct ast_cli_args *a)
 				(long)ast_tvdiff_ms(end, it_cdr->start) / 1000);
 	}
 	ao2_unlock(cdr);
+
+	ao2_cleanup(cdr);
+
 #undef FORMAT_STRING
 #undef TITLE_STRING
 }
@@ -4216,8 +4227,8 @@ int ast_cdr_engine_init(void)
 	stasis_message_router_add(stasis_router, ast_parked_call_type(), handle_parked_call_message, NULL);
 	stasis_message_router_add(stasis_router, cdr_sync_message_type(), handle_cdr_sync_message, NULL);
 
-	active_cdrs_by_channel = ao2_container_alloc(NUM_CDR_BUCKETS,
-		cdr_object_channel_hash_fn, cdr_object_channel_cmp_fn);
+	active_cdrs_by_channel = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
+		NUM_CDR_BUCKETS, cdr_object_channel_hash_fn, NULL, cdr_object_channel_cmp_fn);
 	if (!active_cdrs_by_channel) {
 		return -1;
 	}
@@ -4239,8 +4250,6 @@ int ast_cdr_engine_init(void)
 void ast_cdr_engine_term(void)
 {
 	RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
-	RAII_VAR(void *, payload, NULL, ao2_cleanup);
-	RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
 
 	/* Since this is called explicitly during process shutdown, we might not have ever
 	 * been initialized. If so, the config object will be NULL.
@@ -4250,9 +4259,16 @@ void ast_cdr_engine_term(void)
 	}
 
 	if (cdr_sync_message_type()) {
+		void *payload;
+		struct stasis_message *message;
+
+		if (!stasis_router) {
+			return;
+		}
+
 		/* Make sure we have the needed items */
 		payload = ao2_alloc(sizeof(*payload), NULL);
-		if (!stasis_router || !payload) {
+		if (!payload) {
 			return;
 		}
 
@@ -4262,6 +4278,8 @@ void ast_cdr_engine_term(void)
 		if (message) {
 			stasis_message_router_publish_sync(stasis_router, message);
 		}
+		ao2_cleanup(message);
+		ao2_cleanup(payload);
 	}
 
 	if (ast_test_flag(&mod_cfg->general->settings, CDR_BATCHMODE)) {
@@ -4274,7 +4292,7 @@ int ast_cdr_engine_reload(void)
 	RAII_VAR(struct module_config *, old_mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
 	RAII_VAR(struct module_config *, mod_cfg, NULL, ao2_cleanup);
 
-	if (process_config(1)) {
+	if (!old_mod_cfg || process_config(1)) {
 		return -1;
 	}
 
@@ -4290,5 +4308,3 @@ int ast_cdr_engine_reload(void)
 
 	return cdr_toggle_runtime_options();
 }
-
-
diff --git a/main/channel.c b/main/channel.c
index c6c035f..d22c987 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -4293,7 +4293,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
 				}
 #else
 				int jump = calc_monitor_jump((ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)),
-					ast_format_get_sample_rate(f->subclass.codec),
+					ast_format_get_sample_rate(f->subclass.format),
 					ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format));
 				if (jump - MONITOR_DELAY >= 0) {
 					if (ast_seekstream(ast_channel_monitor(chan)->read_stream, jump - f->samples, SEEK_FORCECUR) == -1) {
@@ -5375,7 +5375,7 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
 				}
 #else
 				int jump = calc_monitor_jump((ast_channel_insmpl(chan) - ast_channel_outsmpl(chan)),
-				                             ast_format_get_sample_rate(f->subclass.codec),
+				                             ast_format_get_sample_rate(f->subclass.format),
 				                             ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format));
 				if (jump - MONITOR_DELAY >= 0) {
 					if (ast_seekstream(ast_channel_monitor(chan)->write_stream, jump - cur->samples, SEEK_FORCECUR) == -1) {
diff --git a/main/cli.c b/main/cli.c
index 1af9177..e9ed709 100644
--- a/main/cli.c
+++ b/main/cli.c
@@ -374,7 +374,7 @@ static char *complete_number(const char *partial, unsigned int min, unsigned int
 	int i, count = 0;
 	unsigned int prospective[2];
 	unsigned int part = strtoul(partial, NULL, 10);
-	char next[12];
+	char next[13];
 
 	if (part < min || part > max) {
 		return NULL;
@@ -1031,7 +1031,7 @@ static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
 	it_chans = ao2_iterator_init(channels, 0);
 	for (; (msg = ao2_iterator_next(&it_chans)); ao2_ref(msg, -1)) {
 		struct ast_channel_snapshot *cs = stasis_message_data(msg);
-		char durbuf[10] = "-";
+		char durbuf[16] = "-";
 
 		if (!count) {
 			if ((concise || verbose)  && !ast_tvzero(cs->creationtime)) {
diff --git a/main/codec_builtin.c b/main/codec_builtin.c
index 5fdfa7e..9ba33ee 100644
--- a/main/codec_builtin.c
+++ b/main/codec_builtin.c
@@ -783,6 +783,13 @@ static struct ast_codec vp8 = {
 	.sample_rate = 1000,
 };
 
+static struct ast_codec vp9 = {
+	.name = "vp9",
+	.description = "VP9 video",
+	.type = AST_MEDIA_TYPE_VIDEO,
+	.sample_rate = 1000,
+};
+
 static struct ast_codec t140red = {
 	.name = "red",
 	.description = "T.140 Realtime Text with redundancy",
@@ -922,6 +929,7 @@ int ast_codec_builtin_init(void)
 	res |= CODEC_REGISTER_AND_CACHE(h264);
 	res |= CODEC_REGISTER_AND_CACHE(mpeg4);
 	res |= CODEC_REGISTER_AND_CACHE(vp8);
+	res |= CODEC_REGISTER_AND_CACHE(vp9);
 	res |= CODEC_REGISTER_AND_CACHE(t140red);
 	res |= CODEC_REGISTER_AND_CACHE(t140);
 	res |= CODEC_REGISTER_AND_CACHE(none);
diff --git a/main/config.c b/main/config.c
index b81a9f6..9be7581 100644
--- a/main/config.c
+++ b/main/config.c
@@ -3743,6 +3743,55 @@ uint32_done:
 		break;
 	}
 
+	case PARSE_TIMELEN:
+	{
+		int x = 0;
+		int *result = p_result;
+		int def = result ? *result : 0;
+		int high = INT_MAX;
+		int low = INT_MIN;
+		enum ast_timelen defunit;
+
+		defunit = va_arg(ap, enum ast_timelen);
+		/* optional arguments: default value and/or (low, high) */
+		if (flags & PARSE_DEFAULT) {
+			def = va_arg(ap, int);
+		}
+		if (flags & (PARSE_IN_RANGE | PARSE_OUT_RANGE)) {
+			low = va_arg(ap, int);
+			high = va_arg(ap, int);
+		}
+		if (ast_strlen_zero(arg)) {
+			error = 1;
+			goto timelen_done;
+		}
+		error = ast_app_parse_timelen(arg, &x, defunit);
+		if (error || x < INT_MIN || x > INT_MAX) {
+			/* Parse error, or type out of int bounds */
+			error = 1;
+			goto timelen_done;
+		}
+		error = (x < low) || (x > high);
+		if (flags & PARSE_RANGE_DEFAULTS) {
+			if (x < low) {
+				def = low;
+			} else if (x > high) {
+				def = high;
+			}
+		}
+		if (flags & PARSE_OUT_RANGE) {
+			error = !error;
+		}
+timelen_done:
+		if (result) {
+			*result  = error ? def : x;
+		}
+
+		ast_debug(3, "extract timelen from [%s] in [%d, %d] gives [%d](%d)\n",
+				arg, low, high, result ? *result : x, error);
+		break;
+	}
+
 	case PARSE_DOUBLE:
 	{
 		double *result = p_result;
diff --git a/main/config_options.c b/main/config_options.c
index 81d0ff9..40fae16 100644
--- a/main/config_options.c
+++ b/main/config_options.c
@@ -36,6 +36,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/config_options.h"
 #include "asterisk/stringfields.h"
 #include "asterisk/acl.h"
+#include "asterisk/app.h"
 #include "asterisk/frame.h"
 #include "asterisk/xmldoc.h"
 #include "asterisk/cli.h"
@@ -120,6 +121,7 @@ static void config_option_destroy(void *obj)
 
 static int int_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj);
 static int uint_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj);
+static int timelen_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj);
 static int double_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj);
 static int sockaddr_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj);
 static int stringfield_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj);
@@ -153,6 +155,7 @@ static aco_option_handler ast_config_option_default_handler(enum aco_option_type
 	case OPT_SOCKADDR_T: return sockaddr_handler_fn;
 	case OPT_STRINGFIELD_T: return stringfield_handler_fn;
 	case OPT_UINT_T: return uint_handler_fn;
+	case OPT_TIMELEN_T: return timelen_handler_fn;
 
 	case OPT_CUSTOM_T: return NULL;
 	}
@@ -1380,6 +1383,39 @@ static int uint_handler_fn(const struct aco_option *opt, struct ast_variable *va
 	return res;
 }
 
+/*! \brief Default option handler for timelen signed integers
+ * \note For a description of the opt->flags and opt->args values, see the documentation for
+ * enum aco_option_type in config_options.h
+ */
+static int timelen_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+	int *field = (int *)(obj + opt->args[0]);
+	unsigned int flags = PARSE_TIMELEN | opt->flags;
+	int res = 0;
+	if (opt->flags & PARSE_IN_RANGE) {
+		if (opt->flags & PARSE_DEFAULT) {
+			res = ast_parse_arg(var->value, flags, field, (enum ast_timelen) opt->args[1], (int) opt->args[2], (int) opt->args[3], opt->args[4]);
+		} else {
+			res = ast_parse_arg(var->value, flags, field, (enum ast_timelen) opt->args[1], (int) opt->args[2], (int) opt->args[3]);
+		}
+		if (res) {
+			if (opt->flags & PARSE_RANGE_DEFAULTS) {
+				ast_log(LOG_WARNING, "Failed to set %s=%s. Set to %d instead due to range limit (%d, %d)\n", var->name, var->value, *field, (int) opt->args[2], (int) opt->args[3]);
+				res = 0;
+			} else if (opt->flags & PARSE_DEFAULT) {
+				ast_log(LOG_WARNING, "Failed to set %s=%s, Set to default value %d instead.\n", var->name, var->value, *field);
+				res = 0;
+			}
+		}
+	} else if ((opt->flags & PARSE_DEFAULT) && ast_parse_arg(var->value, flags, field, (enum ast_timelen) opt->args[1], (int) opt->args[2])) {
+		ast_log(LOG_WARNING, "Attempted to set %s=%s, but set it to %d instead due to default)\n", var->name, var->value, *field);
+	} else {
+		res = ast_parse_arg(var->value, flags, field, (enum ast_timelen) opt->args[1]);
+	}
+
+	return res;
+}
+
 /*! \brief Default option handler for doubles
  * \note For a description of the opt->flags and opt->args values, see the documentation for
  * enum aco_option_type in config_options.h
diff --git a/main/features_config.c b/main/features_config.c
index 4a86f4b..2689687 100644
--- a/main/features_config.c
+++ b/main/features_config.c
@@ -1173,6 +1173,21 @@ char *ast_get_chan_features_xferfailsound(struct ast_channel *chan)
 	return res;
 }
 
+char *ast_get_chan_features_atxferabort(struct ast_channel *chan)
+{
+	char *res;
+	struct ast_features_xfer_config *cfg = ast_get_chan_features_xfer_config(chan);
+
+	if (!cfg) {
+		return NULL;
+	}
+
+	res = ast_strdup(cfg->atxferabort);
+	ao2_ref(cfg, -1);
+
+	return res;
+}
+
 struct ast_features_pickup_config *ast_get_chan_features_pickup_config(struct ast_channel *chan)
 {
 	RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
diff --git a/main/format_cache.c b/main/format_cache.c
index 74ebfe8..00563e8 100644
--- a/main/format_cache.c
+++ b/main/format_cache.c
@@ -193,6 +193,11 @@ struct ast_format *ast_format_mp4;
 struct ast_format *ast_format_vp8;
 
 /*!
+ * \brief Built-in cached vp9 format.
+ */
+struct ast_format *ast_format_vp9;
+
+/*!
  * \brief Built-in cached jpeg format.
  */
 struct ast_format *ast_format_jpeg;
@@ -336,6 +341,7 @@ static void format_cache_shutdown(void)
 	ao2_replace(ast_format_h264, NULL);
 	ao2_replace(ast_format_mp4, NULL);
 	ao2_replace(ast_format_vp8, NULL);
+	ao2_replace(ast_format_vp9, NULL);
 	ao2_replace(ast_format_t140_red, NULL);
 	ao2_replace(ast_format_t140, NULL);
 	ao2_replace(ast_format_none, NULL);
@@ -432,6 +438,8 @@ static void set_cached_format(const char *name, struct ast_format *format)
 		ao2_replace(ast_format_mp4, format);
 	} else if (!strcmp(name, "vp8")) {
 		ao2_replace(ast_format_vp8, format);
+	} else if (!strcmp(name, "vp9")) {
+		ao2_replace(ast_format_vp9, format);
 	} else if (!strcmp(name, "red")) {
 		ao2_replace(ast_format_t140_red, format);
 	} else if (!strcmp(name, "t140")) {
diff --git a/main/heap.c b/main/heap.c
index d40682a..c048565 100644
--- a/main/heap.c
+++ b/main/heap.c
@@ -146,9 +146,9 @@ struct ast_heap *ast_heap_create(unsigned int init_height, ast_heap_cmp_fn cmp_f
 
 	if (!(h->heap =
 #ifdef __AST_DEBUG_MALLOC
-			__ast_calloc(1, h->avail_len * sizeof(void *), file, lineno, func)
+			__ast_malloc(h->avail_len * sizeof(void *), file, lineno, func)
 #else
-			ast_calloc(1, h->avail_len * sizeof(void *))
+			ast_malloc(h->avail_len * sizeof(void *))
 #endif
 		)) {
 		ast_free(h);
diff --git a/main/http.c b/main/http.c
index cccc60b..d1a443a 100644
--- a/main/http.c
+++ b/main/http.c
@@ -451,9 +451,12 @@ void ast_http_send(struct ast_tcptls_session_instance *ser,
 	struct timeval now = ast_tvnow();
 	struct ast_tm tm;
 	char timebuf[80];
+	char buf[256];
+	int len;
 	int content_length = 0;
 	int close_connection;
 	struct ast_str *server_header_field = ast_str_create(MAX_SERVER_NAME_LENGTH);
+	int send_content;
 
 	if (!ser || !ser->f || !server_header_field) {
 		/* The connection is not open. */
@@ -504,8 +507,10 @@ void ast_http_send(struct ast_tcptls_session_instance *ser,
 		lseek(fd, 0, SEEK_SET);
 	}
 
+	send_content = method != AST_HTTP_HEAD || status_code >= 400;
+
 	/* send http header */
-	fprintf(ser->f,
+	if (fprintf(ser->f,
 		"HTTP/1.1 %d %s\r\n"
 		"%s"
 		"Date: %s\r\n"
@@ -513,43 +518,30 @@ void ast_http_send(struct ast_tcptls_session_instance *ser,
 		"%s"
 		"%s"
 		"Content-Length: %d\r\n"
-		"\r\n",
+		"\r\n"
+		"%s",
 		status_code, status_title ? status_title : "OK",
 		ast_str_buffer(server_header_field),
 		timebuf,
 		close_connection ? "Connection: close\r\n" : "",
 		static_content ? "" : "Cache-Control: no-cache, no-store\r\n",
 		http_header ? ast_str_buffer(http_header) : "",
-		content_length
-		);
-
-	/* send content */
-	if (method != AST_HTTP_HEAD || status_code >= 400) {
-		if (out && ast_str_strlen(out)) {
+		content_length,
+		send_content && out && ast_str_strlen(out) ? ast_str_buffer(out) : ""
+		) <= 0) {
+		ast_debug(1, "fprintf() failed: %s\n", strerror(errno));
+		close_connection = 1;
+	} else if (send_content && fd) {
+		/* send file content */
+		while ((len = read(fd, buf, sizeof(buf))) > 0) {
 			/*
 			 * NOTE: Because ser->f is a non-standard FILE *, fwrite() will probably not
 			 * behave exactly as documented.
 			 */
-			if (fwrite(ast_str_buffer(out), ast_str_strlen(out), 1, ser->f) != 1) {
-				ast_log(LOG_ERROR, "fwrite() failed: %s\n", strerror(errno));
+			if (fwrite(buf, len, 1, ser->f) != 1) {
+				ast_debug(1, "fwrite() failed: %s\n", strerror(errno));
 				close_connection = 1;
-			}
-		}
-
-		if (fd) {
-			char buf[256];
-			int len;
-
-			while ((len = read(fd, buf, sizeof(buf))) > 0) {
-				/*
-				 * NOTE: Because ser->f is a non-standard FILE *, fwrite() will probably not
-				 * behave exactly as documented.
-				 */
-				if (fwrite(buf, len, 1, ser->f) != 1) {
-					ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
-					close_connection = 1;
-					break;
-				}
+				break;
 			}
 		}
 	}
diff --git a/main/json.c b/main/json.c
index 9f42f0a..f45d585 100644
--- a/main/json.c
+++ b/main/json.c
@@ -825,6 +825,7 @@ struct ast_json *ast_json_vpack(char const *format, va_list ap)
 			ast_log(LOG_ERROR,
 				"Error building JSON from '%s': %s.\n",
 				format, error.text);
+			ast_log_backtrace();
 		}
 	}
 	return r;
diff --git a/main/libasteriskssl.c b/main/libasteriskssl.c
index dcb3f88..a89f191 100644
--- a/main/libasteriskssl.c
+++ b/main/libasteriskssl.c
@@ -31,20 +31,21 @@
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
+#include "asterisk/_private.h" /* ast_ssl_init() */
+
 #ifdef HAVE_OPENSSL
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 #endif
 
-#include <dlfcn.h>
+#if defined(HAVE_OPENSSL) && \
+	!defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 
-#include "asterisk/_private.h" /* ast_ssl_init() */
+#include <dlfcn.h>
 
 #include "asterisk/utils.h"
 #include "asterisk/lock.h"
 
-#ifdef HAVE_OPENSSL
-
 #define get_OpenSSL_function(func) do { real_##func = dlsym(RTLD_NEXT, __stringify(func)); } while(0)
 
 static int startup_complete;
@@ -74,7 +75,6 @@ static void ssl_lock(int mode, int n, const char *file, int line)
 	}
 }
 
-#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 int SSL_library_init(void)
 {
 #if defined(AST_DEVMODE)
@@ -116,9 +116,6 @@ void ERR_free_strings(void)
 {
 	/* we can't allow this to be called, ever */
 }
-#endif /* !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000L */
-
-#endif /* HAVE_OPENSSL */
 
 /*!
  * \internal
@@ -128,8 +125,6 @@ void ERR_free_strings(void)
  */
 int ast_ssl_init(void)
 {
-#if defined(HAVE_OPENSSL) && defined(OPENSSL_VERSION_NUMBER) && \
-	(OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER))
 	unsigned int i;
 	int (*real_SSL_library_init)(void);
 	void (*real_CRYPTO_set_id_callback)(unsigned long (*)(void));
@@ -194,7 +189,14 @@ int ast_ssl_init(void)
 
 	startup_complete = 1;
 
-#endif /* HAVE_OPENSSL and its version < 1.1 */
 	return 0;
 }
 
+#else
+
+int ast_ssl_init(void)
+{
+	return 0;
+}
+
+#endif
diff --git a/main/manager.c b/main/manager.c
index 2f16f04..aece73a 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -622,6 +622,25 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 			<ref type="managerEvent">AttendedTransfer</ref>
 		</see-also>
 	</manager>
+	<manager name="CancelAtxfer" language="en_US">
+		<synopsis>
+			Cancel an attended transfer.
+		</synopsis>
+		<syntax>
+			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+			<parameter name="Channel" required="true">
+				<para>The transferer channel.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Cancel an attended transfer. Note, this uses the configured cancel attended transfer
+			feature option (atxferabort) to cancel the transfer. If not available this action will fail.
+			</para>
+		</description>
+		<see-also>
+			<ref type="managerEvent">AttendedTransfer</ref>
+		</see-also>
+	</manager>
 	<manager name="Originate" language="en_US">
 		<synopsis>
 			Originate a call.
@@ -4930,6 +4949,47 @@ static int action_atxfer(struct mansession *s, const struct message *m)
 	return 0;
 }
 
+static int action_cancel_atxfer(struct mansession *s, const struct message *m)
+{
+	const char *name = astman_get_header(m, "Channel");
+	struct ast_channel *chan = NULL;
+	char *feature_code;
+	const char *digit;
+
+	if (ast_strlen_zero(name)) {
+		astman_send_error(s, m, "No channel specified");
+		return 0;
+	}
+
+	if (!(chan = ast_channel_get_by_name(name))) {
+		astman_send_error(s, m, "Channel specified does not exist");
+		return 0;
+	}
+
+	ast_channel_lock(chan);
+	feature_code = ast_get_chan_features_atxferabort(chan);
+	ast_channel_unlock(chan);
+
+	if (!feature_code) {
+		astman_send_error(s, m, "No disconnect feature code found");
+		ast_channel_unref(chan);
+		return 0;
+	}
+
+	for (digit = feature_code; *digit; ++digit) {
+		struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *digit };
+		ast_queue_frame(chan, &f);
+	}
+	ast_free(feature_code);
+
+	chan = ast_channel_unref(chan);
+
+	astman_send_ack(s, m, "CancelAtxfer successfully queued");
+
+	return 0;
+}
+
+
 static int check_blacklist(const char *cmd)
 {
 	char *cmd_copy, *cur_cmd;
@@ -6081,7 +6141,7 @@ static int action_coreshowchannels(struct mansession *s, const struct message *m
 	for (; (msg = ao2_iterator_next(&it_chans)); ao2_ref(msg, -1)) {
 		struct ast_channel_snapshot *cs = stasis_message_data(msg);
 		struct ast_str *built = ast_manager_build_channel_state_string_prefix(cs, "");
-		char durbuf[10] = "";
+		char durbuf[16] = "";
 
 		if (!built) {
 			continue;
@@ -8665,6 +8725,7 @@ static void manager_shutdown(void)
 	ast_manager_unregister("ListCategories");
 	ast_manager_unregister("Redirect");
 	ast_manager_unregister("Atxfer");
+	ast_manager_unregister("CancelAtxfer");
 	ast_manager_unregister("Originate");
 	ast_manager_unregister("Command");
 	ast_manager_unregister("ExtensionState");
@@ -8880,6 +8941,7 @@ static int __init_manager(int reload, int by_external_config)
 		ast_manager_register_xml_core("ListCategories", EVENT_FLAG_CONFIG, action_listcategories);
 		ast_manager_register_xml_core("Redirect", EVENT_FLAG_CALL, action_redirect);
 		ast_manager_register_xml_core("Atxfer", EVENT_FLAG_CALL, action_atxfer);
+		ast_manager_register_xml_core("CancelAtxfer", EVENT_FLAG_CALL, action_cancel_atxfer);
 		ast_manager_register_xml_core("Originate", EVENT_FLAG_ORIGINATE, action_originate);
 		ast_manager_register_xml_core("Command", EVENT_FLAG_COMMAND, action_command);
 		ast_manager_register_xml_core("ExtensionState", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_extensionstate);
diff --git a/main/netsock2.c b/main/netsock2.c
index 83538b5..73595fe 100644
--- a/main/netsock2.c
+++ b/main/netsock2.c
@@ -477,8 +477,12 @@ uint32_t ast_sockaddr_ipv4(const struct ast_sockaddr *addr)
 
 int ast_sockaddr_is_ipv4(const struct ast_sockaddr *addr)
 {
-	return addr->ss.ss_family == AF_INET &&
-	    addr->len == sizeof(struct sockaddr_in);
+	/*
+	 * Test addr->len first to be tolerant of an ast_sockaddr_setnull()
+	 * addr.  In that case addr->len might be the only value initialized.
+	 */
+	return addr->len == sizeof(struct sockaddr_in)
+		&& addr->ss.ss_family == AF_INET;
 }
 
 int ast_sockaddr_is_ipv4_mapped(const struct ast_sockaddr *addr)
@@ -500,8 +504,12 @@ int ast_sockaddr_is_ipv6_link_local(const struct ast_sockaddr *addr)
 
 int ast_sockaddr_is_ipv6(const struct ast_sockaddr *addr)
 {
-	return addr->ss.ss_family == AF_INET6 &&
-	    addr->len == sizeof(struct sockaddr_in6);
+	/*
+	 * Test addr->len first to be tolerant of an ast_sockaddr_setnull()
+	 * addr.  In that case addr->len might be the only value initialized.
+	 */
+	return addr->len == sizeof(struct sockaddr_in6)
+		&& addr->ss.ss_family == AF_INET6;
 }
 
 int ast_sockaddr_is_any(const struct ast_sockaddr *addr)
diff --git a/main/rtp_engine.c b/main/rtp_engine.c
index 3377087..d82bc49 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -2105,35 +2105,35 @@ int ast_rtp_dtls_cfg_parse(struct ast_rtp_dtls_cfg *dtls_cfg, const char *name,
 			return -1;
 		}
 	} 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;
 		}
+		ast_free(dtls_cfg->certfile);
 		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;
 		}
+		ast_free(dtls_cfg->pvtfile);
 		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;
 		}
+		ast_free(dtls_cfg->cafile);
 		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;
 		}
+		ast_free(dtls_cfg->capath);
 		dtls_cfg->capath = ast_strdup(value);
 	} else if (!strcasecmp(name, "dtlssetup")) {
 		if (!strcasecmp(value, "active")) {
@@ -2695,9 +2695,10 @@ int ast_rtp_engine_init(void)
 	set_next_mime_type(ast_format_siren7, 0, "audio", "G7221", 16000);
 	set_next_mime_type(ast_format_siren14, 0, "audio", "G7221", 32000);
 	set_next_mime_type(ast_format_g719, 0, "audio", "G719", 48000);
-	/* Opus and VP8 */
+	/* Opus, VP8, and VP9 */
 	set_next_mime_type(ast_format_opus, 0,  "audio", "opus", 48000);
 	set_next_mime_type(ast_format_vp8, 0,  "video", "VP8", 90000);
+	set_next_mime_type(ast_format_vp9, 0, "video", "VP9", 90000);
 
 	/* Define the static rtp payload mappings */
 	add_static_payload(0, ast_format_ulaw, 0);
@@ -2730,6 +2731,8 @@ int ast_rtp_engine_init(void)
 	add_static_payload(104, ast_format_mp4, 0);
 	add_static_payload(105, ast_format_t140_red, 0);   /* Real time text chat (with redundancy encoding) */
 	add_static_payload(106, ast_format_t140, 0);     /* Real time text chat */
+	add_static_payload(108, ast_format_vp9, 0);
+
 	add_static_payload(110, ast_format_speex, 0);
 	add_static_payload(111, ast_format_g726, 0);
 	add_static_payload(112, ast_format_g726_aal2, 0);
diff --git a/main/say.c b/main/say.c
index 1624194..48bd0ee 100644
--- a/main/say.c
+++ b/main/say.c
@@ -4082,9 +4082,9 @@ int ast_say_date_with_format_da(struct ast_channel *chan, time_t t, const char *
 				}
 				if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
 					if (tm.tm_min == 1) {
-						res = wait_file(chan, ints, "digits/minute", lang);
+						res = wait_file(chan, ints, "minute", lang);
 					} else {
-						res = wait_file(chan, ints, "digits/minutes", lang);
+						res = wait_file(chan, ints, "minutes", lang);
 					}
 				}
 				break;
@@ -4158,7 +4158,7 @@ int ast_say_date_with_format_da(struct ast_channel *chan, time_t t, const char *
 				if (!res) {
 					res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
 					if (!res) {
-						res = wait_file(chan, ints, "digits/seconds", lang);
+						res = wait_file(chan, ints, "seconds", lang);
 					}
 				}
 				break;
@@ -4285,9 +4285,9 @@ int ast_say_date_with_format_de(struct ast_channel *chan, time_t t, const char *
 
 				if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
 					if (tm.tm_min == 1) {
-						res = wait_file(chan, ints, "digits/minute", lang);
+						res = wait_file(chan, ints, "minute", lang);
 					} else {
-						res = wait_file(chan, ints, "digits/minutes", lang);
+						res = wait_file(chan, ints, "minutes", lang);
 					}
 				}
 				break;
@@ -4361,7 +4361,7 @@ int ast_say_date_with_format_de(struct ast_channel *chan, time_t t, const char *
 				if (!res) {
 					res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
 					if (!res) {
-						res = wait_file(chan, ints, tm.tm_sec == 1 ? "digits/second" : "digits/seconds", lang);
+						res = wait_file(chan, ints, tm.tm_sec == 1 ? "second" : "seconds", lang);
 					}
 				}
 				break;
@@ -5081,7 +5081,7 @@ int ast_say_date_with_format_fr(struct ast_channel *chan, time_t t, const char *
 				/* Seconds */
 				res = ast_say_number(chan, tm.tm_sec, ints, lang, (char * ) NULL);
 				if (!res) {
-					res = wait_file(chan, ints, "digits/second", lang);
+					res = wait_file(chan, ints, "second", lang);
 				}
 				break;
 			case 'T':
@@ -5732,9 +5732,9 @@ int ast_say_date_with_format_pl(struct ast_channel *chan, time_t thetime, const
 							one = tm.tm_sec % 10;
 
 							if (one > 1 && one < 5 && ten != 1)
-								res = wait_file(chan, ints, "digits/seconds", lang);
+								res = wait_file(chan, ints, "seconds", lang);
 							else
-								res = wait_file(chan, ints, "digits/second", lang);
+								res = wait_file(chan, ints, "second", lang);
 						}
 					}
 				}
@@ -5898,9 +5898,9 @@ int ast_say_date_with_format_pt(struct ast_channel *chan, time_t t, const char *
 					res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
 					if (!res) {
 						if (tm.tm_min > 1) {
-							res = wait_file(chan, ints, "digits/minutes", lang);
+							res = wait_file(chan, ints, "minutes", lang);
 						} else {
-							res = wait_file(chan, ints, "digits/minute", lang);
+							res = wait_file(chan, ints, "minute", lang);
 						}
 					}
 				} else {
@@ -5996,9 +5996,9 @@ int ast_say_date_with_format_pt(struct ast_channel *chan, time_t t, const char *
 					res = ast_say_number(chan, tm.tm_sec, ints, lang, NULL);
 					if (!res) {
 						if (tm.tm_sec > 1) {
-							res = wait_file(chan, ints, "digits/seconds", lang);
+							res = wait_file(chan, ints, "seconds", lang);
 						} else {
-							res = wait_file(chan, ints, "digits/second", lang);
+							res = wait_file(chan, ints, "second", lang);
 						}
 					}
 				} else {
@@ -6212,7 +6212,7 @@ int ast_say_date_with_format_zh(struct ast_channel *chan, time_t t, const char *
 					}
 				}
 				if (!res) {
-					res = wait_file(chan, ints, "digits/minute", lang);
+					res = wait_file(chan, ints, "minute", lang);
 				}
 				break;
 			case 'P':
@@ -6296,7 +6296,7 @@ int ast_say_date_with_format_zh(struct ast_channel *chan, time_t t, const char *
 					}
 				}
 				if (!res) {
-					res = wait_file(chan, ints, "digits/second", lang);
+					res = wait_file(chan, ints, "second", lang);
 				}
 				break;
 			case 'T':
@@ -6451,7 +6451,7 @@ int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const
 	    if (tm.tm_min > 0) {
 			res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
 			if (!res)
-				res = ast_streamfile(chan, "digits/minute", lang);
+				res = ast_streamfile(chan, "minute", lang);
 		}
 	return res;
 }
@@ -6546,9 +6546,9 @@ int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, con
 			res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
 		if (!res) {
 			if (tm.tm_min > 1)
-				res = wait_file(chan, ints, "digits/minutes", lang);
+				res = wait_file(chan, ints, "minutes", lang);
 			else
-				res = wait_file(chan, ints, "digits/minute", lang);
+				res = wait_file(chan, ints, "minute", lang);
 		}
 	}
 	return res;
@@ -6608,7 +6608,7 @@ int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const
 	if (!res)
 		res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
 	if (!res)
-		res = ast_streamfile(chan, "digits/minute", lang);
+		res = ast_streamfile(chan, "minute", lang);
 	if (!res)
 		res = ast_waitstream(chan, ints);
 	return res;
@@ -7031,7 +7031,7 @@ int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, co
 	if (!res)
 		res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
 	if (!res)
-		res = ast_streamfile(chan, "digits/minute", lang);
+		res = ast_streamfile(chan, "minute", lang);
 	if (!res)
 		res = ast_waitstream(chan, ints);
 	return res;
@@ -7909,7 +7909,7 @@ static int ast_say_date_with_format_gr(struct ast_channel *chan, time_t t, const
 			if (!res)
 				res = ast_say_number_full_gr(chan, tm.tm_sec, ints, lang, -1, -1);
 			if (!res)
-				ast_copy_string(nextmsg, "digits/seconds", sizeof(nextmsg));
+				ast_copy_string(nextmsg, "seconds", sizeof(nextmsg));
 			res = wait_file(chan, ints, nextmsg, lang);
 			break;
 		case 'T':
diff --git a/main/stdtime/localtime.c b/main/stdtime/localtime.c
index 9cdf614..5b5526e 100644
--- a/main/stdtime/localtime.c
+++ b/main/stdtime/localtime.c
@@ -2436,7 +2436,7 @@ static const char *store_by_locale(locale_t prevlocale)
 			cur = NULL;
 			AST_LIST_LOCK(&localelist);
 			for (x = 0; x < 10000; x++) {
-				char name[5];
+				char name[6];
 				snprintf(name, sizeof(name), "%04d", x);
 				if (!find_by_name(name)) {
 					if ((cur = ast_calloc(1, sizeof(*cur) + strlen(name) + 1))) {
diff --git a/main/strings.c b/main/strings.c
index 7f2025a..8102c3e 100644
--- a/main/strings.c
+++ b/main/strings.c
@@ -314,7 +314,7 @@ regex:
 	}
 
 equals:
-	scan_numeric = (sscanf(left, "%lf", &left_num) && sscanf(internal_right, "%lf", &right_num));
+	scan_numeric = (sscanf(left, "%lf", &left_num) > 0 && sscanf(internal_right, "%lf", &right_num) > 0);
 
 	if (internal_op[0] == '=') {
 		if (ast_strlen_zero(left) && ast_strlen_zero(internal_right)) {
@@ -371,4 +371,23 @@ equals:
 	return 0;
 }
 
+char *ast_read_line_from_buffer(char **buffer)
+{
+	char *start = *buffer;
+
+	if (!buffer || !*buffer || *(*buffer) == '\0') {
+		return NULL;
+	}
+
+	while (*(*buffer) && *(*buffer) != '\n' ) {
+		(*buffer)++;
+	}
+
+	*(*buffer) = '\0';
+	if (*(*buffer - 1) == '\r') {
+		*(*buffer - 1) = '\0';
+	}
+	(*buffer)++;
 
+	return start;
+}
diff --git a/main/stun.c b/main/stun.c
index fe1afba..6ebb2ac 100644
--- a/main/stun.c
+++ b/main/stun.c
@@ -345,6 +345,8 @@ int ast_stun_handle_packet(int s, struct sockaddr_in *src, unsigned char *data,
 			if (st.username) {
 				append_attr_string(&attr, STUN_USERNAME, st.username, &resplen, &respleft);
 				snprintf(combined, sizeof(combined), "%16s%16s", st.username + 16, st.username);
+			} else {
+				combined[0] = '\0';
 			}
 
 			append_attr_address(&attr, STUN_MAPPED_ADDRESS, src, &resplen, &respleft);
@@ -400,8 +402,6 @@ int ast_stun_request(int s, struct sockaddr_in *dst,
 	stun_req_id(req);
 	reqlen = 0;
 	reqleft = sizeof(req_buf) - sizeof(struct stun_header);
-	req->msgtype = 0;
-	req->msglen = 0;
 	attr = (struct stun_attr *) req->ies;
 	if (username) {
 		append_attr_string(&attr, STUN_USERNAME, username, &reqlen, &reqleft);
diff --git a/main/tcptls.c b/main/tcptls.c
index bc2d64b..820a03e 100644
--- a/main/tcptls.c
+++ b/main/tcptls.c
@@ -636,7 +636,6 @@ static int check_tcptls_cert_name(ASN1_STRING *cert_str, const char *hostname, c
 
 	return ret;
 }
-
 #endif
 
 /*! \brief
@@ -1120,7 +1119,8 @@ struct ast_tcptls_session_instance *ast_tcptls_client_create(struct ast_tcptls_s
 
 	/* if a local address was specified, bind to it so the connection will
 	   originate from the desired address */
-	if (!ast_sockaddr_isnull(&desc->local_address)) {
+	if (!ast_sockaddr_isnull(&desc->local_address) &&
+	    !ast_sockaddr_is_any(&desc->local_address)) {
 		setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
 		if (ast_bind(desc->accept_fd, &desc->local_address)) {
 			ast_log(LOG_ERROR, "Unable to bind %s to %s: %s\n",
diff --git a/main/utils.c b/main/utils.c
index b31db59..0824a37 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -1530,7 +1530,7 @@ char *ast_strsep(char **iss, const char sep, uint32_t flags)
 	int found = 0;
 	char stack[8];
 
-	if (iss == NULL || *iss == '\0') {
+	if (ast_strlen_zero(st)) {
 		return NULL;
 	}
 
diff --git a/makeopts.in b/makeopts.in
index 8f83ed9..4baefa2 100644
--- a/makeopts.in
+++ b/makeopts.in
@@ -29,8 +29,9 @@ FETCH=@FETCH@
 DOWNLOAD=@DOWNLOAD@
 DOWNLOAD_TO_STDOUT=@DOWNLOAD_TO_STDOUT@
 DOWNLOAD_TIMEOUT=@DOWNLOAD_TIMEOUT@
-SOUNDS_CACHE_DIR=@SOUNDS_CACHE_DIR@
-EXTERNALS_CACHE_DIR=@EXTERNALS_CACHE_DIR@
+AST_DOWNLOAD_CACHE=@AST_DOWNLOAD_CACHE@
+SOUNDS_CACHE_DIR=$(or @SOUNDS_CACHE_DIR@,${AST_DOWNLOAD_CACHE})
+EXTERNALS_CACHE_DIR=$(or @EXTERNALS_CACHE_DIR@,${AST_DOWNLOAD_CACHE})
 RUBBER=@RUBBER@
 CATDVI=@CATDVI@
 KPATHSEA=@KPATHSEA@
@@ -116,6 +117,7 @@ AST_ASTERISKSSL=@AST_ASTERISKSSL@
 AST_DECLARATION_AFTER_STATEMENT=@AST_DECLARATION_AFTER_STATEMENT@
 AST_TRAMPOLINES=@AST_TRAMPOLINES@
 AST_NO_STRICT_OVERFLOW=@AST_NO_STRICT_OVERFLOW@
+AST_NO_FORMAT_TRUNCATION=@AST_NO_FORMAT_TRUNCATION@
 AST_SHADOW_WARNINGS=@AST_SHADOW_WARNINGS@
 AST_NESTED_FUNCTIONS=@AST_NESTED_FUNCTIONS@
 AST_CLANG_BLOCKS=@AST_CLANG_BLOCKS@
diff --git a/res/res_ari.c b/res/res_ari.c
index 809b7f6..1ac5327 100644
--- a/res/res_ari.c
+++ b/res/res_ari.c
@@ -883,7 +883,7 @@ static int ast_ari_callback(struct ast_tcptls_session_instance *ser,
 	RAII_VAR(struct ast_variable *, post_vars, NULL, ast_variables_destroy);
 	struct ast_variable *var;
 	const char *app_name = NULL;
-	RAII_VAR(struct ast_json *, body, ast_json_null(), ast_json_free);
+	RAII_VAR(struct ast_json *, body, ast_json_null(), ast_json_unref);
 	int debug_app = 0;
 
 	if (!response_body) {
diff --git a/res/res_calendar.c b/res/res_calendar.c
index 3725c94..16a3265 100644
--- a/res/res_calendar.c
+++ b/res/res_calendar.c
@@ -341,10 +341,7 @@ static void calendar_destructor(void *obj)
 	}
 	ast_calendar_clear_events(cal);
 	ast_string_field_free_memory(cal);
-	if (cal->vars) {
-		ast_variables_destroy(cal->vars);
-		cal->vars = NULL;
-	}
+	ast_variables_destroy(cal->vars);
 	ao2_ref(cal->events, -1);
 	ao2_unlock(cal);
 }
@@ -406,28 +403,22 @@ static struct ast_calendar *build_calendar(struct ast_config *cfg, const char *c
 {
 	struct ast_calendar *cal;
 	struct ast_variable *v, *last = NULL;
-	int new_calendar = 0;
 
-	if (!(cal = find_calendar(cat))) {
-		new_calendar = 1;
-		if (!(cal = ao2_alloc(sizeof(*cal), calendar_destructor))) {
-			ast_log(LOG_ERROR, "Could not allocate calendar structure. Stopping.\n");
-			return NULL;
-		}
+	if (!(cal = ao2_alloc(sizeof(*cal), calendar_destructor))) {
+		ast_log(LOG_ERROR, "Could not allocate calendar structure. Stopping.\n");
+		return NULL;
+	}
 
-		if (!(cal->events = ao2_container_alloc(CALENDAR_BUCKETS, event_hash_fn, event_cmp_fn))) {
-			ast_log(LOG_ERROR, "Could not allocate events container for %s\n", cat);
-			cal = unref_calendar(cal);
-			return NULL;
-		}
+	if (!(cal->events = ao2_container_alloc(CALENDAR_BUCKETS, event_hash_fn, event_cmp_fn))) {
+		ast_log(LOG_ERROR, "Could not allocate events container for %s\n", cat);
+		cal = unref_calendar(cal);
+		return NULL;
+	}
 
-		if (ast_string_field_init(cal, 32)) {
-			ast_log(LOG_ERROR, "Couldn't create string fields for %s\n", cat);
-			cal = unref_calendar(cal);
-			return NULL;
-		}
-	} else {
-		cal->pending_deletion = 0;
+	if (ast_string_field_init(cal, 32)) {
+		ast_log(LOG_ERROR, "Couldn't create string fields for %s\n", cat);
+		cal = unref_calendar(cal);
+		return NULL;
 	}
 
 	ast_string_field_set(cal, name, cat);
@@ -482,17 +473,22 @@ static struct ast_calendar *build_calendar(struct ast_config *cfg, const char *c
 		}
 	}
 
-	if (new_calendar) {
-		cal->thread = AST_PTHREADT_NULL;
-		ast_cond_init(&cal->unload, NULL);
-		ao2_link(calendars, cal);
-		if (ast_pthread_create(&cal->thread, NULL, cal->tech->load_calendar, cal)) {
-			/* If we start failing to create threads, go ahead and return NULL
-			 * and the tech module will be unregistered
-			 */ 
-			ao2_unlink(calendars, cal);
-			cal = unref_calendar(cal);
-		}
+	if (cal->autoreminder && ast_strlen_zero(cal->notify_channel)) {
+		ast_log(LOG_WARNING,
+				"You have set 'autoreminder' but not 'channel' for calendar '%s.' "
+				"Notifications will not occur.\n",
+				cal->name);
+	}
+
+	cal->thread = AST_PTHREADT_NULL;
+	ast_cond_init(&cal->unload, NULL);
+	ao2_link(calendars, cal);
+	if (ast_pthread_create(&cal->thread, NULL, cal->tech->load_calendar, cal)) {
+		/* If we start failing to create threads, go ahead and return NULL
+		 * and the tech module will be unregistered
+		 */
+		ao2_unlink(calendars, cal);
+		cal = unref_calendar(cal);
 	}
 
 	return cal;
@@ -735,7 +731,7 @@ static void *do_notify(void *data)
 	struct ast_channel *chan = NULL;
 	struct ast_variable *itervar;
 	char *tech, *dest;
-	char buf[8];
+	char buf[33];
 	struct ast_format_cap *caps;
 
 	tech = ast_strdupa(event->owner->notify_channel);
@@ -948,7 +944,7 @@ static int schedule_calendar_event(struct ast_calendar *cal, struct ast_calendar
 	event = cmp_event ? cmp_event : old_event;
 
 	ao2_lock(event);
-	if (!cmp_event || old_event->alarm != event->alarm) {
+	if (!ast_strlen_zero(cal->notify_channel) && (!cmp_event || old_event->alarm != event->alarm)) {
 		changed = 1;
 		if (cal->autoreminder) {
 			alarm_notify_sched = (event->start - (60 * cal->autoreminder) - now.tv_sec) * 1000;
@@ -957,7 +953,7 @@ static int schedule_calendar_event(struct ast_calendar *cal, struct ast_calendar
 		}
 
 		/* For now, send the notification if we missed it, but the meeting hasn't happened yet */
-		if (event->start >=  now.tv_sec) {
+		if (event->start >= now.tv_sec) {
 			if (alarm_notify_sched <= 0) {
 				alarm_notify_sched = 1;
 			}
@@ -1590,7 +1586,7 @@ static char *epoch_to_string(char *buf, size_t buflen, time_t epoch)
 
 static char *handle_show_calendar(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-#define FORMAT "%-17.17s : %-20.20s\n"
+#define FORMAT  "%-18.18s : %-20.20s\n"
 #define FORMAT2 "%-12.12s: %-40.60s\n"
 	struct ao2_iterator i;
 	struct ast_calendar *cal;
@@ -1639,7 +1635,13 @@ static char *handle_show_calendar(struct ast_cli_entry *e, int cmd, struct ast_c
 	ast_cli(a->fd, FORMAT, "Notify appdata", cal->notify_appdata);
 	ast_cli(a->fd, "%-17.17s : %d\n", "Refresh time", cal->refresh);
 	ast_cli(a->fd, "%-17.17s : %d\n", "Timeframe", cal->timeframe);
-	ast_cli(a->fd, "%-17.17s : %d\n", "Autoreminder", cal->autoreminder);
+
+	if (cal->autoreminder) {
+		ast_cli(a->fd, "%-17.17s : %d minutes before event\n", "Autoreminder", cal->autoreminder);
+	} else {
+		ast_cli(a->fd, "%-17.17s : None\n", "Autoreminder");
+	}
+
 	ast_cli(a->fd, "%s\n", "Events");
 	ast_cli(a->fd, "%s\n", "------");
 
@@ -1757,30 +1759,16 @@ static struct ast_custom_function calendar_event_function = {
 	.read = calendar_event_read,
 };
 
-static int cb_pending_deletion(void *user_data, void *arg, int flags)
-{
-	struct ast_calendar *cal = user_data;
-
-	cal->pending_deletion = 1;
-
-	return CMP_MATCH;
-}
-
-static int cb_rm_pending_deletion(void *user_data, void *arg, int flags)
-{
-	struct ast_calendar *cal = user_data;
-
-	return cal->pending_deletion ? CMP_MATCH : 0;
-}
-
 static int reload(void)
 {
 	struct ast_calendar_tech *iter;
 
 	ast_mutex_lock(&reloadlock);
 
-	/* Mark existing calendars for deletion */
-	ao2_callback(calendars, OBJ_NODATA | OBJ_MULTIPLE, cb_pending_deletion, NULL);
+	/* Delete all of the calendars */
+	ao2_callback(calendars, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
+
+	/* Load configuration */
 	load_config(1);
 
 	AST_LIST_LOCK(&techs);
@@ -1791,9 +1779,6 @@ static int reload(void)
 	}
 	AST_LIST_UNLOCK(&techs);
 
-	/* Delete calendars that no longer show up in the config */
-	ao2_callback(calendars, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, cb_rm_pending_deletion, NULL);
-
 	ast_mutex_unlock(&reloadlock);
 
 	return 0;
diff --git a/res/res_calendar_caldav.c b/res/res_calendar_caldav.c
index 6b4f908..02a44c7 100644
--- a/res/res_calendar_caldav.c
+++ b/res/res_calendar_caldav.c
@@ -80,6 +80,7 @@ static void caldav_destructor(void *obj)
 	if (pvt->session) {
 		ne_session_destroy(pvt->session);
 	}
+	ne_uri_free(&pvt->uri);
 	ast_string_field_free_memory(pvt);
 
 	ao2_callback(pvt->events, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
@@ -158,6 +159,7 @@ static struct ast_str *caldav_request(struct caldav_pvt *pvt, const char *method
 	ne_add_response_body_reader(req, debug_response_handler, fetch_response_reader, &response);
 	ne_set_request_body_buffer(req, ast_str_buffer(req_body), ast_str_strlen(req_body));
 	ne_add_request_header(req, "Content-type", ast_strlen_zero(content_type) ? "text/xml" : content_type);
+	ne_add_request_header(req, "Depth", "1");
 
 	ret = ne_request_dispatch(req);
 	ne_request_destroy(req);
@@ -478,17 +480,26 @@ struct xmlstate {
 	time_t end;
 };
 
-static void handle_start_element(void *data, const xmlChar *fullname, const xmlChar **atts)
+static const xmlChar *caldav_node_localname = BAD_CAST "calendar-data";
+static const xmlChar *caldav_node_nsuri     = BAD_CAST "urn:ietf:params:xml:ns:caldav";
+
+static void handle_start_element(void *data,
+								 const xmlChar *localname, const xmlChar *prefix, const xmlChar *uri,
+								 int nb_namespaces, const xmlChar **namespaces,
+								 int nb_attributes, int nb_defaulted, const xmlChar **attributes)
 {
 	struct xmlstate *state = data;
 
-	if (!xmlStrcasecmp(fullname, BAD_CAST "C:calendar-data") || !xmlStrcasecmp(fullname, BAD_CAST "caldav:calendar-data")) {
-		state->in_caldata = 1;
-		ast_str_reset(state->cdata);
+	if (xmlStrcmp(localname, caldav_node_localname) || xmlStrcmp(uri, caldav_node_nsuri)) {
+		return;
 	}
+
+	state->in_caldata = 1;
+	ast_str_reset(state->cdata);
 }
 
-static void handle_end_element(void *data, const xmlChar *name)
+static void handle_end_element(void *data,
+							   const xmlChar *localname, const xmlChar *prefix, const xmlChar *uri)
 {
 	struct xmlstate *state = data;
 	struct icaltimetype start, end;
@@ -496,7 +507,7 @@ static void handle_end_element(void *data, const xmlChar *name)
 	icalcomponent *iter;
 	icalcomponent *comp;
 
-	if (xmlStrcasecmp(name, BAD_CAST "C:calendar-data") && xmlStrcasecmp(name, BAD_CAST "caldav:calendar-data")) {
+	if (xmlStrcmp(localname, caldav_node_localname) || xmlStrcmp(uri, caldav_node_nsuri)) {
 		return;
 	}
 
@@ -559,9 +570,23 @@ static int update_caldav(struct caldav_pvt *pvt)
 	state.start = start;
 	state.end = end;
 
+	/*
+	 * We want SAX2, so you assume that we want to call xmlSAXVersion() here, and
+	 * that certainly seems like the right thing to do, but the default SAX
+	 * handling functions assume that the 'data' pointer is going to be a
+	 * xmlParserCtxtPtr, not a user data pointer, so we have to make sure that we
+	 * are only calling the handlers that we control.
+	 *
+	 * So instead we hack things up a bit, clearing the struct and then assigning
+	 * the magic number manually.
+	 *
+	 * There may be a cleaner way to do this, but frankly the libxml2 docs are
+	 * pretty sparse.
+	 */
 	memset(&saxHandler, 0, sizeof(saxHandler));
-	saxHandler.startElement = handle_start_element;
-	saxHandler.endElement = handle_end_element;
+	saxHandler.initialized = XML_SAX2_MAGIC;
+	saxHandler.startElementNs = handle_start_element;
+	saxHandler.endElementNs = handle_end_element;
 	saxHandler.characters = handle_characters;
 
 	xmlSAXUserParseMemory(&saxHandler, &state, ast_str_buffer(response), ast_str_strlen(response));
diff --git a/res/res_calendar_icalendar.c b/res/res_calendar_icalendar.c
index 8ac9051..4e335dd 100644
--- a/res/res_calendar_icalendar.c
+++ b/res/res_calendar_icalendar.c
@@ -179,7 +179,7 @@ static time_t icalfloat_to_timet(icaltimetype time)
 }
 
 /* span->start & span->end may be dates or floating times which have no timezone,
- * which would mean that they should apply to the local timezone for all recepients.
+ * which would mean that they should apply to the local timezone for all recipients.
  * For example, if a meeting was set for 1PM-2PM floating time, people in different time
  * zones would not be scheduled at the same local times.  Dates are often treated as
  * floating times, so all day events will need to be converted--so we can trust the
@@ -251,7 +251,42 @@ static void icalendar_add_event(icalcomponent *comp, struct icaltime_span *span,
 		}
 	}
 
-	/* Get the attendees */
+	/*
+	 * If comp has an RRULE and/or RDATE property, we need to check whether
+	 * another vevent component supercedes this span. Such a component would
+	 * have two characteristics:
+	 *  - its UID is the same as comp
+	 *  - its RECURRENCE-ID property is the same time as span->start
+	 */
+	if (icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY)
+	   || icalcomponent_get_first_property(comp, ICAL_RDATE_PROPERTY)) {
+		icalcompiter comp_iter;
+		icaltimetype span_start = icaltime_from_timet_with_zone(
+			event->start, icaltime_is_date(start), icaltime_get_timezone(start));
+
+		icaltime_set_timezone(&span_start, icaltime_get_timezone(start));
+		for (comp_iter = icalcomponent_begin_component(pvt->data, ICAL_VEVENT_COMPONENT);
+			 icalcompiter_deref(&comp_iter);
+			 icalcompiter_next(&comp_iter)) {
+			icalcomponent *vevent = icalcompiter_deref(&comp_iter);
+			icalproperty *uid = icalcomponent_get_first_property(vevent, ICAL_UID_PROPERTY);
+
+			if (uid && !strcmp(icalproperty_get_value_as_string(uid), event->uid)) {
+				icaltimetype recurrence_id = icalcomponent_get_recurrenceid(vevent);
+
+				/* Set the same timezone that we want to compare against */
+				icaltime_set_timezone(&recurrence_id, icaltime_get_timezone(start));
+
+				if (!icaltime_compare(recurrence_id, span_start)
+				   && icaltime_is_date(span_start) == icaltime_is_date(recurrence_id)) {
+					event = ast_calendar_unref_event(event);
+					return;
+				}
+			}
+		}
+	}
+
+    /* Get the attendees */
 	for (prop = icalcomponent_get_first_property(comp, ICAL_ATTENDEE_PROPERTY);
 			prop; prop = icalcomponent_get_next_property(comp, ICAL_ATTENDEE_PROPERTY)) {
 		struct ast_calendar_attendee *attendee;
@@ -335,7 +370,7 @@ static void icalendar_add_event(icalcomponent *comp, struct icaltime_span *span,
 	start_time = icaltime_current_time_with_zone(icaltimezone_get_utc_timezone());
 	end_time = icaltime_current_time_with_zone(icaltimezone_get_utc_timezone());
 	end_time.second += pvt->owner->timeframe * 60;
-	icaltime_normalize(end_time);
+	end_time = icaltime_normalize(end_time);
 
 	for (iter = icalcomponent_get_first_component(pvt->data, ICAL_VEVENT_COMPONENT);
 	     iter;
diff --git a/res/res_config_pgsql.c b/res/res_config_pgsql.c
index e74b730..0244001 100644
--- a/res/res_config_pgsql.c
+++ b/res/res_config_pgsql.c
@@ -54,6 +54,7 @@ AST_THREADSTORAGE(semibuf_buf);
 static PGconn *pgsqlConn = NULL;
 static int version;
 #define has_schema_support	(version > 70300 ? 1 : 0)
+#define USE_BACKSLASH_AS_STRING	(version >= 90100 ? 1 : 0)
 
 #define MAX_DB_OPTION_SIZE 64
 
@@ -419,7 +420,7 @@ static struct columns *find_column(struct tables *t, const char *colname)
 }
 
 #define IS_SQL_LIKE_CLAUSE(x) ((x) && ast_ends_with(x, " LIKE"))
-static char *ESCAPE_CLAUSE = " ESCAPE '\\'";
+#define ESCAPE_CLAUSE (USE_BACKSLASH_AS_STRING ? " ESCAPE '\\'" : " ESCAPE '\\\\'")
 
 static struct ast_variable *realtime_pgsql(const char *database, const char *tablename, const struct ast_variable *fields)
 {
@@ -1329,7 +1330,7 @@ static int require_pgsql(const char *database, const char *tablename, va_list ap
 					/* Size is minimum length; make it at least 50% greater,
 					 * just to be sure, because PostgreSQL doesn't support
 					 * resizing columns. */
-					snprintf(fieldtype, sizeof(fieldtype), "CHAR(%d)",
+					snprintf(fieldtype, sizeof(fieldtype), "CHAR(%hhu)",
 						size < 15 ? size * 2 :
 						(size * 3 / 2 > 255) ? 255 : size * 3 / 2);
 				} else if (type == RQ_INTEGER1 || type == RQ_UINTEGER1 || type == RQ_INTEGER2) {
diff --git a/res/res_http_post.c b/res/res_http_post.c
index 2ee792a..3e1ed03 100644
--- a/res/res_http_post.c
+++ b/res/res_http_post.c
@@ -57,6 +57,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #ifdef GMIME_TYPE_CONTENT_TYPE
 #define AST_GMIME_VER_24
 #endif
+#if GMIME_MAJOR_VERSION >= 3
+#define AST_GMIME_VER_30
+#endif
 
 /* just a little structure to hold callback info for gmime */
 struct mime_cbinfo {
@@ -86,7 +89,11 @@ static void post_raw(GMimePart *part, const char *post_dir, const char *fn)
 
 	stream = g_mime_stream_fs_new(fd);
 
+#ifdef AST_GMIME_VER_30
+	content = g_mime_part_get_content(part);
+#else
 	content = g_mime_part_get_content_object(part);
+#endif
 	g_mime_data_wrapper_write_to_stream(content, stream);
 	g_mime_stream_flush(stream);
 
@@ -109,7 +116,11 @@ static GMimeMessage *parse_message(FILE *f)
 	
 	g_object_unref(stream);
 
-	message = g_mime_parser_construct_message(parser);
+	message = g_mime_parser_construct_message(parser
+#ifdef AST_GMIME_VER_30
+			, NULL
+#endif
+	);
 
 	g_object_unref(parser);
 
@@ -488,7 +499,11 @@ static int reload(void)
 
 static int load_module(void)
 {
-	g_mime_init(0);
+	g_mime_init(
+#ifndef AST_GMIME_VER_30
+			0
+#endif
+	);
 
 	__ast_http_post_load(0);
 
diff --git a/res/res_monitor.c b/res/res_monitor.c
index ebf9843..c4ee674 100644
--- a/res/res_monitor.c
+++ b/res/res_monitor.c
@@ -62,17 +62,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 		<syntax>
 			<parameter name="file_format" argsep=":">
 				<argument name="file_format" required="true">
-					<para>optional, if not set, defaults to <literal>wav</literal></para>
+					<para>Optional.  If not set, defaults to <literal>wav</literal></para>
 				</argument>
 				<argument name="urlbase" />
 			</parameter>
 			<parameter name="fname_base">
-				<para>if set, changes the filename used to the one specified.</para>
+				<para>If set, changes the filename used to the one specified.</para>
 			</parameter>
 			<parameter name="options">
 				<optionlist>
 					<option name="m">
-						<para>when the recording ends mix the two leg files into one and
+						<para>When the recording ends mix the two leg files into one and
 						delete the two leg files. If the variable <variable>MONITOR_EXEC</variable>
 						is set, the application referenced in it will be executed instead of
 						soxmix/sox and the raw leg files will NOT be deleted automatically.
@@ -83,6 +83,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 						will be passed on as additional arguments to <variable>MONITOR_EXEC</variable>.
 						Both <variable>MONITOR_EXEC</variable> and the Mix flag can be set from the
 						administrator interface.</para>
+						<warning><para>Do not use untrusted strings such as
+						<variable>CALLERID(num)</variable> or <variable>CALLERID(name)</variable>
+						as part of <variable>MONITOR_EXEC</variable> or
+						<variable>MONITOR_EXEC_ARGS</variable>.  You risk a command injection
+						attack executing arbitrary commands if the untrusted strings aren't
+						filtered to remove dangerous characters.  See function
+						<variable>FILTER()</variable>.</para></warning>
 					</option>
 					<option name="b">
 						<para>Don't begin recording unless a call is bridged to another channel.</para>
@@ -460,7 +467,7 @@ int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_l
 	LOCK_IF_NEEDED(chan, need_lock);
 
 	if (ast_channel_monitor(chan)) {
-		char filename[ FILENAME_MAX ];
+		RAII_VAR(struct ast_str *, tmp, ast_str_create(1024), ast_free);
 
 		if (ast_channel_monitor(chan)->read_stream) {
 			ast_closestream(ast_channel_monitor(chan)->read_stream);
@@ -469,31 +476,29 @@ int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_l
 			ast_closestream(ast_channel_monitor(chan)->write_stream);
 		}
 
-		if (ast_channel_monitor(chan)->filename_changed && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) {
+		if (tmp && ast_channel_monitor(chan)->filename_changed && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) {
 			if (ast_fileexists(ast_channel_monitor(chan)->read_filename,NULL,NULL) > 0) {
-				snprintf(filename, FILENAME_MAX, "%s-in", ast_channel_monitor(chan)->filename_base);
-				if (ast_fileexists(filename, NULL, NULL) > 0) {
-					ast_filedelete(filename, NULL);
+				ast_str_set(&tmp, 0, "%s-in", ast_channel_monitor(chan)->filename_base);
+				if (ast_fileexists(ast_str_buffer(tmp), NULL, NULL) > 0) {
+					ast_filedelete(ast_str_buffer(tmp), NULL);
 				}
-				ast_filerename(ast_channel_monitor(chan)->read_filename, filename, ast_channel_monitor(chan)->format);
+				ast_filerename(ast_channel_monitor(chan)->read_filename, ast_str_buffer(tmp), ast_channel_monitor(chan)->format);
 			} else {
 				ast_log(LOG_WARNING, "File %s not found\n", ast_channel_monitor(chan)->read_filename);
 			}
 
-			if (ast_fileexists(ast_channel_monitor(chan)->write_filename,NULL,NULL) > 0) {
-				snprintf(filename, FILENAME_MAX, "%s-out", ast_channel_monitor(chan)->filename_base);
-				if (ast_fileexists(filename, NULL, NULL) > 0) {
-					ast_filedelete(filename, NULL);
+			if (tmp && ast_fileexists(ast_channel_monitor(chan)->write_filename,NULL,NULL) > 0) {
+				ast_str_set(&tmp, 0, "%s-out", ast_channel_monitor(chan)->filename_base);
+				if (ast_fileexists(ast_str_buffer(tmp), NULL, NULL) > 0) {
+					ast_filedelete(ast_str_buffer(tmp), NULL);
 				}
-				ast_filerename(ast_channel_monitor(chan)->write_filename, filename, ast_channel_monitor(chan)->format);
+				ast_filerename(ast_channel_monitor(chan)->write_filename, ast_str_buffer(tmp), ast_channel_monitor(chan)->format);
 			} else {
 				ast_log(LOG_WARNING, "File %s not found\n", ast_channel_monitor(chan)->write_filename);
 			}
 		}
 
-		if (ast_channel_monitor(chan)->joinfiles && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) {
-			char tmp[1024];
-			char tmp2[1024];
+		if (tmp && ast_channel_monitor(chan)->joinfiles && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) {
 			const char *format = !strcasecmp(ast_channel_monitor(chan)->format,"wav49") ? "WAV" : ast_channel_monitor(chan)->format;
 			char *fname_base = ast_channel_monitor(chan)->filename_base;
 			const char *execute, *execute_args;
@@ -514,16 +519,17 @@ int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_l
 			if (ast_strlen_zero(execute_args)) {
 				execute_args = "";
 			}
-			
-			snprintf(tmp, sizeof(tmp), "%s \"%s-in.%s\" \"%s-out.%s\" \"%s.%s\" %s &",
+
+			ast_str_set(&tmp, 0, delfiles ? "( " : "");
+			ast_str_append(&tmp, 0, "%s \"%s-in.%s\" \"%s-out.%s\" \"%s.%s\" %s &",
 				execute, fname_base, format, fname_base, format, fname_base, format,execute_args);
 			if (delfiles) {
-				snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s-\"* ) &",tmp, fname_base); /* remove legs when done mixing */
-				ast_copy_string(tmp, tmp2, sizeof(tmp));
+				/* remove legs when done mixing */
+				ast_str_append(&tmp, 0, "& rm -f \"%s-\"* ) &", fname_base);
 			}
-			ast_debug(1,"monitor executing %s\n",tmp);
-			if (ast_safe_system(tmp) == -1)
-				ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
+			ast_debug(1,"monitor executing %s\n", ast_str_buffer(tmp));
+			if (ast_safe_system(ast_str_buffer(tmp)) == -1)
+				ast_log(LOG_WARNING, "Execute of %s failed.\n", ast_str_buffer(tmp));
 		}
 
 		if (!ast_strlen_zero(ast_channel_monitor(chan)->beep_id)) {
diff --git a/res/res_musiconhold.c b/res/res_musiconhold.c
index c52c964..d791516 100644
--- a/res/res_musiconhold.c
+++ b/res/res_musiconhold.c
@@ -158,6 +158,11 @@ struct moh_files_state {
 
 static struct ast_flags global_flags[1] = {{0}};        /*!< global MOH_ flags */
 
+enum kill_methods {
+	KILL_METHOD_PROCESS_GROUP = 0,
+	KILL_METHOD_PROCESS
+};
+
 struct mohclass {
 	char name[MAX_MUSICCLASS];
 	char dir[256];
@@ -178,6 +183,10 @@ struct mohclass {
 	int pid;
 	time_t start;
 	pthread_t thread;
+	/*! Millisecond delay between kill attempts */
+	size_t kill_delay;
+	/*! Kill method */
+	enum kill_methods kill_method;
 	/*! Source of audio */
 	int srcfd;
 	/*! Generic timer */
@@ -678,6 +687,51 @@ static int spawn_mp3(struct mohclass *class)
 	return fds[0];
 }
 
+static int killer(pid_t pid, int signum, enum kill_methods kill_method)
+{
+	switch (kill_method) {
+	case KILL_METHOD_PROCESS_GROUP:
+		return killpg(pid, signum);
+	case KILL_METHOD_PROCESS:
+		return kill(pid, signum);
+	}
+
+	return -1;
+}
+
+static void killpid(int pid, size_t delay, enum kill_methods kill_method)
+{
+	if (killer(pid, SIGHUP, kill_method) < 0) {
+		if (errno == ESRCH) {
+			return;
+		}
+		ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process '%d'?!!: %s\n", pid, strerror(errno));
+	} else {
+		ast_debug(1, "Sent HUP to pid %d%s\n", pid,
+			kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
+	}
+	usleep(delay);
+	if (killer(pid, SIGTERM, kill_method) < 0) {
+		if (errno == ESRCH) {
+			return;
+		}
+		ast_log(LOG_WARNING, "Unable to terminate MOH process '%d'?!!: %s\n", pid, strerror(errno));
+	} else {
+		ast_debug(1, "Sent TERM to pid %d%s\n", pid,
+			kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
+	}
+	usleep(delay);
+	if (killer(pid, SIGKILL, kill_method) < 0) {
+		if (errno == ESRCH) {
+			return;
+		}
+		ast_log(LOG_WARNING, "Unable to kill MOH process '%d'?!!: %s\n", pid, strerror(errno));
+	} else {
+		ast_debug(1, "Sent KILL to pid %d%s\n", pid,
+			kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
+	}
+}
+
 static void *monmp3thread(void *data)
 {
 #define	MOH_MS_INTERVAL		100
@@ -753,28 +807,7 @@ static void *monmp3thread(void *data)
 				class->srcfd = -1;
 				pthread_testcancel();
 				if (class->pid > 1) {
-					do {
-						if (killpg(class->pid, SIGHUP) < 0) {
-							if (errno == ESRCH) {
-								break;
-							}
-							ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
-						}
-						usleep(100000);
-						if (killpg(class->pid, SIGTERM) < 0) {
-							if (errno == ESRCH) {
-								break;
-							}
-							ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
-						}
-						usleep(100000);
-						if (killpg(class->pid, SIGKILL) < 0) {
-							if (errno == ESRCH) {
-								break;
-							}
-							ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
-						}
-					} while (0);
+					killpid(class->pid, class->kill_delay, class->kill_method);
 					class->pid = 0;
 				}
 			} else {
@@ -1328,6 +1361,7 @@ static struct mohclass *_moh_class_malloc(const char *file, int line, const char
 		)) {
 		class->format = ao2_bump(ast_format_slin);
 		class->srcfd = -1;
+		class->kill_delay = 100000;
 	}
 
 	return class;
@@ -1600,44 +1634,22 @@ static void moh_class_destructor(void *obj)
 
 	if (class->pid > 1) {
 		char buff[8192];
-		int bytes, tbytes = 0, stime = 0, pid = 0;
+		int bytes, tbytes = 0, stime = 0;
 
 		ast_debug(1, "killing %d!\n", class->pid);
 
 		stime = time(NULL) + 2;
-		pid = class->pid;
-		class->pid = 0;
-
-		/* Back when this was just mpg123, SIGKILL was fine.  Now we need
-		 * to give the process a reason and time enough to kill off its
-		 * children. */
-		do {
-			if (killpg(pid, SIGHUP) < 0) {
-				ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
-			}
-			usleep(100000);
-			if (killpg(pid, SIGTERM) < 0) {
-				if (errno == ESRCH) {
-					break;
-				}
-				ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
-			}
-			usleep(100000);
-			if (killpg(pid, SIGKILL) < 0) {
-				if (errno == ESRCH) {
-					break;
-				}
-				ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
-			}
-		} while (0);
+		killpid(class->pid, class->kill_delay, class->kill_method);
 
 		while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
 				(bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
 			tbytes = tbytes + bytes;
 		}
 
-		ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
+		ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n",
+			class->pid, tbytes);
 
+		class->pid = 0;
 		close(class->srcfd);
 		class->srcfd = -1;
 	}
@@ -1765,6 +1777,22 @@ static int load_moh_classes(int reload)
 					ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
 					class->format = ao2_bump(ast_format_slin);
 				}
+			} else if (!strcasecmp(var->name, "kill_escalation_delay")) {
+				if (sscanf(var->value, "%zu", &class->kill_delay) == 1) {
+					class->kill_delay *= 1000;
+				} else {
+					ast_log(LOG_WARNING, "kill_escalation_delay '%s' is invalid.  Setting to 100ms\n", var->value);
+					class->kill_delay = 100000;
+				}
+			} else if (!strcasecmp(var->name, "kill_method")) {
+				if (!strcasecmp(var->value, "process")) {
+					class->kill_method = KILL_METHOD_PROCESS;
+				} else if (!strcasecmp(var->value, "process_group")){
+					class->kill_method = KILL_METHOD_PROCESS_GROUP;
+				} else {
+					ast_log(LOG_WARNING, "kill_method '%s' is invalid.  Setting to 'process_group'\n", var->value);
+					class->kill_method = KILL_METHOD_PROCESS_GROUP;
+				}
 			}
 		}
 
@@ -1899,6 +1927,9 @@ static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struc
 		ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
 		if (ast_test_flag(class, MOH_CUSTOM)) {
 			ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
+			ast_cli(a->fd, "\tKill Escalation Delay: %zu ms\n", class->kill_delay / 1000);
+			ast_cli(a->fd, "\tKill Method: %s\n",
+				class->kill_method == KILL_METHOD_PROCESS ? "process" : "process_group");
 		}
 		if (strcasecmp(class->mode, "files")) {
 			ast_cli(a->fd, "\tFormat: %s\n", ast_format_get_name(class->format));
diff --git a/res/res_pjproject.c b/res/res_pjproject.c
index e02515b..fa99919 100644
--- a/res/res_pjproject.c
+++ b/res/res_pjproject.c
@@ -546,7 +546,7 @@ static int unload_module(void)
 	pj_log_set_log_func(log_cb_orig);
 	pj_log_set_decor(decor_orig);
 
-	AST_VECTOR_REMOVE_CMP_UNORDERED(&buildopts, NULL, NOT_EQUALS, ast_free);
+	AST_VECTOR_CALLBACK_VOID(&buildopts, ast_free);
 	AST_VECTOR_FREE(&buildopts);
 
 	ast_debug(3, "Stopped PJPROJECT logging to Asterisk logger\n");
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 02d24e9..1c97e20 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -367,9 +367,12 @@
 				<configOption name="rewrite_contact">
 					<synopsis>Allow Contact header to be rewritten with the source IP address-port</synopsis>
 					<description><para>
-						On inbound SIP messages from this endpoint, the Contact header or an appropriate Record-Route
-						header will be changed to have the source IP address and port. This option does not affect
-						outbound messages sent to this endpoint.
+						On inbound SIP messages from this endpoint, the Contact header or an
+						appropriate Record-Route header will be changed to have the source IP
+						address and port.  This option does not affect outbound messages sent to
+						this endpoint.  This option helps servers communicate with endpoints
+						that are behind NATs.  This option also helps reuse reliable transport
+						connections such as TCP and TLS.
 					</para></description>
 				</configOption>
 				<configOption name="rtp_ipv6" default="no">
@@ -982,6 +985,14 @@
 						on Ringing when already INUSE.
 					</para></description>
 				</configOption>
+				<configOption name="incoming_mwi_mailbox">
+					<synopsis>Mailbox name to use when incoming MWI NOTIFYs are received</synopsis>
+					<description><para>
+						If an MWI NOTIFY is received <emphasis>from</emphasis> this endpoint,
+						this mailbox will be used when notifying other modules of MWI status
+						changes.  If not set, incoming MWI NOTIFYs are ignored.
+					</para></description>
+				</configOption>
 			</configObject>
 			<configObject name="auth">
 				<synopsis>Authentication type</synopsis>
@@ -1324,6 +1335,13 @@
 						in incoming SIP REGISTER requests and is not intended to be configured manually.
 					</para></description>
 				</configOption>
+				<configOption name="prune_on_boot">
+					<synopsis>A contact that cannot survive a restart/boot.</synopsis>
+					<description><para>
+						The option is set if the incoming SIP REGISTER contact is rewritten
+						on a reliable transport and is not intended to be configured manually.
+					</para></description>
+				</configOption>
 			</configObject>
 			<configObject name="aor">
 				<synopsis>The configuration for a location of an endpoint</synopsis>
@@ -1390,6 +1408,18 @@
 						It only limits contacts added through external interaction, such as
 						registration.
 						</para>
+						<note><para>The <replaceable>rewrite_contact</replaceable> option
+						registers the source address as the contact address to help with
+						NAT and reusing connection oriented transports such as TCP and
+						TLS.  Unfortunately, refreshing a registration may register a
+						different contact address and exceed
+						<replaceable>max_contacts</replaceable>.  The
+						<replaceable>remove_existing</replaceable> option can help by
+						removing the soonest to expire contact(s) over
+						<replaceable>max_contacts</replaceable> which is likely the
+						old <replaceable>rewrite_contact</replaceable> contact source
+						address being refreshed.
+						</para></note>
 						<note><para>This should be set to <literal>1</literal> and
 						<replaceable>remove_existing</replaceable> set to <literal>yes</literal> if you
 						wish to stick with the older <literal>chan_sip</literal> behaviour.
@@ -1399,15 +1429,29 @@
 				<configOption name="minimum_expiration" default="60">
 					<synopsis>Minimum keep alive time for an AoR</synopsis>
 					<description><para>
-						Minimum time to keep a peer with an explict expiration. Time in seconds.
+						Minimum time to keep a peer with an explicit expiration. Time in seconds.
 					</para></description>
 				</configOption>
 				<configOption name="remove_existing" default="no">
 					<synopsis>Determines whether new contacts replace existing ones.</synopsis>
 					<description><para>
-						On receiving a new registration to the AoR should it remove
-						the existing contact that was registered against it?
+						On receiving a new registration to the AoR should it remove enough
+						existing contacts not added or updated by the registration to
+						satisfy <replaceable>max_contacts</replaceable>?  Any removed
+						contacts will expire the soonest.
 						</para>
+						<note><para>The <replaceable>rewrite_contact</replaceable> option
+						registers the source address as the contact address to help with
+						NAT and reusing connection oriented transports such as TCP and
+						TLS.  Unfortunately, refreshing a registration may register a
+						different contact address and exceed
+						<replaceable>max_contacts</replaceable>.  The
+						<replaceable>remove_existing</replaceable> option can help by
+						removing the soonest to expire contact(s) over
+						<replaceable>max_contacts</replaceable> which is likely the
+						old <replaceable>rewrite_contact</replaceable> contact source
+						address being refreshed.
+						</para></note>
 						<note><para>This should be set to <literal>yes</literal> and
 						<replaceable>max_contacts</replaceable> set to <literal>1</literal> if you
 						wish to stick with the older <literal>chan_sip</literal> behaviour.
@@ -3029,6 +3073,11 @@ void ast_sip_add_usereqphone(const struct ast_sip_endpoint *endpoint, pj_pool_t
 		return;
 	}
 
+	if (pjsip_param_find(&sip_uri->other_param, &STR_USER)) {
+		/* Don't add it if it's already there */
+		return;
+	}
+
 	param = PJ_POOL_ALLOC_T(pool, pjsip_param);
 	param->name = STR_USER;
 	param->value = STR_PHONE;
@@ -3075,6 +3124,14 @@ pjsip_dialog *ast_sip_create_dialog_uac(const struct ast_sip_endpoint *endpoint,
 	/* Update the dialog with the new local URI, we do it afterwards so we can use the dialog pool for construction */
 	pj_strdup_with_null(dlg->pool, &dlg->local.info_str, &local_uri);
 	dlg->local.info->uri = pjsip_parse_uri(dlg->pool, dlg->local.info_str.ptr, dlg->local.info_str.slen, 0);
+	if (!dlg->local.info->uri) {
+		ast_log(LOG_ERROR,
+			"Could not parse URI '%s' for endpoint '%s'\n",
+			dlg->local.info_str.ptr, ast_sorcery_object_get_id(endpoint));
+		dlg->sess_count--;
+		pjsip_dlg_terminate(dlg);
+		return NULL;
+	}
 
 	dlg->local.contact = pjsip_parse_hdr(dlg->pool, &HCONTACT, local_uri.ptr, local_uri.slen, NULL);
 
@@ -3176,7 +3233,7 @@ pjsip_dialog *ast_sip_create_dialog_uas(const struct ast_sip_endpoint *endpoint,
 	ast_assert(status != NULL);
 
 	contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
-	if (ast_sip_set_tpselector_from_ep_or_uri(endpoint, pjsip_uri_get_uri(contact_hdr->uri),
+	if (!contact_hdr || ast_sip_set_tpselector_from_ep_or_uri(endpoint, pjsip_uri_get_uri(contact_hdr->uri),
 		&selector)) {
 		return NULL;
 	}
@@ -3454,7 +3511,7 @@ int ast_sip_create_request(const char *method, struct pjsip_dialog *dlg,
 
 AST_RWLIST_HEAD_STATIC(supplements, ast_sip_supplement);
 
-int ast_sip_register_supplement(struct ast_sip_supplement *supplement)
+void internal_sip_register_supplement(struct ast_sip_supplement *supplement)
 {
 	struct ast_sip_supplement *iter;
 	int inserted = 0;
@@ -3472,22 +3529,39 @@ int ast_sip_register_supplement(struct ast_sip_supplement *supplement)
 	if (!inserted) {
 		AST_RWLIST_INSERT_TAIL(&supplements, supplement, next);
 	}
+}
+
+int ast_sip_register_supplement(struct ast_sip_supplement *supplement)
+{
+	internal_sip_register_supplement(supplement);
 	ast_module_ref(ast_module_info->self);
+
 	return 0;
 }
 
-void ast_sip_unregister_supplement(struct ast_sip_supplement *supplement)
+int internal_sip_unregister_supplement(struct ast_sip_supplement *supplement)
 {
 	struct ast_sip_supplement *iter;
 	SCOPED_LOCK(lock, &supplements, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
+	int res = -1;
+
 	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&supplements, iter, next) {
 		if (supplement == iter) {
 			AST_RWLIST_REMOVE_CURRENT(next);
-			ast_module_unref(ast_module_info->self);
+			res = 0;
 			break;
 		}
 	}
 	AST_RWLIST_TRAVERSE_SAFE_END;
+
+	return res;
+}
+
+void ast_sip_unregister_supplement(struct ast_sip_supplement *supplement)
+{
+	if (!internal_sip_unregister_supplement(supplement)) {
+		ast_module_unref(ast_module_info->self);
+	}
 }
 
 static int send_in_dialog_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg)
@@ -4389,6 +4463,56 @@ const char *ast_sip_get_host_ip_string(int af)
 	return NULL;
 }
 
+int ast_sip_dtmf_to_str(const enum ast_sip_dtmf_mode dtmf,
+		        char *buf, size_t buf_len)
+{
+	switch (dtmf) {
+	case AST_SIP_DTMF_NONE:
+		ast_copy_string(buf, "none", buf_len);
+		break;
+	case AST_SIP_DTMF_RFC_4733:
+		ast_copy_string(buf, "rfc4733", buf_len);
+		break;
+	case AST_SIP_DTMF_INBAND:
+		ast_copy_string(buf, "inband", buf_len);
+		break;
+	case AST_SIP_DTMF_INFO:
+		ast_copy_string(buf, "info", buf_len);
+		break;
+	case AST_SIP_DTMF_AUTO:
+		ast_copy_string(buf, "auto", buf_len);
+		break;
+	case AST_SIP_DTMF_AUTO_INFO:
+		ast_copy_string(buf, "auto_info", buf_len);
+		break;
+	default:
+		buf[0] = '\0';
+		return -1;
+	}
+	return 0;
+}
+
+int ast_sip_str_to_dtmf(const char * dtmf_mode)
+{
+	int result = -1;
+
+	if (!strcasecmp(dtmf_mode, "info")) {
+		result = AST_SIP_DTMF_INFO;
+	} else if (!strcasecmp(dtmf_mode, "rfc4733")) {
+		result = AST_SIP_DTMF_RFC_4733;
+	} else if (!strcasecmp(dtmf_mode, "inband")) {
+		result = AST_SIP_DTMF_INBAND;
+	} else if (!strcasecmp(dtmf_mode, "none")) {
+		result = AST_SIP_DTMF_NONE;
+	} else if (!strcasecmp(dtmf_mode, "auto")) {
+		result = AST_SIP_DTMF_AUTO;
+	} else if (!strcasecmp(dtmf_mode, "auto_info")) {
+		result = AST_SIP_DTMF_AUTO_INFO;
+	}
+
+	return result;
+}
+
 /*!
  * \brief Set name and number information on an identity header.
  *
@@ -4504,6 +4628,16 @@ static int reload_configuration_task(void *obj)
 	return 0;
 }
 
+void internal_res_pjsip_ref(void)
+{
+	ast_module_ref(ast_module_info->self);
+}
+
+void internal_res_pjsip_unref(void)
+{
+	ast_module_unref(ast_module_info->self);
+}
+
 static int unload_pjsip(void *data)
 {
 	/*
@@ -4513,12 +4647,13 @@ static int unload_pjsip(void *data)
 	if (ast_pjsip_endpoint && serializer_pool[0]) {
 		ast_res_pjsip_cleanup_options_handling();
 		internal_sip_destroy_outbound_authentication();
-		ast_res_pjsip_cleanup_message_ip_updater();
+		ast_res_pjsip_cleanup_message_filter();
 		ast_sip_destroy_distributor();
 		ast_res_pjsip_destroy_configuration();
 		ast_sip_destroy_system();
 		ast_sip_destroy_global_headers();
 		internal_sip_unregister_service(&supplement_module);
+		ast_sip_destroy_transport_events();
 	}
 
 	if (monitor_thread) {
@@ -4597,7 +4732,6 @@ static int load_pjsip(void)
 	return AST_MODULE_LOAD_SUCCESS;
 
 error:
-	unload_pjsip(NULL);
 	return AST_MODULE_LOAD_DECLINE;
 }
 
@@ -4663,6 +4797,11 @@ static int load_module(void)
 		goto error;
 	}
 
+	if (ast_sip_initialize_transport_events()) {
+		ast_log(LOG_ERROR, "Failed to initialize SIP transport monitor. Aborting load\n");
+		goto error;
+	}
+
 	ast_sip_initialize_dns();
 
 	ast_sip_initialize_global_headers();
@@ -4689,7 +4828,7 @@ static int load_module(void)
 
 	ast_res_pjsip_init_options_handling(0);
 
-	if (ast_res_pjsip_init_message_ip_updater()) {
+	if (ast_res_pjsip_init_message_filter()) {
 		ast_log(LOG_ERROR, "Failed to initialize message IP updating. Aborting load\n");
 		goto error;
 	}
diff --git a/res/res_pjsip/config_transport.c b/res/res_pjsip/config_transport.c
index 62bc9d6..0c804b8 100644
--- a/res/res_pjsip/config_transport.c
+++ b/res/res_pjsip/config_transport.c
@@ -248,8 +248,11 @@ static int destroy_sip_transport_state(void *data)
 	ast_free(transport_state->id);
 	ast_free_ha(transport_state->localnet);
 
-	if (transport_state->external_address_refresher) {
-		ast_dnsmgr_release(transport_state->external_address_refresher);
+	if (transport_state->external_signaling_address_refresher) {
+		ast_dnsmgr_release(transport_state->external_signaling_address_refresher);
+	}
+	if (transport_state->external_media_address_refresher) {
+		ast_dnsmgr_release(transport_state->external_media_address_refresher);
 	}
 	if (transport_state->transport) {
 		pjsip_transport_shutdown(transport_state->transport);
@@ -399,8 +402,8 @@ static void copy_state_to_transport(struct ast_sip_transport *transport)
 	memcpy(&transport->tls, &transport->state->tls, sizeof(transport->tls));
 	memcpy(&transport->ciphers, &transport->state->ciphers, sizeof(transport->ciphers));
 	transport->localnet = transport->state->localnet;
-	transport->external_address_refresher = transport->state->external_address_refresher;
-	memcpy(&transport->external_address, &transport->state->external_address, sizeof(transport->external_address));
+	transport->external_address_refresher = transport->state->external_signaling_address_refresher;
+	memcpy(&transport->external_address, &transport->state->external_signaling_address, sizeof(transport->external_signaling_address));
 }
 
 static int has_state_changed(struct ast_sip_transport_state *a, struct ast_sip_transport_state *b)
@@ -421,7 +424,11 @@ static int has_state_changed(struct ast_sip_transport_state *a, struct ast_sip_t
 		return -1;
 	}
 
-	if (ast_sockaddr_cmp(&a->external_address, &b->external_address)) {
+	if (ast_sockaddr_cmp(&a->external_signaling_address, &b->external_signaling_address)) {
+		return -1;
+	}
+
+	if (ast_sockaddr_cmp(&a->external_media_address, &b->external_media_address)) {
 		return -1;
 	}
 
@@ -515,24 +522,41 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
 		pj_sockaddr_set_port(&temp_state->state->host, (transport->type == AST_TRANSPORT_TLS) ? 5061 : 5060);
 	}
 
-	/* Now that we know what address family we can set up a dnsmgr refresh for the external media address if present */
+	/* Now that we know what address family we can set up a dnsmgr refresh for the external addresses if present */
 	if (!ast_strlen_zero(transport->external_signaling_address)) {
 		if (temp_state->state->host.addr.sa_family == pj_AF_INET()) {
-			temp_state->state->external_address.ss.ss_family = AF_INET;
+			temp_state->state->external_signaling_address.ss.ss_family = AF_INET;
 		} else if (temp_state->state->host.addr.sa_family == pj_AF_INET6()) {
-			temp_state->state->external_address.ss.ss_family = AF_INET6;
+			temp_state->state->external_signaling_address.ss.ss_family = AF_INET6;
 		} else {
 			ast_log(LOG_ERROR, "Unknown address family for transport '%s', could not get external signaling address\n",
 					transport_id);
 			return -1;
 		}
 
-		if (ast_dnsmgr_lookup(transport->external_signaling_address, &temp_state->state->external_address, &temp_state->state->external_address_refresher, NULL) < 0) {
+		if (ast_dnsmgr_lookup(transport->external_signaling_address, &temp_state->state->external_signaling_address, &temp_state->state->external_signaling_address_refresher, NULL) < 0) {
 			ast_log(LOG_ERROR, "Could not create dnsmgr for external signaling address on '%s'\n", transport_id);
 			return -1;
 		}
 	}
 
+	if (!ast_strlen_zero(transport->external_media_address)) {
+		if (temp_state->state->host.addr.sa_family == pj_AF_INET()) {
+			temp_state->state->external_media_address.ss.ss_family = AF_INET;
+		} else if (temp_state->state->host.addr.sa_family == pj_AF_INET6()) {
+			temp_state->state->external_media_address.ss.ss_family = AF_INET6;
+		} else {
+			ast_log(LOG_ERROR, "Unknown address family for transport '%s', could not get external media address\n",
+					transport_id);
+			return -1;
+		}
+
+		if (ast_dnsmgr_lookup(transport->external_media_address, &temp_state->state->external_media_address, &temp_state->state->external_media_address_refresher, NULL) < 0) {
+			ast_log(LOG_ERROR, "Could not create dnsmgr for external media address on '%s'\n", transport_id);
+			return -1;
+		}
+	}
+
 	if (transport->type == AST_TRANSPORT_UDP) {
 
 		for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) {
@@ -1103,7 +1127,9 @@ static int transport_localnet_handler(const struct aco_option *opt, struct ast_v
 		return 0;
 	}
 
-	if (!(state->localnet = ast_append_ha("d", var->value, state->localnet, &error))) {
+	/* We use only the ast_apply_ha() which defaults to ALLOW
+	 * ("permit"), so we add DENY rules. */
+	if (!(state->localnet = ast_append_ha("deny", var->value, state->localnet, &error))) {
 		return -1;
 	}
 
diff --git a/res/res_pjsip/include/res_pjsip_private.h b/res/res_pjsip/include/res_pjsip_private.h
index 11ad12c..151f598 100644
--- a/res/res_pjsip/include/res_pjsip_private.h
+++ b/res/res_pjsip/include/res_pjsip_private.h
@@ -135,6 +135,29 @@ void ast_sip_destroy_distributor(void);
 
 /*!
  * \internal
+ * \brief Initialize the transport events notify module
+ * \since 13.18.0
+ *
+ * The transport events notify module is responsible for monitoring
+ * when transports die and calling any registered callbacks when that
+ * happens.  It also manages any PJPROJECT transport state callbacks
+ * registered to it so the callbacks be more dynamic allowing module
+ * loading/unloading.
+ *
+ * \retval -1 Failure
+ * \retval 0 Success
+ */
+int ast_sip_initialize_transport_events(void);
+
+/*!
+ * \internal
+ * \brief Destruct the transport events notify module.
+ * \since 13.18.0
+ */
+void ast_sip_destroy_transport_events(void);
+
+/*!
+ * \internal
  * \brief Initialize global type on a sorcery instance
  *
  * \retval -1 failure
@@ -189,7 +212,7 @@ int ast_res_pjsip_init_options_handling(int reload);
  * \retval 0 on success
  * \retval other on failure
  */
-int ast_res_pjsip_init_message_ip_updater(void);
+int ast_res_pjsip_init_message_filter(void);
 
 /*!
  * \internal
@@ -258,7 +281,7 @@ void ast_res_pjsip_cleanup_options_handling(void);
  * \internal
  * \brief Clean up res_pjsip message ip updating handling
  */
-void ast_res_pjsip_cleanup_message_ip_updater(void);
+void ast_res_pjsip_cleanup_message_filter(void);
 
 /*!
  * \internal
@@ -317,6 +340,18 @@ int internal_sip_unregister_service(pjsip_module *module);
 
 /*!
  * \internal
+ * \brief Used by res_pjsip.so to register a supplement without adding a self reference
+ */
+void internal_sip_register_supplement(struct ast_sip_supplement *supplement);
+
+/*!
+ * \internal
+ * \brief Used by res_pjsip.so to unregister a supplement without removing a self reference
+ */
+int internal_sip_unregister_supplement(struct ast_sip_supplement *supplement);
+
+/*!
+ * \internal
  * \brief Used by res_pjsip.so to register an endpoint formatter without adding a self reference
  */
 void internal_sip_register_endpoint_formatter(struct ast_sip_endpoint_formatter *obj);
@@ -327,6 +362,20 @@ void internal_sip_register_endpoint_formatter(struct ast_sip_endpoint_formatter
  */
 int internal_sip_unregister_endpoint_formatter(struct ast_sip_endpoint_formatter *obj);
 
+struct ast_sip_session_supplement;
+
+/*!
+ * \internal
+ * \brief Used by res_pjsip.so to register a session supplement without adding a self reference
+ */
+void internal_sip_session_register_supplement(struct ast_sip_session_supplement *supplement);
+
+/*!
+ * \internal
+ * \brief Used by res_pjsip.so to unregister a session supplement without removing a self reference
+ */
+int internal_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement);
+
 /*!
  * \internal
  * \brief Finds or creates contact_status for a contact
@@ -358,4 +407,16 @@ int ast_sip_initialize_scheduler(void);
  */
 int ast_sip_destroy_scheduler(void);
 
+/*!
+ * \internal
+ * \brief Add a reference to the res_pjsip module
+ */
+void internal_res_pjsip_ref(void);
+
+/*!
+ * \internal
+ * \brief Remove a reference from the res_pjsip module
+ */
+void internal_res_pjsip_unref(void);
+
 #endif /* RES_PJSIP_PRIVATE_H_ */
diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c
index 05e19f5..66fc85c 100644
--- a/res/res_pjsip/location.c
+++ b/res/res_pjsip/location.c
@@ -356,13 +356,12 @@ struct ast_sip_contact *ast_sip_location_retrieve_contact(const char *contact_na
 	return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "contact", contact_name);
 }
 
-int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri,
-		struct timeval expiration_time, const char *path_info, const char *user_agent,
-		const char *via_addr, int via_port, const char *call_id,
-		struct ast_sip_endpoint *endpoint)
+struct ast_sip_contact *ast_sip_location_create_contact(struct ast_sip_aor *aor,
+	const char *uri, struct timeval expiration_time, const char *path_info,
+	const char *user_agent, const char *via_addr, int via_port, const char *call_id,
+	int prune_on_boot, struct ast_sip_endpoint *endpoint)
 {
 	struct ast_sip_contact *contact;
-	int res;
 	char name[MAX_OBJECT_FIELD * 2 + 3];
 	char hash[33];
 
@@ -371,7 +370,7 @@ int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri
 
 	contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", name);
 	if (!contact) {
-		return -1;
+		return NULL;
 	}
 
 	ast_string_field_set(contact, uri, uri);
@@ -405,14 +404,30 @@ int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri
 	}
 
 	contact->endpoint = ao2_bump(endpoint);
-
 	if (endpoint) {
 		ast_string_field_set(contact, endpoint_name, ast_sorcery_object_get_id(endpoint));
 	}
 
-	res = ast_sorcery_create(ast_sip_get_sorcery(), contact);
-	ao2_ref(contact, -1);
-	return res;
+	contact->prune_on_boot = prune_on_boot;
+
+	if (ast_sorcery_create(ast_sip_get_sorcery(), contact)) {
+		ao2_ref(contact, -1);
+		return NULL;
+	}
+	return contact;
+}
+
+int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri,
+		struct timeval expiration_time, const char *path_info, const char *user_agent,
+		const char *via_addr, int via_port, const char *call_id,
+		struct ast_sip_endpoint *endpoint)
+{
+	struct ast_sip_contact *contact;
+
+	contact = ast_sip_location_create_contact(aor, uri, expiration_time, path_info,
+		user_agent, via_addr, via_port, call_id, 0, endpoint);
+	ao2_cleanup(contact);
+	return contact ? 0 : -1;
 }
 
 int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
@@ -448,6 +463,32 @@ int ast_sip_location_delete_contact(struct ast_sip_contact *contact)
 	return ast_sorcery_delete(ast_sip_get_sorcery(), contact);
 }
 
+static int prune_boot_contacts_cb(void *obj, void *arg, int flags)
+{
+	struct ast_sip_contact *contact = obj;
+
+	if (contact->prune_on_boot
+		&& !strcmp(contact->reg_server, ast_config_AST_SYSTEM_NAME ?: "")) {
+		ast_verb(3, "Removed contact '%s' from AOR '%s' due to system boot\n",
+			contact->uri, contact->aor);
+		ast_sip_location_delete_contact(contact);
+	}
+
+	return 0;
+}
+
+void ast_sip_location_prune_boot_contacts(void)
+{
+	struct ao2_container *contacts;
+
+	contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "contact",
+		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	if (contacts) {
+		ao2_callback(contacts, 0, prune_boot_contacts_cb, NULL);
+		ao2_ref(contacts, -1);
+	}
+}
+
 /*! \brief Custom handler for translating from a string timeval to actual structure */
 static int expiration_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
 {
@@ -579,7 +620,7 @@ static int permanent_uri_handler(const struct aco_option *opt, struct ast_variab
 		}
 
 		if (ast_sip_validate_uri_length(contact_uri)) {
-			ast_log(LOG_ERROR, "Contact uri or hostname length exceeds pjproject limit: %s\n", contact_uri);
+			ast_log(LOG_ERROR, "Contact uri or hostname length exceeds pjproject limit or is not a sip(s) uri: %s\n", contact_uri);
 			return -1;
 		}
 
@@ -1228,6 +1269,7 @@ int ast_sip_initialize_sorcery_location(void)
 	ast_sorcery_object_field_register(sorcery, "contact", "via_addr", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, via_addr));
 	ast_sorcery_object_field_register(sorcery, "contact", "via_port", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_contact, via_port));
 	ast_sorcery_object_field_register(sorcery, "contact", "call_id", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, call_id));
+	ast_sorcery_object_field_register(sorcery, "contact", "prune_on_boot", "no", OPT_YESNO_T, 1, FLDSET(struct ast_sip_contact, prune_on_boot));
 
 	ast_sorcery_object_field_register(sorcery, "aor", "type", "", OPT_NOOP_T, 0, 0);
 	ast_sorcery_object_field_register(sorcery, "aor", "minimum_expiration", "60", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, minimum_expiration));
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 9604ff2..fa54448 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -368,47 +368,29 @@ static int contact_acl_to_str(const void *obj, const intptr_t *args, char **buf)
 static int dtmf_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
 {
 	struct ast_sip_endpoint *endpoint = obj;
+	enum ast_sip_dtmf_mode dtmf = ast_sip_str_to_dtmf(var->value);
 
-	if (!strcasecmp(var->value, "rfc4733")) {
-		endpoint->dtmf = AST_SIP_DTMF_RFC_4733;
-	} else if (!strcasecmp(var->value, "inband")) {
-		endpoint->dtmf = AST_SIP_DTMF_INBAND;
-	} else if (!strcasecmp(var->value, "auto_info")) {
-		endpoint->dtmf = AST_SIP_DTMF_AUTO_INFO;
-	} else if (!strcasecmp(var->value, "info")) {
-		endpoint->dtmf = AST_SIP_DTMF_INFO;
-	} else if (!strcasecmp(var->value, "auto")) {
-		endpoint->dtmf = AST_SIP_DTMF_AUTO;
-	} else if (!strcasecmp(var->value, "none")) {
-		endpoint->dtmf = AST_SIP_DTMF_NONE;
-	} else {
+	if (dtmf == -1) {
 		return -1;
 	}
 
+	endpoint->dtmf = dtmf;
 	return 0;
 }
 
 static int dtmf_to_str(const void *obj, const intptr_t *args, char **buf)
 {
 	const struct ast_sip_endpoint *endpoint = obj;
+	char dtmf_str[20];
+	int result = -1;
 
-	switch (endpoint->dtmf) {
-	case AST_SIP_DTMF_RFC_4733 :
-		*buf = "rfc4733"; break;
-	case AST_SIP_DTMF_INBAND :
-		*buf = "inband"; break;
-	case AST_SIP_DTMF_INFO :
-		*buf = "info"; break;
-	case AST_SIP_DTMF_AUTO :
-		*buf = "auto"; break;
-	case AST_SIP_DTMF_AUTO_INFO :
-		*buf = "auto_info";
-		break;
-	default:
-		*buf = "none";
-	}
+	result = ast_sip_dtmf_to_str(endpoint->dtmf, dtmf_str, sizeof(dtmf_str));
 
-	*buf = ast_strdup(*buf);
+	if (result == 0) {
+		*buf = ast_strdup(dtmf_str);
+	} else {
+		*buf = ast_strdup("none");
+	}
 	return 0;
 }
 
@@ -1150,6 +1132,37 @@ static int tos_video_to_str(const void *obj, const intptr_t *args, char **buf)
 	return 0;
 }
 
+static int from_user_handler(const struct aco_option *opt,
+	struct ast_variable *var, void *obj)
+{
+	struct ast_sip_endpoint *endpoint = obj;
+	/* Valid non-alphanumeric characters for URI */
+	char *valid_uri_marks = "-._~%!$&'()*+,;=:";
+	const char *val;
+
+	for (val = var->value; *val; val++) {
+		if (!isalpha(*val) && !isdigit(*val) && !strchr(valid_uri_marks, *val)) {
+			ast_log(LOG_ERROR, "Error configuring endpoint '%s' - '%s' field "
+			"contains invalid character '%c'\n",
+			ast_sorcery_object_get_id(endpoint), var->name, *val);
+			return -1;
+		}
+	}
+
+	ast_string_field_set(endpoint, fromuser, var->value);
+
+	return 0;
+}
+
+static int from_user_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+	const struct ast_sip_endpoint *endpoint = obj;
+
+	*buf = ast_strdup(endpoint->fromuser);
+
+	return 0;
+}
+
 static int set_var_handler(const struct aco_option *opt,
 	struct ast_variable *var, void *obj)
 {
@@ -1304,6 +1317,14 @@ static struct ast_endpoint *persistent_endpoint_find_or_create(const struct ast_
 		ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_OFFLINE);
 
 		ao2_link_flags(persistent_endpoints, persistent, OBJ_NOLOCK);
+	} else if (strcmp(persistent->aors, endpoint->aors)) {
+		char *new_aors = ast_strdup(endpoint->aors);
+
+		/* make sure we don't NULL persistent->aors if allocation fails. */
+		if (new_aors) {
+			ast_free(persistent->aors);
+			persistent->aors = new_aors;
+		}
 	}
 
 	ao2_ref(persistent->endpoint, +1);
@@ -1780,20 +1801,12 @@ static struct ast_cli_entry cli_commands[] = {
 struct ast_sip_cli_formatter_entry *channel_formatter;
 struct ast_sip_cli_formatter_entry *endpoint_formatter;
 
-static int on_load_endpoint(void *obj, void *arg, int flags)
-{
-	return sip_endpoint_apply_handler(sip_sorcery, obj);
-}
-
 static void load_all_endpoints(void)
 {
 	struct ao2_container *endpoints;
 
 	endpoints = ast_sorcery_retrieve_by_fields(sip_sorcery, "endpoint", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
-	if (endpoints) {
-		ao2_callback(endpoints, OBJ_NODATA, on_load_endpoint, NULL);
-		ao2_ref(endpoints, -1);
-	}
+	ao2_cleanup(endpoints);
 }
 
 int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_module_info)
@@ -1914,7 +1927,7 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "cos_video", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.cos_video));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow_subscribe", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, subscription.allow));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "sub_min_expiry", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, subscription.minexpiry));
-	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "from_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, fromuser));
+	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "from_user", "", from_user_handler, from_user_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "from_domain", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, fromdomain));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mwi_from_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, subscription.mwi.fromuser));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_engine", "asterisk", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.rtp.engine));
@@ -1947,6 +1960,7 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow_overlap", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, allow_overlap));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "refer_blind_progress", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, refer_blind_progress));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "notify_early_inuse_ringing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, notify_early_inuse_ringing));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "incoming_mwi_mailbox", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, incoming_mwi_mailbox));
 
 	if (ast_sip_initialize_sorcery_transport()) {
 		ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
@@ -2009,6 +2023,8 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
 
 	load_all_endpoints();
 
+	ast_sip_location_prune_boot_contacts();
+
 	return 0;
 }
 
@@ -2108,6 +2124,9 @@ void *ast_sip_endpoint_alloc(const char *name)
 		ao2_cleanup(endpoint);
 		return NULL;
 	}
+
+	ast_string_field_init_extended(endpoint, incoming_mwi_mailbox);
+
 	if (!(endpoint->media.codecs = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
 		ao2_cleanup(endpoint);
 		return NULL;
diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c
index 3f245ee..591d7fe 100644
--- a/res/res_pjsip/pjsip_distributor.c
+++ b/res/res_pjsip/pjsip_distributor.c
@@ -1132,9 +1132,9 @@ static void global_loaded(const char *object_type)
 		fake_auth = alloc_artificial_auth(default_realm);
 		if (fake_auth) {
 			ao2_global_obj_replace_unref(artificial_auth, fake_auth);
-			ao2_ref(fake_auth, -1);
 		}
 	}
+	ao2_cleanup(fake_auth);
 
 	ast_sip_get_unidentified_request_thresholds(&unidentified_count, &unidentified_period, &unidentified_prune_interval);
 
diff --git a/res/res_pjsip/pjsip_message_ip_updater.c b/res/res_pjsip/pjsip_message_filter.c
similarity index 54%
rename from res/res_pjsip/pjsip_message_ip_updater.c
rename to res/res_pjsip/pjsip_message_filter.c
index 2d07464..8a63219 100644
--- a/res/res_pjsip/pjsip_message_ip_updater.c
+++ b/res/res_pjsip/pjsip_message_filter.c
@@ -27,72 +27,78 @@
 
 #define MOD_DATA_RESTRICTIONS "restrictions"
 
-static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata);
-static pj_bool_t multihomed_on_rx_message(pjsip_rx_data *rdata);
+static pj_status_t filter_on_tx_message(pjsip_tx_data *tdata);
+static pj_bool_t filter_on_rx_message(pjsip_rx_data *rdata);
 
 /*! \brief Outgoing message modification restrictions */
-struct multihomed_message_restrictions {
+struct filter_message_restrictions {
 	/*! \brief Disallow modification of the From domain */
 	unsigned int disallow_from_domain_modification;
 };
 
-static pjsip_module multihomed_module = {
-	.name = { "Multihomed Routing", 18 },
+static pjsip_module filter_module_transport = {
+	.name = { "Message Filtering Transport", 27 },
+	.id = -1,
+	.priority = PJSIP_MOD_PRIORITY_TRANSPORT_LAYER,
+	.on_rx_request = filter_on_rx_message,
+};
+
+static pjsip_module filter_module_tsx = {
+	.name = { "Message Filtering TSX", 21 },
 	.id = -1,
 	.priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 1,
-	.on_tx_request = multihomed_on_tx_message,
-	.on_tx_response = multihomed_on_tx_message,
-	.on_rx_request = multihomed_on_rx_message,
+	.on_tx_request = filter_on_tx_message,
+	.on_tx_response = filter_on_tx_message,
 };
 
 /*! \brief Helper function to get (or allocate if not already present) restrictions on a message */
-static struct multihomed_message_restrictions *multihomed_get_restrictions(pjsip_tx_data *tdata)
+static struct filter_message_restrictions *get_restrictions(pjsip_tx_data *tdata)
 {
-	struct multihomed_message_restrictions *restrictions;
+	struct filter_message_restrictions *restrictions;
 
-	restrictions = ast_sip_mod_data_get(tdata->mod_data, multihomed_module.id, MOD_DATA_RESTRICTIONS);
+	restrictions = ast_sip_mod_data_get(tdata->mod_data, filter_module_tsx.id, MOD_DATA_RESTRICTIONS);
 	if (restrictions) {
 		return restrictions;
 	}
 
-	restrictions = PJ_POOL_ALLOC_T(tdata->pool, struct multihomed_message_restrictions);
-	ast_sip_mod_data_set(tdata->pool, tdata->mod_data, multihomed_module.id, MOD_DATA_RESTRICTIONS, restrictions);
+	restrictions = PJ_POOL_ALLOC_T(tdata->pool, struct filter_message_restrictions);
+	ast_sip_mod_data_set(tdata->pool, tdata->mod_data, filter_module_tsx.id, MOD_DATA_RESTRICTIONS, restrictions);
 
 	return restrictions;
 }
 
 /*! \brief Callback invoked on non-session outgoing messages */
-static void multihomed_outgoing_message(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, struct pjsip_tx_data *tdata)
+static void filter_outgoing_message(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, struct pjsip_tx_data *tdata)
 {
-	struct multihomed_message_restrictions *restrictions = multihomed_get_restrictions(tdata);
+	struct filter_message_restrictions *restrictions = get_restrictions(tdata);
 
 	restrictions->disallow_from_domain_modification = !ast_strlen_zero(endpoint->fromdomain);
 }
 
 /*! \brief PJSIP Supplement for tagging messages with restrictions */
-static struct ast_sip_supplement multihomed_supplement = {
+static struct ast_sip_supplement filter_supplement = {
 	.priority = AST_SIP_SUPPLEMENT_PRIORITY_FIRST,
-	.outgoing_request = multihomed_outgoing_message,
-	.outgoing_response = multihomed_outgoing_message,
+	.outgoing_request = filter_outgoing_message,
+	.outgoing_response = filter_outgoing_message,
 };
 
 /*! \brief Callback invoked on session outgoing messages */
-static void multihomed_session_outgoing_message(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
+static void filter_session_outgoing_message(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
 {
-	struct multihomed_message_restrictions *restrictions = multihomed_get_restrictions(tdata);
+	struct filter_message_restrictions *restrictions = get_restrictions(tdata);
 
 	restrictions->disallow_from_domain_modification = !ast_strlen_zero(session->endpoint->fromdomain);
 }
 
 /*! \brief PJSIP Session Supplement for tagging messages with restrictions */
-static struct ast_sip_session_supplement multihomed_session_supplement = {
+static struct ast_sip_session_supplement filter_session_supplement = {
 	.priority = 1,
-	.outgoing_request = multihomed_session_outgoing_message,
-	.outgoing_response = multihomed_session_outgoing_message,
+	.outgoing_request = filter_session_outgoing_message,
+	.outgoing_response = filter_session_outgoing_message,
 };
 
 /*! \brief Helper function which returns a UDP transport bound to the given address and port */
-static pjsip_transport *multihomed_get_udp_transport(pj_str_t *address, int port)
+static pjsip_transport *get_udp_transport(pj_str_t *address, int port)
 {
 	struct ao2_container *transport_states = ast_sip_get_transport_states();
 	struct ast_sip_transport_state *transport_state;
@@ -121,7 +127,7 @@ static pjsip_transport *multihomed_get_udp_transport(pj_str_t *address, int port
 }
 
 /*! \brief Helper function which determines if a transport is bound to any */
-static int multihomed_bound_any(pjsip_transport *transport)
+static int is_bound_any(pjsip_transport *transport)
 {
 	pj_uint32_t loop6[4] = {0, 0, 0, 0};
 
@@ -153,37 +159,61 @@ static int multihomed_rewrite_sdp(struct pjmedia_sdp_session *sdp)
 	return 0;
 }
 
-static void sanitize_tdata(pjsip_tx_data *tdata)
+#define is_sip_uri(uri) \
+	(PJSIP_URI_SCHEME_IS_SIP(uri) || PJSIP_URI_SCHEME_IS_SIPS(uri))
+
+static void print_sanitize_debug(char *msg, pjsip_uri_context_e context, pjsip_sip_uri *uri)
+{
+#ifdef AST_DEVMODE
+	char hdrbuf[512];
+	int hdrbuf_len;
+
+	hdrbuf_len = pjsip_uri_print(context, uri, hdrbuf, 512);
+	hdrbuf[hdrbuf_len] = '\0';
+	ast_debug(2, "%s: %s\n", msg, hdrbuf);
+#endif
+}
+
+/* If in DEVMODE, prevent inlining to assist in debugging */
+#ifdef AST_DEVMODE
+#define FUNC_ATTRS __attribute__ ((noinline))
+#else
+#define FUNC_ATTRS
+#endif
+
+static void FUNC_ATTRS sanitize_tdata(pjsip_tx_data *tdata)
 {
 	static const pj_str_t x_name = { AST_SIP_X_AST_TXP, AST_SIP_X_AST_TXP_LEN };
 	pjsip_param *x_transport;
 	pjsip_sip_uri *uri;
-	pjsip_fromto_hdr *fromto;
-	pjsip_contact_hdr *contact;
 	pjsip_hdr *hdr;
 
 	if (tdata->msg->type == PJSIP_REQUEST_MSG) {
-		uri = pjsip_uri_get_uri(tdata->msg->line.req.uri);
-		x_transport = pjsip_param_find(&uri->other_param, &x_name);
-		if (x_transport) {
-			pj_list_erase(x_transport);
+		if (is_sip_uri(tdata->msg->line.req.uri)) {
+			uri = pjsip_uri_get_uri(tdata->msg->line.req.uri);
+			print_sanitize_debug("Sanitizing Request", PJSIP_URI_IN_REQ_URI, uri);
+			while ((x_transport = pjsip_param_find(&uri->other_param, &x_name))) {
+				pj_list_erase(x_transport);
+			}
 		}
 	}
 
 	for (hdr = tdata->msg->hdr.next; hdr != &tdata->msg->hdr; hdr = hdr->next) {
 		if (hdr->type == PJSIP_H_TO || hdr->type == PJSIP_H_FROM) {
-			fromto = (pjsip_fromto_hdr *) hdr;
-			uri = pjsip_uri_get_uri(fromto->uri);
-			x_transport = pjsip_param_find(&uri->other_param, &x_name);
-			if (x_transport) {
-				pj_list_erase(x_transport);
+			if (is_sip_uri(((pjsip_fromto_hdr *) hdr)->uri)) {
+				uri = pjsip_uri_get_uri(((pjsip_fromto_hdr *) hdr)->uri);
+				print_sanitize_debug("Sanitizing From/To header", PJSIP_URI_IN_FROMTO_HDR, uri);
+				while ((x_transport = pjsip_param_find(&uri->other_param, &x_name))) {
+					pj_list_erase(x_transport);
+				}
 			}
 		} else if (hdr->type == PJSIP_H_CONTACT) {
-			contact = (pjsip_contact_hdr *) hdr;
-			uri = pjsip_uri_get_uri(contact->uri);
-			x_transport = pjsip_param_find(&uri->other_param, &x_name);
-			if (x_transport) {
-				pj_list_erase(x_transport);
+			if (!((pjsip_contact_hdr *) hdr)->star && is_sip_uri(((pjsip_contact_hdr *) hdr)->uri)) {
+				uri = pjsip_uri_get_uri(((pjsip_contact_hdr *) hdr)->uri);
+				print_sanitize_debug("Sanitizing Contact header", PJSIP_URI_IN_CONTACT_HDR, uri);
+				while ((x_transport = pjsip_param_find(&uri->other_param, &x_name))) {
+					pj_list_erase(x_transport);
+				}
 			}
 		}
 	}
@@ -191,9 +221,10 @@ static void sanitize_tdata(pjsip_tx_data *tdata)
 	pjsip_tx_data_invalidate_msg(tdata);
 }
 
-static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata)
+static pj_status_t filter_on_tx_message(pjsip_tx_data *tdata)
 {
-	struct multihomed_message_restrictions *restrictions = ast_sip_mod_data_get(tdata->mod_data, multihomed_module.id, MOD_DATA_RESTRICTIONS);
+	struct filter_message_restrictions *restrictions =
+		ast_sip_mod_data_get(tdata->mod_data, filter_module_transport.id, MOD_DATA_RESTRICTIONS);
 	pjsip_tpmgr_fla2_param prm;
 	pjsip_cseq_hdr *cseq;
 	pjsip_via_hdr *via;
@@ -226,7 +257,7 @@ static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata)
 			tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP6) {
 			pjsip_transport *transport;
 
-			transport = multihomed_get_udp_transport(&prm.ret_addr, prm.ret_port);
+			transport = get_udp_transport(&prm.ret_addr, prm.ret_port);
 
 			if (transport) {
 				tdata->tp_info.transport = transport;
@@ -234,7 +265,7 @@ static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata)
 		}
 
 		/* If the chosen transport is not bound to any we can't use the source address as it won't get back to us */
-		if (!multihomed_bound_any(tdata->tp_info.transport)) {
+		if (!is_bound_any(tdata->tp_info.transport)) {
 			pj_strassign(&prm.ret_addr, &tdata->tp_info.transport->local_name.host);
 		}
 	} else {
@@ -253,7 +284,7 @@ static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata)
 			/* prm.ret_addr is allocated from the tdata pool OR the transport so it is perfectly fine to just do an assignment like this */
 			pj_strassign(&uri->host, &prm.ret_addr);
 			uri->port = prm.ret_port;
-			ast_debug(4, "Re-wrote Contact URI host/port to %.*s:%d\n",
+			ast_debug(5, "Re-wrote Contact URI host/port to %.*s:%d (this may be re-written again later)\n",
 				(int)pj_strlen(&uri->host), pj_strbuf(&uri->host), uri->port);
 
 			if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP ||
@@ -315,7 +346,116 @@ static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata)
 	return PJ_SUCCESS;
 }
 
-static pj_bool_t multihomed_on_rx_message(pjsip_rx_data *rdata)
+enum uri_type {
+	URI_TYPE_REQUEST = -1,
+	URI_TYPE_TO = PJSIP_H_TO,
+	URI_TYPE_FROM = PJSIP_H_FROM,
+	URI_TYPE_CONTACT = PJSIP_H_CONTACT,
+};
+
+static void print_uri_debug(enum uri_type ut, pjsip_rx_data *rdata, pjsip_hdr *hdr)
+{
+#ifdef AST_DEVMODE
+	pjsip_uri *local_uri = NULL;
+	char hdrbuf[512];
+	int hdrbuf_len;
+	char *request_uri;
+	pjsip_uri_context_e context = PJSIP_URI_IN_OTHER;
+	char header_name[32];
+
+	switch (ut) {
+	case(URI_TYPE_REQUEST):
+		context = PJSIP_URI_IN_REQ_URI;
+		strcpy(header_name, "Request"); /* Safe */
+		local_uri = rdata->msg_info.msg->line.req.uri;
+		break;
+	case(PJSIP_H_FROM):
+		strcpy(header_name, "From"); /* Safe */
+		context = PJSIP_URI_IN_FROMTO_HDR;
+		local_uri = pjsip_uri_get_uri(((pjsip_from_hdr *)hdr)->uri);
+		break;
+	case(PJSIP_H_TO):
+		strcpy(header_name, "To"); /* Safe */
+		context = PJSIP_URI_IN_FROMTO_HDR;
+		local_uri = pjsip_uri_get_uri(((pjsip_to_hdr *)hdr)->uri);
+		break;
+	case(PJSIP_H_CONTACT):
+		strcpy(header_name, "Contact"); /* Safe */
+		context = PJSIP_URI_IN_CONTACT_HDR;
+		local_uri = pjsip_uri_get_uri(((pjsip_contact_hdr *)hdr)->uri);
+		break;
+	}
+
+	hdrbuf_len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, rdata->msg_info.msg->line.req.uri, hdrbuf, 512);
+	hdrbuf[hdrbuf_len] = '\0';
+	request_uri = ast_strdupa(hdrbuf);
+	hdrbuf_len = pjsip_uri_print(context, local_uri, hdrbuf, 512);
+	hdrbuf[hdrbuf_len] = '\0';
+
+	ast_debug(2, "There was a non sip(s) URI scheme in %s URI '%s' for request '%*.*s %s'\n",
+		header_name, hdrbuf,
+		(int)rdata->msg_info.msg->line.req.method.name.slen,
+		(int)rdata->msg_info.msg->line.req.method.name.slen,
+		rdata->msg_info.msg->line.req.method.name.ptr, request_uri);
+#endif
+}
+
+static pj_bool_t on_rx_process_uris(pjsip_rx_data *rdata)
+{
+	pjsip_contact_hdr *contact = NULL;
+
+	if (rdata->msg_info.msg->type != PJSIP_REQUEST_MSG) {
+		return PJ_FALSE;
+	}
+
+	if (!is_sip_uri(rdata->msg_info.msg->line.req.uri)) {
+		print_uri_debug(URI_TYPE_REQUEST, rdata, NULL);
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata,
+			PJSIP_SC_UNSUPPORTED_URI_SCHEME, NULL, NULL, NULL);
+		return PJ_TRUE;
+	}
+
+	if (!is_sip_uri(rdata->msg_info.from->uri)) {
+		print_uri_debug(URI_TYPE_FROM, rdata, (pjsip_hdr *)rdata->msg_info.from);
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata,
+			PJSIP_SC_UNSUPPORTED_URI_SCHEME, NULL, NULL, NULL);
+		return PJ_TRUE;
+	}
+
+	if (!is_sip_uri(rdata->msg_info.to->uri)) {
+		print_uri_debug(URI_TYPE_TO, rdata, (pjsip_hdr *)rdata->msg_info.to);
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata,
+			PJSIP_SC_UNSUPPORTED_URI_SCHEME, NULL, NULL, NULL);
+		return PJ_TRUE;
+	}
+
+
+	contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(
+		rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
+
+	if (!contact && pjsip_method_creates_dialog(&rdata->msg_info.msg->line.req.method)) {
+		/* A contact header is required for dialog creating methods */
+		static const pj_str_t missing_contact = { "Missing Contact header", 22 };
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400,
+				&missing_contact, NULL, NULL);
+		return PJ_TRUE;
+	}
+
+	while (contact) {
+		if (!contact->star && !is_sip_uri(contact->uri)) {
+			print_uri_debug(URI_TYPE_CONTACT, rdata, (pjsip_hdr *)contact);
+			pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata,
+				PJSIP_SC_UNSUPPORTED_URI_SCHEME, NULL, NULL, NULL);
+			return PJ_TRUE;
+		}
+		contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(
+			rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next);
+	}
+
+	return PJ_FALSE;
+}
+
+static pj_bool_t on_rx_process_symmetric_transport(pjsip_rx_data *rdata)
 {
 	pjsip_contact_hdr *contact;
 	pjsip_sip_uri *uri;
@@ -358,29 +498,45 @@ static pj_bool_t multihomed_on_rx_message(pjsip_rx_data *rdata)
 	return PJ_FALSE;
 }
 
-void ast_res_pjsip_cleanup_message_ip_updater(void)
+static pj_bool_t filter_on_rx_message(pjsip_rx_data *rdata)
 {
-	ast_sip_unregister_service(&multihomed_module);
-	ast_sip_unregister_supplement(&multihomed_supplement);
-	ast_sip_session_unregister_supplement(&multihomed_session_supplement);
+	pj_bool_t rc;
+
+	rc = on_rx_process_uris(rdata);
+	if (rc == PJ_TRUE) {
+		return rc;
+	}
+
+	rc = on_rx_process_symmetric_transport(rdata);
+	if (rc == PJ_TRUE) {
+		return rc;
+	}
+
+	return PJ_FALSE;
 }
 
-int ast_res_pjsip_init_message_ip_updater(void)
+void ast_res_pjsip_cleanup_message_filter(void)
 {
-	if (ast_sip_session_register_supplement(&multihomed_session_supplement)) {
-		ast_log(LOG_ERROR, "Could not register multihomed session supplement for outgoing requests\n");
-		return -1;
-	}
+	internal_sip_unregister_service(&filter_module_tsx);
+	internal_sip_unregister_service(&filter_module_transport);
+	internal_sip_unregister_supplement(&filter_supplement);
+	internal_sip_session_unregister_supplement(&filter_session_supplement);
+}
+
+int ast_res_pjsip_init_message_filter(void)
+{
+	internal_sip_session_register_supplement(&filter_session_supplement);
+	internal_sip_register_supplement(&filter_supplement);
 
-	if (ast_sip_register_supplement(&multihomed_supplement)) {
-		ast_log(LOG_ERROR, "Could not register multihomed supplement for outgoing requests\n");
-		ast_res_pjsip_cleanup_message_ip_updater();
+	if (internal_sip_register_service(&filter_module_transport)) {
+		ast_log(LOG_ERROR, "Could not register message filter module for incoming and outgoing requests\n");
+		ast_res_pjsip_cleanup_message_filter();
 		return -1;
 	}
 
-	if (ast_sip_register_service(&multihomed_module)) {
-		ast_log(LOG_ERROR, "Could not register multihomed module for incoming and outgoing requests\n");
-		ast_res_pjsip_cleanup_message_ip_updater();
+	if (internal_sip_register_service(&filter_module_tsx)) {
+		ast_log(LOG_ERROR, "Could not register message filter module for incoming and outgoing requests\n");
+		ast_res_pjsip_cleanup_message_filter();
 		return -1;
 	}
 
diff --git a/res/res_pjsip/pjsip_session.c b/res/res_pjsip/pjsip_session.c
new file mode 100644
index 0000000..7460e0a
--- /dev/null
+++ b/res/res_pjsip/pjsip_session.c
@@ -0,0 +1,121 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2017, CFWare, LLC
+ *
+ * Corey Farrell <git at cfware.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.
+ */
+
+#include "asterisk.h"
+
+#include <pjsip.h>
+#include <pjsip_ua.h>
+#include <pjlib.h>
+
+#include "asterisk/res_pjsip.h"
+#include "asterisk/res_pjsip_session.h"
+#include "include/res_pjsip_private.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/lock.h"
+#include "asterisk/module.h"
+
+
+AST_RWLIST_HEAD_STATIC(session_supplements, ast_sip_session_supplement);
+
+void internal_sip_session_register_supplement(struct ast_sip_session_supplement *supplement)
+{
+	struct ast_sip_session_supplement *iter;
+	int inserted = 0;
+	SCOPED_LOCK(lock, &session_supplements, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
+
+	if (!supplement->response_priority) {
+		supplement->response_priority = AST_SIP_SESSION_BEFORE_MEDIA;
+	}
+
+	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&session_supplements, iter, next) {
+		if (iter->priority > supplement->priority) {
+			AST_RWLIST_INSERT_BEFORE_CURRENT(supplement, next);
+			inserted = 1;
+			break;
+		}
+	}
+	AST_RWLIST_TRAVERSE_SAFE_END;
+
+	if (!inserted) {
+		AST_RWLIST_INSERT_TAIL(&session_supplements, supplement, next);
+	}
+}
+
+int ast_sip_session_register_supplement(struct ast_sip_session_supplement *supplement)
+{
+	internal_sip_session_register_supplement(supplement);
+	internal_res_pjsip_ref();
+
+	return 0;
+}
+
+int internal_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement)
+{
+	struct ast_sip_session_supplement *iter;
+	int res = -1;
+	SCOPED_LOCK(lock, &session_supplements, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
+
+	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&session_supplements, iter, next) {
+		if (supplement == iter) {
+			AST_RWLIST_REMOVE_CURRENT(next);
+			res = 0;
+			break;
+		}
+	}
+	AST_RWLIST_TRAVERSE_SAFE_END;
+
+	return res;
+}
+
+void ast_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement)
+{
+	if (!internal_sip_session_unregister_supplement(supplement)) {
+		internal_res_pjsip_unref();
+	}
+}
+
+static struct ast_sip_session_supplement *supplement_dup(const struct ast_sip_session_supplement *src)
+{
+	struct ast_sip_session_supplement *dst = ast_calloc(1, sizeof(*dst));
+
+	if (!dst) {
+		return NULL;
+	}
+	/* Will need to revisit if shallow copy becomes an issue */
+	*dst = *src;
+
+	return dst;
+}
+
+int ast_sip_session_add_supplements(struct ast_sip_session *session)
+{
+	struct ast_sip_session_supplement *iter;
+	SCOPED_LOCK(lock, &session_supplements, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK);
+
+	AST_RWLIST_TRAVERSE(&session_supplements, iter, next) {
+		struct ast_sip_session_supplement *copy = supplement_dup(iter);
+
+		if (!copy) {
+			return -1;
+		}
+		AST_LIST_INSERT_TAIL(&session->supplements, copy, next);
+	}
+
+	return 0;
+}
+
diff --git a/res/res_pjsip/pjsip_transport_events.c b/res/res_pjsip/pjsip_transport_events.c
new file mode 100644
index 0000000..0f57303
--- /dev/null
+++ b/res/res_pjsip/pjsip_transport_events.c
@@ -0,0 +1,366 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2017, Digium Inc.
+ *
+ * Richard Mudgett <rmudgett 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 Manages the global transport event notification callbacks.
+ *
+ * \author Richard Mudgett <rmudgett at digium.com>
+ * 	See Also:
+ *
+ * \arg \ref AstCREDITS
+ */
+
+
+#include "asterisk.h"
+
+#include "asterisk/res_pjsip.h"
+#include "include/res_pjsip_private.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/vector.h"
+
+/* ------------------------------------------------------------------- */
+
+/*! \brief Number of buckets for monitored active transports */
+#define ACTIVE_TRANSPORTS_BUCKETS 127
+
+/*! Who to notify when transport shuts down. */
+struct transport_monitor_notifier {
+	/*! Who to call when transport shuts down. */
+	ast_transport_monitor_shutdown_cb cb;
+	/*! ao2 data object to pass to callback. */
+	void *data;
+};
+
+/*! \brief Structure for transport to be monitored */
+struct transport_monitor {
+	/*! \brief The underlying PJSIP transport */
+	pjsip_transport *transport;
+	/*! Who is interested in when this transport shuts down. */
+	AST_VECTOR(, struct transport_monitor_notifier) monitors;
+};
+
+/*! \brief Global container of active reliable transports */
+static AO2_GLOBAL_OBJ_STATIC(active_transports);
+
+/*! \brief Existing transport events callback that we need to invoke */
+static pjsip_tp_state_callback tpmgr_state_callback;
+
+/*! List of registered transport state callbacks. */
+static AST_RWLIST_HEAD(, ast_sip_tpmgr_state_callback) transport_state_list;
+
+
+/*! \brief Hashing function for struct transport_monitor */
+AO2_STRING_FIELD_HASH_FN(transport_monitor, transport->obj_name);
+
+/*! \brief Comparison function for struct transport_monitor */
+AO2_STRING_FIELD_CMP_FN(transport_monitor, transport->obj_name);
+
+static const char *transport_state2str(pjsip_transport_state state)
+{
+	const char *name;
+
+	switch (state) {
+	case PJSIP_TP_STATE_CONNECTED:
+		name = "CONNECTED";
+		break;
+	case PJSIP_TP_STATE_DISCONNECTED:
+		name = "DISCONNECTED";
+		break;
+	case PJSIP_TP_STATE_SHUTDOWN:
+		name = "SHUTDOWN";
+		break;
+	case PJSIP_TP_STATE_DESTROY:
+		name = "DESTROY";
+		break;
+	default:
+		/*
+		 * We have to have a default case because the enum is
+		 * defined by a third-party library.
+		 */
+		ast_assert(0);
+		name = "<unknown>";
+		break;
+	}
+	return name;
+}
+
+static void transport_monitor_dtor(void *vdoomed)
+{
+	struct transport_monitor *monitored = vdoomed;
+	int idx;
+
+	for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
+		struct transport_monitor_notifier *notifier;
+
+		notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
+		ao2_cleanup(notifier->data);
+	}
+	AST_VECTOR_FREE(&monitored->monitors);
+}
+
+/*! \brief Callback invoked when transport state changes occur */
+static void transport_state_callback(pjsip_transport *transport,
+	pjsip_transport_state state, const pjsip_transport_state_info *info)
+{
+	struct ao2_container *transports;
+
+	/* We only care about monitoring reliable transports */
+	if (PJSIP_TRANSPORT_IS_RELIABLE(transport)
+		&& (transports = ao2_global_obj_ref(active_transports))) {
+		struct transport_monitor *monitored;
+
+		ast_debug(3, "Reliable transport '%s' state:%s\n",
+			transport->obj_name, transport_state2str(state));
+		switch (state) {
+		case PJSIP_TP_STATE_CONNECTED:
+			monitored = ao2_alloc_options(sizeof(*monitored),
+				transport_monitor_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
+			if (!monitored) {
+				break;
+			}
+			monitored->transport = transport;
+			if (AST_VECTOR_INIT(&monitored->monitors, 2)) {
+				ao2_ref(monitored, -1);
+				break;
+			}
+
+			ao2_link(transports, monitored);
+			ao2_ref(monitored, -1);
+			break;
+		case PJSIP_TP_STATE_DISCONNECTED:
+			if (!transport->is_shutdown) {
+				pjsip_transport_shutdown(transport);
+			}
+			break;
+		case PJSIP_TP_STATE_SHUTDOWN:
+			/*
+			 * Set shutdown flag early so we can force a new transport to be
+			 * created if a monitor callback needs to reestablish a link.
+			 * PJPROJECT sets the flag after this routine returns even though
+			 * it has already called the transport's shutdown routine.
+			 */
+			transport->is_shutdown = PJ_TRUE;
+
+			monitored = ao2_find(transports, transport->obj_name,
+				OBJ_SEARCH_KEY | OBJ_UNLINK);
+			if (monitored) {
+				int idx;
+
+				for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
+					struct transport_monitor_notifier *notifier;
+
+					notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
+					notifier->cb(notifier->data);
+				}
+				ao2_ref(monitored, -1);
+			}
+			break;
+		default:
+			break;
+		}
+
+		ao2_ref(transports, -1);
+	}
+
+	/* Loop over other transport state callbacks registered with us. */
+	if (!AST_LIST_EMPTY(&transport_state_list)) {
+		struct ast_sip_tpmgr_state_callback *tpmgr_notifier;
+
+		AST_RWLIST_RDLOCK(&transport_state_list);
+		AST_LIST_TRAVERSE(&transport_state_list, tpmgr_notifier, node) {
+			tpmgr_notifier->cb(transport, state, info);
+		}
+		AST_RWLIST_UNLOCK(&transport_state_list);
+	}
+
+	/* Forward to the old state callback if present */
+	if (tpmgr_state_callback) {
+		tpmgr_state_callback(transport, state, info);
+	}
+}
+
+static int transport_monitor_unregister_all(void *obj, void *arg, int flags)
+{
+	struct transport_monitor *monitored = obj;
+	ast_transport_monitor_shutdown_cb cb = arg;
+	int idx;
+
+	for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
+		struct transport_monitor_notifier *notifier;
+
+		notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
+		if (notifier->cb == cb) {
+			ao2_cleanup(notifier->data);
+			AST_VECTOR_REMOVE_UNORDERED(&monitored->monitors, idx);
+			break;
+		}
+	}
+	return 0;
+}
+
+void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb)
+{
+	struct ao2_container *transports;
+
+	transports = ao2_global_obj_ref(active_transports);
+	if (!transports) {
+		return;
+	}
+	ao2_callback(transports, OBJ_MULTIPLE | OBJ_NODATA, transport_monitor_unregister_all,
+		cb);
+	ao2_ref(transports, -1);
+}
+
+void ast_sip_transport_monitor_unregister(pjsip_transport *transport, ast_transport_monitor_shutdown_cb cb)
+{
+	struct ao2_container *transports;
+	struct transport_monitor *monitored;
+
+	transports = ao2_global_obj_ref(active_transports);
+	if (!transports) {
+		return;
+	}
+
+	ao2_lock(transports);
+	monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+	if (monitored) {
+		int idx;
+
+		for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
+			struct transport_monitor_notifier *notifier;
+
+			notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
+			if (notifier->cb == cb) {
+				ao2_cleanup(notifier->data);
+				AST_VECTOR_REMOVE_UNORDERED(&monitored->monitors, idx);
+				break;
+			}
+		}
+		ao2_ref(monitored, -1);
+	}
+	ao2_unlock(transports);
+	ao2_ref(transports, -1);
+}
+
+enum ast_transport_monitor_reg ast_sip_transport_monitor_register(pjsip_transport *transport,
+	ast_transport_monitor_shutdown_cb cb, void *ao2_data)
+{
+	struct ao2_container *transports;
+	struct transport_monitor *monitored;
+	enum ast_transport_monitor_reg res = AST_TRANSPORT_MONITOR_REG_NOT_FOUND;
+
+	transports = ao2_global_obj_ref(active_transports);
+	if (!transports) {
+		return res;
+	}
+
+	ao2_lock(transports);
+	monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+	if (monitored) {
+		int idx;
+		struct transport_monitor_notifier new_monitor;
+
+		/* Check if the callback monitor already exists */
+		for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
+			struct transport_monitor_notifier *notifier;
+
+			notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
+			if (notifier->cb == cb) {
+				/* The monitor is already in the vector replace with new ao2_data. */
+				ao2_replace(notifier->data, ao2_data);
+				res = AST_TRANSPORT_MONITOR_REG_REPLACED;
+				goto register_done;
+			}
+		}
+
+		/* Add new monitor to vector */
+		new_monitor.cb = cb;
+		new_monitor.data = ao2_bump(ao2_data);
+		if (AST_VECTOR_APPEND(&monitored->monitors, new_monitor)) {
+			ao2_cleanup(ao2_data);
+			res = AST_TRANSPORT_MONITOR_REG_FAILED;
+		}
+
+register_done:
+		ao2_ref(monitored, -1);
+	}
+	ao2_unlock(transports);
+	ao2_ref(transports, -1);
+	return res;
+}
+
+void ast_sip_transport_state_unregister(struct ast_sip_tpmgr_state_callback *element)
+{
+	AST_RWLIST_WRLOCK(&transport_state_list);
+	AST_LIST_REMOVE(&transport_state_list, element, node);
+	AST_RWLIST_UNLOCK(&transport_state_list);
+}
+
+void ast_sip_transport_state_register(struct ast_sip_tpmgr_state_callback *element)
+{
+	struct ast_sip_tpmgr_state_callback *tpmgr_notifier;
+
+	AST_RWLIST_WRLOCK(&transport_state_list);
+	AST_LIST_TRAVERSE(&transport_state_list, tpmgr_notifier, node) {
+		if (element == tpmgr_notifier) {
+			/* Already registered. */
+			AST_RWLIST_UNLOCK(&transport_state_list);
+			return;
+		}
+	}
+	AST_LIST_INSERT_HEAD(&transport_state_list, element, node);
+	AST_RWLIST_UNLOCK(&transport_state_list);
+}
+
+void ast_sip_destroy_transport_events(void)
+{
+	pjsip_tpmgr *tpmgr;
+
+	tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
+	if (tpmgr) {
+		pjsip_tpmgr_set_state_cb(tpmgr, tpmgr_state_callback);
+	}
+
+	ao2_global_obj_release(active_transports);
+}
+
+int ast_sip_initialize_transport_events(void)
+{
+	pjsip_tpmgr *tpmgr;
+	struct ao2_container *transports;
+
+	tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
+	if (!tpmgr) {
+		return -1;
+	}
+
+	transports = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
+		ACTIVE_TRANSPORTS_BUCKETS, transport_monitor_hash_fn, NULL,
+		transport_monitor_cmp_fn);
+	if (!transports) {
+		return -1;
+	}
+	ao2_global_obj_replace_unref(active_transports, transports);
+	ao2_ref(transports, -1);
+
+	tpmgr_state_callback = pjsip_tpmgr_get_state_cb(tpmgr);
+	pjsip_tpmgr_set_state_cb(tpmgr, &transport_state_callback);
+
+	return 0;
+}
diff --git a/res/res_pjsip/presence_xml.c b/res/res_pjsip/presence_xml.c
index 1aca307..3cea79e 100644
--- a/res/res_pjsip/presence_xml.c
+++ b/res/res_pjsip/presence_xml.c
@@ -89,7 +89,7 @@ void ast_sip_presence_exten_state_to_str(int state, char **statestring, char **p
 	case AST_EXTENSION_RINGING:
 		*statestring = "early";
 		*local_state = NOTIFY_INUSE;
-		*pidfstate = "busy";
+		*pidfstate = "on-the-phone";
 		*pidfnote = "Ringing";
 		break;
 	case (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING):
@@ -99,31 +99,31 @@ void ast_sip_presence_exten_state_to_str(int state, char **statestring, char **p
 			*statestring = "confirmed";
 		}
 		*local_state = NOTIFY_INUSE;
-		*pidfstate = "busy";
+		*pidfstate = "on-the-phone";
 		*pidfnote = "Ringing";
 		break;
 	case AST_EXTENSION_INUSE:
 		*statestring = "confirmed";
 		*local_state = NOTIFY_INUSE;
-		*pidfstate = "busy";
+		*pidfstate = "on-the-phone";
 		*pidfnote = "On the phone";
 		break;
 	case AST_EXTENSION_BUSY:
 		*statestring = "confirmed";
-		*local_state = NOTIFY_CLOSED;
-		*pidfstate = "busy";
+		*local_state = NOTIFY_INUSE;
+		*pidfstate = "on-the-phone";
 		*pidfnote = "On the phone";
 		break;
 	case AST_EXTENSION_UNAVAILABLE:
 		*statestring = "terminated";
 		*local_state = NOTIFY_CLOSED;
-		*pidfstate = "away";
+		*pidfstate = "--";
 		*pidfnote = "Unavailable";
 		break;
 	case AST_EXTENSION_ONHOLD:
 		*statestring = "confirmed";
-		*local_state = NOTIFY_CLOSED;
-		*pidfstate = "busy";
+		*local_state = NOTIFY_INUSE;
+		*pidfstate = "on-the-phone";
 		*pidfnote = "On hold";
 		break;
 	case AST_EXTENSION_NOT_INUSE:
diff --git a/res/res_pjsip_caller_id.c b/res/res_pjsip_caller_id.c
index 0aca2db..0c1325c 100644
--- a/res/res_pjsip_caller_id.c
+++ b/res/res_pjsip_caller_id.c
@@ -149,12 +149,12 @@ static int set_id_from_pai(pjsip_rx_data *rdata, struct ast_party_id *id)
 	}
 
 	privacy = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &privacy_str, NULL);
-	if (privacy && !pj_stricmp2(&privacy->hvalue, "id")) {
-		id->number.presentation = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
-		id->name.presentation = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
-	} else {
+	if (!privacy || !pj_stricmp2(&privacy->hvalue, "none")) {
 		id->number.presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
 		id->name.presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
+	} else {
+		id->number.presentation = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
+		id->name.presentation = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
 	}
 
 	return 0;
diff --git a/res/res_pjsip_messaging.c b/res/res_pjsip_messaging.c
index 8b465e0..3209141 100644
--- a/res/res_pjsip_messaging.c
+++ b/res/res_pjsip_messaging.c
@@ -69,8 +69,8 @@ static enum pjsip_status_code check_content_type(const pjsip_rx_data *rdata)
 			&rdata->msg_info.msg->body->content_type, "text", "plain");
 	} else {
 		res = rdata->msg_info.ctype &&
-			!pj_strcmp2(&rdata->msg_info.ctype->media.type, "text") &&
-			!pj_strcmp2(&rdata->msg_info.ctype->media.subtype, "plain");
+			ast_sip_is_content_type(
+				&rdata->msg_info.ctype->media, "text", "plain");
 	}
 
 	return res ? PJSIP_SC_OK : PJSIP_SC_UNSUPPORTED_MEDIA_TYPE;
@@ -512,7 +512,7 @@ static enum pjsip_status_code rx_data_to_ast_msg(pjsip_rx_data *rdata, struct as
 	buf[size] = '\0';
 	res |= ast_msg_set_from(msg, "%s", buf);
 
-	field = pj_sockaddr_print(&rdata->pkt_info.src_addr, buf, sizeof(buf) - 1, 1);
+	field = pj_sockaddr_print(&rdata->pkt_info.src_addr, buf, sizeof(buf) - 1, 3);
 	res |= ast_msg_set_var(msg, "PJSIP_RECVADDR", field);
 
 	switch (rdata->tp_info.transport->key.type) {
diff --git a/res/res_pjsip_nat.c b/res/res_pjsip_nat.c
index a0ce2a9..370004a 100644
--- a/res/res_pjsip_nat.c
+++ b/res/res_pjsip_nat.c
@@ -35,6 +35,7 @@
 static void rewrite_uri(pjsip_rx_data *rdata, pjsip_sip_uri *uri)
 {
 	pj_cstr(&uri->host, rdata->pkt_info.src_name);
+	uri->port = rdata->pkt_info.src_port;
 	if (!strcasecmp("WSS", rdata->tp_info.transport->type_name)) {
 		/* WSS is special, we don't want to overwrite the URI at all as it needs to be ws */
 	} else if (strcasecmp("udp", rdata->tp_info.transport->type_name)) {
@@ -42,7 +43,6 @@ static void rewrite_uri(pjsip_rx_data *rdata, pjsip_sip_uri *uri)
 	} else {
 		uri->transport_param.slen = 0;
 	}
-	uri->port = rdata->pkt_info.src_port;
 }
 
 static int rewrite_route_set(pjsip_rx_data *rdata, pjsip_dialog *dlg)
@@ -165,7 +165,7 @@ static int find_transport_state_in_use(void *obj, void *arg, int flags)
 		((details->type == transport_state->type) && (transport_state->factory) &&
 			!pj_strcmp(&transport_state->factory->addr_name.host, &details->local_address) &&
 			transport_state->factory->addr_name.port == details->local_port))) {
-		return CMP_MATCH | CMP_STOP;
+		return CMP_MATCH;
 	}
 
 	return 0;
@@ -267,16 +267,16 @@ static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata)
 		ast_sockaddr_set_port(&addr, tdata->tp_info.dst_port);
 
 		/* See if where we are sending this request is local or not, and if not that we can get a Contact URI to modify */
-		if (ast_apply_ha(transport_state->localnet, &addr) != AST_SENSE_ALLOW) {
+		if (ast_sip_transport_is_local(transport_state, &addr)) {
 			ast_debug(5, "Request is being sent to local address, skipping NAT manipulation\n");
 			return PJ_SUCCESS;
 		}
 	}
 
-	if (!ast_sockaddr_isnull(&transport_state->external_address)) {
+	if (!ast_sockaddr_isnull(&transport_state->external_signaling_address)) {
 		/* Update the contact header with the external address */
 		if (uri || (uri = nat_get_contact_sip_uri(tdata))) {
-			pj_strdup2(tdata->pool, &uri->host, ast_sockaddr_stringify_host(&transport_state->external_address));
+			pj_strdup2(tdata->pool, &uri->host, ast_sockaddr_stringify_host(&transport_state->external_signaling_address));
 			if (transport->external_signaling_port) {
 				uri->port = transport->external_signaling_port;
 				ast_debug(4, "Re-wrote Contact URI port to %d\n", uri->port);
@@ -285,7 +285,7 @@ static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata)
 
 		/* Update the via header if relevant */
 		if ((tdata->msg->type == PJSIP_REQUEST_MSG) && (via || (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL)))) {
-			pj_strdup2(tdata->pool, &via->sent_by.host, ast_sockaddr_stringify_host(&transport_state->external_address));
+			pj_strdup2(tdata->pool, &via->sent_by.host, ast_sockaddr_stringify_host(&transport_state->external_signaling_address));
 			if (transport->external_signaling_port) {
 				via->sent_by.port = transport->external_signaling_port;
 			}
diff --git a/res/res_pjsip_outbound_publish.c b/res/res_pjsip_outbound_publish.c
index f84f118..a9f9233 100644
--- a/res/res_pjsip_outbound_publish.c
+++ b/res/res_pjsip_outbound_publish.c
@@ -33,6 +33,7 @@
 #include "asterisk/taskprocessor.h"
 #include "asterisk/threadpool.h"
 #include "asterisk/datastore.h"
+#include "res_pjsip/include/res_pjsip_private.h"
 
 /*** DOCUMENTATION
 	<configInfo name="res_pjsip_outbound_publish" language="en_US">
@@ -1115,10 +1116,27 @@ static int validate_publish_config(struct ast_sip_outbound_publish *publish)
 		ast_log(LOG_ERROR, "No server URI specified on outbound publish '%s'\n",
 			ast_sorcery_object_get_id(publish));
 		return -1;
+	} else if (ast_sip_validate_uri_length(publish->server_uri)) {
+		ast_log(LOG_ERROR, "Server URI or hostname length exceeds pjproject limit or is not a sip(s) uri: '%s' on outbound publish '%s'\n",
+			publish->server_uri,
+			ast_sorcery_object_get_id(publish));
+		return -1;
 	} else if (ast_strlen_zero(publish->event)) {
 		ast_log(LOG_ERROR, "No event type specified for outbound publish '%s'\n",
 			ast_sorcery_object_get_id(publish));
 		return -1;
+	} else if (!ast_strlen_zero(publish->from_uri)
+		&& ast_sip_validate_uri_length(publish->from_uri)) {
+		ast_log(LOG_ERROR, "From URI or hostname length exceeds pjproject limit or is not a sip(s) uri: '%s' on outbound publish '%s'\n",
+			publish->from_uri,
+			ast_sorcery_object_get_id(publish));
+		return -1;
+	} else if (!ast_strlen_zero(publish->to_uri)
+		&& ast_sip_validate_uri_length(publish->to_uri)) {
+		ast_log(LOG_ERROR, "To URI or hostname length exceeds pjproject limit or is not a sip(s) uri: '%s' on outbound publish '%s'\n",
+			publish->to_uri,
+			ast_sorcery_object_get_id(publish));
+		return -1;
 	}
 	return 0;
 }
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index 0a65e6e..0fed59f 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -358,6 +358,8 @@ struct sip_outbound_registration_client_state {
 	unsigned int auth_attempted:1;
 	/*! \brief The name of the transport to be used for the registration */
 	char *transport_name;
+	/*! \brief The name of the registration sorcery object */
+	char *registration_name;
 };
 
 /*! \brief Outbound registration state information (persists for lifetime that registration should exist) */
@@ -459,7 +461,7 @@ static int line_identify_relationship(void *obj, void *arg, int flags)
 	struct sip_outbound_registration_state *state = obj;
 	pjsip_param *line = arg;
 
-	return !pj_strcmp2(&line->value, state->client_state->line) ? CMP_MATCH | CMP_STOP : 0;
+	return !pj_strcmp2(&line->value, state->client_state->line) ? CMP_MATCH : 0;
 }
 
 static struct pjsip_param *get_uri_option_line(const void *uri)
@@ -559,20 +561,21 @@ static int handle_client_registration(void *data)
 {
 	RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);
 	pjsip_tx_data *tdata;
-	pjsip_regc_info info;
-	char server_uri[PJSIP_MAX_URL_SIZE];
-	char client_uri[PJSIP_MAX_URL_SIZE];
 
 	if (client_state->status == SIP_REGISTRATION_STOPPED
 		|| pjsip_regc_register(client_state->client, PJ_FALSE, &tdata) != PJ_SUCCESS) {
 		return 0;
 	}
 
-	pjsip_regc_get_info(client_state->client, &info);
-	ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
-	ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
-	ast_debug(1, "Outbound REGISTER attempt %u to '%s' with client '%s'\n",
-		client_state->retries + 1, server_uri, client_uri);
+	if (DEBUG_ATLEAST(1)) {
+		pjsip_regc_info info;
+
+		pjsip_regc_get_info(client_state->client, &info);
+		ast_log(LOG_DEBUG, "Outbound REGISTER attempt %u to '%.*s' with client '%.*s'\n",
+			client_state->retries + 1,
+			(int) info.server_uri.slen, info.server_uri.ptr,
+			(int) info.client_uri.slen, info.client_uri.ptr);
+	}
 
 	if (client_state->support_path) {
 		pjsip_supported_hdr *hdr;
@@ -795,6 +798,82 @@ static void schedule_retry(struct registration_response *response, unsigned int
 	}
 }
 
+static int reregister_immediately_cb(void *obj)
+{
+	struct sip_outbound_registration_state *state = obj;
+
+	if (state->client_state->status != SIP_REGISTRATION_REGISTERED) {
+		ao2_ref(state, -1);
+		return 0;
+	}
+
+	if (DEBUG_ATLEAST(1)) {
+		pjsip_regc_info info;
+
+		pjsip_regc_get_info(state->client_state->client, &info);
+		ast_log(LOG_DEBUG,
+			"Outbound registration transport to server '%.*s' from client '%.*s' shutdown\n",
+			(int) info.server_uri.slen, info.server_uri.ptr,
+			(int) info.client_uri.slen, info.client_uri.ptr);
+	}
+
+	cancel_registration(state->client_state);
+
+	ao2_ref(state->client_state, +1);
+	handle_client_registration(state->client_state);
+
+	ao2_ref(state, -1);
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief The reliable transport we registered using has shutdown.
+ * \since 13.18.0
+ *
+ * \param obj What is needed to initiate a reregister attempt.
+ *
+ * \return Nothing
+ */
+static void registration_transport_shutdown_cb(void *obj)
+{
+	const char *registration_name = obj;
+	struct sip_outbound_registration_state *state;
+
+	state = get_state(registration_name);
+	if (!state) {
+		/* Registration no longer exists or shutting down. */
+		return;
+	}
+	if (ast_sip_push_task(state->client_state->serializer, reregister_immediately_cb, state)) {
+		ao2_ref(state, -1);
+	}
+}
+
+static void registration_transport_monitor_setup(pjsip_transport *transport, const char *registration_name)
+{
+	char *monitor;
+
+	if (!PJSIP_TRANSPORT_IS_RELIABLE(transport)) {
+		return;
+	}
+	monitor = ao2_alloc_options(strlen(registration_name) + 1, NULL,
+		AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!monitor) {
+		return;
+	}
+	strcpy(monitor, registration_name);/* Safe */
+
+	/*
+	 * We'll ignore if the transport has already been shutdown before we
+	 * register the monitor.  We might get into a message spamming infinite
+	 * loop of registration, shutdown, reregistration...
+	 */
+	ast_sip_transport_monitor_register(transport, registration_transport_shutdown_cb,
+		monitor);
+	ao2_ref(monitor, -1);
+}
+
 /*! \brief Callback function for handling a response to a registration attempt */
 static int handle_registration_response(void *data)
 {
@@ -863,9 +942,15 @@ static int handle_registration_response(void *data)
 				next_registration_round = 0;
 			}
 			schedule_registration(response->client_state, next_registration_round);
+
+			/* See if we should monitor for transport shutdown */
+			registration_transport_monitor_setup(response->rdata->tp_info.transport,
+				response->client_state->registration_name);
 		} else {
 			ast_debug(1, "Outbound unregistration to '%s' with client '%s' successful\n", server_uri, client_uri);
 			update_client_state_status(response->client_state, SIP_REGISTRATION_UNREGISTERED);
+			ast_sip_transport_monitor_unregister(response->rdata->tp_info.transport,
+				registration_transport_shutdown_cb);
 		}
 	} else if (response->client_state->destroy) {
 		/* We need to deal with the pending destruction instead. */
@@ -988,7 +1073,8 @@ static void sip_outbound_registration_state_destroy(void *obj)
 	struct sip_outbound_registration_state *state = obj;
 
 	ast_debug(3, "Destroying registration state for registration to server '%s' from client '%s'\n",
-			state->registration->server_uri, state->registration->client_uri);
+		state->registration ? state->registration->server_uri : "",
+		state->registration ? state->registration->client_uri : "");
 	ao2_cleanup(state->registration);
 
 	if (!state->client_state) {
@@ -1007,12 +1093,13 @@ static void sip_outbound_registration_client_state_destroy(void *obj)
 {
 	struct sip_outbound_registration_client_state *client_state = obj;
 
-	ast_free(client_state->transport_name);
 	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);
+	ast_free(client_state->transport_name);
+	ast_free(client_state->registration_name);
 }
 
 /*! \brief Allocator function for registration state */
@@ -1032,6 +1119,23 @@ static struct sip_outbound_registration_state *sip_outbound_registration_state_a
 		return NULL;
 	}
 
+	state->client_state->status = SIP_REGISTRATION_UNREGISTERED;
+	state->client_state->timer.user_data = state->client_state;
+	state->client_state->timer.cb = sip_outbound_registration_timer_cb;
+	state->client_state->transport_name = ast_strdup(registration->transport);
+	state->client_state->registration_name =
+		ast_strdup(ast_sorcery_object_get_id(registration));
+
+	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));
+
+	if (!state->client_state->transport_name
+		|| !state->client_state->registration_name) {
+		ao2_cleanup(state);
+		return NULL;
+	}
+
 	/* Create name with seq number appended. */
 	ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/outreg/%s",
 		ast_sorcery_object_get_id(registration));
@@ -1042,14 +1146,6 @@ static struct sip_outbound_registration_state *sip_outbound_registration_state_a
 		ao2_cleanup(state);
 		return NULL;
 	}
-	state->client_state->status = SIP_REGISTRATION_UNREGISTERED;
-	state->client_state->timer.user_data = state->client_state;
-	state->client_state->timer.cb = sip_outbound_registration_timer_cb;
-	state->client_state->transport_name = ast_strdup(registration->transport);
-
-	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;
@@ -1333,7 +1429,7 @@ static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, vo
 			ast_sorcery_object_get_id(applied));
 		return -1;
 	} else if (ast_sip_validate_uri_length(applied->server_uri)) {
-			ast_log(LOG_ERROR, "Server URI or hostname length exceeds pjpropject limit '%s'\n",
+			ast_log(LOG_ERROR, "Server URI or hostname length exceeds pjproject limit or is not a sip(s) uri: '%s'\n",
 				ast_sorcery_object_get_id(applied));
 			return -1;
 	} else if (ast_strlen_zero(applied->client_uri)) {
@@ -1341,7 +1437,7 @@ static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, vo
 			ast_sorcery_object_get_id(applied));
 		return -1;
 	} else if (ast_sip_validate_uri_length(applied->client_uri)) {
-			ast_log(LOG_ERROR, "Client URI or hostname length exceeds pjpropject limit '%s'\n",
+			ast_log(LOG_ERROR, "Client URI or hostname length exceeds pjproject limit or is not a sip(s) uri: '%s'\n",
 				ast_sorcery_object_get_id(applied));
 			return -1;
 	} else if (applied->line && ast_strlen_zero(applied->endpoint)) {
@@ -2053,6 +2149,8 @@ static int unload_module(void)
 
 	ao2_global_obj_release(current_states);
 
+	ast_sip_transport_monitor_unregister_all(registration_transport_shutdown_cb);
+
 	/* Wait for registration serializers to get destroyed. */
 	ast_debug(2, "Waiting for registration transactions to complete for unload.\n");
 	remaining = ast_serializer_shutdown_group_join(shutdown_group, MAX_UNLOAD_TIMEOUT_TIME);
diff --git a/res/res_pjsip_pidf_body_generator.c b/res/res_pjsip_pidf_body_generator.c
index 7d84ded..4daff96 100644
--- a/res/res_pjsip_pidf_body_generator.c
+++ b/res/res_pjsip_pidf_body_generator.c
@@ -75,7 +75,7 @@ static int pidf_generate_body_content(void *body, void *data)
 	pjpidf_tuple_set_contact(state_data->pool, tuple, pj_cstr(&contact, sanitized));
 	pjpidf_tuple_set_contact_prio(state_data->pool, tuple, pj_cstr(&priority, "1"));
 	pjpidf_status_set_basic_open(pjpidf_tuple_get_status(tuple),
-			local_state == NOTIFY_OPEN);
+			local_state == NOTIFY_OPEN || local_state == NOTIFY_INUSE);
 
 	return 0;
 }
diff --git a/res/res_pjsip_pidf_eyebeam_body_supplement.c b/res/res_pjsip_pidf_eyebeam_body_supplement.c
index 95f0da9..0200a46 100644
--- a/res/res_pjsip_pidf_eyebeam_body_supplement.c
+++ b/res/res_pjsip_pidf_eyebeam_body_supplement.c
@@ -46,30 +46,28 @@
  */
 static void add_eyebeam(pj_pool_t *pool, pj_xml_node *node, const char *pidfstate)
 {
-	static const char *XMLNS_PP = "xmlns:pp";
-	static const char *XMLNS_PERSON = "urn:ietf:params:xml:ns:pidf:person";
+	static const char *XMLNS_DM_PREFIX = "xmlns:dm";
+	static const char *XMLNS_DM = "urn:ietf:params:xml:ns:pidf:data-model";
 
-	static const char *XMLNS_ES = "xmlns:es";
-	static const char *XMLNS_RPID_STATUS = "urn:ietf:params:xml:ns:pidf:rpid:status:rpid-status";
+	static const char *XMLNS_RPID_PREFIX = "xmlns:rpid";
+	static const char *XMLNS_RPID = "urn:ietf:params:xml:ns:pidf:rpid";
 
-	static const char *XMLNS_EP = "xmlns:ep";
-	static const char *XMLNS_RPID_PERSON = "urn:ietf:params:xml:ns:pidf:rpid:rpid-person";
-
-	pj_xml_node *person = ast_sip_presence_xml_create_node(pool, node, "pp:person");
-	pj_xml_node *status = ast_sip_presence_xml_create_node(pool, person, "status");
+	pj_xml_node *person = ast_sip_presence_xml_create_node(pool, node, "dm:person");
 
 	if (pidfstate[0] != '-') {
-		pj_xml_node *activities = ast_sip_presence_xml_create_node(pool, status, "ep:activities");
-		size_t str_size = sizeof("ep:") + strlen(pidfstate);
+		pj_xml_node *activities = ast_sip_presence_xml_create_node(pool, person, "rpid:activities");
+		size_t str_size = sizeof("rpid:") + strlen(pidfstate);
+		char *act_str = ast_alloca(str_size);
+
+		/* Safe */
+		strcpy(act_str, "rpid:");
+		strcat(act_str, pidfstate);
 
-		activities->content.ptr = pj_pool_alloc(pool, str_size);
-		activities->content.slen = pj_ansi_snprintf(activities->content.ptr, str_size,
-				"ep:%s", pidfstate);
+		ast_sip_presence_xml_create_node(pool, activities, act_str);
 	}
 
-	ast_sip_presence_xml_create_attr(pool, node, XMLNS_PP, XMLNS_PERSON);
-	ast_sip_presence_xml_create_attr(pool, node, XMLNS_ES, XMLNS_RPID_STATUS);
-	ast_sip_presence_xml_create_attr(pool, node, XMLNS_EP, XMLNS_RPID_PERSON);
+	ast_sip_presence_xml_create_attr(pool, node, XMLNS_DM_PREFIX, XMLNS_DM);
+	ast_sip_presence_xml_create_attr(pool, node, XMLNS_RPID_PREFIX, XMLNS_RPID);
 }
 
 static int pidf_supplement_body(void *body, void *data)
diff --git a/res/res_pjsip_publish_asterisk.c b/res/res_pjsip_publish_asterisk.c
index 7e87762..fa5e4ce 100644
--- a/res/res_pjsip_publish_asterisk.c
+++ b/res/res_pjsip_publish_asterisk.c
@@ -605,8 +605,7 @@ static int asterisk_publication_devicestate_state_change(struct ast_sip_publicat
 	}
 
 	/* We only accept JSON for content */
-	if (pj_strcmp2(&body->content_type.type, "application") ||
-		pj_strcmp2(&body->content_type.subtype, "json")) {
+	if (!ast_sip_is_content_type(&body->content_type, "application", "json")) {
 		ast_debug(2, "Received unsupported content type for Asterisk event on resource '%s'\n",
 			ast_sorcery_object_get_id(config));
 		return -1;
@@ -697,8 +696,7 @@ static int asterisk_publication_mwi_state_change(struct ast_sip_publication *pub
 	}
 
 	/* We only accept JSON for content */
-	if (pj_strcmp2(&body->content_type.type, "application") ||
-		pj_strcmp2(&body->content_type.subtype, "json")) {
+	if (!ast_sip_is_content_type(&body->content_type, "application", "json")) {
 		ast_debug(2, "Received unsupported content type for Asterisk event on resource '%s'\n",
 			ast_sorcery_object_get_id(config));
 		return -1;
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index c62bddd..2682207 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -31,6 +31,7 @@
 #include <pjsip_simple.h>
 #include <pjlib.h>
 
+#include "asterisk/app.h"
 #include "asterisk/res_pjsip_pubsub.h"
 #include "asterisk/module.h"
 #include "asterisk/linkedlists.h"
@@ -515,6 +516,8 @@ AST_RWLIST_HEAD_STATIC(subscriptions, sip_subscription_tree);
 AST_RWLIST_HEAD_STATIC(body_generators, ast_sip_pubsub_body_generator);
 AST_RWLIST_HEAD_STATIC(body_supplements, ast_sip_pubsub_body_supplement);
 
+static pjsip_media_type rlmi_media_type;
+
 static void pubsub_on_evsub_state(pjsip_evsub *sub, pjsip_event *event);
 static void pubsub_on_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata,
 		int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body);
@@ -610,8 +613,12 @@ static void subscription_persistence_update(struct sip_subscription_tree *sub_tr
 		expires = expires_hdr ? expires_hdr->ivalue : DEFAULT_PUBLISH_EXPIRES;
 		sub_tree->persistence->expires = ast_tvadd(ast_tvnow(), ast_samp2tv(expires, 1));
 
-		pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, contact_hdr->uri,
-			sub_tree->persistence->contact_uri, sizeof(sub_tree->persistence->contact_uri));
+		if (contact_hdr) {
+			pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, contact_hdr->uri,
+					sub_tree->persistence->contact_uri, sizeof(sub_tree->persistence->contact_uri));
+		} else {
+			ast_log(LOG_WARNING, "Contact not updated due to missing contact header\n");
+		}
 
 		/* When receiving a packet on an streaming transport, it's possible to receive more than one SIP
 		 * message at a time into the rdata->pkt_info.packet buffer. However, the rdata->msg_info.msg_buf
@@ -2043,8 +2050,6 @@ static void *rlmi_clone_data(pj_pool_t *pool, const void *data, unsigned len)
 static pjsip_multipart_part *build_rlmi_body(pj_pool_t *pool, struct ast_sip_subscription *sub,
 		struct body_part_list *body_parts, unsigned int full_state)
 {
-	static const pj_str_t rlmi_type = { "application", 11 };
-	static const pj_str_t rlmi_subtype = { "rlmi+xml", 8 };
 	pj_xml_node *rlmi;
 	pj_xml_node *name;
 	pjsip_multipart_part *rlmi_part;
@@ -2075,9 +2080,7 @@ static pjsip_multipart_part *build_rlmi_body(pj_pool_t *pool, struct ast_sip_sub
 	rlmi_part = pjsip_multipart_create_part(pool);
 
 	rlmi_part->body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body);
-	pj_strdup(pool, &rlmi_part->body->content_type.type, &rlmi_type);
-	pj_strdup(pool, &rlmi_part->body->content_type.subtype, &rlmi_subtype);
-	pj_list_init(&rlmi_part->body->content_type.param);
+	pjsip_media_type_cp(pool, &rlmi_part->body->content_type, &rlmi_media_type);
 
 	rlmi_part->body->data = pj_xml_clone(pool, rlmi);
 	rlmi_part->body->clone_data = rlmi_clone_data;
@@ -3462,12 +3465,145 @@ end:
 	return res;
 }
 
+struct simple_message_summary {
+	int messages_waiting;
+	int voice_messages_new;
+	int voice_messages_old;
+	int voice_messages_urgent_new;
+	int voice_messages_urgent_old;
+	char message_account[PJSIP_MAX_URL_SIZE];
+};
+
+static int parse_simple_message_summary(char *body,
+	struct simple_message_summary *summary)
+{
+	char *line;
+	char *buffer;
+	int found_counts = 0;
+
+	if (ast_strlen_zero(body) || !summary) {
+		return -1;
+	}
+
+	buffer = ast_strdupa(body);
+	memset(summary, 0, sizeof(*summary));
+
+	while ((line = ast_read_line_from_buffer(&buffer))) {
+		line = ast_str_to_lower(line);
+
+		if (sscanf(line, "voice-message: %d/%d (%d/%d)",
+			&summary->voice_messages_new, &summary->voice_messages_old,
+			&summary->voice_messages_urgent_new, &summary->voice_messages_urgent_old)) {
+			found_counts = 1;
+		} else {
+			sscanf(line, "message-account: %s", summary->message_account);
+		}
+	}
+
+	return !found_counts;
+}
+
+static pj_bool_t pubsub_on_rx_mwi_notify_request(pjsip_rx_data *rdata)
+{
+	RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
+	struct simple_message_summary summary;
+	const char *endpoint_name;
+	char *atsign;
+	char *context;
+	char *body;
+	char *mailbox;
+	int rc;
+
+	endpoint = ast_pjsip_rdata_get_endpoint(rdata);
+	if (!endpoint) {
+		ast_debug(1, "Incoming MWI: Endpoint not found in rdata (%p)\n", rdata);
+		rc = 404;
+		goto error;
+	}
+
+	endpoint_name = ast_sorcery_object_get_id(endpoint);
+	ast_debug(1, "Incoming MWI: Found endpoint: %s\n", endpoint_name);
+	if (ast_strlen_zero(endpoint->incoming_mwi_mailbox)) {
+		ast_debug(1, "Incoming MWI: No incoming mailbox specified for endpoint '%s'\n", endpoint_name);
+		ast_test_suite_event_notify("PUBSUB_NO_INCOMING_MWI_MAILBOX",
+			"Endpoint: %s", endpoint_name);
+		rc = 404;
+		goto error;
+	}
+
+	mailbox = ast_strdupa(endpoint->incoming_mwi_mailbox);
+	atsign = strchr(mailbox, '@');
+	if (!atsign) {
+		ast_debug(1, "Incoming MWI: No '@' found in endpoint %s's incoming mailbox '%s'.  Can't parse context\n",
+			endpoint_name, endpoint->incoming_mwi_mailbox);
+		rc = 404;
+		goto error;
+	}
+
+	*atsign = '\0';
+	context = atsign + 1;
+
+	body = ast_alloca(rdata->msg_info.msg->body->len + 1);
+	rdata->msg_info.msg->body->print_body(rdata->msg_info.msg->body, body,
+		rdata->msg_info.msg->body->len + 1);
+
+	if (parse_simple_message_summary(body, &summary) != 0) {
+		ast_debug(1, "Incoming MWI: Endpoint: '%s' There was an issue getting message info from body '%s'\n",
+			ast_sorcery_object_get_id(endpoint), body);
+		rc = 404;
+		goto error;
+	}
+
+	if (ast_publish_mwi_state(mailbox, context,
+		summary.voice_messages_new, summary.voice_messages_old)) {
+		ast_log(LOG_ERROR, "Incoming MWI: Endpoint: '%s' Could not publish MWI to stasis.  "
+			"Mailbox: %s Message-Account: %s Voice-Messages: %d/%d (%d/%d)\n",
+			endpoint_name, endpoint->incoming_mwi_mailbox, summary.message_account,
+			summary.voice_messages_new, summary.voice_messages_old,
+			summary.voice_messages_urgent_new, summary.voice_messages_urgent_old);
+		rc = 404;
+	} else {
+		ast_debug(1, "Incoming MWI: Endpoint: '%s' Mailbox: %s Message-Account: %s Voice-Messages: %d/%d (%d/%d)\n",
+			endpoint_name, endpoint->incoming_mwi_mailbox, summary.message_account,
+			summary.voice_messages_new, summary.voice_messages_old,
+			summary.voice_messages_urgent_new, summary.voice_messages_urgent_old);
+		ast_test_suite_event_notify("PUBSUB_INCOMING_MWI_PUBLISH",
+			"Endpoint: %s\r\n"
+			"Mailbox: %s\r\n"
+			"MessageAccount: %s\r\n"
+			"VoiceMessagesNew: %d\r\n"
+			"VoiceMessagesOld: %d\r\n"
+			"VoiceMessagesUrgentNew: %d\r\n"
+			"VoiceMessagesUrgentOld: %d",
+				endpoint_name, endpoint->incoming_mwi_mailbox, summary.message_account,
+				summary.voice_messages_new, summary.voice_messages_old,
+				summary.voice_messages_urgent_new, summary.voice_messages_urgent_old);
+		rc = 200;
+	}
+
+error:
+	pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, rc, NULL, NULL, NULL);
+	return PJ_TRUE;
+}
+
+static pj_bool_t pubsub_on_rx_notify_request(pjsip_rx_data *rdata)
+{
+	if (rdata->msg_info.msg->body &&
+		ast_sip_is_content_type(&rdata->msg_info.msg->body->content_type,
+								"application", "simple-message-summary")) {
+		return pubsub_on_rx_mwi_notify_request(rdata);
+	}
+	return PJ_FALSE;
+}
+
 static pj_bool_t pubsub_on_rx_request(pjsip_rx_data *rdata)
 {
 	if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method())) {
 		return pubsub_on_rx_subscribe_request(rdata);
 	} else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_publish_method)) {
 		return pubsub_on_rx_publish_request(rdata);
+	} else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_notify_method)) {
+		return pubsub_on_rx_notify_request(rdata);
 	}
 
 	return PJ_FALSE;
@@ -5290,6 +5426,8 @@ static int load_module(void)
 		return AST_MODULE_LOAD_DECLINE;
 	}
 
+	pjsip_media_type_init2(&rlmi_media_type, "application", "rlmi+xml");
+
 	if (ast_sched_start_thread(sched)) {
 		ast_log(LOG_ERROR, "Could not start scheduler thread for publication expiration\n");
 		ast_sched_context_destroy(sched);
diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c
index d54bffa..cdf045f 100644
--- a/res/res_pjsip_registrar.c
+++ b/res/res_pjsip_registrar.c
@@ -123,13 +123,14 @@ static int registrar_find_contact(void *obj, void *arg, int flags)
 	const struct registrar_contact_details *details = arg;
 	pjsip_uri *contact_uri = pjsip_parse_uri(details->pool, (char*)contact->uri, strlen(contact->uri), 0);
 
-	return (pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, details->uri, contact_uri) == PJ_SUCCESS) ? CMP_MATCH | CMP_STOP : 0;
+	return (pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, details->uri, contact_uri) == PJ_SUCCESS) ? CMP_MATCH : 0;
 }
 
 /*! \brief Internal function which validates provided Contact headers to confirm that they are acceptable, and returns number of contacts */
 static int registrar_validate_contacts(const pjsip_rx_data *rdata, struct ao2_container *contacts, struct ast_sip_aor *aor, int *added, int *updated, int *deleted)
 {
-	pjsip_contact_hdr *previous = NULL, *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr;
+	pjsip_contact_hdr *previous = NULL;
+	pjsip_contact_hdr *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr;
 	struct registrar_contact_details details = {
 		.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256),
 	};
@@ -140,15 +141,18 @@ static int registrar_validate_contacts(const pjsip_rx_data *rdata, struct ao2_co
 
 	while ((contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next))) {
 		int expiration = registrar_get_expiration(aor, contact, rdata);
-		RAII_VAR(struct ast_sip_contact *, existing, NULL, ao2_cleanup);
+		struct ast_sip_contact *existing;
 		char contact_uri[pjsip_max_url_size];
 
 		if (contact->star) {
 			/* The expiration MUST be 0 when a '*' contact is used and there must be no other contact */
-			if ((expiration != 0) || previous) {
+			if (expiration != 0 || previous) {
 				pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
 				return -1;
 			}
+			/* Count all contacts to delete */
+			*deleted = ao2_container_count(contacts);
+			previous = contact;
 			continue;
 		} else if (previous && previous->star) {
 			/* If there is a previous contact and it is a '*' this is a deal breaker */
@@ -177,14 +181,16 @@ static int registrar_validate_contacts(const pjsip_rx_data *rdata, struct ao2_co
 		}
 
 		/* Determine if this is an add, update, or delete for policy enforcement purposes */
-		if (!(existing = ao2_callback(contacts, 0, registrar_find_contact, &details))) {
+		existing = ao2_callback(contacts, 0, registrar_find_contact, &details);
+		ao2_cleanup(existing);
+		if (!existing) {
 			if (expiration) {
-				(*added)++;
+				++*added;
 			}
 		} else if (expiration) {
-			(*updated)++;
+			++*updated;
 		} else {
-			(*deleted)++;
+			++*deleted;
 		}
 	}
 
@@ -219,7 +225,7 @@ static int registrar_delete_contact(void *obj, void *arg, int flags)
 				contact->user_agent);
 	}
 
-	return 0;
+	return CMP_MATCH;
 }
 
 /*! \brief Internal function which adds a contact to a response */
@@ -310,15 +316,157 @@ static int registrar_validate_path(pjsip_rx_data *rdata, struct ast_sip_aor *aor
 	return -1;
 }
 
-static int register_aor_core(pjsip_rx_data *rdata,
+/*! Transport monitor for incoming REGISTER contacts */
+struct contact_transport_monitor {
+	/*!
+	 * \brief Sorcery contact name to remove on transport shutdown
+	 * \note Stored after aor_name in space reserved when struct allocated.
+	 */
+	char *contact_name;
+	/*! AOR name the contact is associated */
+	char aor_name[0];
+};
+
+static void register_contact_transport_shutdown_cb(void *data)
+{
+	struct contact_transport_monitor *monitor = data;
+	struct ast_sip_contact *contact;
+	struct ast_named_lock *lock;
+
+	lock = ast_named_lock_get(AST_NAMED_LOCK_TYPE_MUTEX, "aor", monitor->aor_name);
+	if (!lock) {
+		return;
+	}
+
+	ao2_lock(lock);
+	contact = ast_sip_location_retrieve_contact(monitor->contact_name);
+	if (contact) {
+		ast_sip_location_delete_contact(contact);
+		ast_verb(3, "Removed contact '%s' from AOR '%s' due to transport shutdown\n",
+			contact->uri, monitor->aor_name);
+		ast_test_suite_event_notify("AOR_CONTACT_REMOVED",
+			"Contact: %s\r\n"
+			"AOR: %s\r\n"
+			"UserAgent: %s",
+			contact->uri,
+			monitor->aor_name,
+			contact->user_agent);
+		ao2_ref(contact, -1);
+	}
+	ao2_unlock(lock);
+	ast_named_lock_put(lock);
+}
+
+AST_VECTOR(excess_contact_vector, struct ast_sip_contact *);
+
+static int vec_contact_cmp(struct ast_sip_contact *left, struct ast_sip_contact *right)
+{
+	struct ast_sip_contact *left_contact = left;
+	struct ast_sip_contact *right_contact = right;
+
+	/* Sort from soonest to expire to last to expire */
+	return ast_tvcmp(left_contact->expiration_time, right_contact->expiration_time);
+}
+
+static int vec_contact_add(void *obj, void *arg, int flags)
+{
+	struct ast_sip_contact *contact = obj;
+	struct excess_contact_vector *contact_vec = arg;
+
+	/*
+	 * Performance wise, an insertion sort is fine because we
+	 * shouldn't need to remove more than a handful of contacts.
+	 * I expect we'll typically be removing only one contact.
+	 */
+	AST_VECTOR_ADD_SORTED(contact_vec, contact, vec_contact_cmp);
+	if (AST_VECTOR_SIZE(contact_vec) == AST_VECTOR_MAX_SIZE(contact_vec)) {
+		/*
+		 * We added a contact over the number we need to remove.
+		 * Remove the longest to expire contact from the vector
+		 * which is the last element in the vector.  It may be
+		 * the one we just added or the one we just added pushed
+		 * out an earlier contact from removal consideration.
+		 */
+		--AST_VECTOR_SIZE(contact_vec);
+	}
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Remove excess existing contacts that expire the soonest.
+ * \since 13.18.0
+ *
+ * \param contacts Container of unmodified contacts that could remove.
+ * \param to_remove Maximum number of contacts to remove.
+ *
+ * \return Nothing
+ */
+static void remove_excess_contacts(struct ao2_container *contacts, unsigned int to_remove)
+{
+	struct excess_contact_vector contact_vec;
+
+	/*
+	 * Create a sorted vector to hold the to_remove soonest to
+	 * expire contacts.  The vector has an extra space to
+	 * temporarily hold the longest to expire contact that we
+	 * won't remove.
+	 */
+	if (AST_VECTOR_INIT(&contact_vec, to_remove + 1)) {
+		return;
+	}
+	ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, vec_contact_add, &contact_vec);
+
+	/*
+	 * The vector should always be populated with the number
+	 * of contacts we need to remove.  Just in case, we will
+	 * remove all contacts in the vector even if the contacts
+	 * container had fewer contacts than there should be.
+	 */
+	ast_assert(AST_VECTOR_SIZE(&contact_vec) == to_remove);
+	to_remove = AST_VECTOR_SIZE(&contact_vec);
+
+	/* Remove the excess contacts that expire the soonest */
+	while (to_remove--) {
+		struct ast_sip_contact *contact;
+
+		contact = AST_VECTOR_GET(&contact_vec, to_remove);
+
+		ast_sip_location_delete_contact(contact);
+		ast_verb(3, "Removed contact '%s' from AOR '%s' due to remove_existing\n",
+			contact->uri, contact->aor);
+		ast_test_suite_event_notify("AOR_CONTACT_REMOVED",
+			"Contact: %s\r\n"
+			"AOR: %s\r\n"
+			"UserAgent: %s",
+			contact->uri,
+			contact->aor,
+			contact->user_agent);
+	}
+
+	AST_VECTOR_FREE(&contact_vec);
+}
+
+struct aor_core_response {
+	/*! Tx data to use for statefull response.  NULL for stateless response. */
+	pjsip_tx_data *tdata;
+	/*! SIP response code to send in stateless response */
+	int code;
+};
+
+static void register_aor_core(pjsip_rx_data *rdata,
 	struct ast_sip_endpoint *endpoint,
 	struct ast_sip_aor *aor,
 	const char *aor_name,
-	struct ao2_container *contacts)
+	struct ao2_container *contacts,
+	struct aor_core_response *response)
 {
 	static const pj_str_t USER_AGENT = { "User-Agent", 10 };
 
-	int added = 0, updated = 0, deleted = 0;
+	int added = 0;
+	int updated = 0;
+	int deleted = 0;
+	int contact_count;
 	pjsip_contact_hdr *contact_hdr = NULL;
 	struct registrar_contact_details details = { 0, };
 	pjsip_tx_data *tdata;
@@ -340,33 +488,42 @@ static int register_aor_core(pjsip_rx_data *rdata,
 
 	if (registrar_validate_contacts(rdata, contacts, aor, &added, &updated, &deleted)) {
 		/* The provided Contact headers do not conform to the specification */
-		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400, NULL, NULL, NULL);
 		ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_contacts_provided");
 		ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n",
 				ast_sorcery_object_get_id(endpoint));
-		return PJ_TRUE;
+		response->code = 400;
+		return;
 	}
 
 	if (registrar_validate_path(rdata, aor, &path_str)) {
 		/* Ensure that intervening proxies did not make invalid modifications to the request */
-		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 420, NULL, NULL, NULL);
 		ast_log(LOG_WARNING, "Invalid modifications made to REGISTER request from '%s' by intervening proxy\n",
 				ast_sorcery_object_get_id(endpoint));
-		return PJ_TRUE;
+		response->code = 420;
+		return;
 	}
 
-	if ((MAX(added - deleted, 0) + (!aor->remove_existing ? ao2_container_count(contacts) : 0)) > aor->max_contacts) {
+	if (aor->remove_existing) {
+		/* Cumulative number of contacts affected by this registration */
+		contact_count = MAX(updated + added - deleted,  0);
+	} else {
+		/* Total contacts after this registration */
+		contact_count = ao2_container_count(contacts) + added - deleted;
+	}
+	if (contact_count > aor->max_contacts) {
 		/* Enforce the maximum number of contacts */
-		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
 		ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_exceeds_maximum_configured_contacts");
 		ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %u\n",
 				ast_sorcery_object_get_id(endpoint), aor_name, aor->max_contacts);
-		return PJ_TRUE;
+		response->code = 403;
+		return;
 	}
 
-	if (!(details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256))) {
-		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
-		return PJ_TRUE;
+	details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
+		"Contact Comparison", 256, 256);
+	if (!details.pool) {
+		response->code = 500;
+		return;
 	}
 
 	user_agent_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &USER_AGENT, NULL);
@@ -405,7 +562,8 @@ static int register_aor_core(pjsip_rx_data *rdata,
 
 		if (contact_hdr->star) {
 			/* A star means to unregister everything, so do so for the possible contacts */
-			ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, (void *)aor_name);
+			ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE,
+				registrar_delete_contact, (void *)aor_name);
 			break;
 		}
 
@@ -418,7 +576,11 @@ static int register_aor_core(pjsip_rx_data *rdata,
 		details.uri = pjsip_uri_get_uri(contact_hdr->uri);
 		pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri));
 
-		if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details))) {
+		contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details);
+		if (!contact) {
+			int prune_on_boot = 0;
+			pj_str_t host_name;
+
 			/* If they are actually trying to delete a contact that does not exist... be forgiving */
 			if (!expiration) {
 				ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n",
@@ -426,14 +588,68 @@ static int register_aor_core(pjsip_rx_data *rdata,
 				continue;
 			}
 
-			if (ast_sip_location_add_contact_nolock(aor, contact_uri, ast_tvadd(ast_tvnow(),
-				ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL,
-					user_agent, via_addr, via_port, call_id, endpoint)) {
+			/* Determine if the contact cannot survive a restart/boot. */
+			if (details.uri->port == rdata->pkt_info.src_port
+				&& !pj_strcmp(&details.uri->host,
+					pj_cstr(&host_name, rdata->pkt_info.src_name))
+				/* We have already checked if the URI scheme is sip: or sips: */
+				&& PJSIP_TRANSPORT_IS_RELIABLE(rdata->tp_info.transport)) {
+				pj_str_t type_name;
+
+				/* Determine the transport parameter value */
+				if (!strcasecmp("WSS", rdata->tp_info.transport->type_name)) {
+					/* WSS is special, as it needs to be ws. */
+					pj_cstr(&type_name, "ws");
+				} else {
+					pj_cstr(&type_name, rdata->tp_info.transport->type_name);
+				}
+
+				if (!pj_stricmp(&details.uri->transport_param, &type_name)
+					&& (endpoint->nat.rewrite_contact
+						/* Websockets are always rewritten */
+						|| !pj_stricmp(&details.uri->transport_param,
+							pj_cstr(&type_name, "ws")))) {
+					/*
+					 * The contact was rewritten to the reliable transport's
+					 * source address.  Disconnecting the transport for any
+					 * reason invalidates the contact.
+					 */
+					prune_on_boot = 1;
+				}
+			}
+
+			contact = ast_sip_location_create_contact(aor, contact_uri,
+				ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)),
+				path_str ? ast_str_buffer(path_str) : NULL,
+				user_agent, via_addr, via_port, call_id, prune_on_boot, endpoint);
+			if (!contact) {
 				ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n",
-						contact_uri, aor_name);
+					contact_uri, aor_name);
 				continue;
 			}
 
+			if (prune_on_boot) {
+				const char *contact_name;
+				struct contact_transport_monitor *monitor;
+
+				/*
+				 * Monitor the transport in case it gets disconnected because
+				 * the contact won't be valid anymore if that happens.
+				 */
+				contact_name = ast_sorcery_object_get_id(contact);
+				monitor = ao2_alloc_options(sizeof(*monitor) + 2 + strlen(aor_name)
+					+ strlen(contact_name), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
+				if (monitor) {
+					strcpy(monitor->aor_name, aor_name);/* Safe */
+					monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1;
+					strcpy(monitor->contact_name, contact_name);/* Safe */
+
+					ast_sip_transport_monitor_register(rdata->tp_info.transport,
+						register_contact_transport_shutdown_cb, monitor);
+					ao2_ref(monitor, -1);
+				}
+			}
+
 			ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n",
 				contact_uri, aor_name, expiration);
 			ast_test_suite_event_notify("AOR_CONTACT_ADDED",
@@ -502,22 +718,37 @@ static int register_aor_core(pjsip_rx_data *rdata,
 
 	pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
 
-	/* If the AOR is configured to remove any existing contacts that have not been updated/added as a result of this REGISTER
-	 * do so
+	/*
+	 * If the AOR is configured to remove any contacts over max_contacts
+	 * that have not been updated/added/deleted as a result of this
+	 * REGISTER do so.
+	 *
+	 * The contacts container currently holds the existing contacts that
+	 * were not affected by this REGISTER.
 	 */
 	if (aor->remove_existing) {
-		ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL);
+		/* Total contacts after this registration */
+		contact_count = ao2_container_count(contacts) + updated + added;
+		if (contact_count > aor->max_contacts) {
+			/* Remove excess existing contacts that expire the soonest */
+			remove_excess_contacts(contacts, contact_count - aor->max_contacts);
+		}
 	}
 
 	/* Re-retrieve contacts.  Caller will clean up the original container. */
 	contacts = ast_sip_location_retrieve_aor_contacts_nolock(aor);
+	if (!contacts) {
+		response->code = 500;
+		return;
+	}
 	response_contact = ao2_callback(contacts, 0, NULL, NULL);
 
 	/* Send a response containing all of the contacts (including static) that are present on this AOR */
 	if (ast_sip_create_response(rdata, 200, response_contact, &tdata) != PJ_SUCCESS) {
 		ao2_cleanup(response_contact);
 		ao2_cleanup(contacts);
-		return PJ_TRUE;
+		response->code = 500;
+		return;
 	}
 	ao2_cleanup(response_contact);
 
@@ -532,9 +763,7 @@ static int register_aor_core(pjsip_rx_data *rdata,
 		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr);
 	}
 
-	ast_sip_send_stateful_response(rdata, tdata, endpoint);
-
-	return PJ_TRUE;
+	response->tdata = tdata;
 }
 
 static int register_aor(pjsip_rx_data *rdata,
@@ -542,12 +771,16 @@ static int register_aor(pjsip_rx_data *rdata,
 	struct ast_sip_aor *aor,
 	const char *aor_name)
 {
-	int res;
+	struct aor_core_response response = {
+		.code = 500,
+	};
 	struct ao2_container *contacts = NULL;
 	struct ast_named_lock *lock;
 
 	lock = ast_named_lock_get(AST_NAMED_LOCK_TYPE_MUTEX, "aor", aor_name);
 	if (!lock) {
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(),
+			rdata, response.code, NULL, NULL, NULL);
 		return PJ_TRUE;
 	}
 
@@ -556,15 +789,24 @@ static int register_aor(pjsip_rx_data *rdata,
 	if (!contacts) {
 		ao2_unlock(lock);
 		ast_named_lock_put(lock);
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(),
+			rdata, response.code, NULL, NULL, NULL);
 		return PJ_TRUE;
 	}
 
-	res = register_aor_core(rdata, endpoint, aor, aor_name, contacts);
+	register_aor_core(rdata, endpoint, aor, aor_name, contacts, &response);
 	ao2_cleanup(contacts);
 	ao2_unlock(lock);
 	ast_named_lock_put(lock);
 
-	return res;
+	/* Now send the REGISTER response to the peer */
+	if (response.tdata) {
+		ast_sip_send_stateful_response(rdata, response.tdata, endpoint);
+	} else {
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(),
+			rdata, response.code, NULL, NULL, NULL);
+	}
+	return PJ_TRUE;
 }
 
 static int match_aor(const char *aor_name, const char *id)
@@ -893,6 +1135,7 @@ static int unload_module(void)
 	ast_manager_unregister(AMI_SHOW_REGISTRATIONS);
 	ast_manager_unregister(AMI_SHOW_REGISTRATION_CONTACT_STATUSES);
 	ast_sip_unregister_service(&registrar_module);
+	ast_sip_transport_monitor_unregister_all(register_contact_transport_shutdown_cb);
 	return 0;
 }
 
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index a6bd2d7..16ffaca 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -241,15 +241,16 @@ static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_me
 	}
 
 	ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_NAT, session->endpoint->media.rtp.symmetric);
+	ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_ASYMMETRIC_CODEC, session->endpoint->asymmetric_rtp_codec);
 
 	if (!session->endpoint->media.rtp.ice_support && (ice = ast_rtp_instance_get_ice(session_media->rtp))) {
 		ice->stop(session_media->rtp);
 	}
 
-	if (session->endpoint->dtmf == AST_SIP_DTMF_RFC_4733 || session->endpoint->dtmf == AST_SIP_DTMF_AUTO || session->endpoint->dtmf == AST_SIP_DTMF_AUTO_INFO) {
+	if (session->dtmf == AST_SIP_DTMF_RFC_4733 || session->dtmf == AST_SIP_DTMF_AUTO || session->dtmf == AST_SIP_DTMF_AUTO_INFO) {
 		ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_RFC2833);
 		ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_DTMF, 1);
-	} else if (session->endpoint->dtmf == AST_SIP_DTMF_INBAND) {
+	} else if (session->dtmf == AST_SIP_DTMF_INBAND) {
 		ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_INBAND);
 	}
 
@@ -332,11 +333,11 @@ static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp
 			}
 		}
 	}
-	if (!tel_event && (session->endpoint->dtmf == AST_SIP_DTMF_AUTO)) {
+	if (!tel_event && (session->dtmf == AST_SIP_DTMF_AUTO)) {
 		ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_INBAND);
 	}
 
-	if (session->endpoint->dtmf == AST_SIP_DTMF_AUTO_INFO) {
+	if (session->dtmf == AST_SIP_DTMF_AUTO_INFO) {
 		if  (tel_event) {
 			ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_RFC2833);
 		} else {
@@ -440,7 +441,7 @@ static int set_caps(struct ast_sip_session *session, struct ast_sip_session_medi
 			ast_set_write_format(session->channel, ast_channel_writeformat(session->channel));
 		}
 
-		if ( ((session->endpoint->dtmf == AST_SIP_DTMF_AUTO) || (session->endpoint->dtmf == AST_SIP_DTMF_AUTO_INFO) )
+		if ( ((session->dtmf == AST_SIP_DTMF_AUTO) || (session->dtmf == AST_SIP_DTMF_AUTO_INFO) )
 		    && (ast_rtp_instance_dtmf_mode_get(session_media->rtp) == AST_RTP_DTMF_MODE_RFC2833)
 		    && (session->dsp)) {
 			dsp_features = ast_dsp_get_features(session->dsp);
@@ -1160,7 +1161,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
 	pj_str_t stmp;
 	pjmedia_sdp_attr *attr;
 	int index = 0;
-	int noncodec = (session->endpoint->dtmf == AST_SIP_DTMF_RFC_4733 || session->endpoint->dtmf == AST_SIP_DTMF_AUTO || session->endpoint->dtmf == AST_SIP_DTMF_AUTO_INFO) ? AST_RTP_DTMF : 0;
+	int noncodec = (session->dtmf == AST_SIP_DTMF_RFC_4733 || session->dtmf == AST_SIP_DTMF_AUTO || session->dtmf == AST_SIP_DTMF_AUTO_INFO) ? AST_RTP_DTMF : 0;
 	int min_packet_size = 0, max_packet_size = 0;
 	int rtp_code;
 	RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup);
@@ -1505,7 +1506,7 @@ static void change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struc
 {
 	RAII_VAR(struct ast_sip_transport_state *, transport_state, ast_sip_get_transport_state(ast_sorcery_object_get_id(transport)), ao2_cleanup);
 	char host[NI_MAXHOST];
-	struct ast_sockaddr addr = { { 0, } };
+	struct ast_sockaddr our_sdp_addr = { { 0, } };
 
 	/* If the stream has been rejected there will be no connection line */
 	if (!stream->conn || !transport_state) {
@@ -1513,15 +1514,17 @@ static void change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struc
 	}
 
 	ast_copy_pj_str(host, &stream->conn->addr, sizeof(host));
-	ast_sockaddr_parse(&addr, host, PARSE_PORT_FORBID);
+	ast_sockaddr_parse(&our_sdp_addr, host, PARSE_PORT_FORBID);
 
-	/* Is the address within the SDP inside the same network? */
-	if (transport_state->localnet
-		&& ast_apply_ha(transport_state->localnet, &addr) == AST_SENSE_ALLOW) {
+	/* Reversed check here. We don't check the remote endpoint being
+	 * in our local net, but whether our outgoing session IP is
+	 * local. If it is not, we won't do rewriting. No localnet
+	 * configured? Always rewrite. */
+	if (ast_sip_transport_is_nonlocal(transport_state, &our_sdp_addr) && transport_state->localnet) {
 		return;
 	}
-	ast_debug(5, "Setting media address to %s\n", transport->external_media_address);
-	pj_strdup2(tdata->pool, &stream->conn->addr, transport->external_media_address);
+	ast_debug(5, "Setting media address to %s\n", ast_sockaddr_stringify_host(&transport_state->external_media_address));
+	pj_strdup2(tdata->pool, &stream->conn->addr, ast_sockaddr_stringify_host(&transport_state->external_media_address));
 }
 
 /*! \brief Function which stops the RTP instance */
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index d6c5fbc..2b24bb7 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -396,59 +396,6 @@ static int handle_negotiated_sdp(struct ast_sip_session *session, const pjmedia_
 	return -1;
 }
 
-AST_RWLIST_HEAD_STATIC(session_supplements, ast_sip_session_supplement);
-
-int ast_sip_session_register_supplement(struct ast_sip_session_supplement *supplement)
-{
-	struct ast_sip_session_supplement *iter;
-	int inserted = 0;
-	SCOPED_LOCK(lock, &session_supplements, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
-
-	if (!supplement->response_priority) {
-		supplement->response_priority = AST_SIP_SESSION_BEFORE_MEDIA;
-	}
-
-	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&session_supplements, iter, next) {
-		if (iter->priority > supplement->priority) {
-			AST_RWLIST_INSERT_BEFORE_CURRENT(supplement, next);
-			inserted = 1;
-			break;
-		}
-	}
-	AST_RWLIST_TRAVERSE_SAFE_END;
-
-	if (!inserted) {
-		AST_RWLIST_INSERT_TAIL(&session_supplements, supplement, next);
-	}
-	ast_module_ref(ast_module_info->self);
-	return 0;
-}
-
-void ast_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement)
-{
-	struct ast_sip_session_supplement *iter;
-	SCOPED_LOCK(lock, &session_supplements, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
-	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&session_supplements, iter, next) {
-		if (supplement == iter) {
-			AST_RWLIST_REMOVE_CURRENT(next);
-			ast_module_unref(ast_module_info->self);
-			break;
-		}
-	}
-	AST_RWLIST_TRAVERSE_SAFE_END;
-}
-
-static struct ast_sip_session_supplement *supplement_dup(const struct ast_sip_session_supplement *src)
-{
-	struct ast_sip_session_supplement *dst = ast_calloc(1, sizeof(*dst));
-	if (!dst) {
-		return NULL;
-	}
-	/* Will need to revisit if shallow copy becomes an issue */
-	*dst = *src;
-	return dst;
-}
-
 #define DATASTORE_BUCKETS 53
 #define MEDIA_BUCKETS 7
 
@@ -855,10 +802,9 @@ static void set_from_header(struct ast_sip_session *session)
 		pj_strdup2(dlg_pool, &dlg_info_uri->host, session->endpoint->fromdomain);
 	}
 
-	ast_sip_add_usereqphone(session->endpoint, dlg_pool, dlg_info->uri);
-
 	/* We need to save off the non-anonymized From for RPID/PAI generation (for domain) */
 	session->saved_from_hdr = pjsip_hdr_clone(dlg_pool, dlg_info);
+	ast_sip_add_usereqphone(session->endpoint, dlg_pool, session->saved_from_hdr->uri);
 
 	/* In chan_sip, fromuser and fromdomain trump restricted so we only
 	 * anonymize if they're not set.
@@ -874,7 +820,9 @@ static void set_from_header(struct ast_sip_session *session)
 		if (ast_strlen_zero(session->endpoint->fromdomain)) {
 			pj_strdup2(dlg_pool, &dlg_info_uri->host, "anonymous.invalid");
 		}
-	}
+	} else {
+		ast_sip_add_usereqphone(session->endpoint, dlg_pool, dlg_info->uri);
+    }
 }
 
 int ast_sip_session_refresh(struct ast_sip_session *session,
@@ -968,6 +916,46 @@ int ast_sip_session_refresh(struct ast_sip_session *session,
 	return 0;
 }
 
+int ast_sip_session_regenerate_answer(struct ast_sip_session *session,
+		ast_sip_session_sdp_creation_cb on_sdp_creation)
+{
+	pjsip_inv_session *inv_session = session->inv_session;
+	pjmedia_sdp_session *new_answer = NULL;
+	const pjmedia_sdp_session *previous_offer = NULL;
+
+	/* The SDP answer can only be regenerated if it is still pending to be sent */
+	if (!inv_session->neg || (pjmedia_sdp_neg_get_state(inv_session->neg) != PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER &&
+		pjmedia_sdp_neg_get_state(inv_session->neg) != PJMEDIA_SDP_NEG_STATE_WAIT_NEGO)) {
+		ast_log(LOG_WARNING, "Requested to regenerate local SDP answer for channel '%s' but negotiation in state '%s'\n",
+			ast_channel_name(session->channel), pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_get_state(inv_session->neg)));
+		return -1;
+	}
+
+	pjmedia_sdp_neg_get_neg_remote(inv_session->neg, &previous_offer);
+	if (pjmedia_sdp_neg_get_state(inv_session->neg) == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) {
+		/* Transition the SDP negotiator back to when it received the remote offer */
+		pjmedia_sdp_neg_negotiate(inv_session->pool, inv_session->neg, 0);
+		pjmedia_sdp_neg_set_remote_offer(inv_session->pool, inv_session->neg, previous_offer);
+	}
+
+	new_answer = create_local_sdp(inv_session, session, previous_offer);
+	if (!new_answer) {
+		ast_log(LOG_WARNING, "Could not create a new local SDP answer for channel '%s'\n",
+			ast_channel_name(session->channel));
+		return -1;
+	}
+
+	if (on_sdp_creation) {
+		if (on_sdp_creation(session, new_answer)) {
+			return -1;
+		}
+	}
+
+	pjsip_inv_set_sdp_answer(inv_session, new_answer);
+
+	return 0;
+}
+
 void ast_sip_session_send_response(struct ast_sip_session *session, pjsip_tx_data *tdata)
 {
 	handle_outgoing_response(session, tdata);
@@ -1340,21 +1328,6 @@ static void session_destructor(void *obj)
 	ast_test_suite_event_notify("SESSION_DESTROYED", "Endpoint: %s", endpoint_name);
 }
 
-static int add_supplements(struct ast_sip_session *session)
-{
-	struct ast_sip_session_supplement *iter;
-	SCOPED_LOCK(lock, &session_supplements, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK);
-
-	AST_RWLIST_TRAVERSE(&session_supplements, iter, next) {
-		struct ast_sip_session_supplement *copy = supplement_dup(iter);
-		if (!copy) {
-			return -1;
-		}
-		AST_LIST_INSERT_TAIL(&session->supplements, copy, next);
-	}
-	return 0;
-}
-
 static int add_session_media(void *obj, void *arg, int flags)
 {
 	struct sdp_handler_list *handler_list = obj;
@@ -1481,7 +1454,9 @@ struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint,
 	session->contact = ao2_bump(contact);
 	session->inv_session = inv_session;
 
-	if (add_supplements(session)) {
+	session->dtmf = endpoint->dtmf;
+
+	if (ast_sip_session_add_supplements(session)) {
 		/* Release the ref held by session->inv_session */
 		ao2_ref(session, -1);
 		return NULL;
@@ -2552,6 +2527,11 @@ static int session_end(void *vsession)
 			iter->session_end(session);
 		}
 	}
+
+	/* Release any media resources. */
+	ao2_cleanup(session->media);
+	session->media = NULL;
+
 	return 0;
 }
 
@@ -2674,6 +2654,36 @@ static void session_inv_on_new_session(pjsip_inv_session *inv, pjsip_event *e)
 	/* XXX STUB */
 }
 
+static int session_end_if_disconnected(int id, pjsip_inv_session *inv)
+{
+	struct ast_sip_session *session;
+
+	if (inv->state != PJSIP_INV_STATE_DISCONNECTED) {
+		return 0;
+	}
+
+	/*
+	 * We are locking because ast_sip_dialog_get_session() needs
+	 * the dialog locked to get the session by other threads.
+	 */
+	pjsip_dlg_inc_lock(inv->dlg);
+	session = inv->mod_data[id];
+	inv->mod_data[id] = NULL;
+	pjsip_dlg_dec_lock(inv->dlg);
+
+	/*
+	 * Pass the session ref held by session->inv_session to
+	 * session_end_completion().
+	 */
+	if (session
+		&& ast_sip_push_task(session->serializer, session_end_completion, session)) {
+		/* Do it anyway even though this is not the right thread. */
+		session_end_completion(session);
+	}
+
+	return 1;
+}
+
 static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e)
 {
 	ast_sip_session_response_cb cb;
@@ -2698,6 +2708,17 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
 		/* The session has ended.  Ignore the transaction change. */
 		return;
 	}
+
+	/*
+	 * If the session is disconnected really nothing else to do unless currently transacting
+	 * a BYE. If a BYE then hold off destruction until the transaction timeout occurs. This
+	 * has to be done for BYEs because sometimes the dialog can be in a disconnected
+	 * state but the BYE request transaction has not yet completed.
+	 */
+	if (tsx->method.id != PJSIP_BYE_METHOD && session_end_if_disconnected(id, inv)) {
+		return;
+	}
+
 	switch (e->body.tsx_state.type) {
 	case PJSIP_EVENT_TX_MSG:
 		/* When we create an outgoing request, we do not have access to the transaction that
@@ -2820,49 +2841,12 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
 		}
 		break;
 	case PJSIP_EVENT_TRANSPORT_ERROR:
-		if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
-			/*
-			 * Clear the module data now to block session_inv_on_state_changed()
-			 * from calling session_end() if it hasn't already done so.
-			 */
-			inv->mod_data[id] = NULL;
-
-			/*
-			 * Pass the session ref held by session->inv_session to
-			 * session_end_completion().
-			 */
-			if (session
-				&& ast_sip_push_task(session->serializer, session_end_completion, session)) {
-				/* Do it anyway even though this is not the right thread. */
-				session_end_completion(session);
-			}
-			return;
-		}
-		break;
 	case PJSIP_EVENT_TIMER:
 		/*
 		 * The timer event is run by the pjsip monitor thread and not
 		 * by the session serializer.
 		 */
-		if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
-			/*
-			 * We are locking because ast_sip_dialog_get_session() needs
-			 * the dialog locked to get the session by other threads.
-			 */
-			pjsip_dlg_inc_lock(inv->dlg);
-			session = inv->mod_data[id];
-			inv->mod_data[id] = NULL;
-			pjsip_dlg_dec_lock(inv->dlg);
-
-			/*
-			 * Pass the session ref held by session->inv_session to
-			 * session_end_completion().
-			 */
-			if (session
-				&& ast_sip_push_task(session->serializer, session_end_completion, session)) {
-				/* Do it anyway even though this is not the right thread. */
-				session_end_completion(session);
-			}
+		if (session_end_if_disconnected(id, inv)) {
 			return;
 		}
 		break;
@@ -3134,8 +3118,9 @@ static void session_outgoing_nat_hook(pjsip_tx_data *tdata, struct ast_sip_trans
 	int stream;
 
 	/* SDP produced by us directly will never be multipart */
-	if (!transport_state || hook || !tdata->msg->body || pj_stricmp2(&tdata->msg->body->content_type.type, "application") ||
-		pj_stricmp2(&tdata->msg->body->content_type.subtype, "sdp") || ast_strlen_zero(transport->external_media_address)) {
+	if (!transport_state || hook || !tdata->msg->body ||
+		!ast_sip_is_content_type(&tdata->msg->body->content_type, "application", "sdp") ||
+		ast_strlen_zero(transport->external_media_address)) {
 		return;
 	}
 
@@ -3143,15 +3128,18 @@ static void session_outgoing_nat_hook(pjsip_tx_data *tdata, struct ast_sip_trans
 
 	if (sdp->conn) {
 		char host[NI_MAXHOST];
-		struct ast_sockaddr addr = { { 0, } };
+		struct ast_sockaddr our_sdp_addr = { { 0, } };
 
 		ast_copy_pj_str(host, &sdp->conn->addr, sizeof(host));
-		ast_sockaddr_parse(&addr, host, PARSE_PORT_FORBID);
-
-		if (!transport_state->localnet
-			|| ast_apply_ha(transport_state->localnet, &addr) != AST_SENSE_ALLOW) {
-			ast_debug(5, "Setting external media address to %s\n", transport->external_media_address);
-			pj_strdup2(tdata->pool, &sdp->conn->addr, transport->external_media_address);
+		ast_sockaddr_parse(&our_sdp_addr, host, PARSE_PORT_FORBID);
+
+		/* Reversed check here. We don't check the remote
+		 * endpoint being in our local net, but whether our
+		 * outgoing session IP is local. If it is, we'll do
+		 * rewriting. No localnet configured? Always rewrite. */
+		if (ast_sip_transport_is_local(transport_state, &our_sdp_addr) || !transport_state->localnet) {
+			ast_debug(5, "Setting external media address to %s\n", ast_sockaddr_stringify_host(&transport_state->external_media_address));
+			pj_strdup2(tdata->pool, &sdp->conn->addr, ast_sockaddr_stringify_host(&transport_state->external_media_address));
 		}
 	}
 
diff --git a/res/res_pjsip_session.exports.in b/res/res_pjsip_session.exports.in
index fdfc5fb..5bc0bf4 100644
--- a/res/res_pjsip_session.exports.in
+++ b/res/res_pjsip_session.exports.in
@@ -14,6 +14,7 @@
 		LINKER_SYMBOL_PREFIXast_sip_session_remove_datastore;
 		LINKER_SYMBOL_PREFIXast_sip_session_get_identity;
 		LINKER_SYMBOL_PREFIXast_sip_session_refresh;
+		LINKER_SYMBOL_PREFIXast_sip_session_regenerate_answer;
 		LINKER_SYMBOL_PREFIXast_sip_session_send_response;
 		LINKER_SYMBOL_PREFIXast_sip_session_send_request;
 		LINKER_SYMBOL_PREFIXast_sip_session_create_invite;
diff --git a/res/res_pjsip_t38.c b/res/res_pjsip_t38.c
index 4e76ac3..5f04f0e 100644
--- a/res/res_pjsip_t38.c
+++ b/res/res_pjsip_t38.c
@@ -294,21 +294,22 @@ static int t38_reinvite_response_cb(struct ast_sip_session *session, pjsip_rx_da
 {
 	struct pjsip_status_line status = rdata->msg_info.msg->line.status;
 	struct t38_state *state;
-	RAII_VAR(struct ast_sip_session_media *, session_media, NULL, ao2_cleanup);
+	struct ast_sip_session_media *session_media = NULL;
 
 	if (status.code == 100) {
 		return 0;
 	}
 
-	if (!(state = t38_state_get_or_alloc(session)) ||
+	if (!session->channel || !(state = t38_state_get_or_alloc(session)) ||
 		!(session_media = ao2_find(session->media, "image", OBJ_KEY))) {
 		ast_log(LOG_WARNING, "Received response to T.38 re-invite on '%s' but state unavailable\n",
-			ast_channel_name(session->channel));
+			session->channel ? ast_channel_name(session->channel) : "unknown channel");
 		return 0;
 	}
 
 	t38_change_state(session, session_media, state, (status.code == 200) ? T38_ENABLED : T38_REJECTED);
 
+	ao2_cleanup(session_media);
 	return 0;
 }
 
@@ -403,16 +404,21 @@ static int t38_interpret_parameters(void *obj)
 static struct ast_frame *t38_framehook_write(struct ast_channel *chan,
 	struct ast_sip_session *session, struct ast_frame *f)
 {
-	if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_T38_PARAMETERS &&
-		session->endpoint->media.t38.enabled) {
-		struct t38_parameters_task_data *data = t38_parameters_task_data_alloc(session, f);
+	if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_T38_PARAMETERS) {
+		if (session->endpoint->media.t38.enabled) {
+			struct t38_parameters_task_data *data = t38_parameters_task_data_alloc(session, f);
 
-		if (!data) {
-			return f;
-		}
+			if (!data) {
+				return f;
+			}
 
-		if (ast_sip_push_task(session->serializer, t38_interpret_parameters, data)) {
-			ao2_ref(data, -1);
+			if (ast_sip_push_task(session->serializer, t38_interpret_parameters, data)) {
+				ao2_ref(data, -1);
+			}
+		} else {
+			struct ast_control_t38_parameters parameters = { .request_response = AST_T38_REFUSED, };
+			ast_debug(2, "T.38 support not enabled, rejecting T.38 control packet\n");
+			ast_queue_control_data(session->channel, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters));
 		}
 	} else if (f->frametype == AST_FRAME_MODEM) {
 		struct ast_sip_session_media *session_media;
@@ -503,10 +509,7 @@ static void t38_attach_framehook(struct ast_sip_session *session)
 		return;
 	}
 
-	/* Only attach the framehook if t38 is enabled for the endpoint */
-	if (!session->endpoint->media.t38.enabled) {
-		return;
-	}
+	/* Always attach the framehook so we can quickly reject */
 
 	ast_channel_lock(session->channel);
 
@@ -868,7 +871,7 @@ static void change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struc
 {
 	RAII_VAR(struct ast_sip_transport_state *, transport_state, ast_sip_get_transport_state(ast_sorcery_object_get_id(transport)), ao2_cleanup);
 	char host[NI_MAXHOST];
-	struct ast_sockaddr addr = { { 0, } };
+	struct ast_sockaddr our_sdp_addr = { { 0, } };
 
 	/* If the stream has been rejected there will be no connection line */
 	if (!stream->conn || !transport_state) {
@@ -876,15 +879,17 @@ static void change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struc
 	}
 
 	ast_copy_pj_str(host, &stream->conn->addr, sizeof(host));
-	ast_sockaddr_parse(&addr, host, PARSE_PORT_FORBID);
+	ast_sockaddr_parse(&our_sdp_addr, host, PARSE_PORT_FORBID);
 
-	/* Is the address within the SDP inside the same network? */
-	if (transport_state->localnet
-		&& ast_apply_ha(transport_state->localnet, &addr) == AST_SENSE_ALLOW) {
+	/* Reversed check here. We don't check the remote endpoint being
+	 * in our local net, but whether our outgoing session IP is
+	 * local. If it is not, we won't do rewriting. No localnet
+	 * configured? Always rewrite. */
+	if (ast_sip_transport_is_nonlocal(transport_state, &our_sdp_addr) && transport_state->localnet) {
 		return;
 	}
-	ast_debug(5, "Setting media address to %s\n", transport->external_media_address);
-	pj_strdup2(tdata->pool, &stream->conn->addr, transport->external_media_address);
+	ast_debug(5, "Setting media address to %s\n", ast_sockaddr_stringify_host(&transport_state->external_media_address));
+	pj_strdup2(tdata->pool, &stream->conn->addr, ast_sockaddr_stringify_host(&transport_state->external_media_address));
 }
 
 /*! \brief Function which destroys the UDPTL instance when session ends */
diff --git a/res/res_pjsip_transport_management.c b/res/res_pjsip_transport_management.c
index 3e129dc..eb92eb7 100644
--- a/res/res_pjsip_transport_management.c
+++ b/res/res_pjsip_transport_management.c
@@ -33,8 +33,8 @@
 #include "asterisk/module.h"
 #include "asterisk/astobj2.h"
 
-/*! \brief Number of buckets for keepalive transports */
-#define TRANSPORTS_BUCKETS 53
+/*! \brief Number of buckets for monitored transports */
+#define TRANSPORTS_BUCKETS 127
 
 #define IDLE_TIMEOUT (pjsip_cfg()->tsx.td)
 
@@ -53,9 +53,6 @@ static pthread_t keepalive_thread = AST_PTHREADT_NULL;
 /*! \brief The global interval at which to send keepalives */
 static unsigned int keepalive_interval;
 
-/*! \brief Existing transport manager callback that we need to invoke */
-static pjsip_tp_state_callback tpmgr_state_callback;
-
 /*! \brief Structure for transport to be monitored */
 struct monitored_transport {
 	/*! \brief The underlying PJSIP transport */
@@ -114,7 +111,7 @@ AST_THREADSTORAGE(desc_storage);
 
 static int idle_sched_cb(const void *data)
 {
-	struct monitored_transport *keepalive = (struct monitored_transport *) data;
+	struct monitored_transport *monitored = (struct monitored_transport *) data;
 
 	if (!pj_thread_is_registered()) {
 		pj_thread_t *thread;
@@ -123,7 +120,7 @@ static int idle_sched_cb(const void *data)
 		desc = ast_threadstorage_get(&desc_storage, sizeof(pj_thread_desc));
 		if (!desc) {
 			ast_log(LOG_ERROR, "Could not get thread desc from thread-local storage.\n");
-			ao2_ref(keepalive, -1);
+			ao2_ref(monitored, -1);
 			return 0;
 		}
 
@@ -132,22 +129,22 @@ static int idle_sched_cb(const void *data)
 		pj_thread_register("Transport Monitor", *desc, &thread);
 	}
 
-	if (!keepalive->sip_received) {
+	if (!monitored->sip_received) {
 		ast_log(LOG_NOTICE, "Shutting down transport '%s' since no request was received in %d seconds\n",
-				keepalive->transport->info, IDLE_TIMEOUT / 1000);
-		pjsip_transport_shutdown(keepalive->transport);
+			monitored->transport->info, IDLE_TIMEOUT / 1000);
+		pjsip_transport_shutdown(monitored->transport);
 	}
 
-	ao2_ref(keepalive, -1);
+	ao2_ref(monitored, -1);
 	return 0;
 }
 
 /*! \brief Destructor for keepalive transport */
 static void monitored_transport_destroy(void *obj)
 {
-	struct monitored_transport *keepalive = obj;
+	struct monitored_transport *monitored = obj;
 
-	pjsip_transport_dec_ref(keepalive->transport);
+	pjsip_transport_dec_ref(monitored->transport);
 }
 
 /*! \brief Callback invoked when transport changes occur */
@@ -178,14 +175,13 @@ static void monitored_transport_state_callback(pjsip_transport *transport, pjsip
 				/* Let the scheduler inherit the reference from allocation */
 				if (ast_sched_add_variable(sched, IDLE_TIMEOUT, idle_sched_cb, monitored, 1) < 0) {
 					/* Uh Oh.  Could not schedule the idle check.  Kill the transport. */
-					ao2_unlink(transports, monitored);
-					ao2_ref(monitored, -1);
 					pjsip_transport_shutdown(transport);
+				} else {
+					/* monitored ref successfully passed to idle_sched_cb() */
+					break;
 				}
-			} else {
-				/* No scheduled task, so get rid of the allocation reference */
-				ao2_ref(monitored, -1);
 			}
+			ao2_ref(monitored, -1);
 			break;
 		case PJSIP_TP_STATE_SHUTDOWN:
 		case PJSIP_TP_STATE_DISCONNECTED:
@@ -197,13 +193,12 @@ static void monitored_transport_state_callback(pjsip_transport *transport, pjsip
 
 		ao2_ref(transports, -1);
 	}
-
-	/* Forward to the old state callback if present */
-	if (tpmgr_state_callback) {
-		tpmgr_state_callback(transport, state, info);
-	}
 }
 
+struct ast_sip_tpmgr_state_callback monitored_transport_reg = {
+	monitored_transport_state_callback,
+};
+
 /*! \brief Hashing function for monitored transport */
 static int monitored_transport_hash_fn(const void *obj, int flags)
 {
@@ -327,16 +322,9 @@ static pjsip_module idle_monitor_module = {
 static int load_module(void)
 {
 	struct ao2_container *transports;
-	pjsip_tpmgr *tpmgr;
 
 	CHECK_PJSIP_MODULE_LOADED();
 
-	tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
-	if (!tpmgr) {
-		ast_log(LOG_ERROR, "No transport manager to attach keepalive functionality to.\n");
-		return AST_MODULE_LOAD_DECLINE;
-	}
-
 	transports = ao2_container_alloc(TRANSPORTS_BUCKETS, monitored_transport_hash_fn,
 		monitored_transport_cmp_fn);
 	if (!transports) {
@@ -363,8 +351,7 @@ static int load_module(void)
 
 	ast_sip_register_service(&idle_monitor_module);
 
-	tpmgr_state_callback = pjsip_tpmgr_get_state_cb(tpmgr);
-	pjsip_tpmgr_set_state_cb(tpmgr, &monitored_transport_state_callback);
+	ast_sip_transport_state_register(&monitored_transport_reg);
 
 	ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &keepalive_global_observer);
 	ast_sorcery_reload_object(ast_sip_get_sorcery(), "global");
@@ -375,8 +362,6 @@ static int load_module(void)
 
 static int unload_module(void)
 {
-	pjsip_tpmgr *tpmgr;
-
 	if (keepalive_interval) {
 		keepalive_interval = 0;
 		if (keepalive_thread != AST_PTHREADT_NULL) {
@@ -388,10 +373,7 @@ static int unload_module(void)
 
 	ast_sorcery_observer_remove(ast_sip_get_sorcery(), "global", &keepalive_global_observer);
 
-	tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
-	if (tpmgr) {
-		pjsip_tpmgr_set_state_cb(tpmgr, tpmgr_state_callback);
-	}
+	ast_sip_transport_state_unregister(&monitored_transport_reg);
 
 	ast_sip_unregister_service(&idle_monitor_module);
 
diff --git a/res/res_pjsip_transport_websocket.c b/res/res_pjsip_transport_websocket.c
index dd3922c..c04594f 100644
--- a/res/res_pjsip_transport_websocket.c
+++ b/res/res_pjsip_transport_websocket.c
@@ -145,6 +145,7 @@ static int transport_create(void *data)
 {
 	struct transport_create_data *create_data = data;
 	struct ws_transport *newtransport = NULL;
+	pjsip_tp_state_callback state_cb;
 
 	pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
 	struct pjsip_tpmgr *tpmgr = pjsip_endpt_get_tpmgr(endpt);
@@ -161,6 +162,10 @@ static int transport_create(void *data)
 		goto on_error;
 	}
 
+	/* Give websocket transport a unique name for its lifetime */
+	snprintf(newtransport->transport.obj_name, PJ_MAX_OBJ_NAME, "ws%p",
+		&newtransport->transport);
+
 	newtransport->transport.endpt = endpt;
 
 	if (!(pool = pjsip_endpt_create_pool(endpt, "ws", 512, 512))) {
@@ -219,6 +224,7 @@ static int transport_create(void *data)
 	newtransport->transport.flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)newtransport->transport.key.type);
 	newtransport->transport.info = (char *)pj_pool_alloc(newtransport->transport.pool, 64);
 
+	newtransport->transport.dir = PJSIP_TP_DIR_INCOMING;
 	newtransport->transport.tpmgr = tpmgr;
 	newtransport->transport.send_msg = &ws_send_msg;
 	newtransport->transport.destroy = &ws_destroy;
@@ -242,6 +248,16 @@ static int transport_create(void *data)
 	}
 
 	create_data->transport = newtransport;
+
+	/* Notify application of transport state */
+	state_cb = pjsip_tpmgr_get_state_cb(newtransport->transport.tpmgr);
+	if (state_cb) {
+		pjsip_transport_state_info state_info;
+
+		memset(&state_info, 0, sizeof(state_info));
+		state_cb(&newtransport->transport, PJSIP_TP_STATE_CONNECTED, &state_info);
+	}
+
 	return 0;
 
 on_error:
@@ -365,6 +381,7 @@ static void websocket_cb(struct ast_websocket *session, struct ast_variable *par
 
 	if (ast_sip_push_task_synchronous(serializer, transport_create, &create_data)) {
 		ast_log(LOG_ERROR, "Could not create WebSocket transport.\n");
+		ast_taskprocessor_unreference(serializer);
 		ast_websocket_unref(session);
 		return;
 	}
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index d047022..8bf49c6 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -89,13 +89,20 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #define TURN_STATE_WAIT_TIME 2000
 
+/*! Full INTRA-frame Request / Fast Update Request (From RFC2032) */
 #define RTCP_PT_FUR     192
+/*! Sender Report (From RFC3550) */
 #define RTCP_PT_SR      AST_RTP_RTCP_SR
+/*! Receiver Report (From RFC3550) */
 #define RTCP_PT_RR      AST_RTP_RTCP_RR
+/*! Source Description (From RFC3550) */
 #define RTCP_PT_SDES    202
+/*! Goodbye (To remove SSRC's from tables) (From RFC3550) */
 #define RTCP_PT_BYE     203
+/*! Application defined (From RFC3550) */
 #define RTCP_PT_APP     204
 /* VP8: RTCP Feedback */
+/*! Payload Specific Feed Back (From RFC4585 also RFC5104) */
 #define RTCP_PT_PSFB    206
 
 #define RTP_MTU		1200
@@ -117,7 +124,9 @@ enum strict_rtp_state {
 	STRICT_RTP_CLOSED,   /*! Drop all RTP packets not coming from source that was learned */
 };
 
-#define DEFAULT_STRICT_RTP STRICT_RTP_CLOSED
+#define STRICT_RTP_LEARN_TIMEOUT	1500	/*!< milliseconds */
+
+#define DEFAULT_STRICT_RTP -1	/*!< Enabled */
 #define DEFAULT_ICESUPPORT 1
 
 extern struct ast_srtp_res *res_srtp;
@@ -218,6 +227,9 @@ static AST_RWLIST_HEAD_STATIC(host_candidates, ast_ice_host_candidate);
 
 /*! \brief RTP learning mode tracking information */
 struct rtp_learning_info {
+	struct ast_sockaddr proposed_address;	/*!< Proposed remote address for strict RTP */
+	struct timeval start;	/*!< The time learning mode was started */
+	struct timeval received; /*!< The time of the last received packet */
 	int max_seq;	/*!< The highest sequence number received */
 	int packets;	/*!< The number of remaining packets before the source is accepted */
 };
@@ -248,7 +260,7 @@ struct ast_rtp {
 	unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET];
 	unsigned int ssrc;		/*!< Synchronization source, RFC 3550, page 10. */
 	unsigned int themssrc;		/*!< Their SSRC */
-	unsigned int rxssrc;
+	unsigned int themssrc_valid;	/*!< True if their SSRC is available. */
 	unsigned int lastts;
 	unsigned int lastrxts;
 	unsigned int lastividtimestamp;
@@ -302,6 +314,7 @@ struct ast_rtp {
 	void *data;
 	struct ast_rtcp *rtcp;
 	struct ast_rtp *bridged;        /*!< Who we are Packet bridged to */
+	unsigned int asymmetric_codec;  /*!< Indicate if asymmetric send/receive codecs are allowed */
 
 	enum strict_rtp_state strict_rtp_state; /*!< Current state that strict RTP protection is in */
 	struct ast_sockaddr strict_rtp_address;  /*!< Remote address information for strict RTP purposes */
@@ -311,7 +324,6 @@ struct ast_rtp {
 	 * but these are in place to keep learning mode sequence values sealed from their normal counterparts.
 	 */
 	struct rtp_learning_info rtp_source_learn;	/* Learning mode track for the expected RTP source */
-	struct rtp_learning_info alt_source_learn;	/* Learning mode tracking for a new RTP source after one has been chosen */
 
 	struct rtp_red *red;
 
@@ -678,6 +690,37 @@ static void ice_wrap_dtor(void *vdoomed)
 	}
 }
 
+static void ast2pj_rtp_ice_role(enum ast_rtp_ice_role ast_role, enum pj_ice_sess_role *pj_role)
+{
+	switch (ast_role) {
+	case AST_RTP_ICE_ROLE_CONTROLLED:
+		*pj_role = PJ_ICE_SESS_ROLE_CONTROLLED;
+		break;
+	case AST_RTP_ICE_ROLE_CONTROLLING:
+		*pj_role = PJ_ICE_SESS_ROLE_CONTROLLING;
+		break;
+	}
+}
+
+static void pj2ast_rtp_ice_role(enum pj_ice_sess_role pj_role, enum ast_rtp_ice_role *ast_role)
+{
+	switch (pj_role) {
+	case PJ_ICE_SESS_ROLE_CONTROLLED:
+		*ast_role = AST_RTP_ICE_ROLE_CONTROLLED;
+		return;
+	case PJ_ICE_SESS_ROLE_CONTROLLING:
+		*ast_role = AST_RTP_ICE_ROLE_CONTROLLING;
+		return;
+	case PJ_ICE_SESS_ROLE_UNKNOWN:
+		/* Don't change anything */
+		return;
+	default:
+		/* If we aren't explicitly handling something, it's a bug */
+		ast_assert(0);
+		return;
+	}
+}
+
 /*! \pre instance is locked */
 static int ice_reset_session(struct ast_rtp_instance *instance)
 {
@@ -694,8 +737,9 @@ static int ice_reset_session(struct ast_rtp_instance *instance)
 	res = ice_create(instance, &rtp->ice_original_rtp_addr, rtp->ice_port, 1);
 	if (!res) {
 		/* Use the current expected role for the ICE session */
-		pj_ice_sess_change_role(rtp->ice->real_ice, rtp->role == AST_RTP_ICE_ROLE_CONTROLLED ?
-			PJ_ICE_SESS_ROLE_CONTROLLED : PJ_ICE_SESS_ROLE_CONTROLLING);
+		enum pj_ice_sess_role role = PJ_ICE_SESS_ROLE_UNKNOWN;
+		ast2pj_rtp_ice_role(rtp->role, &role);
+		pj_ice_sess_change_role(rtp->ice->real_ice, role);
 	}
 
 	/* If we only have one component now, and we previously set up TURN for RTCP,
@@ -767,7 +811,7 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
 		ao2_cleanup(rtp->ice_proposed_remote_candidates);
 		rtp->ice_proposed_remote_candidates = NULL;
 		/* If this ICE session is being preserved then go back to the role it currently is */
-		rtp->role = rtp->ice->real_ice->role;
+		pj2ast_rtp_ice_role(rtp->ice->real_ice->role, &rtp->role);
 		return;
 	}
 
@@ -1291,6 +1335,8 @@ static void ast_rtp_ice_turn_request(struct ast_rtp_instance *instance, enum ast
 	pj_turn_session_info info;
 	struct ast_sockaddr local, loop;
 	pj_status_t status;
+	pj_turn_sock_cfg turn_sock_cfg;
+	struct ice_wrap *ice;
 
 	ast_rtp_instance_get_local_address(instance, &local);
 	if (ast_sockaddr_is_ipv4(&local)) {
@@ -1353,11 +1399,20 @@ static void ast_rtp_ice_turn_request(struct ast_rtp_instance *instance, enum ast
 
 	pj_stun_config_init(&stun_config, &cachingpool.factory, 0, rtp->ioqueue->ioqueue, rtp->ioqueue->timerheap);
 
+	/* Use ICE session group lock for TURN session to avoid deadlock */
+	pj_turn_sock_cfg_default(&turn_sock_cfg);
+	ice = rtp->ice;
+	if (ice) {
+		turn_sock_cfg.grp_lock = ice->real_ice->grp_lock;
+		ao2_ref(ice, +1);
+	}
+
 	/* Release the instance lock to avoid deadlock with PJPROJECT group lock */
 	ao2_unlock(instance);
 	status = pj_turn_sock_create(&stun_config,
 		ast_sockaddr_is_ipv4(&addr) ? pj_AF_INET() : pj_AF_INET6(), conn_type,
-		turn_cb, NULL, instance, turn_sock);
+		turn_cb, &turn_sock_cfg, instance, turn_sock);
+	ao2_cleanup(ice);
 	if (status != PJ_SUCCESS) {
 		ast_log(LOG_WARNING, "Could not create a TURN client socket\n");
 		ao2_lock(instance);
@@ -1940,7 +1995,7 @@ static void dtls_perform_handshake(struct ast_rtp_instance *instance, struct dtl
 #endif
 
 #ifdef HAVE_PJPROJECT
-static void rtp_learning_seq_init(struct rtp_learning_info *info, uint16_t seq);
+static void rtp_learning_start(struct ast_rtp *rtp);
 
 /* PJPROJECT ICE callback */
 static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status)
@@ -1979,8 +2034,8 @@ static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status)
 		return;
 	}
 
-	rtp->strict_rtp_state = STRICT_RTP_LEARN;
-	rtp_learning_seq_init(&rtp->rtp_source_learn, (uint16_t)rtp->seqno);
+	ast_verb(4, "%p -- Strict RTP learning after ICE completion\n", rtp);
+	rtp_learning_start(rtp);
 	ao2_unlock(instance);
 }
 
@@ -2576,17 +2631,22 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz
 
 #ifdef HAVE_PJPROJECT
 	if (rtp->ice) {
+		enum ast_rtp_ice_component_type component = rtcp ? AST_RTP_ICE_COMPONENT_RTCP : AST_RTP_ICE_COMPONENT_RTP;
 		pj_status_t status;
 		struct ice_wrap *ice;
 
+		/* If RTCP is sharing the same socket then use the same component */
+		if (rtcp && rtp->rtcp->s == rtp->s) {
+			component = AST_RTP_ICE_COMPONENT_RTP;
+		}
+
 		pj_thread_register_check();
 
 		/* Release the instance lock to avoid deadlock with PJPROJECT group lock */
 		ice = rtp->ice;
 		ao2_ref(ice, +1);
 		ao2_unlock(instance);
-		status = pj_ice_sess_send_data(ice->real_ice,
-			rtcp ? AST_RTP_ICE_COMPONENT_RTCP : AST_RTP_ICE_COMPONENT_RTP, temp, len);
+		status = pj_ice_sess_send_data(ice->real_ice, component, temp, len);
 		ao2_ref(ice, -1);
 		ao2_lock(instance);
 		if (status == PJ_SUCCESS) {
@@ -2704,8 +2764,9 @@ static int create_new_socket(const char *type, int af)
  */
 static void rtp_learning_seq_init(struct rtp_learning_info *info, uint16_t seq)
 {
-	info->max_seq = seq - 1;
+	info->max_seq = seq;
 	info->packets = learning_min_sequential;
+	memset(&info->received, 0, sizeof(info->received));
 }
 
 /*!
@@ -2720,7 +2781,17 @@ static void rtp_learning_seq_init(struct rtp_learning_info *info, uint16_t seq)
  */
 static int rtp_learning_rtp_seq_update(struct rtp_learning_info *info, uint16_t seq)
 {
-	if (seq == info->max_seq + 1) {
+	/*
+	 * During the learning mode the minimum amount of media we'll accept is
+	 * 10ms so give a reasonable 5ms buffer just in case we get it sporadically.
+	 */
+	if (!ast_tvzero(info->received) && ast_tvdiff_ms(ast_tvnow(), info->received) < 5) {
+		/*
+		 * Reject a flood of packets as acceptable for learning.
+		 * Reset the needed packets.
+		 */
+		info->packets = learning_min_sequential - 1;
+	} else if (seq == (uint16_t) (info->max_seq + 1)) {
 		/* packet is in sequence */
 		info->packets--;
 	} else {
@@ -2728,8 +2799,25 @@ static int rtp_learning_rtp_seq_update(struct rtp_learning_info *info, uint16_t
 		info->packets = learning_min_sequential - 1;
 	}
 	info->max_seq = seq;
+	info->received = ast_tvnow();
 
-	return (info->packets == 0);
+	return info->packets;
+}
+
+/*!
+ * \brief Start the strictrtp learning mode.
+ *
+ * \param rtp RTP session description
+ *
+ * \return Nothing
+ */
+static void rtp_learning_start(struct ast_rtp *rtp)
+{
+	rtp->strict_rtp_state = STRICT_RTP_LEARN;
+	memset(&rtp->rtp_source_learn.proposed_address, 0,
+		sizeof(rtp->rtp_source_learn.proposed_address));
+	rtp->rtp_source_learn.start = ast_tvnow();
+	rtp_learning_seq_init(&rtp->rtp_source_learn, (uint16_t) rtp->lastrxseqno);
 }
 
 #ifdef HAVE_PJPROJECT
@@ -3002,11 +3090,7 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
 	/* Set default parameters on the newly created RTP structure */
 	rtp->ssrc = ast_random();
 	rtp->seqno = ast_random() & 0x7fff;
-	rtp->strict_rtp_state = (strictrtp ? STRICT_RTP_LEARN : STRICT_RTP_OPEN);
-	if (strictrtp) {
-		rtp_learning_seq_init(&rtp->rtp_source_learn, (uint16_t)rtp->seqno);
-		rtp_learning_seq_init(&rtp->alt_source_learn, (uint16_t)rtp->seqno);
-	}
+	rtp->strict_rtp_state = (strictrtp ? STRICT_RTP_CLOSED : STRICT_RTP_OPEN);
 
 	/* Create a new socket for us to listen on and use */
 	if ((rtp->s =
@@ -3580,7 +3664,7 @@ static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr)
 	struct ast_sockaddr remote_address = { { 0, } };
 	struct ast_rtp_rtcp_report_block *report_block = NULL;
 	RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report,
-			ast_rtp_rtcp_report_alloc(rtp->themssrc ? 1 : 0),
+			ast_rtp_rtcp_report_alloc(rtp->themssrc_valid ? 1 : 0),
 			ao2_cleanup);
 
 	if (!rtp || !rtp->rtcp) {
@@ -3600,7 +3684,7 @@ static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr)
 	calculate_lost_packet_statistics(rtp, &lost_packets, &fraction_lost);
 
 	gettimeofday(&now, NULL);
-	rtcp_report->reception_report_count = rtp->themssrc ? 1 : 0;
+	rtcp_report->reception_report_count = rtp->themssrc_valid ? 1 : 0;
 	rtcp_report->ssrc = rtp->ssrc;
 	rtcp_report->type = sr ? RTCP_PT_SR : RTCP_PT_RR;
 	if (sr) {
@@ -3610,7 +3694,7 @@ static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr)
 		rtcp_report->sender_information.octet_count = rtp->txoctetcount;
 	}
 
-	if (rtp->themssrc) {
+	if (rtp->themssrc_valid) {
 		report_block = ast_calloc(1, sizeof(*report_block));
 		if (!report_block) {
 			return 1;
@@ -3955,6 +4039,10 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr
 			 */
 			return 0;
 		}
+		if (!rtp->themssrc_valid) {
+			/* We don't know their SSRC value so we don't know who to update. */
+			return 0;
+		}
 
 		/* Prepare RTCP FIR (PT=206, FMT=4) */
 		rtp->rtcp->firseq++;
@@ -4535,67 +4623,264 @@ static void update_lost_stats(struct ast_rtp *rtp, unsigned int lost_packets)
 	rtp->rtcp->reported_normdev_lost = reported_normdev_lost_current;
 }
 
+static const char *rtcp_payload_type2str(unsigned int pt)
+{
+	const char *str;
+
+	switch (pt) {
+	case RTCP_PT_SR:
+		str = "Sender Report";
+		break;
+	case RTCP_PT_RR:
+		str = "Receiver Report";
+		break;
+	case RTCP_PT_FUR:
+		/* Full INTRA-frame Request / Fast Update Request */
+		str = "H.261 FUR";
+		break;
+	case RTCP_PT_PSFB:
+		/* Payload Specific Feed Back */
+		str = "PSFB";
+		break;
+	case RTCP_PT_SDES:
+		str = "Source Description";
+		break;
+	case RTCP_PT_BYE:
+		str = "BYE";
+		break;
+	default:
+		str = "Unknown";
+		break;
+	}
+	return str;
+}
+
+/*
+ * Unshifted RTCP header bit field masks
+ */
+#define RTCP_LENGTH_MASK			0xFFFF
+#define RTCP_PAYLOAD_TYPE_MASK		0xFF
+#define RTCP_REPORT_COUNT_MASK		0x1F
+#define RTCP_PADDING_MASK			0x01
+#define RTCP_VERSION_MASK			0x03
+
+/*
+ * RTCP header bit field shift offsets
+ */
+#define RTCP_LENGTH_SHIFT			0
+#define RTCP_PAYLOAD_TYPE_SHIFT		16
+#define RTCP_REPORT_COUNT_SHIFT		24
+#define RTCP_PADDING_SHIFT			29
+#define RTCP_VERSION_SHIFT			30
+
+#define RTCP_VERSION				2U
+#define RTCP_VERSION_SHIFTED		(RTCP_VERSION << RTCP_VERSION_SHIFT)
+#define RTCP_VERSION_MASK_SHIFTED	(RTCP_VERSION_MASK << RTCP_VERSION_SHIFT)
+
+/*
+ * RTCP first packet record validity header mask and value.
+ *
+ * RFC3550 intentionally defines the encoding of RTCP_PT_SR and RTCP_PT_RR
+ * such that they differ in the least significant bit.  Either of these two
+ * payload types MUST be the first RTCP packet record in a compound packet.
+ *
+ * RFC3550 checks the padding bit in the algorithm they use to check the
+ * RTCP packet for validity.  However, we aren't masking the padding bit
+ * to check since we don't know if it is a compound RTCP packet or not.
+ */
+#define RTCP_VALID_MASK (RTCP_VERSION_MASK_SHIFTED | (((RTCP_PAYLOAD_TYPE_MASK & ~0x1)) << RTCP_PAYLOAD_TYPE_SHIFT))
+#define RTCP_VALID_VALUE (RTCP_VERSION_SHIFTED | (RTCP_PT_SR << RTCP_PAYLOAD_TYPE_SHIFT))
+
+#define RTCP_SR_BLOCK_WORD_LENGTH 5
+#define RTCP_RR_BLOCK_WORD_LENGTH 6
+#define RTCP_HEADER_SSRC_LENGTH   2
+
 static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, const unsigned char *rtcpdata, size_t size, struct ast_sockaddr *addr)
 {
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 	unsigned int *rtcpheader = (unsigned int *)(rtcpdata);
-	int packetwords, position = 0;
-	int report_counter = 0;
+	unsigned int packetwords;
+	unsigned int position;
+	unsigned int first_word;
+	/*! True if we have seen an acceptable SSRC to learn the remote RTCP address */
+	unsigned int ssrc_seen;
 	struct ast_rtp_rtcp_report_block *report_block;
 	struct ast_frame *f = &ast_null_frame;
 
 	packetwords = size / 4;
 
-	if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT)) {
-		/* Send to whoever sent to us */
-		if (ast_sockaddr_cmp(&rtp->rtcp->them, addr)) {
-			ast_sockaddr_copy(&rtp->rtcp->them, addr);
-			if (rtpdebug) {
-				ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s\n",
-					  ast_sockaddr_stringify(&rtp->rtcp->them));
-			}
+	ast_debug(1, "Got RTCP report of %zu bytes from %s\n",
+		size, ast_sockaddr_stringify(addr));
+
+	/*
+	 * Validate the RTCP packet according to an adapted and slightly
+	 * modified RFC3550 validation algorithm.
+	 */
+	if (packetwords < RTCP_HEADER_SSRC_LENGTH) {
+		ast_debug(1, "%p -- RTCP from %s: Frame size (%u words) is too short\n",
+			rtp, ast_sockaddr_stringify(addr), packetwords);
+		return &ast_null_frame;
+	}
+	position = 0;
+	first_word = ntohl(rtcpheader[position]);
+	if ((first_word & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
+		ast_debug(1, "%p -- RTCP from %s: Failed first packet validity check\n",
+			rtp, ast_sockaddr_stringify(addr));
+		return &ast_null_frame;
+	}
+	do {
+		position += ((first_word >> RTCP_LENGTH_SHIFT) & RTCP_LENGTH_MASK) + 1;
+		if (packetwords <= position) {
+			break;
 		}
+		first_word = ntohl(rtcpheader[position]);
+	} while ((first_word & RTCP_VERSION_MASK_SHIFTED) == RTCP_VERSION_SHIFTED);
+	if (position != packetwords) {
+		ast_debug(1, "%p -- RTCP from %s: Failed packet version or length check\n",
+			rtp, ast_sockaddr_stringify(addr));
+		return &ast_null_frame;
 	}
 
-	ast_debug(1, "Got RTCP report of %zu bytes\n", size);
+	/*
+	 * Note: RFC3605 points out that true NAT (vs NAPT) can cause RTCP
+	 * to have a different IP address and port than RTP.  Otherwise, when
+	 * strictrtp is enabled we could reject RTCP packets not coming from
+	 * the learned RTP IP address if it is available.
+	 */
 
+	/*
+	 * strictrtp safety needs SSRC to match before we use the
+	 * sender's address for symmetrical RTP to send our RTCP
+	 * reports.
+	 *
+	 * If strictrtp is not enabled then claim to have already seen
+	 * a matching SSRC so we'll accept this packet's address for
+	 * symmetrical RTP.
+	 */
+	ssrc_seen = rtp->strict_rtp_state == STRICT_RTP_OPEN;
+
+	position = 0;
 	while (position < packetwords) {
-		int i, pt, rc;
+		unsigned int i;
+		unsigned int pt;
+		unsigned int rc;
+		unsigned int ssrc;
+		/*! True if the ssrc value we have is valid and not garbage because it doesn't exist. */
+		unsigned int ssrc_valid;
 		unsigned int length;
+		unsigned int min_length;
+
 		struct ast_json *message_blob;
 		RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report, NULL, ao2_cleanup);
 
 		i = position;
-		length = ntohl(rtcpheader[i]);
-		pt = (length & 0xff0000) >> 16;
-		rc = (length & 0x1f000000) >> 24;
-		length &= 0xffff;
-
-		rtcp_report = ast_rtp_rtcp_report_alloc(rc);
-		if (!rtcp_report) {
+		first_word = ntohl(rtcpheader[i]);
+		pt = (first_word >> RTCP_PAYLOAD_TYPE_SHIFT) & RTCP_PAYLOAD_TYPE_MASK;
+		rc = (first_word >> RTCP_REPORT_COUNT_SHIFT) & RTCP_REPORT_COUNT_MASK;
+		/* RFC3550 says 'length' is the number of words in the packet - 1 */
+		length = ((first_word >> RTCP_LENGTH_SHIFT) & RTCP_LENGTH_MASK) + 1;
+
+		/* Check expected RTCP packet record length */
+		min_length = RTCP_HEADER_SSRC_LENGTH;
+		switch (pt) {
+		case RTCP_PT_SR:
+			min_length += RTCP_SR_BLOCK_WORD_LENGTH;
+			/* fall through */
+		case RTCP_PT_RR:
+			min_length += (rc * RTCP_RR_BLOCK_WORD_LENGTH);
+			break;
+		case RTCP_PT_FUR:
+		case RTCP_PT_PSFB:
+			break;
+		case RTCP_PT_SDES:
+		case RTCP_PT_BYE:
+			/*
+			 * There may not be a SSRC/CSRC present.  The packet is
+			 * useless but still valid if it isn't present.
+			 *
+			 * We don't know what min_length should be so disable the check
+			 */
+			min_length = length;
+			break;
+		default:
+			ast_debug(1, "%p -- RTCP from %s: %u(%s) skipping record\n",
+				rtp, ast_sockaddr_stringify(addr), pt, rtcp_payload_type2str(pt));
+			if (rtcp_debug_test_addr(addr)) {
+				ast_verbose("\n");
+				ast_verbose("RTCP from %s: %u(%s) skipping record\n",
+					ast_sockaddr_stringify(addr), pt, rtcp_payload_type2str(pt));
+			}
+			position += length;
+			continue;
+		}
+		if (length < min_length) {
+			ast_debug(1, "%p -- RTCP from %s: %u(%s) length field less than expected minimum.  Min:%u Got:%u\n",
+				rtp, ast_sockaddr_stringify(addr), pt, rtcp_payload_type2str(pt),
+				min_length - 1, length - 1);
 			return &ast_null_frame;
 		}
-		rtcp_report->reception_report_count = rc;
-		rtcp_report->ssrc = ntohl(rtcpheader[i + 1]);
 
-		if ((i + length) > packetwords) {
-			if (rtpdebug) {
-				ast_debug(1, "RTCP Read too short\n");
+		/* Get the RTCP record SSRC if defined for the record */
+		ssrc_valid = 1;
+		switch (pt) {
+		case RTCP_PT_SR:
+		case RTCP_PT_RR:
+			rtcp_report = ast_rtp_rtcp_report_alloc(rc);
+			if (!rtcp_report) {
+				return &ast_null_frame;
 			}
-			return &ast_null_frame;
+			rtcp_report->reception_report_count = rc;
+
+			ssrc = ntohl(rtcpheader[i + 1]);
+			rtcp_report->ssrc = ssrc;
+			break;
+		case RTCP_PT_FUR:
+		case RTCP_PT_PSFB:
+			ssrc = ntohl(rtcpheader[i + 1]);
+			break;
+		case RTCP_PT_SDES:
+		case RTCP_PT_BYE:
+		default:
+			ssrc = 0;
+			ssrc_valid = 0;
+			break;
 		}
 
 		if (rtcp_debug_test_addr(addr)) {
-			ast_verbose("\n\nGot RTCP from %s\n",
-				    ast_sockaddr_stringify(addr));
-			ast_verbose("PT: %d(%s)\n", pt, (pt == RTCP_PT_SR) ? "Sender Report" :
-							(pt == RTCP_PT_RR) ? "Receiver Report" :
-							(pt == RTCP_PT_FUR) ? "H.261 FUR" : "Unknown");
-			ast_verbose("Reception reports: %d\n", rc);
-			ast_verbose("SSRC of sender: %u\n", rtcp_report->ssrc);
+			ast_verbose("\n");
+			ast_verbose("RTCP from %s\n", ast_sockaddr_stringify(addr));
+			ast_verbose("PT: %u(%s)\n", pt, rtcp_payload_type2str(pt));
+			ast_verbose("Reception reports: %u\n", rc);
+			ast_verbose("SSRC of sender: %u\n", ssrc);
+		}
+
+		if (ssrc_valid && rtp->themssrc_valid) {
+			if (ssrc != rtp->themssrc) {
+				/*
+				 * Skip over this RTCP record as it does not contain the
+				 * correct SSRC.  We should not act upon RTCP records
+				 * for a different stream.
+				 */
+				position += length;
+				ast_debug(1, "%p -- RTCP from %s: Skipping record, received SSRC '%u' != expected '%u'\n",
+					rtp, ast_sockaddr_stringify(addr), ssrc, rtp->themssrc);
+				continue;
+			}
+			ssrc_seen = 1;
 		}
 
-		i += 2; /* Advance past header and ssrc */
+		if (ssrc_seen && ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT)) {
+			/* Send to whoever sent to us */
+			if (ast_sockaddr_cmp(&rtp->rtcp->them, addr)) {
+				ast_sockaddr_copy(&rtp->rtcp->them, addr);
+				if (rtpdebug) {
+					ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s\n",
+						ast_sockaddr_stringify(addr));
+				}
+			}
+		}
+
+		i += RTCP_HEADER_SSRC_LENGTH; /* Advance past header and ssrc */
 		switch (pt) {
 		case RTCP_PT_SR:
 			gettimeofday(&rtp->rtcp->rxlsr, NULL);
@@ -4619,7 +4904,7 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
 						rtcp_report->sender_information.packet_count,
 						rtcp_report->sender_information.octet_count);
 			}
-			i += 5;
+			i += RTCP_SR_BLOCK_WORD_LENGTH;
 			/* Intentional fall through */
 		case RTCP_PT_RR:
 			if (rtcp_report->type != RTCP_PT_SR) {
@@ -4632,7 +4917,7 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
 				if (!report_block) {
 					return &ast_null_frame;
 				}
-				rtcp_report->report_block[report_counter] = report_block;
+				rtcp_report->report_block[0] = report_block;
 				report_block->source_ssrc = ntohl(rtcpheader[i]);
 				report_block->lost_count.packets = ntohl(rtcpheader[i + 1]) & 0x00ffffff;
 				report_block->lost_count.fraction = ((ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24);
@@ -4669,16 +4954,15 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
 					ast_verbose("  DLSR: %4.4f (sec)\n",(double)report_block->dlsr / 65536.0);
 					ast_verbose("  RTT: %4.4f(sec)\n", rtp->rtcp->rtt);
 				}
-				report_counter++;
 			}
 			/* If and when we handle more than one report block, this should occur outside
 			 * this loop.
 			 */
 
 			message_blob = ast_json_pack("{s: s, s: s, s: f}",
-					"from", ast_sockaddr_stringify(&rtp->rtcp->them),
-					"to", rtp->rtcp->local_addr_str,
-					"rtt", rtp->rtcp->rtt);
+				"from", ast_sockaddr_stringify(addr),
+				"to", rtp->rtcp->local_addr_str,
+				"rtt", rtp->rtcp->rtt);
 			ast_rtp_publish_rtcp_message(instance, ast_rtp_rtcp_received_type(),
 					rtcp_report,
 					message_blob);
@@ -4701,26 +4985,23 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
 		case RTCP_PT_SDES:
 			if (rtcp_debug_test_addr(addr)) {
 				ast_verbose("Received an SDES from %s\n",
-					    ast_sockaddr_stringify(&rtp->rtcp->them));
+					ast_sockaddr_stringify(addr));
 			}
 			break;
 		case RTCP_PT_BYE:
 			if (rtcp_debug_test_addr(addr)) {
 				ast_verbose("Received a BYE from %s\n",
-					    ast_sockaddr_stringify(&rtp->rtcp->them));
+					ast_sockaddr_stringify(addr));
 			}
 			break;
 		default:
-			ast_debug(1, "Unknown RTCP packet (pt=%d) received from %s\n",
-				  pt, ast_sockaddr_stringify(&rtp->rtcp->them));
 			break;
 		}
-		position += (length + 1);
+		position += length;
 	}
 	rtp->rtcp->rtcp_info = 1;
 
 	return f;
-
 }
 
 /*! \pre instance is locked */
@@ -4780,7 +5061,7 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance,
 	struct ast_rtp_instance *instance1, unsigned int *rtpheader, int len, int hdrlen)
 {
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
-	struct ast_rtp *bridged = ast_rtp_instance_get_data(instance1);
+	struct ast_rtp *bridged;
 	int res = 0, payload = 0, bridged_payload = 0, mark;
 	RAII_VAR(struct ast_rtp_payload_type *, payload_type, NULL, ao2_cleanup);
 	int reconstruct = ntohl(rtpheader[0]);
@@ -4790,7 +5071,7 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance,
 
 	/* Get fields from packet */
 	payload = (reconstruct & 0x7f0000) >> 16;
-	mark = (((reconstruct & 0x800000) >> 23) != 0);
+	mark = (reconstruct & 0x800000) >> 23;
 
 	/* Check what the payload value should be */
 	payload_type = ast_rtp_codecs_get_payload(ast_rtp_instance_get_codecs(instance), payload);
@@ -4806,21 +5087,12 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance,
 		return -1;
 	}
 
-	rtp->rxcount++;
-	rtp->rxoctetcount += (len - hdrlen);
-
 	/* If the payload coming in is not one of the negotiated ones then send it to the core, this will cause formats to change and the bridge to break */
 	if (ast_rtp_codecs_find_payload_code(ast_rtp_instance_get_codecs(instance1), bridged_payload) == -1) {
 		ast_debug(1, "Unsupported payload type received \n");
 		return -1;
 	}
 
-	/* If bridged peer is in dtmf, feed all packets to core until it finishes to avoid infinite dtmf */
-	if (bridged->sending_digit) {
-		ast_debug(1, "Feeding packets to core until DTMF finishes\n");
-		return -1;
-	}
-
 	/*
 	 * Even if we are no longer in dtmf, we could still be receiving
 	 * re-transmissions of the last dtmf end still.  Feed those to the
@@ -4831,18 +5103,10 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance,
 		return -1;
 	}
 
-	/* If the marker bit has been explicitly set turn it on */
-	if (ast_test_flag(rtp, FLAG_NEED_MARKER_BIT)) {
-		mark = 1;
-		ast_clear_flag(rtp, FLAG_NEED_MARKER_BIT);
+	if (payload_type->asterisk_format) {
+		ao2_replace(rtp->lastrxformat, payload_type->format);
 	}
 
-	/* Reconstruct part of the packet */
-	reconstruct &= 0xFF80FFFF;
-	reconstruct |= (bridged_payload << 16);
-	reconstruct |= (mark << 23);
-	rtpheader[0] = htonl(reconstruct);
-
 	/*
 	 * We have now determined that we need to send the RTP packet
 	 * out the bridged instance to do local bridging so we must unlock
@@ -4858,6 +5122,40 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance,
 	ao2_unlock(instance);
 	ao2_lock(instance1);
 
+	/*
+	 * Get the peer rtp pointer now to emphasize that using it
+	 * must happen while instance1 is locked.
+	 */
+	bridged = ast_rtp_instance_get_data(instance1);
+
+
+	/* If bridged peer is in dtmf, feed all packets to core until it finishes to avoid infinite dtmf */
+	if (bridged->sending_digit) {
+		ast_debug(1, "Feeding packet to core until DTMF finishes\n");
+		ao2_unlock(instance1);
+		ao2_lock(instance);
+		return -1;
+	}
+
+	if (payload_type->asterisk_format) {
+		/*
+		 * If bridged peer has already received rtp, perform the asymmetric codec check
+		 * if that feature has been activated
+		 */
+		if (!bridged->asymmetric_codec
+			&& bridged->lastrxformat != ast_format_none
+			&& ast_format_cmp(payload_type->format, bridged->lastrxformat) == AST_FORMAT_CMP_NOT_EQUAL) {
+			ast_debug(1, "Asymmetric RTP codecs detected (TX: %s, RX: %s) sending frame to core\n",
+				ast_format_get_name(payload_type->format),
+				ast_format_get_name(bridged->lastrxformat));
+			ao2_unlock(instance1);
+			ao2_lock(instance);
+			return -1;
+		}
+
+		ao2_replace(bridged->lasttxformat, payload_type->format);
+	}
+
 	ast_rtp_instance_get_remote_address(instance1, &remote_address);
 
 	if (ast_sockaddr_isnull(&remote_address)) {
@@ -4867,6 +5165,18 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance,
 		return 0;
 	}
 
+	/* If the marker bit has been explicitly set turn it on */
+	if (ast_test_flag(bridged, FLAG_NEED_MARKER_BIT)) {
+		mark = 1;
+		ast_clear_flag(bridged, FLAG_NEED_MARKER_BIT);
+	}
+
+	/* Reconstruct part of the packet */
+	reconstruct &= 0xFF80FFFF;
+	reconstruct |= (bridged_payload << 16);
+	reconstruct |= (mark << 23);
+	rtpheader[0] = htonl(reconstruct);
+
 	/* Send the packet back out */
 	res = rtp_sendto(instance1, (void *)rtpheader, len, 0, &remote_address, &ice);
 	if (res < 0) {
@@ -4991,39 +5301,139 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
 		return &ast_null_frame;
 	}
 
-	/* If strict RTP protection is enabled see if we need to learn the remote address or if we need to drop the packet */
-	if (rtp->strict_rtp_state == STRICT_RTP_LEARN) {
-		ast_debug(1, "%p -- Probation learning mode pass with source address %s\n", rtp, ast_sockaddr_stringify(&addr));
-		/* For now, we always copy the address. */
-		ast_sockaddr_copy(&rtp->strict_rtp_address, &addr);
-
-		/* Send the rtp and the seqno from header to rtp_learning_rtp_seq_update to see whether we can exit or not*/
-		if (rtp_learning_rtp_seq_update(&rtp->rtp_source_learn, seqno)) {
-			ast_debug(1, "%p -- Probation at seq %d with %d to go; discarding frame\n",
-				rtp, rtp->rtp_source_learn.max_seq, rtp->rtp_source_learn.packets);
-			return &ast_null_frame;
-		}
-
-		ast_verb(4, "%p -- Probation passed - setting RTP source address to %s\n", rtp, ast_sockaddr_stringify(&addr));
-		rtp->strict_rtp_state = STRICT_RTP_CLOSED;
+	/* If the version is not what we expected by this point then just drop the packet */
+	if (version != 2) {
+		return &ast_null_frame;
 	}
-	if (rtp->strict_rtp_state == STRICT_RTP_CLOSED) {
-		if (!ast_sockaddr_cmp(&rtp->strict_rtp_address, &addr)) {
-			/* Always reset the alternate learning source */
-			rtp_learning_seq_init(&rtp->alt_source_learn, seqno);
+
+	/* If strict RTP protection is enabled see if we need to learn the remote address or if we need to drop the packet */
+	switch (rtp->strict_rtp_state) {
+	case STRICT_RTP_LEARN:
+		/*
+		 * Scenario setup:
+		 * PartyA -- Ast1 -- Ast2 -- PartyB
+		 *
+		 * The learning timeout is necessary for Ast1 to handle the above
+		 * setup where PartyA calls PartyB and Ast2 initiates direct media
+		 * between Ast1 and PartyB.  Ast1 may lock onto the Ast2 stream and
+		 * never learn the PartyB stream when it starts.  The timeout makes
+		 * Ast1 stay in the learning state long enough to see and learn the
+		 * RTP stream from PartyB.
+		 *
+		 * To mitigate against attack, the learning state cannot switch
+		 * streams while there are competing streams.  The competing streams
+		 * interfere with each other's qualification.  Once we accept a
+		 * stream and reach the timeout, an attacker cannot interfere
+		 * anymore.
+		 *
+		 * Here are a few scenarios and each one assumes that the streams
+		 * are continuous:
+		 *
+		 * 1) We already have a known stream source address and the known
+		 * stream wants to change to a new source address.  An attacking
+		 * stream will block learning the new stream source.  After the
+		 * timeout we re-lock onto the original stream source address which
+		 * likely went away.  The result is one way audio.
+		 *
+		 * 2) We already have a known stream source address and the known
+		 * stream doesn't want to change source addresses.  An attacking
+		 * stream will not be able to replace the known stream.  After the
+		 * timeout we re-lock onto the known stream.  The call is not
+		 * affected.
+		 *
+		 * 3) We don't have a known stream source address.  This presumably
+		 * is the start of a call.  Competing streams will result in staying
+		 * in learning mode until a stream becomes the victor and we reach
+		 * the timeout.  We cannot exit learning if we have no known stream
+		 * to lock onto.  The result is one way audio until there is a victor.
+		 *
+		 * If we learn a stream source address before the timeout we will be
+		 * in scenario 1) or 2) when a competing stream starts.
+		 */
+		if (!ast_sockaddr_isnull(&rtp->strict_rtp_address)
+			&& STRICT_RTP_LEARN_TIMEOUT < ast_tvdiff_ms(ast_tvnow(), rtp->rtp_source_learn.start)) {
+			ast_verb(4, "%p -- Strict RTP learning complete - Locking on source address %s\n",
+				rtp, ast_sockaddr_stringify(&rtp->strict_rtp_address));
+			rtp->strict_rtp_state = STRICT_RTP_CLOSED;
 		} else {
-			/* Start trying to learn from the new address. If we pass a probationary period with
-			 * it, that means we've stopped getting RTP from the original source and we should
-			 * switch to it.
+			struct ast_sockaddr target_address;
+
+			if (!ast_sockaddr_cmp(&rtp->strict_rtp_address, &addr)) {
+				/*
+				 * We are open to learning a new address but have received
+				 * traffic from the current address, accept it and reset
+				 * the learning counts for a new source.  When no more
+				 * current source packets arrive a new source can take over
+				 * once sufficient traffic is received.
+				 */
+				rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
+				break;
+			}
+
+			/*
+			 * We give preferential treatment to the requested target address
+			 * (negotiated SDP address) where we are to send our RTP.  However,
+			 * the other end has no obligation to send from that address even
+			 * though it is practically a requirement when NAT is involved.
 			 */
-			if (rtp_learning_rtp_seq_update(&rtp->alt_source_learn, seqno)) {
-				ast_debug(1, "%p -- Received RTP packet from %s, dropping due to strict RTP protection. Will switch to it in %d packets\n",
-						rtp, ast_sockaddr_stringify(&addr), rtp->alt_source_learn.packets);
-				return &ast_null_frame;
+			ast_rtp_instance_get_requested_target_address(instance, &target_address);
+			if (!ast_sockaddr_cmp(&target_address, &addr)) {
+				/* Accept the negotiated target RTP stream as the source */
+				ast_verb(4, "%p -- Strict RTP switching to RTP target address %s as source\n",
+					rtp, ast_sockaddr_stringify(&addr));
+				ast_sockaddr_copy(&rtp->strict_rtp_address, &addr);
+				rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
+				break;
 			}
-			ast_verb(4, "%p -- Switching RTP source address to %s\n", rtp, ast_sockaddr_stringify(&addr));
-			ast_sockaddr_copy(&rtp->strict_rtp_address, &addr);
+
+			/*
+			 * Trying to learn a new address.  If we pass a probationary period
+			 * with it, that means we've stopped getting RTP from the original
+			 * source and we should switch to it.
+			 */
+			if (!ast_sockaddr_cmp(&rtp->rtp_source_learn.proposed_address, &addr)) {
+				if (!rtp_learning_rtp_seq_update(&rtp->rtp_source_learn, seqno)) {
+					/* Accept the new RTP stream */
+					ast_verb(4, "%p -- Strict RTP switching source address to %s\n",
+						rtp, ast_sockaddr_stringify(&addr));
+					ast_sockaddr_copy(&rtp->strict_rtp_address, &addr);
+					rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
+					break;
+				}
+				/* Not ready to accept the RTP stream candidate */
+				ast_debug(1, "%p -- Received RTP packet from %s, dropping due to strict RTP protection. Will switch to it in %d packets.\n",
+					rtp, ast_sockaddr_stringify(&addr), rtp->rtp_source_learn.packets);
+			} else {
+				/*
+				 * This is either an attacking stream or
+				 * the start of the expected new stream.
+				 */
+				ast_sockaddr_copy(&rtp->rtp_source_learn.proposed_address, &addr);
+				rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
+				ast_debug(1, "%p -- Received RTP packet from %s, dropping due to strict RTP protection. Qualifying new stream.\n",
+					rtp, ast_sockaddr_stringify(&addr));
+			}
+			return &ast_null_frame;
 		}
+		/* Fall through */
+	case STRICT_RTP_CLOSED:
+		/*
+		 * We should not allow a stream address change if the SSRC matches
+		 * once strictrtp learning is closed.  Any kind of address change
+		 * like this should have happened while we were in the learning
+		 * state.  We do not want to allow the possibility of an attacker
+		 * interfering with the RTP stream after the learning period.
+		 * An attacker could manage to get an RTCP packet redirected to
+		 * them which can contain the SSRC value.
+		 */
+		if (!ast_sockaddr_cmp(&rtp->strict_rtp_address, &addr)) {
+			break;
+		}
+		ast_debug(1, "%p -- Received RTP packet from %s, dropping due to strict RTP protection.\n",
+			rtp, ast_sockaddr_stringify(&addr));
+		return &ast_null_frame;
+	case STRICT_RTP_OPEN:
+		break;
 	}
 
 	/* If symmetric RTP is enabled see if the remote side is not what we expected and change where we are sending audio */
@@ -5044,18 +5454,6 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
 		}
 	}
 
-	/* If we are directly bridged to another instance send the audio directly out */
-	instance1 = ast_rtp_instance_get_bridged(instance);
-	if (instance1
-		&& !bridge_p2p_rtp_write(instance, instance1, rtpheader, res, hdrlen)) {
-		return &ast_null_frame;
-	}
-
-	/* If the version is not what we expected by this point then just drop the packet */
-	if (version != 2) {
-		return &ast_null_frame;
-	}
-
 	/* Pull out the various other fields we will need */
 	payloadtype = (seqno & 0x7f0000) >> 16;
 	padding = seqno & (1 << 29);
@@ -5068,7 +5466,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
 
 	AST_LIST_HEAD_INIT_NOLOCK(&frames);
 	/* Force a marker bit and change SSRC if the SSRC changes */
-	if (rtp->rxssrc && rtp->rxssrc != ssrc) {
+	if (rtp->themssrc_valid && rtp->themssrc != ssrc) {
 		struct ast_frame *f, srcupdate = {
 			AST_FRAME_CONTROL,
 			.subclass.integer = AST_CONTROL_SRCCHANGE,
@@ -5096,8 +5494,8 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
 			rtp->rtcp->received_prior = 0;
 		}
 	}
-
-	rtp->rxssrc = ssrc;
+	rtp->themssrc = ssrc; /* Record their SSRC to put in future RR */
+	rtp->themssrc_valid = 1;
 
 	/* Remove any padding bytes that may be present */
 	if (padding) {
@@ -5151,8 +5549,26 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
 	prev_seqno = rtp->lastrxseqno;
 	rtp->lastrxseqno = seqno;
 
-	if (!rtp->themssrc) {
-		rtp->themssrc = ntohl(rtpheader[2]); /* Record their SSRC to put in future RR */
+
+	/* If we are directly bridged to another instance send the audio directly out,
+	 * but only after updating core information about the received traffic so that
+	 * outgoing RTCP reflects it.
+	 */
+	instance1 = ast_rtp_instance_get_bridged(instance);
+	if (instance1
+		&& !bridge_p2p_rtp_write(instance, instance1, rtpheader, res, hdrlen)) {
+		struct timeval rxtime;
+		struct ast_frame *f;
+
+		/* Update statistics for jitter so they are correct in RTCP */
+		calc_rxstamp(&rxtime, rtp, timestamp, mark);
+
+		/* When doing P2P we don't need to raise any frames about SSRC change to the core */
+		while ((f = AST_LIST_REMOVE_HEAD(&frames, frame_list)) != NULL) {
+			ast_frfree(f);
+		}
+
+		return &ast_null_frame;
 	}
 
 	if (rtp_debug_test_addr(&addr)) {
@@ -5483,6 +5899,8 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
 				rtp->rtcp = NULL;
 			}
 		}
+	} else if (property == AST_RTP_PROPERTY_ASYMMETRIC_CODEC) {
+		rtp->asymmetric_codec = value;
 	}
 }
 
@@ -5529,9 +5947,14 @@ static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct
 
 	rtp->rxseqno = 0;
 
-	if (strictrtp && rtp->strict_rtp_state != STRICT_RTP_OPEN) {
-		rtp->strict_rtp_state = STRICT_RTP_LEARN;
-		rtp_learning_seq_init(&rtp->rtp_source_learn, rtp->seqno);
+	if (strictrtp && rtp->strict_rtp_state != STRICT_RTP_OPEN
+		&& !ast_sockaddr_isnull(addr) && ast_sockaddr_cmp(addr, &rtp->strict_rtp_address)) {
+		/* We only need to learn a new strict source address if we've been told the source is
+		 * changing to something different.
+		 */
+		ast_verb(4, "%p -- Strict RTP learning after remote address set to: %s\n",
+			rtp, ast_sockaddr_stringify(addr));
+		rtp_learning_start(rtp);
 	}
 }
 
diff --git a/res/res_smdi.c b/res/res_smdi.c
index f4804c7..9a40227 100644
--- a/res/res_smdi.c
+++ b/res/res_smdi.c
@@ -610,13 +610,12 @@ static void *smdi_read(void *iface_p)
 
 			ast_debug(1, "Read a 'D' ... it's an MD message.\n");
 
-			if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
+			md_msg = ao2_alloc(sizeof(*md_msg), NULL);
+			if (!md_msg) {
 				ao2_ref(iface, -1);
 				return NULL;
 			}
 
-			md_msg = ao2_alloc(sizeof(*md_msg), NULL);
-
 			/* read the message desk number */
 			for (i = 0; i < sizeof(md_msg->mesg_desk_num) - 1; i++) {
 				md_msg->mesg_desk_num[i] = fgetc(iface->file);
@@ -712,13 +711,12 @@ static void *smdi_read(void *iface_p)
 
 			ast_debug(1, "Read a 'W', it's an MWI message. (No more debug coming for MWI messages)\n");
 
-			if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
+			mwi_msg = ao2_alloc(sizeof(*mwi_msg), NULL);
+			if (!mwi_msg) {
 				ao2_ref(iface, -1);
 				return NULL;
 			}
 
-			mwi_msg = ao2_alloc(sizeof(*mwi_msg), NULL);
-
 			/* discard the 'I' (from 'MWI') */
 			fgetc(iface->file);
 			
diff --git a/res/res_srtp.c b/res/res_srtp.c
index a76128f..74587d5 100644
--- a/res/res_srtp.c
+++ b/res/res_srtp.c
@@ -418,11 +418,25 @@ tryagain:
 	}
 
 	if (res != err_status_ok && res != err_status_replay_fail ) {
-		if ((srtp->warned >= 10) && !((srtp->warned - 10) % 100)) {
-			ast_log(AST_LOG_WARNING, "SRTP unprotect failed with: %s %d\n", srtp_errstr(res), srtp->warned);
-			srtp->warned = 11;
+		/*
+		 * Authentication failures happen when an active attacker tries to
+		 * insert malicious RTP packets. Furthermore, authentication failures
+		 * happen, when the other party encrypts the sRTP data in an unexpected
+		 * way. This happens quite often with RTCP. Therefore, when you see
+		 * authentication failures, try to identify the implementation
+		 * (author and product name) used by your other party. Try to investigate
+		 * whether they use a custom library or an outdated version of libSRTP.
+		 */
+		if (rtcp) {
+			ast_verb(2, "SRTCP unprotect failed because of %s\n", srtp_errstr(res));
 		} else {
-			srtp->warned++;
+			if ((srtp->warned >= 10) && !((srtp->warned - 10) % 150)) {
+				ast_verb(2, "SRTP unprotect failed because of %s %d\n",
+					srtp_errstr(res), srtp->warned);
+				srtp->warned = 11;
+			} else {
+				srtp->warned++;
+			}
 		}
 		errno = EAGAIN;
 		return -1;
diff --git a/res/res_stasis_device_state.c b/res/res_stasis_device_state.c
index 29e7566..51101dd 100644
--- a/res/res_stasis_device_state.c
+++ b/res/res_stasis_device_state.c
@@ -108,7 +108,6 @@ static int device_state_subscriptions_cmp(void *obj, void *arg, int flags)
 static void device_state_subscription_destroy(void *obj)
 {
 	struct device_state_subscription *sub = obj;
-	sub->sub = stasis_unsubscribe_and_join(sub->sub);
 	ast_string_field_free_memory(sub);
 }
 
@@ -154,6 +153,9 @@ static struct device_state_subscription *find_device_state_subscription(
 static void remove_device_state_subscription(
 	struct device_state_subscription *sub)
 {
+	if (sub->sub) {
+		sub->sub = stasis_unsubscribe_and_join(sub->sub);
+	}
 	ao2_unlink_flags(device_state_subscriptions, sub, OBJ_NOLOCK);
 }
 
diff --git a/res/res_stasis_snoop.c b/res/res_stasis_snoop.c
index abdef6e..da66894 100644
--- a/res/res_stasis_snoop.c
+++ b/res/res_stasis_snoop.c
@@ -74,6 +74,8 @@ struct stasis_app_snoop {
 	unsigned int whisper_active:1;
 	/*! \brief Uniqueid of the channel this snoop is snooping on */
 	char uniqueid[AST_MAX_UNIQUEID];
+	/*! \brief A frame of silence to use when the audiohook returns null */
+	struct ast_frame silence;
 };
 
 /*! \brief Destructor for snoop structure */
@@ -93,6 +95,11 @@ static void snoop_destroy(void *obj)
 		ast_audiohook_destroy(&snoop->whisper);
 	}
 
+	if (snoop->silence.data.ptr) {
+		ast_free(snoop->silence.data.ptr);
+		snoop->silence.data.ptr = NULL;
+	}
+
 	ast_free(snoop->app);
 
 	ast_channel_cleanup(snoop->chan);
@@ -199,7 +206,7 @@ static struct ast_frame *snoop_read(struct ast_channel *chan)
 	frame = ast_audiohook_read_frame(&snoop->spy, snoop->spy_samples, snoop->spy_direction, snoop->spy_format);
 	ast_audiohook_unlock(&snoop->spy);
 
-	return frame ? frame : &ast_null_frame;
+	return frame ? frame : &snoop->silence;
 }
 
 /*! \brief Callback function for hanging up a Snoop channel */
@@ -385,6 +392,19 @@ struct ast_channel *stasis_app_control_snoop(struct ast_channel *chan,
 
 		snoop->spy_samples = ast_format_get_sample_rate(snoop->spy_format) / (1000 / SNOOP_INTERVAL);
 		snoop->spy_active = 1;
+
+		snoop->silence.frametype = AST_FRAME_VOICE,
+		snoop->silence.datalen = snoop->spy_samples * sizeof(uint16_t),
+		snoop->silence.samples = snoop->spy_samples,
+		snoop->silence.mallocd = 0,
+		snoop->silence.offset = 0,
+		snoop->silence.src = __PRETTY_FUNCTION__,
+		snoop->silence.subclass.format = snoop->spy_format,
+		snoop->silence.data.ptr = ast_calloc(snoop->spy_samples, sizeof(uint16_t));
+		if (!snoop->silence.data.ptr) {
+			ast_hangup(snoop->chan);
+			return NULL;
+		}
 	}
 
 	/* If whispering is enabled set up the audiohook */
diff --git a/res/res_xmpp.c b/res/res_xmpp.c
index 95d3cc0..6ba4014 100644
--- a/res/res_xmpp.c
+++ b/res/res_xmpp.c
@@ -61,6 +61,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/manager.h"
 #include "asterisk/cli.h"
 #include "asterisk/config_options.h"
+#include "asterisk/json.h"
 
 /*** DOCUMENTATION
 	<application name="JabberSend" language="en_US" module="res_xmpp">
@@ -323,6 +324,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 				<configOption name="secret">
 					<synopsis>XMPP password</synopsis>
 				</configOption>
+				<configOption name="refresh_token">
+					<synopsis>Google OAuth 2.0 refresh token</synopsis>
+				</configOption>
+				<configOption name="oauth_clientid">
+					<synopsis>Google OAuth 2.0 application's client id</synopsis>
+				</configOption>
+				<configOption name="oauth_secret">
+					<synopsis>Google OAuth 2.0 application's secret</synopsis>
+				</configOption>
 				<configOption name="serverhost">
 					<synopsis>Route to server, e.g. talk.google.com</synopsis>
 				</configOption>
@@ -461,6 +471,9 @@ struct ast_xmpp_client_config {
 		AST_STRING_FIELD(name);        /*!< Name of the client connection */
 		AST_STRING_FIELD(user);        /*!< Username to use for authentication */
 		AST_STRING_FIELD(password);    /*!< Password to use for authentication */
+		AST_STRING_FIELD(refresh_token);   /*!< Refresh token to use for OAuth authentication */
+		AST_STRING_FIELD(oauth_clientid);  /*!< Client ID to use for OAuth authentication */
+		AST_STRING_FIELD(oauth_secret);    /*!< Secret to use for OAuth authentication */
 		AST_STRING_FIELD(server);      /*!< Server hostname */
 		AST_STRING_FIELD(statusmsg);   /*!< Status message for presence */
 		AST_STRING_FIELD(pubsubnode);  /*!< Pubsub node */
@@ -529,6 +542,7 @@ static ast_cond_t message_received_condition;
 static ast_mutex_t messagelock;
 
 static int xmpp_client_config_post_apply(void *obj, void *arg, int flags);
+static int fetch_access_token(struct ast_xmpp_client_config *cfg);
 
 /*! \brief Destructor function for configuration */
 static void ast_xmpp_client_config_destructor(void *obj)
@@ -761,12 +775,16 @@ static int xmpp_config_prelink(void *newitem)
 	if (ast_strlen_zero(clientcfg->user)) {
 		ast_log(LOG_ERROR, "No user specified on client '%s'\n", clientcfg->name);
 		return -1;
-	} else if (ast_strlen_zero(clientcfg->password)) {
-		ast_log(LOG_ERROR, "No password specified on client '%s'\n", clientcfg->name);
+	} else if (ast_strlen_zero(clientcfg->password) && ast_strlen_zero(clientcfg->refresh_token)) {
+		ast_log(LOG_ERROR, "No password or refresh_token specified on client '%s'\n", clientcfg->name);
 		return -1;
 	} else if (ast_strlen_zero(clientcfg->server)) {
 		ast_log(LOG_ERROR, "No server specified on client '%s'\n", clientcfg->name);
 		return -1;
+	} else if (!ast_strlen_zero(clientcfg->refresh_token) &&
+		   (ast_strlen_zero(clientcfg->oauth_clientid) || ast_strlen_zero(clientcfg->oauth_secret))) {
+		ast_log(LOG_ERROR, "No oauth_clientid or oauth_secret specified, so client '%s' can't be used\n", clientcfg->name);
+		return -1;
 	}
 
 	/* If this is a new connection force a reconnect */
@@ -778,6 +796,9 @@ static int xmpp_config_prelink(void *newitem)
 	/* If any configuration options are changing that would require reconnecting set the bit so we will do so if possible */
 	if (strcmp(clientcfg->user, oldclientcfg->user) ||
 	    strcmp(clientcfg->password, oldclientcfg->password) ||
+	    strcmp(clientcfg->refresh_token, oldclientcfg->refresh_token) ||
+	    strcmp(clientcfg->oauth_clientid, oldclientcfg->oauth_clientid) ||
+	    strcmp(clientcfg->oauth_secret, oldclientcfg->oauth_secret) ||
 	    strcmp(clientcfg->server, oldclientcfg->server) ||
 	    (clientcfg->port != oldclientcfg->port) ||
 	    (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT) != ast_test_flag(&oldclientcfg->flags, XMPP_COMPONENT)) ||
@@ -2786,7 +2807,13 @@ static int xmpp_client_authenticate_sasl(struct ast_xmpp_client *client, struct
 	}
 
 	iks_insert_attrib(auth, "xmlns", IKS_NS_XMPP_SASL);
-	iks_insert_attrib(auth, "mechanism", "PLAIN");
+	if (!ast_strlen_zero(cfg->refresh_token)) {
+		iks_insert_attrib(auth, "mechanism", "X-OAUTH2");
+		iks_insert_attrib(auth, "auth:service", "oauth2");
+		iks_insert_attrib(auth, "xmlns:auth", "http://www.google.com/talk/protocol/auth");
+	} else {
+		iks_insert_attrib(auth, "mechanism", "PLAIN");
+	}
 
 	if (strchr(client->jid->user, '/')) {
 		char *user = ast_strdupa(client->jid->user);
@@ -3285,28 +3312,28 @@ static int xmpp_ping_request(struct ast_xmpp_client *client, const char *to, con
 {
 	iks *iq, *ping;
 	int res;
-	
+
 	ast_debug(2, "JABBER: Sending Keep-Alive Ping for client '%s'\n", client->name);
 
 	if (!(iq = iks_new("iq")) || !(ping = iks_new("ping"))) {
 		iks_delete(iq);
 		return -1;
 	}
-	
+
 	iks_insert_attrib(iq, "type", "get");
 	iks_insert_attrib(iq, "to", to);
 	iks_insert_attrib(iq, "from", from);
-	
+
 	ast_xmpp_client_lock(client);
 	iks_insert_attrib(iq, "id", client->mid);
 	ast_xmpp_increment_mid(client->mid);
 	ast_xmpp_client_unlock(client);
-	
+
 	iks_insert_attrib(ping, "xmlns", "urn:xmpp:ping");
 	iks_insert_node(iq, ping);
-	
+
 	res = ast_xmpp_client_send(client, iq);
-	
+
 	iks_delete(ping);
 	iks_delete(iq);
 
@@ -3637,13 +3664,20 @@ static int xmpp_client_reconnect(struct ast_xmpp_client *client)
 		return -1;
 	}
 
+	if (!ast_strlen_zero(clientcfg->refresh_token)) {
+		ast_debug(2, "Obtaining OAuth access token for client '%s'\n", client->name);
+		if (fetch_access_token(clientcfg)) {
+			return -1;
+		}
+	}
+
 	/* If it's a component connect to user otherwise connect to server */
 	res = iks_connect_via(client->parser, S_OR(clientcfg->server, client->jid->server), clientcfg->port,
 			      ast_test_flag(&clientcfg->flags, XMPP_COMPONENT) ? clientcfg->user : client->jid->server);
 
 	/* Set socket timeout options */
 	setsockopt(iks_fd(client->parser), SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval));
-	
+
 	if (res == IKS_NET_NOCONN) {
 		ast_log(LOG_ERROR, "No XMPP connection available when trying to connect client '%s'\n", client->name);
 		return -1;
@@ -3728,7 +3762,7 @@ static int xmpp_client_receive(struct ast_xmpp_client *client, unsigned int time
 		/* Log the message here, because iksemel's logHook is
 		   unaccessible */
 		xmpp_log_hook(client, buf, len, 1);
-		
+
 		if(buf[0] == ' ') {
 			ast_debug(1, "JABBER: Detected Google Keep Alive. "
 				"Sending out Ping request for client '%s'\n", client->name);
@@ -3869,6 +3903,42 @@ static int xmpp_client_config_merge_buddies(void *obj, void *arg, int flags)
 	return 1;
 }
 
+static int fetch_access_token(struct ast_xmpp_client_config *cfg)
+{
+	RAII_VAR(char *, cmd, NULL, ast_free);
+	char cBuf[1024] = "";
+	const char *url = "https://www.googleapis.com/oauth2/v3/token";
+	struct ast_json_error error;
+	RAII_VAR(struct ast_json *, jobj, NULL, ast_json_unref);
+
+	ast_asprintf(&cmd, "CURL(%s,client_id=%s&client_secret=%s&refresh_token=%s&grant_type=refresh_token)",
+		     url, cfg->oauth_clientid, cfg->oauth_secret, cfg->refresh_token);
+
+	ast_debug(2, "Performing OAuth 2.0 authentication for client '%s' using command: %s\n",
+		cfg->name, cmd);
+
+	if (ast_func_read(NULL, cmd, cBuf, sizeof(cBuf) - 1)) {
+		ast_log(LOG_ERROR, "CURL is unavailable. This is required for OAuth 2.0 authentication of XMPP client '%s'. Please ensure it is loaded.\n",
+			cfg->name);
+		return -1;
+	}
+
+	ast_debug(2, "OAuth 2.0 authentication for client '%s' returned: %s\n", cfg->name, cBuf);
+
+	jobj = ast_json_load_string(cBuf, &error);
+	if (jobj) {
+		const char *token = ast_json_string_get(ast_json_object_get(jobj, "access_token"));
+		if (token) {
+			ast_string_field_set(cfg, password, token);
+			return 0;
+		}
+	}
+
+	ast_log(LOG_ERROR, "An error occurred while performing OAuth 2.0 authentication for client '%s': %s\n", cfg->name, cBuf);
+
+	return -1;
+}
+
 static int xmpp_client_config_post_apply(void *obj, void *arg, int flags)
 {
 	struct ast_xmpp_client_config *cfg = obj;
@@ -4622,8 +4692,8 @@ static int client_buddy_handler(const struct aco_option *opt, struct ast_variabl
  * Module loading including tests for configuration or dependencies.
  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
- * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the 
- * configuration file or other non-critical problem return 
+ * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
+ * configuration file or other non-critical problem return
  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
  */
 static int load_module(void)
@@ -4641,6 +4711,9 @@ static int load_module(void)
 
 	aco_option_register(&cfg_info, "username", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, user));
 	aco_option_register(&cfg_info, "secret", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, password));
+	aco_option_register(&cfg_info, "refresh_token", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, refresh_token));
+	aco_option_register(&cfg_info, "oauth_clientid", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, oauth_clientid));
+	aco_option_register(&cfg_info, "oauth_secret", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, oauth_secret));
 	aco_option_register(&cfg_info, "serverhost", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, server));
 	aco_option_register(&cfg_info, "statusmessage", ACO_EXACT, client_options, "Online and Available", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, statusmsg));
 	aco_option_register(&cfg_info, "pubsub_node", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, pubsubnode));
diff --git a/res/srtp/srtp_compat.h b/res/srtp/srtp_compat.h
index 4ab39f3..bf42082 100644
--- a/res/srtp/srtp_compat.h
+++ b/res/srtp/srtp_compat.h
@@ -5,7 +5,11 @@
 
 #define crypto_policy_t srtp_crypto_policy_t
 
+#if defined(SRTP_AES_ICM_128)
+#define AES_128_ICM SRTP_AES_ICM_128
+#else
 #define AES_128_ICM SRTP_AES_ICM
+#endif
 #define HMAC_SHA1 SRTP_HMAC_SHA1
 
 #define err_status_t srtp_err_status_t
diff --git a/res/stasis/control.c b/res/stasis/control.c
index b2b076b..7e8ea91 100644
--- a/res/stasis/control.c
+++ b/res/stasis/control.c
@@ -927,14 +927,21 @@ static int bridge_channel_depart(struct stasis_app_control *control,
 	return 0;
 }
 
-static void bridge_after_cb(struct ast_channel *chan, void *data)
+static void internal_bridge_after_cb(struct ast_channel *chan, void *data,
+	enum ast_bridge_after_cb_reason reason)
 {
 	struct stasis_app_control *control = data;
 	SCOPED_AO2LOCK(lock, control);
 	struct ast_bridge_channel *bridge_channel;
 
-	ast_debug(3, "%s, %s: Channel leaving bridge\n",
-		ast_channel_uniqueid(chan), control->bridge->uniqueid);
+	ast_debug(3, "%s, %s: %s\n",
+		ast_channel_uniqueid(chan), control->bridge ? control->bridge->uniqueid : "unknown",
+			ast_bridge_after_cb_reason_string(reason));
+
+	if (reason == AST_BRIDGE_AFTER_CB_REASON_IMPART_FAILED) {
+		/* The impart actually failed so control->bridge isn't valid. */
+		control->bridge = NULL;
+	}
 
 	ast_assert(chan == control->channel);
 
@@ -942,18 +949,21 @@ static void bridge_after_cb(struct ast_channel *chan, void *data)
 	ast_channel_pbx_set(control->channel, control->pbx);
 	control->pbx = NULL;
 
-	app_unsubscribe_bridge(control->app, control->bridge);
+	if (control->bridge) {
+		app_unsubscribe_bridge(control->app, control->bridge);
 
-	/* No longer in the bridge */
-	control->bridge = NULL;
+		/* No longer in the bridge */
+		control->bridge = NULL;
 
-	/* Get the bridge channel so we don't depart from the wrong bridge */
-	ast_channel_lock(chan);
-	bridge_channel = ast_channel_get_bridge_channel(chan);
-	ast_channel_unlock(chan);
+		/* Get the bridge channel so we don't depart from the wrong bridge */
+		ast_channel_lock(chan);
+		bridge_channel = ast_channel_get_bridge_channel(chan);
+		ast_channel_unlock(chan);
+
+		/* Depart this channel from the bridge using the command queue if possible */
+		stasis_app_send_command_async(control, bridge_channel_depart, bridge_channel, __ao2_cleanup);
+	}
 
-	/* Depart this channel from the bridge using the command queue if possible */
-	stasis_app_send_command_async(control, bridge_channel_depart, bridge_channel, __ao2_cleanup);
 	if (stasis_app_channel_is_stasis_end_published(chan)) {
 		/* The channel has had a StasisEnd published on it, but until now had remained in
 		 * the bridging system. This means that the channel moved from a Stasis bridge to a
@@ -971,12 +981,19 @@ static void bridge_after_cb(struct ast_channel *chan, void *data)
 	}
 }
 
+static void bridge_after_cb(struct ast_channel *chan, void *data)
+{
+	struct stasis_app_control *control = data;
+
+	internal_bridge_after_cb(control->channel, data, AST_BRIDGE_AFTER_CB_REASON_DEPART);
+}
+
 static void bridge_after_cb_failed(enum ast_bridge_after_cb_reason reason,
 	void *data)
 {
 	struct stasis_app_control *control = data;
 
-	bridge_after_cb(control->channel, data);
+	internal_bridge_after_cb(control->channel, data, reason);
 
 	ast_debug(3, "  reason: %s\n",
 		ast_bridge_after_cb_reason_string(reason));
@@ -1014,42 +1031,53 @@ int control_swap_channel_in_bridge(struct stasis_app_control *control, struct as
 		return -1;
 	}
 
-	{
-		/* pbx and bridge are modified by the bridging impart thread.
-		 * It shouldn't happen concurrently, but we still need to lock
-		 * for the memory fence.
-		 */
-		SCOPED_AO2LOCK(lock, control);
+	ao2_lock(control);
 
-		/* Ensure the controlling application is subscribed early enough
-		 * to receive the ChannelEnteredBridge message. This works in concert
-		 * with the subscription handled in the Stasis application execution
-		 * loop */
-		app_subscribe_bridge(control->app, bridge);
-
-		/* Save off the channel's PBX */
-		ast_assert(control->pbx == NULL);
-		if (!control->pbx) {
-			control->pbx = ast_channel_pbx(chan);
-			ast_channel_pbx_set(chan, NULL);
-		}
+	/* Ensure the controlling application is subscribed early enough
+	 * to receive the ChannelEnteredBridge message. This works in concert
+	 * with the subscription handled in the Stasis application execution
+	 * loop */
+	app_subscribe_bridge(control->app, bridge);
 
-		res = ast_bridge_impart(bridge,
-			chan,
-			swap,
-			NULL, /* features */
-			AST_BRIDGE_IMPART_CHAN_DEPARTABLE);
-		if (res != 0) {
-			ast_log(LOG_ERROR, "Error adding channel to bridge\n");
-			ast_channel_pbx_set(chan, control->pbx);
-			control->pbx = NULL;
-			return -1;
-		}
+	/* Save off the channel's PBX */
+	ast_assert(control->pbx == NULL);
+	if (!control->pbx) {
+		control->pbx = ast_channel_pbx(chan);
+		ast_channel_pbx_set(chan, NULL);
+	}
 
-		ast_assert(stasis_app_get_bridge(control) == NULL);
-		control->bridge = bridge;
+	ast_assert(stasis_app_get_bridge(control) == NULL);
+	/* We need to set control->bridge here since bridge_after_cb may be run
+	 * before ast_bridge_impart returns.  bridge_after_cb gets a reason
+	 * code so it can tell if the bridge is actually valid or not.
+	 */
+	control->bridge = bridge;
+
+	/* We can't be holding the control lock while impart is running
+	 * or we could create a deadlock with bridge_after_cb which also
+	 * tries to lock control.
+	 */
+	ao2_unlock(control);
+	res = ast_bridge_impart(bridge,
+		chan,
+		swap,
+		NULL, /* features */
+		AST_BRIDGE_IMPART_CHAN_DEPARTABLE);
+	if (res != 0) {
+		/* ast_bridge_impart failed before it could spawn the depart
+		 * thread.  The callbacks aren't called in this case.
+		 * The impart could still fail even if ast_bridge_impart returned
+		 * ok but that's handled by bridge_after_cb.
+		 */
+		ast_log(LOG_ERROR, "Error adding channel to bridge\n");
+		ao2_lock(control);
+		ast_channel_pbx_set(chan, control->pbx);
+		control->pbx = NULL;
+		control->bridge = NULL;
+		ao2_unlock(control);
 	}
-	return 0;
+
+	return res;
 }
 
 int control_add_channel_to_bridge(struct stasis_app_control *control, struct ast_channel *chan, void *data)
diff --git a/sounds/Makefile b/sounds/Makefile
index 84d0f45..381776f 100644
--- a/sounds/Makefile
+++ b/sounds/Makefile
@@ -19,13 +19,14 @@ CMD_PREFIX?=@
 SOUNDS_DIR:=$(DESTDIR)$(ASTDATADIR)/sounds
 SOUNDS_CACHE_DIR?=
 MOH_DIR:=$(DESTDIR)$(ASTDATADIR)/moh
-CORE_SOUNDS_VERSION:=1.5
-EXTRA_SOUNDS_VERSION:=1.5
+CORE_SOUNDS_VERSION:=1.6
+EXTRA_SOUNDS_VERSION:=1.5.1
 MOH_VERSION:=2.03
 SOUNDS_URL:=http://downloads.asterisk.org/pub/telephony/sounds/releases
 MCS:=$(subst -EN-,-en-,$(MENUSELECT_CORE_SOUNDS))
 MCS:=$(subst -EN_AU-,-en_AU-,$(MCS))
 MCS:=$(subst -EN_GB-,-en_GB-,$(MCS))
+MCS:=$(subst -EN_NZ-,-en_NZ-,$(MCS))
 MCS:=$(subst -FR-,-fr-,$(MCS))
 MCS:=$(subst -ES-,-es-,$(MCS))
 MCS:=$(subst -RU-,-ru-,$(MCS))
@@ -144,6 +145,8 @@ $(eval $(call sound_format_lang_rule,$(SOUNDS_DIR),core-sounds,en_AU,$(CORE_SOUN
 
 $(eval $(call sound_format_lang_rule,$(SOUNDS_DIR),core-sounds,en_GB,$(CORE_SOUNDS_VERSION)))
 
+$(eval $(call sound_format_lang_rule,$(SOUNDS_DIR),core-sounds,en_NZ,$(CORE_SOUNDS_VERSION)))
+
 $(eval $(call sound_format_lang_rule,$(SOUNDS_DIR),core-sounds,es,$(CORE_SOUNDS_VERSION)))
 
 $(eval $(call sound_format_lang_rule,$(SOUNDS_DIR),core-sounds,fr,$(CORE_SOUNDS_VERSION)))
diff --git a/sounds/sounds.xml b/sounds/sounds.xml
index 547be4b..2d996c5 100644
--- a/sounds/sounds.xml
+++ b/sounds/sounds.xml
@@ -81,6 +81,33 @@
 		<member name="CORE-SOUNDS-EN_GB-SIREN14" displayname="English (British Accent), G.722.1C (Siren14) format">
 			<support_level>core</support_level>
 		</member>
+		<member name="CORE-SOUNDS-EN_NZ-WAV" displayname="English (New Zealand Accent), WAV format">
+			<support_level>core</support_level>
+		</member>
+		<member name="CORE-SOUNDS-EN_NZ-ULAW" displayname="English (New Zealand Accent), mu-Law format">
+			<support_level>core</support_level>
+		</member>
+		<member name="CORE-SOUNDS-EN_NZ-ALAW" displayname="English (New Zealand Accent), a-Law format">
+			<support_level>core</support_level>
+		</member>
+		<member name="CORE-SOUNDS-EN_NZ-GSM" displayname="English (New Zealand Accent), GSM format">
+			<support_level>core</support_level>
+		</member>
+		<member name="CORE-SOUNDS-EN_NZ-G729" displayname="English (New Zealand Accent), G.729 format">
+			<support_level>core</support_level>
+		</member>
+		<member name="CORE-SOUNDS-EN_NZ-G722" displayname="English (New Zealand Accent), G.722 format">
+			<support_level>core</support_level>
+		</member>
+		<member name="CORE-SOUNDS-EN_NZ-SLN16" displayname="English (New Zealand Accent), Signed-linear 16kHz format">
+			<support_level>core</support_level>
+		</member>
+		<member name="CORE-SOUNDS-EN_NZ-SIREN7" displayname="English (New Zealand Accent), G.722.1 (Siren7) format">
+			<support_level>core</support_level>
+		</member>
+		<member name="CORE-SOUNDS-EN_NZ-SIREN14" displayname="English (New Zealand Accent), G.722.1C (Siren14) format">
+			<support_level>core</support_level>
+		</member>
 		<member name="CORE-SOUNDS-ES-WAV" displayname="Spanish, WAV format">
 			<support_level>core</support_level>
 		</member>
diff --git a/tests/test_config.c b/tests/test_config.c
index fd14908..8fb4735 100644
--- a/tests/test_config.c
+++ b/tests/test_config.c
@@ -43,6 +43,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
 #include "asterisk/config_options.h"
 #include "asterisk/netsock2.h"
 #include "asterisk/acl.h"
+#include "asterisk/app.h"
 #include "asterisk/pbx.h"
 #include "asterisk/frame.h"
 #include "asterisk/utils.h"
@@ -1039,6 +1040,7 @@ AST_TEST_DEFINE(config_hook)
 	res = AST_TEST_PASS;
 
 out:
+	ast_config_hook_unregister("test_hook");
 	delete_config_file();
 	return res;
 }
@@ -1082,6 +1084,13 @@ enum {
 				ast_test_status_update(test, "ast_parse_arg double failed with %f != %f\n", *r, e); \
 				ret = AST_TEST_FAIL; \
 			} \
+		} else if (((flags) & PARSE_TYPE) == PARSE_TIMELEN) { \
+			int *r = (int *) (void *) result; \
+			int e = (int) expected_result; \
+			if (*r != e) { \
+				ast_test_status_update(test, "ast_parse_arg timelen failed with %d != %d\n", *r, e); \
+				ret = AST_TEST_FAIL; \
+			} \
 		} \
 	} \
 	*(result) = DEFAULTVAL; \
@@ -1092,6 +1101,7 @@ AST_TEST_DEFINE(ast_parse_arg_test)
 	int ret = AST_TEST_PASS;
 	int32_t int32_t_val = DEFAULTVAL;
 	uint32_t uint32_t_val = DEFAULTVAL;
+	int timelen_val = DEFAULTVAL;
 	double double_val = DEFAULTVAL;
 
 	switch (cmd) {
@@ -1224,6 +1234,60 @@ AST_TEST_DEFINE(ast_parse_arg_test)
 
 	TEST_PARSE("   -123", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32, &uint32_t_val);
 
+	/* timelen testing */
+	TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_TIMELEN, &timelen_val, TIMELEN_MILLISECONDS);
+	TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_TIMELEN, &timelen_val, TIMELEN_MILLISECONDS);
+	TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_TIMELEN, &timelen_val, TIMELEN_MILLISECONDS);
+	TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN, &timelen_val, TIMELEN_MILLISECONDS);
+	TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN, &timelen_val, TIMELEN_MILLISECONDS);
+
+	TEST_PARSE("123s", EXPECT_SUCCEED, 123000, PARSE_TIMELEN, &timelen_val, TIMELEN_MILLISECONDS);
+	TEST_PARSE("-123s", EXPECT_SUCCEED, -123000, PARSE_TIMELEN, &timelen_val, TIMELEN_MILLISECONDS);
+	TEST_PARSE("1m", EXPECT_SUCCEED, 60000, PARSE_TIMELEN, &timelen_val, TIMELEN_MILLISECONDS);
+	TEST_PARSE("1", EXPECT_SUCCEED, 60000, PARSE_TIMELEN, &timelen_val, TIMELEN_MINUTES);
+	TEST_PARSE("1h", EXPECT_SUCCEED, 3600000, PARSE_TIMELEN, &timelen_val, TIMELEN_MILLISECONDS);
+	TEST_PARSE("1", EXPECT_SUCCEED, 3600000, PARSE_TIMELEN, &timelen_val, TIMELEN_HOURS);
+
+	TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_TIMELEN | PARSE_DEFAULT, &timelen_val, TIMELEN_MILLISECONDS, 7);
+	TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_TIMELEN | PARSE_DEFAULT, &timelen_val, TIMELEN_MILLISECONDS, 7);
+	TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_TIMELEN | PARSE_DEFAULT, &timelen_val, TIMELEN_MILLISECONDS, 7);
+	TEST_PARSE("not a number", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT, &timelen_val, TIMELEN_MILLISECONDS, 7);
+	TEST_PARSE("7not a number", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT, &timelen_val, TIMELEN_MILLISECONDS, 7);
+
+	TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_TIMELEN | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 0, 200);
+	TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_TIMELEN | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, -200, 100);
+	TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_TIMELEN | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, -1, 0);
+	TEST_PARSE("123", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 0, 122);
+	TEST_PARSE("-123", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, -122, 100);
+	TEST_PARSE("0", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 1, 100);
+	TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, INT_MIN, INT_MAX);
+	TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, INT_MIN, INT_MAX);
+	TEST_PARSE("123", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 0, 200);
+	TEST_PARSE("-123", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, -200, 100);
+	TEST_PARSE("0", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, -1, 0);
+	TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_TIMELEN | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 0, 122);
+	TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_TIMELEN | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, -122, 100);
+	TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_TIMELEN | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 1, 100);
+	TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, INT_MIN, INT_MAX);
+	TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, INT_MIN, INT_MAX);
+
+	TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, 0, 200);
+	TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, -200, 100);
+	TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, -1, 0);
+	TEST_PARSE("123", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, 0, 122);
+	TEST_PARSE("-123", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, -122, 100);
+	TEST_PARSE("0", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, 1, 100);
+	TEST_PARSE("not a number", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, INT_MIN, INT_MAX);
+	TEST_PARSE("7not a number", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, INT_MIN, INT_MAX);
+	TEST_PARSE("123", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, 0, 200);
+	TEST_PARSE("-123", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, -200, 100);
+	TEST_PARSE("0", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, -1, 0);
+	TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, 0, 122);
+	TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, -122, 100);
+	TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, 1, 100);
+	TEST_PARSE("not a number", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, INT_MIN, INT_MAX);
+	TEST_PARSE("7not a number", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, INT_MIN, INT_MAX);
+
 	/* double testing */
 	TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_DOUBLE, &double_val);
 	TEST_PARSE("123.123", EXPECT_SUCCEED, 123.123, PARSE_DOUBLE, &double_val);
@@ -1283,6 +1347,10 @@ struct test_item {
 	);
 	int32_t intopt;
 	uint32_t uintopt;
+	int timelenopt1;
+	int timelenopt2;
+	int timelenopt3;
+	int timelenopt4;
 	unsigned int flags;
 	double doubleopt;
 	struct ast_sockaddr sockaddropt;
@@ -1437,6 +1505,8 @@ AST_TEST_DEFINE(config_options_test)
 #define INT_CONFIG "-1"
 #define UINT_DEFAULT "2"
 #define UINT_CONFIG "1"
+#define TIMELEN_DEFAULT "2"
+#define TIMELEN_CONFIG "1"
 #define DOUBLE_DEFAULT "1.1"
 #define DOUBLE_CONFIG "0.1"
 #define SOCKADDR_DEFAULT "4.3.2.1:4321"
@@ -1471,6 +1541,10 @@ AST_TEST_DEFINE(config_options_test)
 	/* Register all options */
 	aco_option_register(&cfg_info, "intopt", ACO_EXACT, config_test_conf.types, INT_DEFAULT, OPT_INT_T, 0, FLDSET(struct test_item, intopt));
 	aco_option_register(&cfg_info, "uintopt", ACO_EXACT, config_test_conf.types, UINT_DEFAULT, OPT_UINT_T, 0, FLDSET(struct test_item, uintopt));
+	aco_option_register(&cfg_info, "timelenopt1", ACO_EXACT, config_test_conf.types, TIMELEN_DEFAULT, OPT_TIMELEN_T, 0, FLDSET(struct test_item, timelenopt1), TIMELEN_MILLISECONDS);
+	aco_option_register(&cfg_info, "timelenopt2", ACO_EXACT, config_test_conf.types, TIMELEN_DEFAULT, OPT_TIMELEN_T, 0, FLDSET(struct test_item, timelenopt2), TIMELEN_SECONDS);
+	aco_option_register(&cfg_info, "timelenopt3", ACO_EXACT, config_test_conf.types, TIMELEN_DEFAULT, OPT_TIMELEN_T, 0, FLDSET(struct test_item, timelenopt3), TIMELEN_MINUTES);
+	aco_option_register(&cfg_info, "timelenopt4", ACO_EXACT, config_test_conf.types, TIMELEN_DEFAULT, OPT_TIMELEN_T, 0, FLDSET(struct test_item, timelenopt4), TIMELEN_HOURS);
 	aco_option_register(&cfg_info, "doubleopt", ACO_EXACT, config_test_conf.types, DOUBLE_DEFAULT, OPT_DOUBLE_T, 0, FLDSET(struct test_item, doubleopt));
 	aco_option_register(&cfg_info, "sockaddropt", ACO_EXACT, config_test_conf.types, SOCKADDR_DEFAULT, OPT_SOCKADDR_T, 0, FLDSET(struct test_item, sockaddropt));
 	aco_option_register(&cfg_info, "boolopt", ACO_EXACT, config_test_conf.types, BOOL_DEFAULT, OPT_BOOL_T, 1, FLDSET(struct test_item, boolopt));
@@ -1492,6 +1566,14 @@ AST_TEST_DEFINE(config_options_test)
 
 	ast_parse_arg(INT_DEFAULT, PARSE_INT32, &defaults.intopt);
 	ast_parse_arg(INT_CONFIG, PARSE_INT32, &configs.intopt);
+	ast_parse_arg(TIMELEN_DEFAULT, PARSE_TIMELEN, &defaults.timelenopt1, TIMELEN_MILLISECONDS);
+	ast_parse_arg(TIMELEN_CONFIG, PARSE_TIMELEN, &configs.timelenopt1, TIMELEN_MILLISECONDS);
+	ast_parse_arg(TIMELEN_DEFAULT, PARSE_TIMELEN, &defaults.timelenopt2, TIMELEN_SECONDS);
+	ast_parse_arg(TIMELEN_CONFIG, PARSE_TIMELEN, &configs.timelenopt2, TIMELEN_SECONDS);
+	ast_parse_arg(TIMELEN_DEFAULT, PARSE_TIMELEN, &defaults.timelenopt3, TIMELEN_MINUTES);
+	ast_parse_arg(TIMELEN_CONFIG, PARSE_TIMELEN, &configs.timelenopt3, TIMELEN_MINUTES);
+	ast_parse_arg(TIMELEN_DEFAULT, PARSE_TIMELEN, &defaults.timelenopt4, TIMELEN_HOURS);
+	ast_parse_arg(TIMELEN_CONFIG, PARSE_TIMELEN, &configs.timelenopt4, TIMELEN_HOURS);
 	ast_parse_arg(UINT_DEFAULT, PARSE_UINT32, &defaults.uintopt);
 	ast_parse_arg(UINT_CONFIG, PARSE_UINT32, &configs.uintopt);
 	ast_parse_arg(DOUBLE_DEFAULT, PARSE_DOUBLE, &defaults.doubleopt);
@@ -1553,6 +1635,10 @@ AST_TEST_DEFINE(config_options_test)
 
 		NOT_EQUAL_FAIL(intopt, "%d");
 		NOT_EQUAL_FAIL(uintopt, "%u");
+		NOT_EQUAL_FAIL(timelenopt1, "%d");
+		NOT_EQUAL_FAIL(timelenopt2, "%d");
+		NOT_EQUAL_FAIL(timelenopt3, "%d");
+		NOT_EQUAL_FAIL(timelenopt4, "%d");
 		NOT_EQUAL_FAIL(boolopt, "%d");
 		NOT_EQUAL_FAIL(flags, "%u");
 		NOT_EQUAL_FAIL(customopt, "%d");
@@ -1592,6 +1678,8 @@ AST_TEST_DEFINE(config_options_test)
 	configs.codeccapopt = NULL;
 	ast_string_field_free_memory(&defaults);
 	ast_string_field_free_memory(&configs);
+	aco_info_destroy(&cfg_info);
+	ao2_global_obj_release(global_obj);
 	return res;
 }
 
diff --git a/tests/test_core_format.c b/tests/test_core_format.c
index a3819c6..e4199db 100644
--- a/tests/test_core_format.c
+++ b/tests/test_core_format.c
@@ -860,6 +860,7 @@ AST_TEST_DEFINE(format_attribute_set_without_interface)
 {
 	RAII_VAR(struct ast_codec *, codec, NULL, ao2_cleanup);
 	RAII_VAR(struct ast_format *, format, NULL, ao2_cleanup);
+	struct ast_format *attr_set;
 
 	switch (cmd) {
 	case TEST_INIT:
@@ -885,10 +886,12 @@ AST_TEST_DEFINE(format_attribute_set_without_interface)
 		return AST_TEST_FAIL;
 	}
 
-	if (!ast_format_attribute_set(format, "bees", "cool")) {
+	attr_set = ast_format_attribute_set(format, "bees", "cool");
+	if (!attr_set) {
 		ast_test_status_update(test, "Successfully set an attribute on a format without an interface\n");
 		return AST_TEST_FAIL;
 	}
+	ao2_cleanup(attr_set);
 
 	return AST_TEST_PASS;
 }
diff --git a/tests/test_taskprocessor.c b/tests/test_taskprocessor.c
index be48f92..ad2074c 100644
--- a/tests/test_taskprocessor.c
+++ b/tests/test_taskprocessor.c
@@ -677,7 +677,7 @@ AST_TEST_DEFINE(taskprocessor_push_local)
 {
 	RAII_VAR(struct ast_taskprocessor *, tps, NULL,
 		ast_taskprocessor_unreference);
-	struct task_data *task_data;
+	RAII_VAR(struct task_data *, task_data, NULL, ao2_cleanup);
 	int local_data;
 	int res;
 
diff --git a/tests/test_vector.c b/tests/test_vector.c
index 8e0d121..2dfcc60 100644
--- a/tests/test_vector.c
+++ b/tests/test_vector.c
@@ -210,7 +210,7 @@ AST_TEST_DEFINE(basic_ops)
 	ast_test_validate_cleanup(test, AST_VECTOR_ADD_SORTED(&sv1, ZZZ, strcmp) == 0, rc, cleanup);
 	ast_test_validate_cleanup(test, AST_VECTOR_ADD_SORTED(&sv1, CCC, strcmp) == 0, rc, cleanup);
 	ast_test_validate_cleanup(test, AST_VECTOR_ADD_SORTED(&sv1, AAA, strcmp) == 0, rc, cleanup);
-	ast_test_validate_cleanup(test, AST_VECTOR_ADD_SORTED(&sv1, CCC2, strcmp) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_ADD_SORTED(&sv1, (char*)CCC2, strcmp) == 0, rc, cleanup);
 
 	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 0) == AAA, rc, cleanup);
 	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 1) == BBB, rc, cleanup);
diff --git a/third-party/pjproject/Makefile b/third-party/pjproject/Makefile
index a5b5508..7a42edc 100644
--- a/third-party/pjproject/Makefile
+++ b/third-party/pjproject/Makefile
@@ -86,6 +86,11 @@ SHELL_ECHO_PREFIX := echo '[pjproject] '
 
 _all: $(TARGETS)
 
+define tarball_exists
+	(if [ -f $(TARBALL) -a -f $(PJMD5SUM) ] ; then exit 0 ;\
+	else exit 1; fi; )
+endef
+
 define verify_tarball
 	($(SHELL_ECHO_PREFIX) Verifying $(TARBALL) &&\
 	tarball_sum=$$($(CAT) $(TARBALL) | $(MD5) | $(SED) -n -r -e "s/^([^ ]+)\s+.*/\1/gp") ;\
@@ -111,11 +116,12 @@ TARBALL_URL = $(PJPROJECT_URL)/$(TARBALL_FILE)
 PJMD5SUM = $(patsubst %.tar.bz2,%.md5,$(TARBALL))
 
 $(TARBALL): ../versions.mak
-	$(CMD_PREFIX) $(download_from_pjproject) || (rm -rf $@ ;\
+	$(CMD_PREFIX) ($(tarball_exists) && $(verify_tarball) && touch $@) || (rm -rf $@ ;\
+	$(download_from_pjproject)) || (rm -rf $@ ;\
 	$(SHELL_ECHO_PREFIX) Retrying download ; $(download_from_pjproject))
 
 source/.unpacked: $(DOWNLOAD_DIR)/pjproject-$(PJPROJECT_VERSION).tar.bz2
-	($(verify_tarball)) || (rm -rf $@ ;\
+	$(CMD_PREFIX) $(verify_tarball) || (rm -rf $@ ;\
 	$(SHELL_ECHO_PREFIX) Retrying download ; $(download_from_pjproject))
 	$(ECHO_PREFIX) Unpacking $<
 	- at rm -rf source pjproject-* >/dev/null 2>&1
diff --git a/third-party/pjproject/configure.m4 b/third-party/pjproject/configure.m4
index 709a706..2d33534 100644
--- a/third-party/pjproject/configure.m4
+++ b/third-party/pjproject/configure.m4
@@ -49,11 +49,11 @@ AC_DEFUN([_PJPROJECT_CONFIGURE],
 		PJPROJECT_CONFIGURE_OPTS+=" --host=$host"
 	fi
 
-	export TAR PATCH SED NM EXTERNALS_CACHE_DIR DOWNLOAD_TO_STDOUT DOWNLOAD_TIMEOUT DOWNLOAD MD5 CAT
+	export TAR PATCH SED NM EXTERNALS_CACHE_DIR AST_DOWNLOAD_CACHE DOWNLOAD_TO_STDOUT DOWNLOAD_TIMEOUT DOWNLOAD MD5 CAT
 	export NOISY_BUILD
 	${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} \
 		PJPROJECT_CONFIGURE_OPTS="$PJPROJECT_CONFIGURE_OPTS" \
-		EXTERNALS_CACHE_DIR="${EXTERNALS_CACHE_DIR}" \
+		EXTERNALS_CACHE_DIR="${EXTERNALS_CACHE_DIR:-${AST_DOWNLOAD_CACHE}}" \
 		configure
 	if test $? -ne 0 ; then
 		AC_MSG_RESULT(failed)
@@ -63,7 +63,7 @@ AC_DEFUN([_PJPROJECT_CONFIGURE],
 
 	AC_MSG_CHECKING(for bundled pjproject)
 
-	PJPROJECT_INCLUDE=$(${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} PJPROJECT_CONFIGURE_OPTS="$PJPROJECT_CONFIGURE_OPTS" EXTERNALS_CACHE_DIR="${EXTERNALS_CACHE_DIR}" echo_cflags)
+	PJPROJECT_INCLUDE=$(${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} PJPROJECT_CONFIGURE_OPTS="$PJPROJECT_CONFIGURE_OPTS" EXTERNALS_CACHE_DIR="${EXTERNALS_CACHE_DIR:-${AST_DOWNLOAD_CACHE}}" echo_cflags)
 	PJPROJECT_CFLAGS="$PJPROJECT_INCLUDE"
 	PBX_PJPROJECT=1
 
diff --git a/third-party/pjproject/patches/0075-Fixed-2030-Improve-error-handling-in-OpenSSL-socket.patch b/third-party/pjproject/patches/0075-Fixed-2030-Improve-error-handling-in-OpenSSL-socket.patch
new file mode 100644
index 0000000..1e7035d
--- /dev/null
+++ b/third-party/pjproject/patches/0075-Fixed-2030-Improve-error-handling-in-OpenSSL-socket.patch
@@ -0,0 +1,247 @@
+From 96c06899d95eaf01d05561554b21e8c63baa7129 Mon Sep 17 00:00:00 2001
+From: ming <ming at localhost>
+Date: Thu, 27 Jul 2017 06:07:54 +0000
+Subject: [PATCH 75/76] Fixed #2030: Improve error handling in OpenSSL socket
+
+---
+ pjlib/src/pj/ssl_sock_ossl.c | 173 ++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 156 insertions(+), 17 deletions(-)
+
+diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c
+index c466b3c..b8175e1 100644
+--- a/pjlib/src/pj/ssl_sock_ossl.c
++++ b/pjlib/src/pj/ssl_sock_ossl.c
+@@ -298,14 +298,104 @@ static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock);
+ /* Expected maximum value of reason component in OpenSSL error code */
+ #define MAX_OSSL_ERR_REASON		1200
+ 
+-static pj_status_t STATUS_FROM_SSL_ERR(pj_ssl_sock_t *ssock,
+-				       unsigned long err)
++
++static char *SSLErrorString (int err)
+ {
+-    pj_status_t status;
++    switch (err) {
++    case SSL_ERROR_NONE:
++	return "SSL_ERROR_NONE";
++    case SSL_ERROR_ZERO_RETURN:
++	return "SSL_ERROR_ZERO_RETURN";
++    case SSL_ERROR_WANT_READ:
++	return "SSL_ERROR_WANT_READ";
++    case SSL_ERROR_WANT_WRITE:
++	return "SSL_ERROR_WANT_WRITE";
++    case SSL_ERROR_WANT_CONNECT:
++	return "SSL_ERROR_WANT_CONNECT";
++    case SSL_ERROR_WANT_ACCEPT:
++	return "SSL_ERROR_WANT_ACCEPT";
++    case SSL_ERROR_WANT_X509_LOOKUP:
++	return "SSL_ERROR_WANT_X509_LOOKUP";
++    case SSL_ERROR_SYSCALL:
++	return "SSL_ERROR_SYSCALL";
++    case SSL_ERROR_SSL:
++	return "SSL_ERROR_SSL";
++    default:
++	return "SSL_ERROR_UNKNOWN";
++    }
++}
+ 
+-    /* General SSL error, dig more from OpenSSL error queue */
+-    if (err == SSL_ERROR_SSL)
+-	err = ERR_get_error();
++#define ERROR_LOG(msg, err) \
++    PJ_LOG(2,("SSL", "%s (%s): Level: %d err: <%lu> <%s-%s-%s> len: %d", \
++	      msg, action, level, err, \
++	      (ERR_lib_error_string(err)? ERR_lib_error_string(err): "???"), \
++	      (ERR_func_error_string(err)? ERR_func_error_string(err):"???"),\
++	      (ERR_reason_error_string(err)? \
++	       ERR_reason_error_string(err): "???"), len));
++
++static void SSLLogErrors(char * action, int ret, int ssl_err, int len)
++{
++    char *ssl_err_str = SSLErrorString(ssl_err);
++
++    if (!action) {
++	action = "UNKNOWN";
++    }
++
++    switch (ssl_err) {
++    case SSL_ERROR_SYSCALL:
++    {
++	unsigned long err2 = ERR_get_error();
++	if (err2) {
++	    int level = 0;
++	    while (err2) {
++	        ERROR_LOG("SSL_ERROR_SYSCALL", err2);
++		level++;
++		err2 = ERR_get_error();
++	    }
++	} else if (ret == 0) {
++	    /* An EOF was observed that violates the protocol */
++
++	    /* The TLS/SSL handshake was not successful but was shut down
++	     * controlled and by the specifications of the TLS/SSL protocol.
++	     */
++	} else if (ret == -1) {
++	    /* BIO error - look for more info in errno... */
++	    char errStr[250] = "";
++	    strerror_r(errno, errStr, sizeof(errStr));
++	    /* for now - continue logging these if they occur.... */
++	    PJ_LOG(4,("SSL", "BIO error, SSL_ERROR_SYSCALL (%s): "
++	    		     "errno: <%d> <%s> len: %d",
++		      	     action, errno, errStr, len));
++	} else {
++	    /* ret!=0 & ret!=-1 & nothing on error stack - is this valid??? */
++	    PJ_LOG(2,("SSL", "SSL_ERROR_SYSCALL (%s) ret: %d len: %d",
++		      action, ret, len));
++	}
++	break;
++    }
++    case SSL_ERROR_SSL:
++    {
++	unsigned long err2 = ERR_get_error();
++	int level = 0;
++
++	while (err2) {
++	    ERROR_LOG("SSL_ERROR_SSL", err2);
++	    level++;
++	    err2 = ERR_get_error();
++	}
++	break;
++    }
++    default:
++	PJ_LOG(2,("SSL", "%lu [%s] (%s) ret: %d len: %d",
++		  ssl_err, ssl_err_str, action, ret, len));
++	break;
++    }
++}
++
++
++static pj_status_t GET_STATUS_FROM_SSL_ERR(unsigned long err)
++{
++    pj_status_t status;
+ 
+     /* OpenSSL error range is much wider than PJLIB errno space, so
+      * if it exceeds the space, only the error reason will be kept.
+@@ -317,13 +407,49 @@ static pj_status_t STATUS_FROM_SSL_ERR(pj_ssl_sock_t *ssock,
+ 	status = ERR_GET_REASON(err);
+ 
+     status += PJ_SSL_ERRNO_START;
+-    ssock->last_err = err;
+     return status;
+ }
+ 
++/* err contains ERR_get_error() status */
++static pj_status_t STATUS_FROM_SSL_ERR(char *action, pj_ssl_sock_t *ssock,
++				       unsigned long err)
++{
++    int level = 0;
++    int len = 0; //dummy
++
++    ERROR_LOG("STATUS_FROM_SSL_ERR", err);
++    level++;
++
++    /* General SSL error, dig more from OpenSSL error queue */
++    if (err == SSL_ERROR_SSL) {
++	err = ERR_get_error();
++	ERROR_LOG("STATUS_FROM_SSL_ERR", err);
++    }
++
++    ssock->last_err = err;
++    return GET_STATUS_FROM_SSL_ERR(err);
++}
++
++/* err contains SSL_get_error() status */
++static pj_status_t STATUS_FROM_SSL_ERR2(char *action, pj_ssl_sock_t *ssock,
++					int ret, int err, int len)
++{
++    unsigned long ssl_err = err;
++
++    if (err == SSL_ERROR_SSL) {
++	ssl_err = ERR_peek_error();
++    }
++
++    /* Dig for more from OpenSSL error queue */
++    SSLLogErrors(action, ret, err, len);
++
++    ssock->last_err = ssl_err;
++    return GET_STATUS_FROM_SSL_ERR(ssl_err);
++}
++
+ static pj_status_t GET_SSL_STATUS(pj_ssl_sock_t *ssock)
+ {
+-    return STATUS_FROM_SSL_ERR(ssock, ERR_get_error());
++    return STATUS_FROM_SSL_ERR("status", ssock, ERR_get_error());
+ }
+ 
+ 
+@@ -1514,7 +1640,7 @@ static pj_bool_t on_handshake_complete(pj_ssl_sock_t *ssock,
+ 		unsigned long err;
+ 		err = ERR_get_error();
+ 		if (err != SSL_ERROR_NONE)
+-		    status = STATUS_FROM_SSL_ERR(ssock, err);
++		    status = STATUS_FROM_SSL_ERR("connecting", ssock, err);
+ 	    }
+ 	    reset_ssl_sock_state(ssock);
+ 	}
+@@ -1833,11 +1959,11 @@ static pj_status_t do_handshake(pj_ssl_sock_t *ssock)
+     }
+ 
+     if (err < 0) {
+-	err = SSL_get_error(ssock->ossl_ssl, err);
+-	if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ) 
++	int err2 = SSL_get_error(ssock->ossl_ssl, err);
++	if (err2 != SSL_ERROR_NONE && err2 != SSL_ERROR_WANT_READ)
+ 	{
+ 	    /* Handshake fails */
+-	    status = STATUS_FROM_SSL_ERR(ssock, err);
++	    status = STATUS_FROM_SSL_ERR2("Handshake", ssock, err, err2, 0);
+ 	    return status;
+ 	}
+     }
+@@ -1913,6 +2039,7 @@ static pj_bool_t asock_on_data_read (pj_activesock_t *asock,
+ 	    read_data_t *buf = *(OFFSET_OF_READ_DATA_PTR(ssock, data));
+ 	    void *data_ = (pj_int8_t*)buf->data + buf->len;
+ 	    int size_ = (int)(ssock->read_size - buf->len);
++	    int len = size_;
+ 
+ 	    /* SSL_read() may write some data to BIO write when re-negotiation
+ 	     * is on progress, so let's protect it with write mutex.
+@@ -1965,10 +2092,22 @@ static pj_bool_t asock_on_data_read (pj_activesock_t *asock,
+ 		 */
+ 		if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ)
+ 		{
+-		    /* Reset SSL socket state, then return PJ_FALSE */
+-		    status = STATUS_FROM_SSL_ERR(ssock, err);
+-		    reset_ssl_sock_state(ssock);
+-		    goto on_error;
++		    if (err == SSL_ERROR_SYSCALL && size_ == -1 &&
++			ERR_peek_error() == 0 && errno == 0)
++		    {
++			status = STATUS_FROM_SSL_ERR2("Read", ssock, size_,
++						      err, len);
++			PJ_LOG(4,("SSL", "SSL_read() = -1, with "
++				  	 "SSL_ERROR_SYSCALL, no SSL error, "
++				  	 "and errno = 0 - skip BIO error"));
++		        /* Ignore these errors */
++		    } else {
++		        /* Reset SSL socket state, then return PJ_FALSE */
++		        status = STATUS_FROM_SSL_ERR2("Read", ssock, size_,
++		        			      err, len);
++		        reset_ssl_sock_state(ssock);
++		        goto on_error;
++		    }
+ 		}
+ 
+ 		status = do_handshake(ssock);
+@@ -2856,7 +2995,7 @@ static pj_status_t ssl_write(pj_ssl_sock_t *ssock,
+ 		status = PJ_EBUSY;
+ 	} else {
+ 	    /* Some problem occured */
+-	    status = STATUS_FROM_SSL_ERR(ssock, err);
++	    status = STATUS_FROM_SSL_ERR2("Write", ssock, nwritten, err, size);
+ 	}
+     } else {
+ 	/* nwritten < *size, shouldn't happen, unless write BIO cannot hold 
+-- 
+2.9.4
+
diff --git a/third-party/pjproject/patches/0080-STUN-Fingerprint-with-ICE.patch b/third-party/pjproject/patches/0080-STUN-Fingerprint-with-ICE.patch
new file mode 100644
index 0000000..96d44fa
--- /dev/null
+++ b/third-party/pjproject/patches/0080-STUN-Fingerprint-with-ICE.patch
@@ -0,0 +1,35 @@
+From 28490e9ddee0937516f9edcaf95d274fe5ceaf4c Mon Sep 17 00:00:00 2001
+From: Sean Bright <sean.bright at gmail.com>
+Date: Mon, 25 Sep 2017 14:06:53 -0400
+Subject: [PATCH] ICE: Use STUN FINGERPRINT attribute when sending keepalives
+
+Per RFC 5245 Section 10:
+
+   If STUN is being used for keepalives, a STUN Binding Indication is
+   used [RFC5389].  The Indication MUST NOT utilize any authentication
+   mechanism.  It SHOULD contain the FINGERPRINT attribute to aid in
+   demultiplexing, but SHOULD NOT contain any other attributes.
+---
+ pjnath/src/pjnath/ice_session.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c
+index 159d7b1..f90005a 100644
+--- a/pjnath/src/pjnath/ice_session.c
++++ b/pjnath/src/pjnath/ice_session.c
+@@ -1217,10 +1217,8 @@ static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now)
+ 	msg_data = PJ_POOL_ZALLOC_T(tdata->pool, pj_ice_msg_data);
+ 	msg_data->transport_id = the_check->lcand->transport_id;
+ 
+-	/* Temporarily disable FINGERPRINT. The Binding Indication 
+-	 * SHOULD NOT contain any attributes.
+-	 */
+-	saved = pj_stun_session_use_fingerprint(comp->stun_sess, PJ_FALSE);
++	/* Make sure that the FINGERPRINT attribute is used per RFC 5245 Section 10 */
++	saved = pj_stun_session_use_fingerprint(comp->stun_sess, PJ_TRUE);
+ 
+ 	/* Send to session */
+ 	addr_len = pj_sockaddr_get_len(&the_check->rcand->addr);
+-- 
+2.7.4
+
diff --git a/third-party/pjproject/patches/0090-sip_parser-Add-validity-checking-for-numeric-header-.patch b/third-party/pjproject/patches/0090-sip_parser-Add-validity-checking-for-numeric-header-.patch
new file mode 100644
index 0000000..9c268ec
--- /dev/null
+++ b/third-party/pjproject/patches/0090-sip_parser-Add-validity-checking-for-numeric-header-.patch
@@ -0,0 +1,910 @@
+diff -uprN pjproject-2.6-a/pjlib/build/pjlib.vcproj pjproject-2.6-b/pjlib/build/pjlib.vcproj
+--- pjproject-2.6-a/pjlib/build/pjlib.vcproj	2013-06-19 00:47:43.000000000 -0600
++++ pjproject-2.6-b/pjlib/build/pjlib.vcproj	2017-11-08 06:54:01.531232949 -0700
+@@ -14967,7 +14967,11 @@
+ 			</File>
+ 			<File
+ 				RelativePath="..\include\pj\ip_helper.h"
+-				>
++				>
++			</File>
++			<File
++				RelativePath="..\include\pj\limits.h"
++				>
+ 			</File>
+ 			<File
+ 				RelativePath="..\include\pj\list.h"
+@@ -15070,8 +15074,12 @@
+ 				</File>
+ 				<File
+ 					RelativePath="..\include\pj\compat\high_precision.h"
+-					>
+-				</File>
++					>
++				</File>
++				<File
++					RelativePath="..\include\pj\compat\limits.h"
++					>
++				</File>
+ 				<File
+ 					RelativePath="..\include\pj\compat\m_alpha.h"
+ 					>
+diff -uprN pjproject-2.6-a/pjlib/build/pjlib.vcxproj pjproject-2.6-b/pjlib/build/pjlib.vcxproj
+--- pjproject-2.6-a/pjlib/build/pjlib.vcxproj	2017-01-22 21:32:34.000000000 -0700
++++ pjproject-2.6-b/pjlib/build/pjlib.vcxproj	2017-11-08 06:54:01.531232949 -0700
+@@ -494,7 +494,7 @@
+       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
+       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+     </ClCompile>
+-    <ClCompile Include="..\src\pj\file_io_win32.c" />    
++    <ClCompile Include="..\src\pj\file_io_win32.c" />
+     <ClCompile Include="..\src\pj\guid.c" />
+     <ClCompile Include="..\src\pj\guid_simple.c">
+       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-Dynamic|Win32'">true</ExcludedFromBuild>
+@@ -890,6 +890,7 @@
+     <ClInclude Include="..\include\pj\compat\ctype.h" />
+     <ClInclude Include="..\include\pj\compat\errno.h" />
+     <ClInclude Include="..\include\pj\compat\high_precision.h" />
++    <ClInclude Include="..\include\pj\compat\limits.h" />
+     <ClInclude Include="..\include\pj\compat\malloc.h" />
+     <ClInclude Include="..\include\pj\compat\m_alpha.h" />
+     <ClInclude Include="..\include\pj\compat\m_i386.h" />
+@@ -925,6 +926,7 @@
+     <ClInclude Include="..\include\pj\hash.h" />
+     <ClInclude Include="..\include\pj\ioqueue.h" />
+     <ClInclude Include="..\include\pj\ip_helper.h" />
++    <ClInclude Include="..\include\pj\limits.h" />
+     <ClInclude Include="..\include\pj\list.h" />
+     <ClInclude Include="..\include\pj\list_i.h" />
+     <ClInclude Include="..\include\pj\lock.h" />
+diff -uprN pjproject-2.6-a/pjlib/build/pjlib.vcxproj.filters pjproject-2.6-b/pjlib/build/pjlib.vcxproj.filters
+--- pjproject-2.6-a/pjlib/build/pjlib.vcxproj.filters	2017-01-22 21:32:34.000000000 -0700
++++ pjproject-2.6-b/pjlib/build/pjlib.vcxproj.filters	2017-11-08 06:54:01.532232969 -0700
+@@ -439,5 +439,11 @@
+     <ClInclude Include="..\include\pj\compat\os_winuwp.h">
+       <Filter>Header Files\compat</Filter>
+     </ClInclude>
++    <ClInclude Include="..\include\pj\limits.h">
++      <Filter>Header Files</Filter>
++    </ClInclude>
++    <ClInclude Include="..\include\pj\compat\limits.h">
++      <Filter>Header Files\compat</Filter>
++    </ClInclude>
+   </ItemGroup>
+ </Project>
+\ No newline at end of file
+diff -uprN pjproject-2.6-a/pjlib/include/pj/compat/limits.h pjproject-2.6-b/pjlib/include/pj/compat/limits.h
+--- pjproject-2.6-a/pjlib/include/pj/compat/limits.h	1969-12-31 17:00:00.000000000 -0700
++++ pjproject-2.6-b/pjlib/include/pj/compat/limits.h	2017-11-08 06:54:01.532232969 -0700
+@@ -0,0 +1,65 @@
++/* $Id$ */
++/*
++ * Copyright (C) 2017 Teluu Inc. (http://www.teluu.com)
++ * Copyright (C) 2017 George Joseph <gjoseph at digium.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
++ */
++#ifndef __PJ_COMPAT_LIMITS_H__
++#define __PJ_COMPAT_LIMITS_H__
++
++/**
++ * @file limits.h
++ * @brief Provides integer limits normally found in limits.h.
++ */
++
++#if defined(PJ_HAS_LIMITS_H) && PJ_HAS_LIMITS_H != 0
++#  include <limits.h>
++#else
++
++#  ifdef _MSC_VER
++#  pragma message("limits.h is not found or not supported. LONG_MIN and "\
++		 "LONG_MAX will be defined by the library in "\
++		 "pj/compats/limits.h and overridable in config_site.h")
++#  else
++#  warning "limits.h is not found or not supported. LONG_MIN and LONG_MAX " \
++           "will be defined by the library in pj/compats/limits.h and "\
++           "overridable in config_site.h"
++#  endif
++
++/* Minimum and maximum values a `signed long int' can hold.  */
++#  ifndef LONG_MAX
++#    if __WORDSIZE == 64
++#      define LONG_MAX     9223372036854775807L
++#    else
++#      define LONG_MAX     2147483647L
++#    endif
++#  endif
++
++#  ifndef LONG_MIN
++#    define LONG_MIN      (-LONG_MAX - 1L)
++#  endif
++
++/* Maximum value an `unsigned long int' can hold.  (Minimum is 0.)  */
++#  ifndef ULONG_MAX
++#    if __WORDSIZE == 64
++#      define ULONG_MAX    18446744073709551615UL
++#    else    
++#      define ULONG_MAX    4294967295UL
++#    endif
++#  endif
++#endif
++
++#endif  /* __PJ_COMPAT_LIMITS_H__ */
+diff -uprN pjproject-2.6-a/pjlib/include/pj/compat/os_win32.h pjproject-2.6-b/pjlib/include/pj/compat/os_win32.h
+--- pjproject-2.6-a/pjlib/include/pj/compat/os_win32.h	2011-05-05 00:14:19.000000000 -0600
++++ pjproject-2.6-b/pjlib/include/pj/compat/os_win32.h	2017-11-08 06:54:01.532232969 -0700
+@@ -57,6 +57,7 @@
+ #define PJ_HAS_SYS_TYPES_H	    1
+ #define PJ_HAS_TIME_H		    1
+ #define PJ_HAS_UNISTD_H		    0
++#define PJ_HAS_LIMITS_H		    1
+ 
+ #define PJ_HAS_MSWSOCK_H	    1
+ #define PJ_HAS_WINSOCK_H	    0
+diff -uprN pjproject-2.6-a/pjlib/include/pj/limits.h pjproject-2.6-b/pjlib/include/pj/limits.h
+--- pjproject-2.6-a/pjlib/include/pj/limits.h	1969-12-31 17:00:00.000000000 -0700
++++ pjproject-2.6-b/pjlib/include/pj/limits.h	2017-11-08 06:54:01.532232969 -0700
+@@ -0,0 +1,51 @@
++/* $Id$ */
++/*
++ * Copyright (C) 2017 Teluu Inc. (http://www.teluu.com)
++ * Copyright (C) 2017 George Joseph <gjoseph at digium.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
++ */
++#ifndef __PJ_LIMITS_H__
++#define __PJ_LIMITS_H__
++
++/**
++ * @file limits.h
++ * @brief Common min and max values
++ */
++
++#include <pj/compat/limits.h>
++
++/** Maximum value for signed 32-bit integer. */
++#define PJ_MAXINT32	0x7fffffff
++
++/** Minimum value for signed 32-bit integer. */
++#define PJ_MININT32	0x80000000
++
++/** Maximum value for unsigned 16-bit integer. */
++#define PJ_MAXUINT16	0xffff
++
++/** Maximum value for unsigned char. */
++#define PJ_MAXUINT8	0xff
++
++/** Maximum value for long. */
++#define PJ_MAXLONG	LONG_MAX
++
++/** Minimum value for long. */
++#define PJ_MINLONG	LONG_MIN
++
++/** Minimum value for unsigned long. */
++#define PJ_MAXULONG	ULONG_MAX
++
++#endif  /* __PJ_LIMITS_H__ */
+diff -uprN pjproject-2.6-a/pjlib/include/pj/string.h pjproject-2.6-b/pjlib/include/pj/string.h
+--- pjproject-2.6-a/pjlib/include/pj/string.h	2017-01-10 21:38:29.000000000 -0700
++++ pjproject-2.6-b/pjlib/include/pj/string.h	2017-11-08 06:54:01.532232969 -0700
+@@ -28,7 +28,6 @@
+ #include <pj/types.h>
+ #include <pj/compat/string.h>
+ 
+-
+ PJ_BEGIN_DECL
+ 
+ /**
+@@ -636,6 +635,29 @@ PJ_DECL(char*) pj_create_random_string(c
+ PJ_DECL(long) pj_strtol(const pj_str_t *str);
+ 
+ /**
++ * Convert string to signed long integer. The conversion will stop as
++ * soon as non-digit character is found or all the characters have
++ * been processed.
++ *
++ * @param str   the string.
++ * @param value Pointer to a long to receive the value.
++ *
++ * @return PJ_SUCCESS if successful.  Otherwise:
++ *         PJ_ETOOSMALL if the value was an impossibly long negative number.
++ *         In this case *value will be set to LONG_MIN.
++ *         \n
++ *         PJ_ETOOBIG if the value was an impossibly long positive number.
++ *         In this case, *value will be set to LONG_MAX.
++ *         \n
++ *         PJ_EINVAL if the input string was NULL, the value pointer was NULL 
++ *         or the input string could not be parsed at all such as starting with
++ *         a character other than a '+', '-' or not in the '0' - '9' range.
++ *         In this case, *value will be left untouched.
++ */
++PJ_DECL(pj_status_t) pj_strtol2(const pj_str_t *str, long *value);
++
++
++/**
+  * Convert string to unsigned integer. The conversion will stop as
+  * soon as non-digit character is found or all the characters have
+  * been processed.
+@@ -664,6 +686,27 @@ PJ_DECL(unsigned long) pj_strtoul2(const
+ 				   unsigned base);
+ 
+ /**
++ * Convert string to unsigned long integer. The conversion will stop as
++ * soon as non-digit character is found or all the characters have
++ * been processed.
++ *
++ * @param str       The input string.
++ * @param value     Pointer to an unsigned long to receive the value.
++ * @param base	    Number base to use.
++ *
++ * @return PJ_SUCCESS if successful.  Otherwise:
++ *         PJ_ETOOBIG if the value was an impossibly long positive number.
++ *         In this case, *value will be set to ULONG_MAX.
++ *         \n
++ *         PJ_EINVAL if the input string was NULL, the value pointer was NULL 
++ *         or the input string could not be parsed at all such as starting 
++ *         with a character outside the base character range.  In this case,
++ *         *value will be left untouched.
++ */
++PJ_DECL(pj_status_t) pj_strtoul3(const pj_str_t *str, unsigned long *value,
++				 unsigned base);
++
++/**
+  * Convert string to float.
+  *
+  * @param str	the string.
+@@ -786,7 +829,6 @@ PJ_INLINE(void*) pj_memchr(const void *b
+     return (void*)memchr((void*)buf, c, size);
+ }
+ 
+-
+ /**
+  * @}
+  */
+diff -uprN pjproject-2.6-a/pjlib/include/pj/types.h pjproject-2.6-b/pjlib/include/pj/types.h
+--- pjproject-2.6-a/pjlib/include/pj/types.h	2014-01-15 22:30:46.000000000 -0700
++++ pjproject-2.6-b/pjlib/include/pj/types.h	2017-11-08 06:54:01.532232969 -0700
+@@ -280,9 +280,6 @@ typedef int pj_exception_id_t;
+ /** Utility macro to compute the number of elements in static array. */
+ #define PJ_ARRAY_SIZE(a)    (sizeof(a)/sizeof(a[0]))
+ 
+-/** Maximum value for signed 32-bit integer. */
+-#define PJ_MAXINT32  0x7FFFFFFFL
+-
+ /**
+  * Length of object names.
+  */
+diff -uprN pjproject-2.6-a/pjlib/src/pj/string.c pjproject-2.6-b/pjlib/src/pj/string.c
+--- pjproject-2.6-a/pjlib/src/pj/string.c	2017-01-10 21:38:29.000000000 -0700
++++ pjproject-2.6-b/pjlib/src/pj/string.c	2017-11-08 06:54:01.532232969 -0700
+@@ -23,11 +23,14 @@
+ #include <pj/ctype.h>
+ #include <pj/rand.h>
+ #include <pj/os.h>
++#include <pj/errno.h>
++#include <pj/limits.h>
+ 
+ #if PJ_FUNCTIONS_ARE_INLINED==0
+ #  include <pj/string_i.h>
+ #endif
+ 
++
+ PJ_DEF(pj_ssize_t) pj_strspn(const pj_str_t *str, const pj_str_t *set_char)
+ {
+     pj_ssize_t i, j, count = 0;
+@@ -230,6 +233,55 @@ PJ_DEF(long) pj_strtol(const pj_str_t *s
+         return pj_strtoul(str);
+ }
+ 
++
++PJ_DEF(pj_status_t) pj_strtol2(const pj_str_t *str, long *value)
++{
++    pj_str_t s;
++    unsigned long retval = 0;
++    pj_bool_t is_negative = PJ_FALSE;
++    int rc = 0;
++
++    PJ_CHECK_STACK();
++
++    if (!str || !value) {
++        return PJ_EINVAL;
++    }
++
++    s = *str;
++    pj_strltrim(&s);
++
++    if (s.slen == 0)
++        return PJ_EINVAL;
++
++    if (s.ptr[0] == '+' || s.ptr[0] == '-') {
++        is_negative = (s.ptr[0] == '-');
++        s.ptr += 1;
++        s.slen -= 1;
++    }
++
++    rc = pj_strtoul3(&s, &retval, 10);
++    if (rc == PJ_EINVAL) {
++        return rc;
++    } else if (rc != PJ_SUCCESS) {
++        *value = is_negative ? PJ_MINLONG : PJ_MAXLONG;
++        return is_negative ? PJ_ETOOSMALL : PJ_ETOOBIG;
++    }
++
++    if (retval > PJ_MAXLONG && !is_negative) {
++        *value = PJ_MAXLONG;
++        return PJ_ETOOBIG;
++    }
++
++    if (retval > (PJ_MAXLONG + 1UL) && is_negative) {
++        *value = PJ_MINLONG;
++        return PJ_ETOOSMALL;
++    }
++
++    *value = is_negative ? -(long)retval : retval;
++
++    return PJ_SUCCESS;
++}
++
+ PJ_DEF(unsigned long) pj_strtoul(const pj_str_t *str)
+ {
+     unsigned long value;
+@@ -282,6 +334,71 @@ PJ_DEF(unsigned long) pj_strtoul2(const
+     return value;
+ }
+ 
++PJ_DEF(pj_status_t) pj_strtoul3(const pj_str_t *str, unsigned long *value,
++				unsigned base)
++{
++    pj_str_t s;
++    unsigned i;
++
++    PJ_CHECK_STACK();
++
++    if (!str || !value) {
++        return PJ_EINVAL;
++    }
++
++    s = *str;
++    pj_strltrim(&s);
++
++    if (s.slen == 0 || s.ptr[0] < '0' ||
++	(base <= 10 && (unsigned)s.ptr[0] > ('0' - 1) + base) ||
++	(base == 16 && !pj_isxdigit(s.ptr[0])))
++    {
++        return PJ_EINVAL;
++    }
++
++    *value = 0;
++    if (base <= 10) {
++	for (i=0; i<(unsigned)s.slen; ++i) {
++	    unsigned c = s.ptr[i] - '0';
++	    if (s.ptr[i] < '0' || (unsigned)s.ptr[i] > ('0' - 1) + base) {
++		break;
++	    }
++	    if (*value > PJ_MAXULONG / base) {
++		*value = PJ_MAXULONG;
++		return PJ_ETOOBIG;
++	    }
++
++	    *value *= base;
++	    if ((PJ_MAXULONG - *value) < c) {
++		*value = PJ_MAXULONG;
++		return PJ_ETOOBIG;
++	    }
++	    *value += c;
++	}
++    } else if (base == 16) {
++	for (i=0; i<(unsigned)s.slen; ++i) {
++	    unsigned c = pj_hex_digit_to_val(s.ptr[i]);
++	    if (!pj_isxdigit(s.ptr[i]))
++		break;
++
++	    if (*value > PJ_MAXULONG / base) {
++		*value = PJ_MAXULONG;
++		return PJ_ETOOBIG;
++	    }
++	    *value *= base;
++	    if ((PJ_MAXULONG - *value) < c) {
++		*value = PJ_MAXULONG;
++		return PJ_ETOOBIG;
++	    }
++	    *value += c;
++	}
++    } else {
++	pj_assert(!"Unsupported base");
++	return PJ_EINVAL;
++    }
++    return PJ_SUCCESS;
++}
++
+ PJ_DEF(float) pj_strtof(const pj_str_t *str)
+ {
+     pj_str_t part;
+@@ -356,5 +473,3 @@ PJ_DEF(int) pj_utoa_pad( unsigned long v
+ 
+     return len;
+ }
+-
+-
+diff -uprN pjproject-2.6-a/pjlib/src/pj/timer.c pjproject-2.6-b/pjlib/src/pj/timer.c
+--- pjproject-2.6-a/pjlib/src/pj/timer.c	2014-06-04 03:23:10.000000000 -0600
++++ pjproject-2.6-b/pjlib/src/pj/timer.c	2017-11-08 06:54:01.533232988 -0700
+@@ -36,6 +36,7 @@
+ #include <pj/lock.h>
+ #include <pj/log.h>
+ #include <pj/rand.h>
++#include <pj/limits.h>
+ 
+ #define THIS_FILE	"timer.c"
+ 
+diff -uprN pjproject-2.6-a/pjsip/include/pjsip/sip_parser.h pjproject-2.6-b/pjsip/include/pjsip/sip_parser.h
+--- pjproject-2.6-a/pjsip/include/pjsip/sip_parser.h	2013-03-20 05:29:08.000000000 -0600
++++ pjproject-2.6-b/pjsip/include/pjsip/sip_parser.h	2017-11-08 06:54:01.533232988 -0700
+@@ -39,6 +39,26 @@ PJ_BEGIN_DECL
+  */
+ 
+ /**
++ * Contants for limit checks
++ */
++#define PJSIP_MIN_CONTENT_LENGTH    0
++#define PJSIP_MAX_CONTENT_LENGTH    PJ_MAXINT32
++#define PJSIP_MIN_PORT		    0
++#define PJSIP_MAX_PORT		    PJ_MAXUINT16
++#define PJSIP_MIN_TTL		    0
++#define PJSIP_MAX_TTL		    PJ_MAXUINT8
++#define PJSIP_MIN_STATUS_CODE	    100
++#define PJSIP_MAX_STATUS_CODE	    999
++#define PJSIP_MIN_Q1000		    0
++#define PJSIP_MAX_Q1000		    PJ_MAXINT32 / 1000
++#define PJSIP_MIN_EXPIRES	    0
++#define PJSIP_MAX_EXPIRES	    PJ_MAXINT32
++#define PJSIP_MIN_CSEQ		    0
++#define PJSIP_MAX_CSEQ		    PJ_MAXINT32
++#define PJSIP_MIN_RETRY_AFTER	    0
++#define PJSIP_MAX_RETRY_AFTER	    PJ_MAXINT32
++
++/**
+  * URI Parsing options.
+  */
+ enum
+@@ -64,6 +84,11 @@ enum
+ extern int PJSIP_SYN_ERR_EXCEPTION;
+ 
+ /**
++ * Invalid value error exception value.
++ */
++extern int PJSIP_EINVAL_ERR_EXCEPTION;
++
++/**
+  * This structure is used to get error reporting from parser.
+  */
+ typedef struct pjsip_parser_err_report
+diff -uprN pjproject-2.6-a/pjsip/src/pjsip/sip_parser.c pjproject-2.6-b/pjsip/src/pjsip/sip_parser.c
+--- pjproject-2.6-a/pjsip/src/pjsip/sip_parser.c	2016-04-19 19:58:15.000000000 -0600
++++ pjproject-2.6-b/pjsip/src/pjsip/sip_parser.c	2017-11-08 06:54:01.533232988 -0700
+@@ -34,6 +34,7 @@
+ #include <pj/string.h>
+ #include <pj/ctype.h>
+ #include <pj/assert.h>
++#include <pj/limits.h>
+ 
+ #define THIS_FILE	    "sip_parser.c"
+ 
+@@ -93,6 +94,7 @@ static unsigned uri_handler_count;
+  * Global vars (also extern).
+  */
+ int PJSIP_SYN_ERR_EXCEPTION = -1;
++int PJSIP_EINVAL_ERR_EXCEPTION = -2;
+ 
+ /* Parser constants */
+ static pjsip_parser_const_t pconst =
+@@ -205,7 +207,6 @@ static unsigned long pj_strtoul_mindigit
+ /* Case insensitive comparison */
+ #define parser_stricmp(s1, s2)  (s1.slen!=s2.slen || pj_stricmp_alnum(&s1, &s2))
+ 
+-
+ /* Get a token and unescape */
+ PJ_INLINE(void) parser_get_and_unescape(pj_scanner *scanner, pj_pool_t *pool,
+ 					const pj_cis_t *spec, 
+@@ -223,8 +224,6 @@ PJ_INLINE(void) parser_get_and_unescape(
+ #endif
+ }
+ 
+-
+-
+ /* Syntax error handler for parser. */
+ static void on_syntax_error(pj_scanner *scanner)
+ {
+@@ -232,6 +231,60 @@ static void on_syntax_error(pj_scanner *
+     PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ }
+ 
++/* Syntax error handler for parser. */
++static void on_str_parse_error(const pj_str_t *str, int rc)
++{
++    char *s;
++
++    switch(rc) {
++    case PJ_EINVAL:
++        s = "NULL input string, invalid input string, or NULL return "\
++	    "value pointer";
++        break;
++    case PJ_ETOOSMALL:
++        s = "String value was less than the minimum allowed value.";
++        break;
++    case PJ_ETOOBIG:
++        s = "String value was greater than the maximum allowed value.";
++        break;
++    default:
++        s = "Unknown error";
++    }
++
++    if (str) {
++        PJ_LOG(1, (THIS_FILE, "Error parsing '%.*s': %s",
++                   (int)str->slen, str->ptr, s));
++    } else {
++        PJ_LOG(1, (THIS_FILE, "Can't parse input string: %s", s));
++    }
++    PJ_THROW(PJSIP_EINVAL_ERR_EXCEPTION);
++}
++
++static void strtoi_validate(const pj_str_t *str, int min_val,
++			    int max_val, int *value)
++{ 
++    long retval;
++    pj_status_t status;
++
++    if (!str || !value) {
++        on_str_parse_error(str, PJ_EINVAL);
++    }
++    status = pj_strtol2(str, &retval);
++    if (status != PJ_EINVAL) {
++	if (min_val > retval) {
++	    *value = min_val;
++	    status = PJ_ETOOSMALL;
++	} else if (retval > max_val) {
++	    *value = max_val;
++	    status = PJ_ETOOBIG;
++	} else
++	    *value = (int)retval;
++    }
++
++    if (status != PJ_SUCCESS)
++	on_str_parse_error(str, status);
++}
++
+ /* Get parser constants. */
+ PJ_DEF(const pjsip_parser_const_t*) pjsip_parser_const(void)
+ {
+@@ -285,6 +338,14 @@ static pj_status_t init_parser()
+     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ 
+     /*
++     * Invalid value exception.
++     */
++    pj_assert (PJSIP_EINVAL_ERR_EXCEPTION == -2);
++    status = pj_exception_id_alloc("PJSIP invalid value error", 
++				   &PJSIP_EINVAL_ERR_EXCEPTION);
++    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
++
++    /*
+      * Init character input spec (cis)
+      */
+ 
+@@ -502,6 +563,9 @@ void deinit_sip_parser(void)
+ 	/* Deregister exception ID */
+ 	pj_exception_id_free(PJSIP_SYN_ERR_EXCEPTION);
+ 	PJSIP_SYN_ERR_EXCEPTION = -1;
++
++	pj_exception_id_free(PJSIP_EINVAL_ERR_EXCEPTION);
++	PJSIP_EINVAL_ERR_EXCEPTION = -2;
+     }
+     pj_leave_critical_section();
+ }
+@@ -766,7 +830,7 @@ PJ_DEF(pjsip_msg *) pjsip_parse_rdata( c
+ }
+ 
+ /* Determine if a message has been received. */
+-PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size, 
++PJ_DEF(pj_status_t) pjsip_find_msg( const char *buf, pj_size_t size, 
+ 				  pj_bool_t is_datagram, pj_size_t *msg_size)
+ {
+ #if PJ_HAS_TCP
+@@ -776,6 +840,7 @@ PJ_DEF(pj_bool_t) pjsip_find_msg( const
+     const char *line;
+     int content_length = -1;
+     pj_str_t cur_msg;
++    pj_status_t status = PJ_SUCCESS;
+     const pj_str_t end_hdr = { "\n\r\n", 3};
+ 
+     *msg_size = size;
+@@ -836,9 +901,16 @@ PJ_DEF(pj_bool_t) pjsip_find_msg( const
+ 		pj_scan_get_newline(&scanner);
+ 
+ 		/* Found a valid Content-Length header. */
+-		content_length = pj_strtoul(&str_clen);
++		strtoi_validate(&str_clen, PJSIP_MIN_CONTENT_LENGTH,
++				PJSIP_MAX_CONTENT_LENGTH, &content_length);
+ 	    }
+ 	    PJ_CATCH_ANY {
++		int eid = PJ_GET_EXCEPTION();
++		if (eid == PJSIP_SYN_ERR_EXCEPTION) {
++		    status = PJSIP_EMISSINGHDR;
++		} else if (eid == PJSIP_EINVAL_ERR_EXCEPTION) {
++		    status = PJSIP_EINVALIDHDR;
++		}
+ 		content_length = -1;
+ 	    }
+ 	    PJ_END
+@@ -858,7 +930,7 @@ PJ_DEF(pj_bool_t) pjsip_find_msg( const
+ 
+     /* Found Content-Length? */
+     if (content_length == -1) {
+-	return PJSIP_EMISSINGHDR;
++	return status;
+     }
+ 
+     /* Enough packet received? */
+@@ -938,10 +1010,14 @@ static pj_bool_t is_next_sip_version(pj_
+ static pjsip_msg *int_parse_msg( pjsip_parse_ctx *ctx,
+ 				 pjsip_parser_err_report *err_list)
+ {
+-    pj_bool_t parsing_headers;
+-    pjsip_msg *msg = NULL;
++    /* These variables require "volatile" so their values get
++     * preserved when re-entering the PJ_TRY block after an error.
++     */
++    volatile pj_bool_t parsing_headers;
++    pjsip_msg *volatile msg = NULL;
++    pjsip_ctype_hdr *volatile ctype_hdr = NULL;
++
+     pj_str_t hname;
+-    pjsip_ctype_hdr *ctype_hdr = NULL;
+     pj_scanner *scanner = ctx->scanner;
+     pj_pool_t *pool = ctx->pool;
+     PJ_USE_EXCEPTION;
+@@ -1023,7 +1099,6 @@ parse_headers:
+ 		hdr->name = hdr->sname = hname;
+ 	    }
+ 	    
+-	
+ 	    /* Single parse of header line can produce multiple headers.
+ 	     * For example, if one Contact: header contains Contact list
+ 	     * separated by comma, then these Contacts will be split into
+@@ -1267,7 +1342,7 @@ static void int_parse_uri_host_port( pj_
+ 	pj_str_t port;
+ 	pj_scan_get_char(scanner);
+ 	pj_scan_get(scanner, &pconst.pjsip_DIGIT_SPEC, &port);
+-	*p_port = pj_strtoul(&port);
++	strtoi_validate(&port, PJSIP_MIN_PORT, PJSIP_MAX_PORT, p_port);
+     } else {
+ 	*p_port = 0;
+     }
+@@ -1458,8 +1533,8 @@ static void* int_parse_sip_url( pj_scann
+ 	    url->transport_param = pvalue;
+ 
+ 	} else if (!parser_stricmp(pname, pconst.pjsip_TTL_STR) && pvalue.slen) {
+-	    url->ttl_param = pj_strtoul(&pvalue);
+-
++	    strtoi_validate(&pvalue, PJSIP_MIN_TTL, PJSIP_MAX_TTL,
++			    &url->ttl_param);
+ 	} else if (!parser_stricmp(pname, pconst.pjsip_MADDR_STR) && pvalue.slen) {
+ 	    url->maddr_param = pvalue;
+ 
+@@ -1595,7 +1670,8 @@ static void int_parse_status_line( pj_sc
+ 
+     parse_sip_version(scanner);
+     pj_scan_get( scanner, &pconst.pjsip_DIGIT_SPEC, &token);
+-    status_line->code = pj_strtoul(&token);
++    strtoi_validate(&token, PJSIP_MIN_STATUS_CODE, PJSIP_MAX_STATUS_CODE,
++                    &status_line->code);
+     if (*scanner->curptr != '\r' && *scanner->curptr != '\n')
+ 	pj_scan_get( scanner, &pconst.pjsip_NOT_NEWLINE, &status_line->reason);
+     else
+@@ -1780,20 +1856,34 @@ static void int_parse_contact_param( pjs
+ 	if (!parser_stricmp(pname, pconst.pjsip_Q_STR) && pvalue.slen) {
+ 	    char *dot_pos = (char*) pj_memchr(pvalue.ptr, '.', pvalue.slen);
+ 	    if (!dot_pos) {
+-		hdr->q1000 = pj_strtoul(&pvalue) * 1000;
++		strtoi_validate(&pvalue, PJSIP_MIN_Q1000, PJSIP_MAX_Q1000,
++                                &hdr->q1000);
++		hdr->q1000 *= 1000;
+ 	    } else {
+ 		pj_str_t tmp = pvalue;
++		unsigned long qval_frac;
+ 
+ 		tmp.slen = dot_pos - pvalue.ptr;
+-		hdr->q1000 = pj_strtoul(&tmp) * 1000;
++		strtoi_validate(&tmp, PJSIP_MIN_Q1000, PJSIP_MAX_Q1000,
++                                &hdr->q1000);
++                hdr->q1000 *= 1000;
+ 
+ 		pvalue.slen = (pvalue.ptr+pvalue.slen) - (dot_pos+1);
+ 		pvalue.ptr = dot_pos + 1;
+-		hdr->q1000 += pj_strtoul_mindigit(&pvalue, 3);
++		if (pvalue.slen > 3) {
++		    pvalue.slen = 3;
++		}
++		qval_frac = pj_strtoul_mindigit(&pvalue, 3);
++		if ((unsigned)hdr->q1000 > (PJ_MAXINT32 - qval_frac)) {
++		    PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
++		}
++		hdr->q1000 += qval_frac;
+ 	    }    
+-	} else if (!parser_stricmp(pname, pconst.pjsip_EXPIRES_STR) && pvalue.slen) {
+-	    hdr->expires = pj_strtoul(&pvalue);
+-
++	} else if (!parser_stricmp(pname, pconst.pjsip_EXPIRES_STR) && 
++                   pvalue.slen) 
++        {
++	    strtoi_validate(&pvalue, PJSIP_MIN_EXPIRES, PJSIP_MAX_EXPIRES,
++                            &hdr->expires);
+ 	} else {
+ 	    pjsip_param *p = PJ_POOL_ALLOC_T(pool, pjsip_param);
+ 	    p->name = pname;
+@@ -1890,19 +1980,22 @@ static pjsip_hdr* parse_hdr_content_type
+ static pjsip_hdr* parse_hdr_cseq( pjsip_parse_ctx *ctx )
+ {
+     pj_str_t cseq, method;
+-    pjsip_cseq_hdr *hdr;
++    pjsip_cseq_hdr *hdr = NULL;
++    int cseq_val = 0;
+ 
+-    hdr = pjsip_cseq_hdr_create(ctx->pool);
+     pj_scan_get( ctx->scanner, &pconst.pjsip_DIGIT_SPEC, &cseq);
+-    hdr->cseq = pj_strtoul(&cseq);
++    strtoi_validate(&cseq, PJSIP_MIN_CSEQ, PJSIP_MAX_CSEQ, &cseq_val);
+ 
+-    pj_scan_get( ctx->scanner, &pconst.pjsip_TOKEN_SPEC, &method);
+-    pjsip_method_init_np(&hdr->method, &method);
++    hdr = pjsip_cseq_hdr_create(ctx->pool);
++    hdr->cseq = cseq_val;
+ 
++    pj_scan_get( ctx->scanner, &pconst.pjsip_TOKEN_SPEC, &method);
+     parse_hdr_end( ctx->scanner );
+ 
+-    if (ctx->rdata)
++    pjsip_method_init_np(&hdr->method, &method);
++    if (ctx->rdata) {
+         ctx->rdata->msg_info.cseq = hdr;
++    }
+ 
+     return (pjsip_hdr*)hdr;
+ }
+@@ -1984,7 +2077,8 @@ static pjsip_hdr* parse_hdr_retry_after(
+     hdr = pjsip_retry_after_hdr_create(ctx->pool, 0);
+     
+     pj_scan_get(scanner, &pconst.pjsip_DIGIT_SPEC, &tmp);
+-    hdr->ivalue = pj_strtoul(&tmp);
++    strtoi_validate(&tmp, PJSIP_MIN_RETRY_AFTER, PJSIP_MAX_RETRY_AFTER,
++                    &hdr->ivalue);
+ 
+     while (!pj_scan_is_eof(scanner) && *scanner->curptr!='\r' &&
+ 	   *scanner->curptr!='\n')
+@@ -2073,7 +2167,8 @@ static void int_parse_via_param( pjsip_v
+ 	    hdr->branch_param = pvalue;
+ 
+ 	} else if (!parser_stricmp(pname, pconst.pjsip_TTL_STR) && pvalue.slen) {
+-	    hdr->ttl_param = pj_strtoul(&pvalue);
++	    strtoi_validate(&pvalue, PJSIP_MIN_TTL, PJSIP_MAX_TTL,
++                            &hdr->ttl_param);
+ 	    
+ 	} else if (!parser_stricmp(pname, pconst.pjsip_MADDR_STR) && pvalue.slen) {
+ 	    hdr->maddr_param = pvalue;
+@@ -2082,9 +2177,10 @@ static void int_parse_via_param( pjsip_v
+ 	    hdr->recvd_param = pvalue;
+ 
+ 	} else if (!parser_stricmp(pname, pconst.pjsip_RPORT_STR)) {
+-	    if (pvalue.slen)
+-		hdr->rport_param = pj_strtoul(&pvalue);
+-	    else
++	    if (pvalue.slen) {
++		strtoi_validate(&pvalue, PJSIP_MIN_PORT, PJSIP_MAX_PORT,
++			        &hdr->rport_param);
++            } else
+ 		hdr->rport_param = 0;
+ 	} else {
+ 	    pjsip_param *p = PJ_POOL_ALLOC_T(pool, pjsip_param);
+@@ -2213,7 +2309,8 @@ static pjsip_hdr* parse_hdr_via( pjsip_p
+ 	    pj_str_t digit;
+ 	    pj_scan_get_char(scanner);
+ 	    pj_scan_get(scanner, &pconst.pjsip_DIGIT_SPEC, &digit);
+-	    hdr->sent_by.port = pj_strtoul(&digit);
++	    strtoi_validate(&digit, PJSIP_MIN_PORT, PJSIP_MAX_PORT,
++                            &hdr->sent_by.port);
+ 	}
+ 	
+ 	int_parse_via_param(hdr, scanner, ctx->pool);
+@@ -2298,9 +2395,10 @@ PJ_DEF(pj_status_t) pjsip_parse_headers(
+ 				         unsigned options)
+ {
+     enum { STOP_ON_ERROR = 1 };
++    pj_str_t hname;
+     pj_scanner scanner;
+     pjsip_parse_ctx ctx;
+-    pj_str_t hname;
++
+     PJ_USE_EXCEPTION;
+ 
+     pj_scan_init(&scanner, input, size, PJ_SCAN_AUTOSKIP_WS_HEADER,
+@@ -2323,7 +2421,7 @@ retry_parse:
+ 	     */
+ 	    hname.slen = 0;
+ 
+-	    /* Get hname. */
++	    /* Get hname. */            
+ 	    pj_scan_get( &scanner, &pconst.pjsip_TOKEN_SPEC, &hname);
+ 	    if (pj_scan_get_char( &scanner ) != ':') {
+ 		PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+diff -uprN pjproject-2.6-a/pjsip/src/pjsip/sip_transaction.c pjproject-2.6-b/pjsip/src/pjsip/sip_transaction.c
+--- pjproject-2.6-a/pjsip/src/pjsip/sip_transaction.c	2017-11-08 06:49:48.436246594 -0700
++++ pjproject-2.6-b/pjsip/src/pjsip/sip_transaction.c	2017-11-08 06:54:11.101421471 -0700
+@@ -288,12 +288,12 @@ static pj_status_t create_tsx_key_2543(
+     host = &rdata->msg_info.via->sent_by.host;
+ 
+     /* Calculate length required. */
+-    len_required = method->name.slen +	    /* Method */
+-		   9 +			    /* CSeq number */
++    len_required = method->name.slen +      /* Method */
++                   11 +			    /* CSeq number */
+ 		   rdata->msg_info.from->tag.slen +   /* From tag. */
+ 		   rdata->msg_info.cid->id.slen +    /* Call-ID */
+ 		   host->slen +		    /* Via host. */
+-		   9 +			    /* Via port. */
++		   11 +			    /* Via port. */
+ 		   16;			    /* Separator+Allowance. */
+     key = p = (char*) pj_pool_alloc(pool, len_required);
+ 
+@@ -3396,4 +3396,3 @@ static pj_status_t tsx_on_state_destroye
+ 
+     return PJ_EIGNORED;
+ }
+-
+diff -uprN pjproject-2.6-a/pjsip/src/pjsip/sip_transport.c pjproject-2.6-b/pjsip/src/pjsip/sip_transport.c
+--- pjproject-2.6-a/pjsip/src/pjsip/sip_transport.c	2017-11-08 06:49:48.425246377 -0700
++++ pjproject-2.6-b/pjsip/src/pjsip/sip_transport.c	2017-11-08 06:54:01.534233008 -0700
+@@ -1836,7 +1836,7 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_p
+ 	/* Check for parsing syntax error */
+ 	if (msg==NULL || !pj_list_empty(&rdata->msg_info.parse_err)) {
+ 	    pjsip_parser_err_report *err;
+-	    char buf[128];
++	    char buf[256];
+ 	    pj_str_t tmp;
+ 
+ 	    /* Gather syntax error information */
+@@ -1850,7 +1850,10 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_p
+ 				       pj_exception_id_name(err->except_code),
+ 				       (int)err->hname.slen, err->hname.ptr,
+ 				       err->line, err->col);
+-		if (len > 0 && len < (int) (sizeof(buf)-tmp.slen)) {
++		if (len >= (int)sizeof(buf)-tmp.slen) {
++		    len = (int)sizeof(buf)-tmp.slen;
++		}
++		if (len > 0) {
+ 		    tmp.slen += len;
+ 		}
+ 		err = err->next;
diff --git a/third-party/pjproject/patches/config_site.h b/third-party/pjproject/patches/config_site.h
index a345734..561b3a2 100644
--- a/third-party/pjproject/patches/config_site.h
+++ b/third-party/pjproject/patches/config_site.h
@@ -68,7 +68,7 @@
   Enabling it will result in SEGFAULTS when URIs containing escape sequences are encountered.
 */
 #undef PJSIP_UNESCAPE_IN_PLACE
-#define PJSIP_MAX_PKT_LEN			6000
+#define PJSIP_MAX_PKT_LEN			32000
 
 #undef PJ_TODO
 #define PJ_TODO(x)
diff --git a/utils/astman.c b/utils/astman.c
index 315b3b0..9e0373c 100644
--- a/utils/astman.c
+++ b/utils/astman.c
@@ -289,7 +289,7 @@ static void rebuild_channels(newtComponent c)
 {
 	void *prev = NULL;
 	struct ast_chan *chan;
-	char tmpn[42];
+	char tmpn[sizeof(chan->name) + sizeof(chan->callerid) + 3 - 1];
 	char tmp[256];
 	int x=0;
 	prev = newtListboxGetCurrent(c);
diff --git a/utils/extconf.c b/utils/extconf.c
index 7989bcd..837971b 100644
--- a/utils/extconf.c
+++ b/utils/extconf.c
@@ -4590,6 +4590,10 @@ static struct ast_exten *pbx_find_extension(struct ast_channel *chan,
 	struct ast_exten *e, *eroot;
 	struct ast_include *i;
 
+	if (!context) {
+		return NULL;
+	}
+
 	/* Initialize status if appropriate */
 	if (q->stacklen == 0) {
 		q->status = STATUS_NO_CONTEXT;

-- 
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